@hachej/boring-agent 0.1.34 → 0.1.35

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/README.md CHANGED
@@ -285,7 +285,7 @@ A: Yes. Use the `CatalogDeps` pattern to build tools that bind to `Workspace` an
285
285
  A: It lets the agent programmatically open files, panels, and surfaces in the workbench. The agent calls `exec_ui({ kind: "openFile", params: { path: "src/index.ts" } })` and the panel opens. It's a typed pubsub bus between backend and frontend.
286
286
 
287
287
  **Q: How does stream resumption work?**
288
- A: The server wraps the harness's event stream in a per-turn ring buffer. Disconnected clients reconnect via `GET /api/v1/agent/chat/:sessionId/:turnId?cursor=<n>`. If the turn completed, it replays from `SessionStore`.
288
+ A: The server wraps the harness's event stream in a per-turn ring buffer. Disconnected clients reconnect via `GET /api/v1/agent/pi-chat/:sessionId/events?from=<seq>`; the replay buffer serves missed events.
289
289
 
290
290
  ---
291
291
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DebugDrawer
3
- } from "./chunk-UTUXVT2K.js";
3
+ } from "./chunk-IUJ22EFB.js";
4
4
  export {
5
5
  DebugDrawer
6
6
  };
@@ -1,3 +1,101 @@
1
+ import { b as SessionStore } from './session-BRovhe0D.js';
2
+
3
+ interface TelemetrySink {
4
+ capture(event: TelemetryEvent): void | Promise<void>;
5
+ flush?(): void | Promise<void>;
6
+ }
7
+ interface TelemetryEvent {
8
+ name: string;
9
+ distinctId?: string;
10
+ properties?: Record<string, unknown>;
11
+ }
12
+ declare const noopTelemetry: TelemetrySink;
13
+ declare function safeCapture(telemetry: TelemetrySink, event: TelemetryEvent): void;
14
+
15
+ type JSONSchema = Record<string, unknown>;
16
+ type ToolReadinessRequirement = 'workspace-fs' | 'sandbox-exec' | 'ui-bridge' | 'runtime-dependencies' | `runtime:${string}`;
17
+ interface AgentTool {
18
+ name: string;
19
+ description: string;
20
+ /** Optional one-line prompt entry. Pi-built tools should preserve pi's snippet verbatim. */
21
+ promptSnippet?: string;
22
+ readinessRequirements?: ToolReadinessRequirement[];
23
+ parameters: JSONSchema;
24
+ execute(params: Record<string, unknown>, ctx: ToolExecContext): Promise<ToolResult>;
25
+ }
26
+ interface ToolExecContext {
27
+ abortSignal: AbortSignal;
28
+ toolCallId: string;
29
+ onUpdate?: (partial: string) => void;
30
+ /** Agent chat/session id executing this tool, when known. */
31
+ sessionId?: string;
32
+ }
33
+ interface ToolResult {
34
+ content: Array<{
35
+ type: 'text';
36
+ text: string;
37
+ }>;
38
+ isError?: boolean;
39
+ details?: unknown;
40
+ }
41
+
42
+ interface AgentHarnessFactoryInput {
43
+ tools: AgentTool[];
44
+ /** Host/storage cwd used for harness-owned filesystem resources. */
45
+ cwd: string;
46
+ /** Agent-visible cwd used by Pi/system prompt/session metadata. */
47
+ runtimeCwd?: string;
48
+ systemPromptAppend?: string;
49
+ sessionNamespace?: string;
50
+ sessionDir?: string;
51
+ /**
52
+ * Optional dynamic system-prompt source. Harness calls it whenever it
53
+ * builds or rebuilds a session prompt and appends the returned string.
54
+ * Workspace plugin layer wires this so live-reloaded plugins can contribute
55
+ * prompt context without a workspace-injected harness extension.
56
+ */
57
+ systemPromptDynamic?: () => string | undefined | Promise<string | undefined>;
58
+ /** Host-provided telemetry sink. Optional and best-effort; harnesses may ignore it. */
59
+ telemetry?: TelemetrySink;
60
+ }
61
+ type AgentHarnessFactory = (input: AgentHarnessFactoryInput) => AgentHarness | Promise<AgentHarness>;
62
+ interface AgentHarness {
63
+ readonly id: string;
64
+ readonly placement: 'server' | 'browser';
65
+ /** Session lifecycle; may delegate to an underlying runtime (e.g. pi's JSONL). */
66
+ sessions: SessionStore;
67
+ /**
68
+ * Resolved system prompt currently in effect for `sessionId`. Returns
69
+ * `undefined` when the underlying runtime hasn't yet instantiated a
70
+ * session (typical pre-first-turn state — pi creates lazily on the first
71
+ * prompt). Optional so non-pi harnesses can opt out cleanly.
72
+ */
73
+ getSystemPrompt?: (sessionId: string) => string | undefined;
74
+ /** Reload native agent resources/extensions for an existing session. */
75
+ reloadSession?: (sessionId: string) => Promise<boolean>;
76
+ }
77
+ interface MessageAttachment {
78
+ filename?: string;
79
+ mediaType?: string;
80
+ /** data: URL (base64) or remote URL */
81
+ url: string;
82
+ }
83
+ interface SendMessageInput {
84
+ sessionId: string;
85
+ message: string;
86
+ thinkingLevel?: 'off' | 'low' | 'medium' | 'high';
87
+ model?: {
88
+ provider: string;
89
+ id: string;
90
+ };
91
+ attachments?: MessageAttachment[];
92
+ }
93
+ interface RunContext {
94
+ abortSignal: AbortSignal;
95
+ workdir: string;
96
+ userId?: string;
97
+ }
98
+
1
99
  interface WorkspaceRuntimeContext {
2
100
  /** Agent-visible working directory shared by file-tree and shell execution. */
3
101
  readonly runtimeCwd: string;
@@ -297,4 +395,4 @@ interface PluginRestartWarning {
297
395
  message: string;
298
396
  }
299
397
 
300
- export { type Entry as E, type FileSearch as F, type IsolatedCodeInput as I, type PluginRestartWarning as P, type Sandbox as S, type Workspace as W, type ExecOptions as a, type ExecResult as b, type IsolatedCodeOutput as c, type SandboxCapability as d, type SandboxHandleRecord as e, type SandboxHandleStore as f, type Stat as g, WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT as h, type WorkspaceRuntimeContext as i };
398
+ export { type AgentTool as A, type Entry as E, type FileSearch as F, type IsolatedCodeInput as I, type JSONSchema as J, type PluginRestartWarning as P, type RunContext as R, type Sandbox as S, type TelemetryEvent as T, type Workspace as W, type AgentHarness as a, type ExecOptions as b, type ExecResult as c, type IsolatedCodeOutput as d, type SandboxCapability as e, type SandboxHandleRecord as f, type SandboxHandleStore as g, type SendMessageInput as h, type Stat as i, type TelemetrySink as j, type ToolExecContext as k, type ToolResult as l, WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT as m, type WorkspaceRuntimeContext as n, noopTelemetry as o, type AgentHarnessFactory as p, type AgentHarnessFactoryInput as q, safeCapture as s };
@@ -74,7 +74,7 @@ function CopyButton({ value, label }) {
74
74
  function DebugValue({ label, value }) {
75
75
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border/40 bg-muted/20 p-2", children: [
76
76
  /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
77
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-[0.16em] text-muted-foreground/80", children: label }),
77
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: label }),
78
78
  /* @__PURE__ */ jsx(CopyButton, { value, label: `Copy ${label}` })
79
79
  ] }),
