@arizeai/phoenix-cli 1.5.3 → 1.6.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.
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import type { PxiChatClient, PxiMessage, PxiRuntimeOptions } from "./types.js";
3
+ export type PxiAppProps = {
4
+ options: PxiRuntimeOptions;
5
+ client?: PxiChatClient;
6
+ initialMessages?: PxiMessage[];
7
+ };
8
+ /** Animated "PXI is thinking…" indicator shown while a reply is streaming. */
9
+ export declare function ThinkingIndicator(): React.JSX.Element;
10
+ /**
11
+ * Root component for the PXI chat.
12
+ *
13
+ * Holds the conversation, draft input, streaming status, and any error, and
14
+ * wires keyboard handling: Enter submits, Shift+Enter inserts a newline, Esc
15
+ * interrupts an in-flight request, and Ctrl+C / Ctrl+D exit. On submit it appends the
16
+ * user message, streams the assistant reply into the transcript as it arrives,
17
+ * and ignores errors caused by the user aborting. The `client` and
18
+ * `initialMessages` props exist mainly so tests can drive the UI with a fake
19
+ * client and seeded history.
20
+ */
21
+ export declare function PxiApp({ options, client, initialMessages }: PxiAppProps): React.JSX.Element;
22
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/pxi/App.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAKpE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAoC5E,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,UAAU,EAAE,CAAC;CAChC,CAAC;AAmNF,8EAA8E;AAC9E,wBAAgB,iBAAiB,sBAWhC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,eAAoB,EAAE,EAAE,WAAW,qBA2I5E"}
@@ -0,0 +1,268 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useApp, useInput } from "ink";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { createPxiChatClient, createUserMessage } from "./client.js";
5
+ import { Markdown } from "./inkMarkdown.js";
6
+ import { getToolProgressFromPart } from "./toolProgress.js";
7
+ const PXI_BANNER = String.raw `
8
+ __/\\\\\\\\\\\\\____/\\\_______/\\\__/\\\\\\\\\\\_
9
+ _\/\\\/////////\\\_\///\\\___/\\\/__\/////\\\///__
10
+ _\/\\\_______\/\\\___\///\\\\\\/________\/\\\_____
11
+ _\/\\\\\\\\\\\\\/______\//\\\\__________\/\\\_____
12
+ _\/\\\/////////_________\/\\\\__________\/\\\_____
13
+ _\/\\\__________________/\\\\\\_________\/\\\_____
14
+ _\/\\\________________/\\\////\\\_______\/\\\_____
15
+ _\/\\\______________/\\\/___\///\\\__/\\\\\\\\\\\_
16
+ _\///______________\///_______\///__\///////////__
17
+ `;
18
+ const THINKING_FRAMES = [
19
+ "PXI is thinking ",
20
+ "PXI is thinking. ",
21
+ "PXI is thinking.. ",
22
+ "PXI is thinking...",
23
+ ];
24
+ const KEYBOARD_PROTOCOL_RESPONSE_PATTERN = /^\[\?\d+u$/;
25
+ const INTERRUPTED_MESSAGE_TEXT = "\n\n[Interrupted by user before completion.]";
26
+ // The 3D banner is drawn with two kinds of strokes: the `\` runs form the
27
+ // raised faces of the letters, while `/` and `_` are the recessed shading.
28
+ // Group each line into runs so the raised strokes can be colored distinctly.
29
+ function getBannerSegments(line) {
30
+ const segments = [];
31
+ for (const char of line) {
32
+ const raised = char === "\\";
33
+ const last = segments[segments.length - 1];
34
+ if (last && last.raised === raised) {
35
+ last.text += char;
36
+ }
37
+ else {
38
+ segments.push({ text: char, raised });
39
+ }
40
+ }
41
+ return segments;
42
+ }
43
+ /** The PXI wordmark, colored so the raised letter faces stand out. */
44
+ function PxiBanner() {
45
+ const lines = PXI_BANNER.replace(/^\n|\n$/g, "").split("\n");
46
+ return (_jsx(Box, { flexDirection: "column", marginY: 1, children: lines.map((line, lineIndex) => (_jsx(Text, { children: getBannerSegments(line).map((segment, index) => (_jsx(Text, {
47
+ // `blueBright` + bold reads clearly on both dark and light
48
+ // terminal backgrounds, where plain `blue` washes out.
49
+ color: segment.raised ? "blueBright" : "gray", bold: segment.raised, children: segment.text }, index))) }, lineIndex))) }));
50
+ }
51
+ /** Format the active model for the status line (e.g. `ANTHROPIC/claude-opus-4-8`). */
52
+ function getModelLabel(options) {
53
+ if (options.modelSelection.providerType === "custom") {
54
+ return `custom:${options.modelSelection.providerId}/${options.modelSelection.modelName}`;
55
+ }
56
+ return `${options.modelSelection.provider}/${options.modelSelection.modelName}`;
57
+ }
58
+ /** Render a single tool call inline in the transcript, coloring errors red. */
59
+ function InlineToolProgress({ tool }) {
60
+ const statusColor = tool.state === "output-error" ? "red" : "yellow";
61
+ return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingLeft: 2, children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "[tool]" }), " ", _jsx(Text, { color: statusColor, children: tool.toolName }), " ", _jsx(Text, { color: statusColor, children: tool.statusText })] }), tool.errorText ? _jsx(Text, { color: "red", children: tool.errorText }) : null] }));
62
+ }
63
+ /**
64
+ * Render the ordered parts of one message: text parts as markdown, tool parts
65
+ * as inline progress, skipping anything unrecognized.
66
+ */
67
+ function MessageParts({ message, phoenixBaseUrl, }) {
68
+ return (_jsx(Box, { flexDirection: "column", children: message.parts.map((part, index) => {
69
+ if (part.type === "text") {
70
+ return (_jsx(Markdown, { phoenixBaseUrl: phoenixBaseUrl, children: part.text }, `${message.id}-text-${index}`));
71
+ }
72
+ const tool = getToolProgressFromPart({ part });
73
+ if (tool) {
74
+ return _jsx(InlineToolProgress, { tool: tool }, tool.toolCallId);
75
+ }
76
+ return null;
77
+ }) }));
78
+ }
79
+ /**
80
+ * Render the whole conversation as labeled, color-coded turns ("You" vs "PXI"),
81
+ * or a placeholder when the conversation hasn't started.
82
+ */
83
+ function Transcript({ messages, phoenixBaseUrl, }) {
84
+ if (messages.length === 0) {
85
+ return _jsx(Text, { dimColor: true, children: "Phoenix Intelligence." });
86
+ }
87
+ return (_jsx(Box, { flexDirection: "column", children: messages.map((message) => {
88
+ const label = message.role === "user" ? "You" : "PXI";
89
+ const color = message.role === "user" ? "cyan" : "green";
90
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: color, bold: true, children: label }), _jsx(MessageParts, { message: message, phoenixBaseUrl: phoenixBaseUrl })] }, message.id));
91
+ }) }));
92
+ }
93
+ /** Render the prompt row with helper text below it. */
94
+ function InputPrompt({ draft, status }) {
95
+ const cursor = status === "streaming" ? "" : "█";
96
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 1, children: [_jsx(Box, { borderStyle: "single", borderLeft: false, borderRight: false, borderTop: true, borderBottom: true, borderColor: "gray", children: _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "> " }), draft, cursor] }) }), _jsx(Text, { dimColor: true, children: "Enter sends. Shift+Enter inserts a newline. Esc interrupts a reply. Ctrl+D or Ctrl+C exits." })] }));
97
+ }
98
+ function isKeyboardProtocolResponseInput({ input }) {
99
+ return KEYBOARD_PROTOCOL_RESPONSE_PATTERN.test(input);
100
+ }
101
+ function getCompletedInterruptedPart({ part, }) {
102
+ if (part.type === "text" || part.type === "reasoning") {
103
+ return { ...part, state: "done" };
104
+ }
105
+ if (part.type === "dynamic-tool") {
106
+ return part.state === "output-available" ||
107
+ part.state === "output-error" ||
108
+ part.state === "output-denied"
109
+ ? part
110
+ : null;
111
+ }
112
+ if ("state" in part &&
113
+ typeof part.type === "string" &&
114
+ part.type.startsWith("tool-")) {
115
+ return part.state === "output-available" ||
116
+ part.state === "output-error" ||
117
+ part.state === "output-denied"
118
+ ? part
119
+ : null;
120
+ }
121
+ return part;
122
+ }
123
+ function markMessageInterrupted({ message, }) {
124
+ return {
125
+ ...message,
126
+ parts: [
127
+ ...message.parts.flatMap((part) => {
128
+ const completedPart = getCompletedInterruptedPart({ part });
129
+ return completedPart ? [completedPart] : [];
130
+ }),
131
+ { type: "text", text: INTERRUPTED_MESSAGE_TEXT, state: "done" },
132
+ ],
133
+ };
134
+ }
135
+ /** Animated "PXI is thinking…" indicator shown while a reply is streaming. */
136
+ export function ThinkingIndicator() {
137
+ const [frameIndex, setFrameIndex] = useState(0);
138
+ useEffect(() => {
139
+ const interval = setInterval(() => {
140
+ setFrameIndex((value) => (value + 1) % THINKING_FRAMES.length);
141
+ }, 250);
142
+ return () => clearInterval(interval);
143
+ }, []);
144
+ return _jsx(Text, { color: "yellow", children: THINKING_FRAMES[frameIndex] });
145
+ }
146
+ /**
147
+ * Root component for the PXI chat.
148
+ *
149
+ * Holds the conversation, draft input, streaming status, and any error, and
150
+ * wires keyboard handling: Enter submits, Shift+Enter inserts a newline, Esc
151
+ * interrupts an in-flight request, and Ctrl+C / Ctrl+D exit. On submit it appends the
152
+ * user message, streams the assistant reply into the transcript as it arrives,
153
+ * and ignores errors caused by the user aborting. The `client` and
154
+ * `initialMessages` props exist mainly so tests can drive the UI with a fake
155
+ * client and seeded history.
156
+ */
157
+ export function PxiApp({ options, client, initialMessages = [] }) {
158
+ const { exit } = useApp();
159
+ const [messages, setMessages] = useState(initialMessages);
160
+ const [draft, setDraft] = useState("");
161
+ const [status, setStatus] = useState("idle");
162
+ const [error, setError] = useState(null);
163
+ const abortControllerRef = useRef(null);
164
+ const streamingAssistantMessageRef = useRef(null);
165
+ const chatClient = useMemo(() => client ?? createPxiChatClient({ options }), [client, options]);
166
+ const handleExit = () => {
167
+ abortControllerRef.current?.abort();
168
+ exit();
169
+ };
170
+ const interruptStream = () => {
171
+ if (status !== "streaming") {
172
+ return;
173
+ }
174
+ abortControllerRef.current?.abort();
175
+ const assistantMessage = streamingAssistantMessageRef.current;
176
+ if (assistantMessage) {
177
+ const interruptedMessage = markMessageInterrupted({
178
+ message: assistantMessage,
179
+ });
180
+ streamingAssistantMessageRef.current = interruptedMessage;
181
+ setMessages((currentMessages) => {
182
+ const lastMessage = currentMessages.at(-1);
183
+ if (lastMessage?.id === assistantMessage.id) {
184
+ return [...currentMessages.slice(0, -1), interruptedMessage];
185
+ }
186
+ return [...currentMessages, interruptedMessage];
187
+ });
188
+ }
189
+ setStatus("idle");
190
+ };
191
+ const submitDraft = () => {
192
+ const text = draft.trim();
193
+ if (!text || status === "streaming") {
194
+ return;
195
+ }
196
+ const userMessage = createUserMessage({ text });
197
+ const nextMessages = [...messages, userMessage];
198
+ const abortController = new AbortController();
199
+ abortControllerRef.current = abortController;
200
+ streamingAssistantMessageRef.current = null;
201
+ setDraft("");
202
+ setError(null);
203
+ setStatus("streaming");
204
+ setMessages(nextMessages);
205
+ void chatClient
206
+ .sendMessage({
207
+ messages: nextMessages,
208
+ abortSignal: abortController.signal,
209
+ onAssistantMessage: (assistantMessage) => {
210
+ streamingAssistantMessageRef.current = assistantMessage;
211
+ setMessages([...nextMessages, assistantMessage]);
212
+ },
213
+ })
214
+ .then((assistantMessage) => {
215
+ if (abortController.signal.aborted) {
216
+ return;
217
+ }
218
+ if (assistantMessage) {
219
+ setMessages([...nextMessages, assistantMessage]);
220
+ }
221
+ })
222
+ .catch((err) => {
223
+ if (abortController.signal.aborted) {
224
+ return;
225
+ }
226
+ setError(err instanceof Error ? err.message : String(err));
227
+ })
228
+ .finally(() => {
229
+ if (abortControllerRef.current === abortController) {
230
+ abortControllerRef.current = null;
231
+ }
232
+ setStatus("idle");
233
+ });
234
+ };
235
+ useInput((input, key) => {
236
+ if (key.escape) {
237
+ interruptStream();
238
+ return;
239
+ }
240
+ if ((key.ctrl && input === "c") || (key.ctrl && input === "d")) {
241
+ handleExit();
242
+ return;
243
+ }
244
+ if (status === "streaming") {
245
+ return;
246
+ }
247
+ if (isKeyboardProtocolResponseInput({ input })) {
248
+ return;
249
+ }
250
+ if (key.return && key.shift) {
251
+ setDraft((value) => `${value}\n`);
252
+ return;
253
+ }
254
+ if (key.return) {
255
+ submitDraft();
256
+ return;
257
+ }
258
+ if (key.backspace || key.delete) {
259
+ setDraft((value) => value.slice(0, -1));
260
+ return;
261
+ }
262
+ if (input) {
263
+ setDraft((value) => `${value}${input}`);
264
+ }
265
+ });
266
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(PxiBanner, {}), _jsxs(Text, { dimColor: true, children: ["endpoint: ", options.config.endpoint, " | model: ", getModelLabel(options), " | session: ", options.sessionId] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Transcript, { messages: messages, phoenixBaseUrl: options.config.endpoint }) }), error ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) })) : null, status === "streaming" ? _jsx(ThinkingIndicator, {}) : null, _jsx(InputPrompt, { draft: draft, status: status })] }));
267
+ }
268
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/pxi/App.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type UIMessageChunk } from "ai";
2
+ import type { PhoenixConfig } from "../config.js";
3
+ import type { PxiChatClient, PxiChatRequest, PxiContext, PxiMessage, PxiRuntimeOptions, PxiTransport } from "./types.js";
4
+ /**
5
+ * Build the chat URL for a session, tolerating a trailing slash on the
6
+ * configured endpoint and URL-encoding the session id.
7
+ */
8
+ export declare function buildServerAgentChatUrl({ endpoint, sessionId, }: {
9
+ endpoint: string;
10
+ sessionId: string;
11
+ }): string;
12
+ /**
13
+ * Assemble request headers from the config: any custom headers first, then a
14
+ * bearer `Authorization` header when an API key is set (so the key takes
15
+ * precedence over a manually supplied auth header).
16
+ */
17
+ export declare function buildPxiHeaders({ config, }: {
18
+ config: PhoenixConfig;
19
+ }): Record<string, string>;
20
+ /**
21
+ * Build the {@link PxiContext} list sent with every request, telling the server
22
+ * agent the current local time and zone and which capabilities (GraphQL
23
+ * mutations, web access, subagents) are enabled for the run. `now` and
24
+ * `timeZone` default to the live clock/zone but are injectable for testing.
25
+ */
26
+ export declare function buildPxiContexts({ enableWebAccess, enableSubagents, enableGraphqlMutations, now, timeZone, }: {
27
+ enableWebAccess: boolean;
28
+ enableSubagents: boolean;
29
+ enableGraphqlMutations: boolean;
30
+ now?: Date;
31
+ timeZone?: string;
32
+ }): PxiContext[];
33
+ /**
34
+ * Assemble the full chat request body from the conversation so far and the
35
+ * resolved runtime options — session id, trace settings, edit permission,
36
+ * capability contexts, and model selection.
37
+ */
38
+ export declare function buildPxiChatRequest({ messages, options, }: {
39
+ messages: PxiMessage[];
40
+ options: PxiRuntimeOptions;
41
+ }): PxiChatRequest;
42
+ /**
43
+ * Create the AI SDK transport pointed at the configured Phoenix server-agent
44
+ * endpoint. Each outgoing turn is rebuilt through {@link buildPxiChatRequest},
45
+ * so per-request context (like the current time) stays fresh. Throws if no
46
+ * endpoint is configured. `fetch` is injectable for testing.
47
+ */
48
+ export declare function createServerAgentTransport({ options, fetch, }: {
49
+ options: PxiRuntimeOptions;
50
+ fetch?: typeof globalThis.fetch;
51
+ }): PxiTransport;
52
+ /**
53
+ * Consume a UI-message chunk stream, invoking `onAssistantMessage` with each
54
+ * progressively-accumulated snapshot so the UI can render the reply as it
55
+ * arrives. Resolves with the final, complete message (or `null` if the stream
56
+ * produced nothing).
57
+ */
58
+ export declare function streamAssistantMessage({ stream, onAssistantMessage, }: {
59
+ stream: ReadableStream<UIMessageChunk>;
60
+ onAssistantMessage: (message: PxiMessage) => void;
61
+ }): Promise<PxiMessage | null>;
62
+ /**
63
+ * Create the {@link PxiChatClient} the UI talks to. It sends the conversation
64
+ * over the transport, streams the assistant reply back, and on failure wraps
65
+ * the error via {@link formatPxiRuntimeError} so the user sees an actionable
66
+ * message (e.g. how to fix missing credentials). The transport defaults to a
67
+ * real server-agent transport but is injectable for testing.
68
+ */
69
+ export declare function createPxiChatClient({ options, transport, }: {
70
+ options: PxiRuntimeOptions;
71
+ transport?: PxiTransport;
72
+ }): PxiChatClient;
73
+ /** Wrap raw user input in a {@link PxiMessage} with a fresh id. */
74
+ export declare function createUserMessage({ text }: {
75
+ text: string;
76
+ }): PxiMessage;
77
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/pxi/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,IAAI,CAAC;AAEZ,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,YAAY,EACb,MAAM,SAAS,CAAC;AAsCjB;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAIT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,GACP,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKzB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,GAAgB,EAChB,QAA2D,GAC5D,EAAE;IACD,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,UAAU,EAAE,CAoBf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;CAC5B,GAAG,cAAc,CAgBjB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,OAAO,EACP,KAAK,GACN,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC,GAAG,YAAY,CAgBf;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,MAAM,EACN,kBAAkB,GACnB,EAAE;IACD,MAAM,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IACvC,kBAAkB,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;CACnD,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAO7B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,SAAmD,GACpD,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B,GAAG,aAAa,CAoBhB;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAMxE"}
@@ -0,0 +1,170 @@
1
+ import { DefaultChatTransport, readUIMessageStream, } from "ai";
2
+ import { formatPxiRuntimeError } from "./preflight.js";
3
+ /**
4
+ * Chat client for the Phoenix server-agent endpoint.
5
+ *
6
+ * This wires the Vercel AI SDK's {@link DefaultChatTransport} to Phoenix's
7
+ * `/agents/server/sessions/{id}/chat` route: it builds the request URL, auth
8
+ * headers, and request body, then streams the assistant reply back as a series
9
+ * of {@link PxiMessage} snapshots.
10
+ */
11
+ function trimTrailingSlash(value) {
12
+ return value.replace(/\/+$/, "");
13
+ }
14
+ /**
15
+ * Format a date as a local ISO-8601 timestamp with an explicit UTC offset
16
+ * (e.g. `2026-06-25T13:45:00.000+02:00`). Unlike `Date#toISOString`, which
17
+ * always emits UTC, this preserves the caller's wall-clock time and zone so the
18
+ * agent reasons about "now" the way the user experiences it.
19
+ */
20
+ function toLocalISOWithOffset(date) {
21
+ const offsetMinutes = -date.getTimezoneOffset();
22
+ const sign = offsetMinutes >= 0 ? "+" : "-";
23
+ const absoluteOffsetMinutes = Math.abs(offsetMinutes);
24
+ const offsetHours = String(Math.floor(absoluteOffsetMinutes / 60)).padStart(2, "0");
25
+ const offsetRemainderMinutes = String(absoluteOffsetMinutes % 60).padStart(2, "0");
26
+ const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
27
+ const localIso = localDate.toISOString().slice(0, -1);
28
+ return `${localIso}${sign}${offsetHours}:${offsetRemainderMinutes}`;
29
+ }
30
+ /**
31
+ * Build the chat URL for a session, tolerating a trailing slash on the
32
+ * configured endpoint and URL-encoding the session id.
33
+ */
34
+ export function buildServerAgentChatUrl({ endpoint, sessionId, }) {
35
+ return `${trimTrailingSlash(endpoint)}/agents/server/sessions/${encodeURIComponent(sessionId)}/chat`;
36
+ }
37
+ /**
38
+ * Assemble request headers from the config: any custom headers first, then a
39
+ * bearer `Authorization` header when an API key is set (so the key takes
40
+ * precedence over a manually supplied auth header).
41
+ */
42
+ export function buildPxiHeaders({ config, }) {
43
+ return {
44
+ ...(config.headers ?? {}),
45
+ ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
46
+ };
47
+ }
48
+ /**
49
+ * Build the {@link PxiContext} list sent with every request, telling the server
50
+ * agent the current local time and zone and which capabilities (GraphQL
51
+ * mutations, web access, subagents) are enabled for the run. `now` and
52
+ * `timeZone` default to the live clock/zone but are injectable for testing.
53
+ */
54
+ export function buildPxiContexts({ enableWebAccess, enableSubagents, enableGraphqlMutations, now = new Date(), timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone, }) {
55
+ return [
56
+ {
57
+ type: "app",
58
+ currentDateTime: toLocalISOWithOffset(now),
59
+ timeZone,
60
+ },
61
+ {
62
+ type: "graphql",
63
+ mutationsEnabled: enableGraphqlMutations,
64
+ },
65
+ {
66
+ type: "web_access",
67
+ enabled: enableWebAccess,
68
+ },
69
+ {
70
+ type: "subagents",
71
+ enabled: enableSubagents,
72
+ },
73
+ ];
74
+ }
75
+ /**
76
+ * Assemble the full chat request body from the conversation so far and the
77
+ * resolved runtime options — session id, trace settings, edit permission,
78
+ * capability contexts, and model selection.
79
+ */
80
+ export function buildPxiChatRequest({ messages, options, }) {
81
+ return {
82
+ id: options.sessionId,
83
+ messages,
84
+ trigger: "submit-message",
85
+ ingestTraces: options.ingestTraces,
86
+ exportRemoteTraces: options.exportRemoteTraces,
87
+ attachUserId: options.attachUserId,
88
+ editPermission: options.editPermission,
89
+ contexts: buildPxiContexts({
90
+ enableWebAccess: options.enableWebAccess,
91
+ enableSubagents: options.enableSubagents,
92
+ enableGraphqlMutations: options.enableGraphqlMutations,
93
+ }),
94
+ model: options.modelSelection,
95
+ };
96
+ }
97
+ /**
98
+ * Create the AI SDK transport pointed at the configured Phoenix server-agent
99
+ * endpoint. Each outgoing turn is rebuilt through {@link buildPxiChatRequest},
100
+ * so per-request context (like the current time) stays fresh. Throws if no
101
+ * endpoint is configured. `fetch` is injectable for testing.
102
+ */
103
+ export function createServerAgentTransport({ options, fetch, }) {
104
+ if (!options.config.endpoint) {
105
+ throw new Error("Phoenix endpoint not configured.");
106
+ }
107
+ return new DefaultChatTransport({
108
+ api: buildServerAgentChatUrl({
109
+ endpoint: options.config.endpoint,
110
+ sessionId: options.sessionId,
111
+ }),
112
+ headers: buildPxiHeaders({ config: options.config }),
113
+ fetch,
114
+ prepareSendMessagesRequest: ({ messages }) => ({
115
+ body: buildPxiChatRequest({ messages, options }),
116
+ }),
117
+ });
118
+ }
119
+ /**
120
+ * Consume a UI-message chunk stream, invoking `onAssistantMessage` with each
121
+ * progressively-accumulated snapshot so the UI can render the reply as it
122
+ * arrives. Resolves with the final, complete message (or `null` if the stream
123
+ * produced nothing).
124
+ */
125
+ export async function streamAssistantMessage({ stream, onAssistantMessage, }) {
126
+ let finalMessage = null;
127
+ for await (const message of readUIMessageStream({ stream })) {
128
+ finalMessage = message;
129
+ onAssistantMessage(message);
130
+ }
131
+ return finalMessage;
132
+ }
133
+ /**
134
+ * Create the {@link PxiChatClient} the UI talks to. It sends the conversation
135
+ * over the transport, streams the assistant reply back, and on failure wraps
136
+ * the error via {@link formatPxiRuntimeError} so the user sees an actionable
137
+ * message (e.g. how to fix missing credentials). The transport defaults to a
138
+ * real server-agent transport but is injectable for testing.
139
+ */
140
+ export function createPxiChatClient({ options, transport = createServerAgentTransport({ options }), }) {
141
+ return {
142
+ async sendMessage({ messages, abortSignal, onAssistantMessage }) {
143
+ try {
144
+ const stream = await transport.sendMessages({
145
+ trigger: "submit-message",
146
+ chatId: options.sessionId,
147
+ messageId: undefined,
148
+ messages,
149
+ abortSignal,
150
+ });
151
+ return await streamAssistantMessage({ stream, onAssistantMessage });
152
+ }
153
+ catch (error) {
154
+ throw formatPxiRuntimeError({
155
+ error,
156
+ modelSelection: options.modelSelection,
157
+ });
158
+ }
159
+ },
160
+ };
161
+ }
162
+ /** Wrap raw user input in a {@link PxiMessage} with a fresh id. */
163
+ export function createUserMessage({ text }) {
164
+ return {
165
+ id: crypto.randomUUID(),
166
+ role: "user",
167
+ parts: [{ type: "text", text }],
168
+ };
169
+ }
170
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/pxi/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry point for the `pxi` command.
4
+ *
5
+ * Parses CLI flags into runtime options, runs the model preflight (verifying
6
+ * the chosen provider/model is available and credentialed on the Phoenix
7
+ * server) before anything is drawn, then mounts the Ink chat UI and blocks
8
+ * until the user exits. Preflight runs first so configuration problems surface
9
+ * as a clean error instead of a half-rendered terminal.
10
+ */
11
+ export declare function main({ argv, }?: {
12
+ argv?: string[];
13
+ }): Promise<void>;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pxi/index.tsx"],"names":[],"mappings":";AAWA;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,EACzB,IAAmB,GACpB,GAAE;IACD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrB"}
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { render } from "ink";
4
+ import { getExitCodeForError } from "../exitCodes.js";
5
+ import { writeError } from "../io.js";
6
+ import { PxiApp } from "./App.js";
7
+ import { parsePxiRuntimeOptions } from "./options.js";
8
+ import { runPxiModelPreflight } from "./preflight.js";
9
+ /**
10
+ * Entry point for the `pxi` command.
11
+ *
12
+ * Parses CLI flags into runtime options, runs the model preflight (verifying
13
+ * the chosen provider/model is available and credentialed on the Phoenix
14
+ * server) before anything is drawn, then mounts the Ink chat UI and blocks
15
+ * until the user exits. Preflight runs first so configuration problems surface
16
+ * as a clean error instead of a half-rendered terminal.
17
+ */
18
+ export async function main({ argv = process.argv, } = {}) {
19
+ const options = await parsePxiRuntimeOptions({ argv });
20
+ await runPxiModelPreflight({ options });
21
+ const instance = render(_jsx(PxiApp, { options: options }), {
22
+ exitOnCtrlC: false,
23
+ kittyKeyboard: {
24
+ mode: "auto",
25
+ flags: ["disambiguateEscapeCodes"],
26
+ },
27
+ });
28
+ await instance.waitUntilExit();
29
+ }
30
+ void main().catch((error) => {
31
+ writeError({
32
+ message: `Error: ${error instanceof Error ? error.message : String(error)}`,
33
+ });
34
+ process.exit(getExitCodeForError(error));
35
+ });
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pxi/index.tsx"],"names":[],"mappings":";;AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAG7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,EACzB,IAAI,GAAG,OAAO,CAAC,IAAI,MAGjB,EAAE;IACJ,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAC,MAAM,IAAC,OAAO,EAAE,OAAO,GAAI,EAAE;QACpD,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE;YACb,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,CAAC,yBAAyB,CAAC;SACnC;KACF,CAAC,CAAC;IACH,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;AACjC,CAAC;AAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,UAAU,CAAC;QACT,OAAO,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;KAC5E,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ /**
3
+ * Renders markdown text as styled terminal output inside an Ink `<Text>`.
4
+ *
5
+ * Modeled on the `ink-markdown` component
6
+ * (https://github.com/cameronhunter/ink-markdown, MIT). That package is
7
+ * published as CommonJS and `require()`s Ink internally, which is incompatible
8
+ * with Ink 6 (pure ESM with top-level await — `require()` throws
9
+ * `ERR_REQUIRE_ASYNC_MODULE`). We keep its API — `<Markdown>{text}</Markdown>` —
10
+ * but render through {@link formatMarkdownForTerminal}, which uses the current
11
+ * `marked` + `marked-terminal` API and preserves our width-aware table layout.
12
+ */
13
+ export declare function Markdown({ children, phoenixBaseUrl, }: {
14
+ children: string;
15
+ phoenixBaseUrl?: string;
16
+ }): React.JSX.Element;
17
+ //# sourceMappingURL=inkMarkdown.d.ts.map