80
80
  /* @__PURE__ */ jsx("code", { className: "block break-all font-mono text-[11px] leading-relaxed text-foreground dark:text-zinc-100", children: value })
@@ -86,7 +86,7 @@ function SessionTab({ sessionId }) {
86
86
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground dark:text-zinc-300", children: "This web chat is backed by a pi session. Use the id below from the workspace root to resume the same conversation in a terminal." }),
87
87
  /* @__PURE__ */ jsx(DebugValue, { label: "Pi session id", value: sessionId }),
88
88
  /* @__PURE__ */ jsx(DebugValue, { label: "Resume command", value: resumeCommand }),
89
- /* @__PURE__ */ jsxs("p", { className: "rounded-md border border-border/30 bg-muted/10 p-2 text-[10px] leading-relaxed text-muted-foreground/75 dark:text-zinc-300", children: [
89
+ /* @__PURE__ */ jsxs("p", { className: "rounded-md border border-border/30 bg-muted/10 p-2 text-[10px] leading-relaxed text-muted-foreground dark:text-zinc-300", children: [
90
90
  "Tip: ",
91
91
  /* @__PURE__ */ jsx("code", { className: "font-mono text-foreground/80", children: "pi --continue" }),
92
92
  " ",
@@ -97,8 +97,11 @@ function SessionTab({ sessionId }) {
97
97
  var RETRY_DELAY_MS = 2500;
98
98
  var MAX_RETRIES = 20;
99
99
  function SystemPromptTab({
100
+ apiBaseUrl,
101
+ fetch: fetchImpl,
100
102
  sessionId,
101
- requestHeaders
103
+ requestHeaders,
104
+ storageScope
102
105
  }) {
103
106
  const [state, setState] = useState({ kind: "loading" });
104
107
  const [retryKey, setRetryKey] = useState(0);
@@ -111,8 +114,10 @@ function SystemPromptTab({
111
114
  let aborted = false;
112
115
  let retryTimer = null;
113
116
  setState({ kind: "loading" });
114
- const opts = requestHeaders ? { headers: requestHeaders } : void 0;
115
- fetch(`/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/system-prompt`, opts).then(async (res) => {
117
+ const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
118
+ nextFetch(agentResourceUrl(apiBaseUrl, `/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/system-prompt`), {
119
+ headers: scopedHeaders(requestHeaders, storageScope)
120
+ }).then(async (res) => {
116
121
  if (aborted) return;
117
122
  if (res.ok) {
118
123
  const payload2 = await res.json();
@@ -142,7 +147,7 @@ function SystemPromptTab({
142
147
  aborted = true;
143
148
  if (retryTimer) clearTimeout(retryTimer);
144
149
  };
145
- }, [sessionId, requestHeaders, retryKey]);
150
+ }, [apiBaseUrl, fetchImpl, sessionId, requestHeaders, retryKey, storageScope]);
146
151
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full min-h-0", children: [
147
152
  /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between px-3 pt-2 pb-1 border-b border-border/40", children: [
148
153
  /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/80 font-mono", children: state.kind === "ok" ? `${state.text.length.toLocaleString()} chars` : state.kind === "loading" ? "loading\u2026" : state.kind === "empty" ? `waiting for session \xB7 retry ${retryCount.current}/${MAX_RETRIES}` : "error" }),
@@ -265,7 +270,7 @@ var TABS = [
265
270
  ];
266
271
  var MIN_WIDTH = 280;
267
272
  var MAX_WIDTH = 800;
268
- function DebugDrawer({ sessionId, messages, requestHeaders, width, onWidthChange }) {
273
+ function DebugDrawer({ apiBaseUrl, fetch, sessionId, messages, requestHeaders, storageScope, width, onWidthChange }) {
269
274
  const [tab, setTab] = useState("session");
270
275
  const onDragStart = useCallback((e) => {
271
276
  e.preventDefault();
@@ -310,13 +315,24 @@ function DebugDrawer({ sessionId, messages, requestHeaders, width, onWidthChange
310
315
  id
311
316
  )) }) }),
312
317
  /* @__PURE__ */ jsx(TabsContent, { value: "session", forceMount: true, className: "flex flex-col flex-1 min-h-0 overflow-hidden data-[state=inactive]:hidden", children: /* @__PURE__ */ jsx(SessionTab, { sessionId }) }),
313
- /* @__PURE__ */ jsx(TabsContent, { value: "prompt", forceMount: true, className: "flex flex-col flex-1 min-h-0 overflow-hidden data-[state=inactive]:hidden", children: /* @__PURE__ */ jsx(SystemPromptTab, { sessionId, requestHeaders }) }),
318
+ /* @__PURE__ */ jsx(TabsContent, { value: "prompt", forceMount: true, className: "flex flex-col flex-1 min-h-0 overflow-hidden data-[state=inactive]:hidden", children: /* @__PURE__ */ jsx(SystemPromptTab, { apiBaseUrl, fetch, sessionId, requestHeaders, storageScope }) }),
314
319
  /* @__PURE__ */ jsx(TabsContent, { value: "messages", forceMount: true, className: "flex flex-col flex-1 min-h-0 overflow-hidden data-[state=inactive]:hidden", children: /* @__PURE__ */ jsx(MessagesTab, { messages }) })
315
320
  ] })
316
321
  }
317
322
  )
318
323
  ] });
319
324
  }
325
+ function agentResourceUrl(apiBaseUrl, path) {
326
+ const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
327
+ return `${base}${path}`;
328
+ }
329
+ function scopedHeaders(headers, storageScope) {
330
+ if (!headers && !storageScope) return void 0;
331
+ const result = { ...headers ?? {} };
332
+ const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
333
+ if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
334
+ return result;
335
+ }
320
336
 
321
337
  export {
322
338
  cn,
@@ -0,0 +1,6 @@
1
+ // src/shared/agentPluginEvents.ts
2
+ var WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT = "boring-ui:agent-plugins-reloaded";
3
+
4
+ export {
5
+ WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT
6
+ };
@@ -0,0 +1,369 @@
1
+ // src/shared/tool-ui.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4
+ }
5
+ function isToolUiMetadata(value) {
6
+ if (!isRecord(value)) return false;
7
+ return (value.rendererId === void 0 || typeof value.rendererId === "string") && (value.displayGroup === void 0 || typeof value.displayGroup === "string") && (value.icon === void 0 || typeof value.icon === "string");
8
+ }
9
+ function sanitizeToolUiMetadata(value) {
10
+ if (!isToolUiMetadata(value)) return void 0;
11
+ const rendererId = value.rendererId?.trim();
12
+ const displayGroup = value.displayGroup?.trim();
13
+ const icon = value.icon?.trim();
14
+ return {
15
+ ...rendererId ? { rendererId } : {},
16
+ ...displayGroup ? { displayGroup } : {},
17
+ ...icon ? { icon } : {},
18
+ ...value.details !== void 0 ? { details: value.details } : {}
19
+ };
20
+ }
21
+ function extractToolUiMetadata(output) {
22
+ if (!isRecord(output)) return void 0;
23
+ const details = output.details;
24
+ if (!isRecord(details)) return void 0;
25
+ return sanitizeToolUiMetadata(details.ui);
26
+ }
27
+
28
+ // src/shared/error-codes.ts
29
+ import { z } from "zod";
30
+ var ErrorCode = z.enum([
31
+ // Auth / config
32
+ "MISSING_API_KEY",
33
+ "INVALID_API_KEY",
34
+ "OIDC_REFRESH_FAILED",
35
+ "VERCEL_AUTH_FAILED",
36
+ "CONFIG_INVALID",
37
+ // Workspace / path
38
+ "PATH_ESCAPE",
39
+ "PATH_ABSOLUTE",
40
+ "PATH_NULL_BYTE",
41
+ "PATH_SYMLINK_ESCAPE",
42
+ "PATH_NOT_FOUND",
43
+ "PATH_NOT_WRITABLE",
44
+ "WORKSPACE_UNINITIALIZED",
45
+ "WORKSPACE_NOT_READY",
46
+ // Agent runtime / provisioning
47
+ "AGENT_RUNTIME_NOT_READY",
48
+ "RUNTIME_PROVISIONING_FAILED",
49
+ "RUNTIME_PROVISIONING_LOCKED",
50
+ // Sandbox / exec
51
+ "BWRAP_UNAVAILABLE",
52
+ "BWRAP_TIMEOUT",
53
+ "OUTPUT_TRUNCATED",
54
+ "SANDBOX_NOT_READY",
55
+ "SANDBOX_EXPIRED",
56
+ "VERCEL_API_ERROR",
57
+ "CIRCUIT_OPEN",
58
+ "ABORTED",
59
+ // Session / bridge
60
+ "SESSION_NOT_FOUND",
61
+ "SESSION_LOCKED",
62
+ "STREAM_BUFFER_EVICTED",
63
+ "CURSOR_OUT_OF_RANGE",
64
+ "BRIDGE_COMMAND_INVALID",
65
+ // Tool
66
+ "TOOL_NOT_FOUND",
67
+ "TOOL_INVALID_INPUT",
68
+ "TOOL_EXECUTION_ERROR",
69
+ // Plugin
70
+ "PLUGIN_LOAD_FAILED",
71
+ "PLUGIN_NAME_COLLISION",
72
+ "PLUGIN_RUNTIME_REVISION_MISMATCH",
73
+ "PLUGIN_RUNTIME_PRIVATE_FILE",
74
+ "PLUGIN_RUNTIME_UNSAFE_IMPORT",
75
+ "PLUGIN_RUNTIME_TRANSFORM_FAILED",
76
+ "RUNTIME_PLUGIN_NOT_FOUND",
77
+ "RUNTIME_PLUGIN_ROUTE_NOT_FOUND",
78
+ "RUNTIME_PLUGIN_HANDLER_FAILED",
79
+ "RUNTIME_PLUGIN_LOAD_FAILED",
80
+ "RUNTIME_PLUGIN_RESPONSE_UNSUPPORTED",
81
+ // Runtime provisioning
82
+ "PROVISIONING_LAYOUT_FAILED",
83
+ "PROVISIONING_SKILLS_FAILED",
84
+ "PROVISIONING_TEMPLATES_FAILED",
85
+ "PROVISIONING_NODE_PREFLIGHT_FAILED",
86
+ "PROVISIONING_NPM_INSTALL_FAILED",
87
+ "PROVISIONING_UV_BOOTSTRAP_FAILED",
88
+ "PROVISIONING_UV_INSTALL_FAILED",
89
+ "PROVISIONING_ARTIFACT_FAILED",
90
+ // Internal
91
+ "INTERNAL_ERROR"
92
+ ]);
93
+ var ERROR_CODES = ErrorCode.options;
94
+ var ApiErrorPayloadSchema = z.object({
95
+ code: ErrorCode,
96
+ message: z.string().min(1),
97
+ details: z.record(z.unknown()).optional()
98
+ });
99
+ var ApiErrorResponseSchema = z.object({
100
+ error: ApiErrorPayloadSchema
101
+ });
102
+ var ErrorLogFieldsSchema = z.object({
103
+ level: z.enum(["warn", "error"]),
104
+ code: ErrorCode,
105
+ prefix: z.string().min(1),
106
+ msg: z.string().min(1)
107
+ }).catchall(z.unknown());
108
+
109
+ // src/shared/chat/piChatSchemas.ts
110
+ import { z as z2 } from "zod";
111
+ var nonEmptyString = z2.string().min(1);
112
+ var seqNumber = z2.number().int().nonnegative();
113
+ var ShallowPayloadSchema = z2.union([
114
+ z2.string(),
115
+ z2.number(),
116
+ z2.boolean(),
117
+ z2.null(),
118
+ z2.array(z2.unknown()),
119
+ z2.record(z2.unknown())
120
+ ]);
121
+ function isRecord2(value) {
122
+ return typeof value === "object" && value !== null && !Array.isArray(value);
123
+ }
124
+ var ToolUiMetadataSchema = z2.object({
125
+ rendererId: z2.string().optional(),
126
+ displayGroup: z2.string().optional(),
127
+ icon: z2.string().optional(),
128
+ details: z2.unknown().optional()
129
+ });
130
+ function sanitizeToolUiMetadata2(value) {
131
+ if (!isRecord2(value)) return void 0;
132
+ const parsed = ToolUiMetadataSchema.safeParse(value);
133
+ return parsed.success ? parsed.data : void 0;
134
+ }
135
+ var OptionalToolUiMetadataSchema = z2.preprocess(
136
+ (value) => sanitizeToolUiMetadata2(value),
137
+ ToolUiMetadataSchema.optional()
138
+ );
139
+ var ChatErrorSchema = z2.object({
140
+ code: ErrorCode,
141
+ message: nonEmptyString,
142
+ retryable: z2.boolean().optional(),
143
+ details: z2.unknown().optional()
144
+ });
145
+ var BoringChatPartSchema = z2.discriminatedUnion("type", [
146
+ z2.object({ type: z2.literal("text"), id: z2.string().optional(), text: z2.string() }),
147
+ z2.object({
148
+ type: z2.literal("reasoning"),
149
+ id: nonEmptyString,
150
+ text: z2.string(),
151
+ state: z2.enum(["streaming", "done"]).optional()
152
+ }),
153
+ z2.object({
154
+ type: z2.literal("tool-call"),
155
+ id: nonEmptyString,
156
+ toolName: nonEmptyString,
157
+ input: z2.unknown().optional(),
158
+ state: z2.enum(["input-streaming", "input-available", "output-available", "output-error", "aborted"]),
159
+ output: z2.unknown().optional(),
160
+ errorText: z2.string().optional(),
161
+ ui: OptionalToolUiMetadataSchema
162
+ }),
163
+ z2.object({
164
+ type: z2.literal("file"),
165
+ id: z2.string().optional(),
166
+ filename: z2.string().optional(),
167
+ mediaType: z2.string().optional(),
168
+ url: z2.string().optional()
169
+ }),
170
+ z2.object({
171
+ type: z2.literal("notice"),
172
+ id: z2.string().optional(),
173
+ level: z2.enum(["info", "warning", "error"]),
174
+ text: z2.string()
175
+ })
176
+ ]);
177
+ var BoringChatMessageSchema = z2.object({
178
+ id: nonEmptyString,
179
+ role: z2.enum(["user", "assistant", "system"]),
180
+ status: z2.enum(["pending", "streaming", "done", "aborted", "error"]).optional(),
181
+ parts: z2.array(BoringChatPartSchema),
182
+ createdAt: z2.string().optional(),
183
+ clientNonce: z2.string().optional(),
184
+ clientSeq: z2.number().int().nonnegative().optional(),
185
+ piEntryId: z2.string().optional(),
186
+ turnId: z2.string().optional()
187
+ });
188
+ var QueuedUserMessageSchema = z2.object({
189
+ id: nonEmptyString,
190
+ kind: z2.literal("followup"),
191
+ clientNonce: z2.string().optional(),
192
+ clientSeq: z2.number().int().nonnegative().optional(),
193
+ displayText: z2.string(),
194
+ createdAt: z2.string().optional()
195
+ });
196
+ var PiChatStatusSchema = z2.enum(["idle", "hydrating", "submitted", "streaming", "aborting", "error"]);
197
+ var PiChatSnapshotSchema = z2.object({
198
+ protocolVersion: z2.literal(1),
199
+ sessionId: nonEmptyString,
200
+ seq: seqNumber,
201
+ status: PiChatStatusSchema,
202
+ activeTurnId: z2.string().optional(),
203
+ messages: z2.array(BoringChatMessageSchema),
204
+ queue: z2.object({ followUps: z2.array(QueuedUserMessageSchema) }),
205
+ followUpMode: z2.literal("one-at-a-time"),
206
+ error: ChatErrorSchema.optional()
207
+ });
208
+ var baseEvent = z2.object({ seq: seqNumber });
209
+ var PiChatEventSchema = z2.discriminatedUnion("type", [
210
+ baseEvent.extend({ type: z2.literal("agent-start"), turnId: nonEmptyString }),
211
+ baseEvent.extend({ type: z2.literal("agent-end"), turnId: nonEmptyString, status: z2.enum(["ok", "aborted", "error"]) }),
212
+ baseEvent.extend({
213
+ type: z2.literal("message-start"),
214
+ messageId: nonEmptyString,
215
+ role: z2.enum(["user", "assistant"]),
216
+ clientNonce: z2.string().optional(),
217
+ clientSeq: z2.number().int().nonnegative().optional(),
218
+ createdAt: z2.string().optional(),
219
+ text: z2.string().optional(),
220
+ files: z2.array(BoringChatPartSchema).optional()
221
+ }),
222
+ baseEvent.extend({
223
+ type: z2.literal("message-delta"),
224
+ messageId: nonEmptyString,
225
+ partId: nonEmptyString,
226
+ kind: z2.enum(["text", "reasoning"]),
227
+ delta: z2.string()
228
+ }),
229
+ baseEvent.extend({
230
+ type: z2.literal("message-part-end"),
231
+ messageId: nonEmptyString,
232
+ partId: nonEmptyString,
233
+ kind: z2.enum(["text", "reasoning"]),
234
+ text: z2.string()
235
+ }),
236
+ baseEvent.extend({ type: z2.literal("message-end"), messageId: nonEmptyString, final: BoringChatMessageSchema }),
237
+ baseEvent.extend({
238
+ type: z2.literal("tool-call"),
239
+ messageId: nonEmptyString,
240
+ toolCallId: nonEmptyString,
241
+ toolName: nonEmptyString,
242
+ input: ShallowPayloadSchema,
243
+ ui: OptionalToolUiMetadataSchema
244
+ }),
245
+ baseEvent.extend({
246
+ type: z2.literal("tool-result"),
247
+ messageId: nonEmptyString,
248
+ toolCallId: nonEmptyString,
249
+ output: ShallowPayloadSchema,
250
+ isError: z2.boolean().optional(),
251
+ errorText: z2.string().optional(),
252
+ ui: OptionalToolUiMetadataSchema
253
+ }),
254
+ baseEvent.extend({ type: z2.literal("queue-updated"), queue: z2.object({ followUps: z2.array(QueuedUserMessageSchema) }) }),
255
+ baseEvent.extend({
256
+ type: z2.literal("followup-consumed"),
257
+ clientNonce: z2.string().optional(),
258
+ clientSeq: z2.number().int().nonnegative().optional(),
259
+ messageId: nonEmptyString
260
+ }),
261
+ baseEvent.extend({ type: z2.literal("file-changed"), path: nonEmptyString, changeType: nonEmptyString }),
262
+ baseEvent.extend({ type: z2.literal("ui-command"), command: ShallowPayloadSchema, displayOnly: z2.literal(true) }),
263
+ baseEvent.extend({ type: z2.literal("usage"), usage: ShallowPayloadSchema }),
264
+ baseEvent.extend({
265
+ type: z2.literal("auto-retry-start"),
266
+ attempt: z2.number().int().nonnegative(),
267
+ maxAttempts: z2.number().int().nonnegative(),
268
+ delayMs: z2.number().int().nonnegative(),
269
+ errorMessage: z2.string()
270
+ }),
271
+ baseEvent.extend({
272
+ type: z2.literal("auto-retry-end"),
273
+ success: z2.boolean(),
274
+ attempt: z2.number().int().nonnegative(),
275
+ finalError: z2.string().optional()
276
+ }),
277
+ baseEvent.extend({ type: z2.literal("error"), turnId: z2.string().optional(), retryable: z2.boolean().optional(), error: ChatErrorSchema })
278
+ ]);
279
+ var PiChatHeartbeatFrameSchema = z2.object({ type: z2.literal("heartbeat"), now: z2.string() });
280
+ var PiChatStreamFrameSchema = z2.union([PiChatEventSchema, PiChatHeartbeatFrameSchema]);
281
+ var ChatModelSelectionSchema = z2.object({
282
+ provider: nonEmptyString,
283
+ id: nonEmptyString
284
+ });
285
+ var ThinkingLevelSchema = z2.enum(["off", "low", "medium", "high"]);
286
+ var ChatAttachmentPayloadSchema = z2.object({
287
+ filename: z2.string().optional(),
288
+ mediaType: z2.string().optional(),
289
+ url: nonEmptyString
290
+ });
291
+ var PromptPayloadSchema = z2.object({
292
+ message: z2.string().min(1).max(1e6),
293
+ displayMessage: z2.string().min(1).max(1e6).optional(),
294
+ clientNonce: nonEmptyString.max(128),
295
+ model: ChatModelSelectionSchema.optional(),
296
+ thinkingLevel: ThinkingLevelSchema.optional(),
297
+ attachments: z2.array(ChatAttachmentPayloadSchema).max(20).optional()
298
+ });
299
+ var FollowUpPayloadSchema = z2.object({
300
+ message: z2.string().min(1).max(1e6),
301
+ displayMessage: z2.string().min(1).max(1e6).optional(),
302
+ clientNonce: nonEmptyString.max(128),
303
+ clientSeq: z2.number().int().nonnegative()
304
+ });
305
+ var QueueClearPayloadSchema = z2.preprocess(
306
+ (value) => value ?? {},
307
+ z2.object({
308
+ clientNonce: nonEmptyString.max(128).optional(),
309
+ clientSeq: z2.number().int().nonnegative().optional()
310
+ }).strict()
311
+ );
312
+ var InterruptPayloadSchema = z2.object({}).strict();
313
+ var StopPayloadSchema = z2.object({}).strict();
314
+ var CommandReceiptSchema = z2.object({
315
+ accepted: z2.literal(true),
316
+ cursor: seqNumber
317
+ });
318
+ var PromptReceiptSchema = CommandReceiptSchema.extend({
319
+ clientNonce: nonEmptyString,
320
+ duplicate: z2.boolean().optional()
321
+ });
322
+ var FollowUpReceiptSchema = CommandReceiptSchema.extend({
323
+ clientNonce: nonEmptyString,
324
+ clientSeq: z2.number().int().nonnegative(),
325
+ queued: z2.literal(true),
326
+ duplicate: z2.boolean().optional()
327
+ });
328
+ var QueueClearReceiptSchema = CommandReceiptSchema.extend({
329
+ cleared: z2.number().int().nonnegative()
330
+ });
331
+ var StopReceiptSchema = CommandReceiptSchema.extend({
332
+ stopped: z2.boolean(),
333
+ clearedQueue: z2.array(QueuedUserMessageSchema)
334
+ });
335
+
336
+ export {
337
+ isToolUiMetadata,
338
+ sanitizeToolUiMetadata,
339
+ extractToolUiMetadata,
340
+ ErrorCode,
341
+ ERROR_CODES,
342
+ ApiErrorPayloadSchema,
343
+ ApiErrorResponseSchema,
344
+ ErrorLogFieldsSchema,
345
+ ToolUiMetadataSchema,
346
+ sanitizeToolUiMetadata2,
347
+ ChatErrorSchema,
348
+ BoringChatPartSchema,
349
+ BoringChatMessageSchema,
350
+ QueuedUserMessageSchema,
351
+ PiChatStatusSchema,
352
+ PiChatSnapshotSchema,
353
+ PiChatEventSchema,
354
+ PiChatHeartbeatFrameSchema,
355
+ PiChatStreamFrameSchema,
356
+ ChatModelSelectionSchema,
357
+ ThinkingLevelSchema,
358
+ ChatAttachmentPayloadSchema,
359
+ PromptPayloadSchema,
360
+ FollowUpPayloadSchema,
361
+ QueueClearPayloadSchema,
362
+ InterruptPayloadSchema,
363
+ StopPayloadSchema,
364
+ CommandReceiptSchema,
365
+ PromptReceiptSchema,
366
+ FollowUpReceiptSchema,
367
+ QueueClearReceiptSchema,
368
+ StopReceiptSchema
369
+ };