xo-harness 0.1.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.
Files changed (86) hide show
  1. package/README.md +32 -0
  2. package/dist/harness.d.ts +1 -0
  3. package/dist/harness.js +1 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +4 -0
  6. package/dist/internal/harness/event-stream.d.ts +7 -0
  7. package/dist/internal/harness/event-stream.js +42 -0
  8. package/dist/internal/harness/index.d.ts +7 -0
  9. package/dist/internal/harness/index.js +7 -0
  10. package/dist/internal/harness/message.d.ts +26 -0
  11. package/dist/internal/harness/message.js +192 -0
  12. package/dist/internal/harness/report.d.ts +51 -0
  13. package/dist/internal/harness/report.js +115 -0
  14. package/dist/internal/harness/shadow.d.ts +28 -0
  15. package/dist/internal/harness/shadow.js +118 -0
  16. package/dist/internal/harness/socket-bridge.d.ts +65 -0
  17. package/dist/internal/harness/socket-bridge.js +227 -0
  18. package/dist/internal/harness/task-supervisor.d.ts +20 -0
  19. package/dist/internal/harness/task-supervisor.js +180 -0
  20. package/dist/internal/harness/tool-runtime.d.ts +20 -0
  21. package/dist/internal/harness/tool-runtime.js +93 -0
  22. package/dist/internal/harness/tools.d.ts +32 -0
  23. package/dist/internal/harness/tools.js +30 -0
  24. package/dist/internal/harness/voice-session.d.ts +33 -0
  25. package/dist/internal/harness/voice-session.js +268 -0
  26. package/dist/internal/harness/xo.d.ts +25 -0
  27. package/dist/internal/harness/xo.js +24 -0
  28. package/dist/internal/protocol/async-queue.d.ts +8 -0
  29. package/dist/internal/protocol/async-queue.js +34 -0
  30. package/dist/internal/protocol/audio.d.ts +12 -0
  31. package/dist/internal/protocol/audio.js +24 -0
  32. package/dist/internal/protocol/events.d.ts +350 -0
  33. package/dist/internal/protocol/events.js +167 -0
  34. package/dist/internal/protocol/index.d.ts +6 -0
  35. package/dist/internal/protocol/index.js +6 -0
  36. package/dist/internal/protocol/parts.d.ts +219 -0
  37. package/dist/internal/protocol/parts.js +105 -0
  38. package/dist/internal/protocol/provider.d.ts +118 -0
  39. package/dist/internal/protocol/provider.js +77 -0
  40. package/dist/internal/protocol/tools.d.ts +62 -0
  41. package/dist/internal/protocol/tools.js +47 -0
  42. package/dist/internal/provider/contract.d.ts +24 -0
  43. package/dist/internal/provider/contract.js +1 -0
  44. package/dist/internal/provider/grok-voice.d.ts +45 -0
  45. package/dist/internal/provider/grok-voice.js +77 -0
  46. package/dist/internal/provider/index.d.ts +6 -0
  47. package/dist/internal/provider/index.js +9 -0
  48. package/dist/internal/provider/node-socket.d.ts +3 -0
  49. package/dist/internal/provider/node-socket.js +74 -0
  50. package/dist/internal/provider/openai-realtime.d.ts +43 -0
  51. package/dist/internal/provider/openai-realtime.js +76 -0
  52. package/dist/internal/provider/realtime-session.d.ts +30 -0
  53. package/dist/internal/provider/realtime-session.js +461 -0
  54. package/dist/internal/provider/realtime-socket.d.ts +42 -0
  55. package/dist/internal/provider/realtime-socket.js +30 -0
  56. package/dist/internal/provider/workers-socket.d.ts +11 -0
  57. package/dist/internal/provider/workers-socket.js +108 -0
  58. package/dist/internal/provider/workers.d.ts +6 -0
  59. package/dist/internal/provider/workers.js +11 -0
  60. package/dist/internal/provider-fake/index.d.ts +46 -0
  61. package/dist/internal/provider-fake/index.js +112 -0
  62. package/dist/internal/provider-fake/replay-voice-provider.d.ts +47 -0
  63. package/dist/internal/provider-fake/replay-voice-provider.js +133 -0
  64. package/dist/internal/provider-fake/scripted-voice-provider.d.ts +61 -0
  65. package/dist/internal/provider-fake/scripted-voice-provider.js +185 -0
  66. package/dist/internal/storage/append-tail.d.ts +12 -0
  67. package/dist/internal/storage/append-tail.js +19 -0
  68. package/dist/internal/storage/event-store.d.ts +19 -0
  69. package/dist/internal/storage/event-store.js +1 -0
  70. package/dist/internal/storage/index.d.ts +4 -0
  71. package/dist/internal/storage/index.js +4 -0
  72. package/dist/internal/storage/jsonl-event-store.d.ts +13 -0
  73. package/dist/internal/storage/jsonl-event-store.js +105 -0
  74. package/dist/internal/storage/memory-event-store.d.ts +8 -0
  75. package/dist/internal/storage/memory-event-store.js +22 -0
  76. package/dist/protocol.d.ts +1 -0
  77. package/dist/protocol.js +1 -0
  78. package/dist/provider-workers.d.ts +1 -0
  79. package/dist/provider-workers.js +1 -0
  80. package/dist/provider.d.ts +1 -0
  81. package/dist/provider.js +1 -0
  82. package/dist/storage.d.ts +1 -0
  83. package/dist/storage.js +1 -0
  84. package/dist/testing.d.ts +1 -0
  85. package/dist/testing.js +1 -0
  86. package/package.json +76 -0
@@ -0,0 +1,219 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Tool call + result as one part's lifecycle — no separate tool_result. `running`
4
+ * starts at `tool.requested` (arguments are complete by then); `settled` embeds the
5
+ * canonical ToolOutcome verbatim, so nothing is lost re-encoding it — including task
6
+ * handoffs, which later settle to their real result when `task.completed` arrives.
7
+ */
8
+ export declare const ToolPartStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
9
+ status: z.ZodLiteral<"running">;
10
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
11
+ startedAtMs: z.ZodNumber;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ status: z.ZodLiteral<"settled">;
14
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
15
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
16
+ type: z.ZodLiteral<"completed">;
17
+ value: z.ZodJSONSchema;
18
+ }, z.core.$strip>, z.ZodObject<{
19
+ type: z.ZodLiteral<"accepted_task">;
20
+ taskId: z.ZodString;
21
+ }, z.core.$strip>, z.ZodObject<{
22
+ type: z.ZodLiteral<"failed">;
23
+ error: z.ZodString;
24
+ }, z.core.$strip>], "type">;
25
+ startedAtMs: z.ZodNumber;
26
+ endedAtMs: z.ZodNumber;
27
+ }, z.core.$strip>], "status">;
28
+ export type ToolPartState = z.infer<typeof ToolPartStateSchema>;
29
+ /** Assistant/model output parts. */
30
+ export declare const OutputPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
31
+ id: z.ZodString;
32
+ sessionId: z.ZodString;
33
+ messageId: z.ZodString;
34
+ parentToolUseId: z.ZodOptional<z.ZodString>;
35
+ type: z.ZodLiteral<"text">;
36
+ text: z.ZodString;
37
+ state: z.ZodEnum<{
38
+ done: "done";
39
+ streaming: "streaming";
40
+ }>;
41
+ }, z.core.$strip>, z.ZodObject<{
42
+ id: z.ZodString;
43
+ sessionId: z.ZodString;
44
+ messageId: z.ZodString;
45
+ parentToolUseId: z.ZodOptional<z.ZodString>;
46
+ type: z.ZodLiteral<"reasoning">;
47
+ text: z.ZodString;
48
+ state: z.ZodEnum<{
49
+ done: "done";
50
+ streaming: "streaming";
51
+ }>;
52
+ providerMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
53
+ }, z.core.$strip>, z.ZodObject<{
54
+ id: z.ZodString;
55
+ sessionId: z.ZodString;
56
+ messageId: z.ZodString;
57
+ parentToolUseId: z.ZodOptional<z.ZodString>;
58
+ type: z.ZodLiteral<"tool">;
59
+ toolCallId: z.ZodString;
60
+ toolName: z.ZodString;
61
+ state: z.ZodDiscriminatedUnion<[z.ZodObject<{
62
+ status: z.ZodLiteral<"running">;
63
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
64
+ startedAtMs: z.ZodNumber;
65
+ }, z.core.$strip>, z.ZodObject<{
66
+ status: z.ZodLiteral<"settled">;
67
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
68
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
69
+ type: z.ZodLiteral<"completed">;
70
+ value: z.ZodJSONSchema;
71
+ }, z.core.$strip>, z.ZodObject<{
72
+ type: z.ZodLiteral<"accepted_task">;
73
+ taskId: z.ZodString;
74
+ }, z.core.$strip>, z.ZodObject<{
75
+ type: z.ZodLiteral<"failed">;
76
+ error: z.ZodString;
77
+ }, z.core.$strip>], "type">;
78
+ startedAtMs: z.ZodNumber;
79
+ endedAtMs: z.ZodNumber;
80
+ }, z.core.$strip>], "status">;
81
+ }, z.core.$strip>, z.ZodObject<{
82
+ id: z.ZodString;
83
+ sessionId: z.ZodString;
84
+ messageId: z.ZodString;
85
+ parentToolUseId: z.ZodOptional<z.ZodString>;
86
+ type: z.ZodLiteral<"file">;
87
+ mediaType: z.ZodString;
88
+ url: z.ZodString;
89
+ filename: z.ZodOptional<z.ZodString>;
90
+ }, z.core.$strip>, z.ZodObject<{
91
+ id: z.ZodString;
92
+ sessionId: z.ZodString;
93
+ messageId: z.ZodString;
94
+ parentToolUseId: z.ZodOptional<z.ZodString>;
95
+ type: z.ZodLiteral<"audio">;
96
+ streamId: z.ZodString;
97
+ sampleRate: z.ZodNumber;
98
+ state: z.ZodEnum<{
99
+ done: "done";
100
+ streaming: "streaming";
101
+ }>;
102
+ }, z.core.$strip>], "type">;
103
+ export type OutputPart = z.infer<typeof OutputPartSchema>;
104
+ /** User input parts — multimodal in. A bare string stays first-class via AgentInput. */
105
+ export declare const InputPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
106
+ type: z.ZodLiteral<"text">;
107
+ text: z.ZodString;
108
+ }, z.core.$strip>, z.ZodObject<{
109
+ type: z.ZodLiteral<"image">;
110
+ mediaType: z.ZodString;
111
+ url: z.ZodString;
112
+ }, z.core.$strip>, z.ZodObject<{
113
+ type: z.ZodLiteral<"file">;
114
+ mediaType: z.ZodString;
115
+ url: z.ZodString;
116
+ filename: z.ZodOptional<z.ZodString>;
117
+ }, z.core.$strip>], "type">;
118
+ export type InputPart = z.infer<typeof InputPartSchema>;
119
+ /** A turn's input: a plain string, or an ordered list of typed parts. */
120
+ export declare const AgentInputSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
121
+ type: z.ZodLiteral<"text">;
122
+ text: z.ZodString;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ type: z.ZodLiteral<"image">;
125
+ mediaType: z.ZodString;
126
+ url: z.ZodString;
127
+ }, z.core.$strip>, z.ZodObject<{
128
+ type: z.ZodLiteral<"file">;
129
+ mediaType: z.ZodString;
130
+ url: z.ZodString;
131
+ filename: z.ZodOptional<z.ZodString>;
132
+ }, z.core.$strip>], "type">>]>;
133
+ export type AgentInput = z.infer<typeof AgentInputSchema>;
134
+ /** Collapse any AgentInput to its text (for providers/logging that only want a string). */
135
+ export declare function agentInputToText(input: AgentInput): string;
136
+ /** A rendered conversation message: role + ordered parts, folded from the event log. */
137
+ export declare const MessageSchema: z.ZodObject<{
138
+ id: z.ZodString;
139
+ sessionId: z.ZodString;
140
+ role: z.ZodEnum<{
141
+ assistant: "assistant";
142
+ user: "user";
143
+ }>;
144
+ createdAtMs: z.ZodNumber;
145
+ parts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
146
+ id: z.ZodString;
147
+ sessionId: z.ZodString;
148
+ messageId: z.ZodString;
149
+ parentToolUseId: z.ZodOptional<z.ZodString>;
150
+ type: z.ZodLiteral<"text">;
151
+ text: z.ZodString;
152
+ state: z.ZodEnum<{
153
+ done: "done";
154
+ streaming: "streaming";
155
+ }>;
156
+ }, z.core.$strip>, z.ZodObject<{
157
+ id: z.ZodString;
158
+ sessionId: z.ZodString;
159
+ messageId: z.ZodString;
160
+ parentToolUseId: z.ZodOptional<z.ZodString>;
161
+ type: z.ZodLiteral<"reasoning">;
162
+ text: z.ZodString;
163
+ state: z.ZodEnum<{
164
+ done: "done";
165
+ streaming: "streaming";
166
+ }>;
167
+ providerMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
+ }, z.core.$strip>, z.ZodObject<{
169
+ id: z.ZodString;
170
+ sessionId: z.ZodString;
171
+ messageId: z.ZodString;
172
+ parentToolUseId: z.ZodOptional<z.ZodString>;
173
+ type: z.ZodLiteral<"tool">;
174
+ toolCallId: z.ZodString;
175
+ toolName: z.ZodString;
176
+ state: z.ZodDiscriminatedUnion<[z.ZodObject<{
177
+ status: z.ZodLiteral<"running">;
178
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
179
+ startedAtMs: z.ZodNumber;
180
+ }, z.core.$strip>, z.ZodObject<{
181
+ status: z.ZodLiteral<"settled">;
182
+ input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
183
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
184
+ type: z.ZodLiteral<"completed">;
185
+ value: z.ZodJSONSchema;
186
+ }, z.core.$strip>, z.ZodObject<{
187
+ type: z.ZodLiteral<"accepted_task">;
188
+ taskId: z.ZodString;
189
+ }, z.core.$strip>, z.ZodObject<{
190
+ type: z.ZodLiteral<"failed">;
191
+ error: z.ZodString;
192
+ }, z.core.$strip>], "type">;
193
+ startedAtMs: z.ZodNumber;
194
+ endedAtMs: z.ZodNumber;
195
+ }, z.core.$strip>], "status">;
196
+ }, z.core.$strip>, z.ZodObject<{
197
+ id: z.ZodString;
198
+ sessionId: z.ZodString;
199
+ messageId: z.ZodString;
200
+ parentToolUseId: z.ZodOptional<z.ZodString>;
201
+ type: z.ZodLiteral<"file">;
202
+ mediaType: z.ZodString;
203
+ url: z.ZodString;
204
+ filename: z.ZodOptional<z.ZodString>;
205
+ }, z.core.$strip>, z.ZodObject<{
206
+ id: z.ZodString;
207
+ sessionId: z.ZodString;
208
+ messageId: z.ZodString;
209
+ parentToolUseId: z.ZodOptional<z.ZodString>;
210
+ type: z.ZodLiteral<"audio">;
211
+ streamId: z.ZodString;
212
+ sampleRate: z.ZodNumber;
213
+ state: z.ZodEnum<{
214
+ done: "done";
215
+ streaming: "streaming";
216
+ }>;
217
+ }, z.core.$strip>], "type">>;
218
+ }, z.core.$strip>;
219
+ export type Message = z.infer<typeof MessageSchema>;
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+ import { ToolOutcomeSchema } from "./tools.js";
3
+ /**
4
+ * The content-part model — the shape every mature agent harness converged on (opencode,
5
+ * Vercel AI SDK v5, Claude Code, Codex, agent-server, echo-backend, ACP vocabulary): a
6
+ * message is an ordered array of typed parts with stable identity, and a tool call folds
7
+ * its result into one part's nested state rather than a separate result event.
8
+ *
9
+ * XO's addition to the family is the `audio` part — the addressable handle for a duplex
10
+ * speech stream, linked to its transcript by streamId. Ordering rules and the event
11
+ * catalog these fold from live in docs/protocol.md.
12
+ *
13
+ * These are the projection/persistence types; the durable transport stays the flat,
14
+ * sequence-ordered HarnessEvent log. `projectMessages` (in the harness) folds the log
15
+ * into these deterministically: the same log always yields the same ids.
16
+ */
17
+ /** Identity + sub-agent threading shared by every part. */
18
+ const PartBaseSchema = z.object({
19
+ id: z.string().min(1),
20
+ sessionId: z.string().min(1),
21
+ messageId: z.string().min(1),
22
+ /** Set when a part was produced inside a nested tool/sub-agent, linking it to that call. */
23
+ parentToolUseId: z.string().min(1).optional(),
24
+ });
25
+ const PartStateSchema = z.enum(["streaming", "done"]);
26
+ /**
27
+ * Tool call + result as one part's lifecycle — no separate tool_result. `running`
28
+ * starts at `tool.requested` (arguments are complete by then); `settled` embeds the
29
+ * canonical ToolOutcome verbatim, so nothing is lost re-encoding it — including task
30
+ * handoffs, which later settle to their real result when `task.completed` arrives.
31
+ */
32
+ export const ToolPartStateSchema = z.discriminatedUnion("status", [
33
+ z.object({
34
+ status: z.literal("running"),
35
+ input: z.record(z.string(), z.unknown()),
36
+ startedAtMs: z.number(),
37
+ }),
38
+ z.object({
39
+ status: z.literal("settled"),
40
+ input: z.record(z.string(), z.unknown()),
41
+ outcome: ToolOutcomeSchema,
42
+ startedAtMs: z.number(),
43
+ endedAtMs: z.number(),
44
+ }),
45
+ ]);
46
+ /** Assistant/model output parts. */
47
+ export const OutputPartSchema = z.discriminatedUnion("type", [
48
+ PartBaseSchema.extend({ type: z.literal("text"), text: z.string(), state: PartStateSchema }),
49
+ PartBaseSchema.extend({
50
+ type: z.literal("reasoning"),
51
+ text: z.string(),
52
+ state: PartStateSchema,
53
+ providerMetadata: z.record(z.string(), z.unknown()).optional(),
54
+ }),
55
+ PartBaseSchema.extend({
56
+ type: z.literal("tool"),
57
+ toolCallId: z.string().min(1),
58
+ toolName: z.string().min(1),
59
+ state: ToolPartStateSchema,
60
+ }),
61
+ PartBaseSchema.extend({
62
+ type: z.literal("file"),
63
+ mediaType: z.string().min(1),
64
+ url: z.string().min(1),
65
+ filename: z.string().optional(),
66
+ }),
67
+ // XO addition: a duplex speech stream. PCM rides audio.output frames keyed by streamId;
68
+ // the paired text transcript is a sibling text part linked by the same streamId.
69
+ PartBaseSchema.extend({
70
+ type: z.literal("audio"),
71
+ streamId: z.string().min(1),
72
+ sampleRate: z.number().positive(),
73
+ state: PartStateSchema,
74
+ }),
75
+ ]);
76
+ /** User input parts — multimodal in. A bare string stays first-class via AgentInput. */
77
+ export const InputPartSchema = z.discriminatedUnion("type", [
78
+ z.object({ type: z.literal("text"), text: z.string().min(1) }),
79
+ z.object({ type: z.literal("image"), mediaType: z.string().min(1), url: z.string().min(1) }),
80
+ z.object({
81
+ type: z.literal("file"),
82
+ mediaType: z.string().min(1),
83
+ url: z.string().min(1),
84
+ filename: z.string().optional(),
85
+ }),
86
+ ]);
87
+ /** A turn's input: a plain string, or an ordered list of typed parts. */
88
+ export const AgentInputSchema = z.union([z.string().min(1), z.array(InputPartSchema).min(1)]);
89
+ /** Collapse any AgentInput to its text (for providers/logging that only want a string). */
90
+ export function agentInputToText(input) {
91
+ if (typeof input === "string")
92
+ return input;
93
+ return input
94
+ .map((part) => (part.type === "text" ? part.text : ""))
95
+ .filter((text) => text !== "")
96
+ .join("\n");
97
+ }
98
+ /** A rendered conversation message: role + ordered parts, folded from the event log. */
99
+ export const MessageSchema = z.object({
100
+ id: z.string().min(1),
101
+ sessionId: z.string().min(1),
102
+ role: z.enum(["user", "assistant"]),
103
+ createdAtMs: z.number().nonnegative(),
104
+ parts: z.array(OutputPartSchema),
105
+ });
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * What a provider can actually do, declared at session start and recorded in
4
+ * `session.started` so consumers adapt instead of probing.
5
+ */
6
+ export declare const ProviderCapabilitiesSchema: z.ZodObject<{
7
+ continuousInputDuringOutput: z.ZodBoolean;
8
+ modelManagedFloor: z.ZodBoolean;
9
+ supportsToolCalls: z.ZodBoolean;
10
+ supportsAsyncContext: z.ZodBoolean;
11
+ supportsPlayoutAcknowledgements: z.ZodBoolean;
12
+ }, z.core.$strip>;
13
+ export type ProviderCapabilities = z.infer<typeof ProviderCapabilitiesSchema>;
14
+ export declare const PlayoutProgressSchema: z.ZodObject<{
15
+ streamId: z.ZodString;
16
+ playedThroughSample: z.ZodInt;
17
+ }, z.core.$strip>;
18
+ export type PlayoutProgress = z.infer<typeof PlayoutProgressSchema>;
19
+ /** What the model may produce; ["text"] runs a session without audio output. */
20
+ export declare const OutputModalitySchema: z.ZodEnum<{
21
+ audio: "audio";
22
+ text: "text";
23
+ }>;
24
+ export type OutputModality = z.infer<typeof OutputModalitySchema>;
25
+ /**
26
+ * Token accounting for one completed model response, as reported by the provider.
27
+ * cachedInputTokens is the slice of input served from the provider's prompt cache —
28
+ * the number that proves (and prices) cache effectiveness.
29
+ */
30
+ export declare const UsagePayloadSchema: z.ZodObject<{
31
+ inputTokens: z.ZodNumber;
32
+ outputTokens: z.ZodNumber;
33
+ totalTokens: z.ZodNumber;
34
+ cachedInputTokens: z.ZodOptional<z.ZodNumber>;
35
+ audioInputTokens: z.ZodOptional<z.ZodNumber>;
36
+ audioOutputTokens: z.ZodOptional<z.ZodNumber>;
37
+ cachedAudioInputTokens: z.ZodOptional<z.ZodNumber>;
38
+ }, z.core.$strip>;
39
+ export type UsagePayload = z.infer<typeof UsagePayloadSchema>;
40
+ /** The one transcript payload shape, shared by provider events and harness events. */
41
+ export declare const TranscriptPayloadSchema: z.ZodObject<{
42
+ role: z.ZodEnum<{
43
+ assistant: "assistant";
44
+ user: "user";
45
+ }>;
46
+ text: z.ZodString;
47
+ streamId: z.ZodOptional<z.ZodString>;
48
+ }, z.core.$strip>;
49
+ export type TranscriptPayload = z.infer<typeof TranscriptPayloadSchema>;
50
+ /** Incremental assistant text as the model streams it; finalized by a `transcript`. */
51
+ export declare const TranscriptDeltaPayloadSchema: z.ZodObject<{
52
+ role: z.ZodEnum<{
53
+ assistant: "assistant";
54
+ user: "user";
55
+ }>;
56
+ delta: z.ZodString;
57
+ streamId: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>;
59
+ export type TranscriptDeltaPayload = z.infer<typeof TranscriptDeltaPayloadSchema>;
60
+ /**
61
+ * What a provider adapter yields to the harness. Deliberately minimal: adapters
62
+ * translate vendor wire dialects into these eight shapes, and the harness turns them
63
+ * into HarnessEvents. Anything not expressible here does not exist to the harness.
64
+ */
65
+ export declare const ProviderEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
66
+ type: z.ZodLiteral<"ready">;
67
+ }, z.core.$strip>, z.ZodObject<{
68
+ type: z.ZodLiteral<"audio.output">;
69
+ chunk: z.ZodObject<{
70
+ streamId: z.ZodString;
71
+ sequence: z.ZodInt;
72
+ encoding: z.ZodLiteral<"pcm_s16le">;
73
+ sampleRate: z.ZodInt;
74
+ channels: z.ZodInt;
75
+ startSample: z.ZodInt;
76
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
77
+ }, z.core.$strip>;
78
+ }, z.core.$strip>, z.ZodObject<{
79
+ role: z.ZodEnum<{
80
+ assistant: "assistant";
81
+ user: "user";
82
+ }>;
83
+ text: z.ZodString;
84
+ streamId: z.ZodOptional<z.ZodString>;
85
+ type: z.ZodLiteral<"transcript">;
86
+ }, z.core.$strip>, z.ZodObject<{
87
+ role: z.ZodEnum<{
88
+ assistant: "assistant";
89
+ user: "user";
90
+ }>;
91
+ delta: z.ZodString;
92
+ streamId: z.ZodOptional<z.ZodString>;
93
+ type: z.ZodLiteral<"transcript.delta">;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ type: z.ZodLiteral<"tool.call">;
96
+ call: z.ZodObject<{
97
+ callId: z.ZodString;
98
+ name: z.ZodString;
99
+ arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
100
+ }, z.core.$strip>;
101
+ }, z.core.$strip>, z.ZodObject<{
102
+ inputTokens: z.ZodNumber;
103
+ outputTokens: z.ZodNumber;
104
+ totalTokens: z.ZodNumber;
105
+ cachedInputTokens: z.ZodOptional<z.ZodNumber>;
106
+ audioInputTokens: z.ZodOptional<z.ZodNumber>;
107
+ audioOutputTokens: z.ZodOptional<z.ZodNumber>;
108
+ cachedAudioInputTokens: z.ZodOptional<z.ZodNumber>;
109
+ type: z.ZodLiteral<"usage">;
110
+ }, z.core.$strip>, z.ZodObject<{
111
+ type: z.ZodLiteral<"error">;
112
+ message: z.ZodString;
113
+ recoverable: z.ZodBoolean;
114
+ }, z.core.$strip>, z.ZodObject<{
115
+ type: z.ZodLiteral<"closed">;
116
+ reason: z.ZodString;
117
+ }, z.core.$strip>], "type">;
118
+ export type ProviderEvent = z.infer<typeof ProviderEventSchema>;
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ import { AudioChunkSchema } from "./audio.js";
3
+ import { ToolCallSchema } from "./tools.js";
4
+ /**
5
+ * What a provider can actually do, declared at session start and recorded in
6
+ * `session.started` so consumers adapt instead of probing.
7
+ */
8
+ export const ProviderCapabilitiesSchema = z.object({
9
+ /** Microphone audio may keep flowing while the model is speaking (full duplex). */
10
+ continuousInputDuringOutput: z.boolean(),
11
+ /** The model decides when to speak (server VAD); the harness never gates turns. */
12
+ modelManagedFloor: z.boolean(),
13
+ supportsToolCalls: z.boolean(),
14
+ /** Accepts out-of-band context injections such as task settlement announcements. */
15
+ supportsAsyncContext: z.boolean(),
16
+ /** Honors playout acknowledgements for truncation-accurate barge-in. */
17
+ supportsPlayoutAcknowledgements: z.boolean(),
18
+ });
19
+ export const PlayoutProgressSchema = z.object({
20
+ streamId: z.string().min(1),
21
+ playedThroughSample: z.int().nonnegative(),
22
+ });
23
+ /** What the model may produce; ["text"] runs a session without audio output. */
24
+ export const OutputModalitySchema = z.enum(["audio", "text"]);
25
+ /**
26
+ * Token accounting for one completed model response, as reported by the provider.
27
+ * cachedInputTokens is the slice of input served from the provider's prompt cache —
28
+ * the number that proves (and prices) cache effectiveness.
29
+ */
30
+ export const UsagePayloadSchema = z.object({
31
+ inputTokens: z.number().int().nonnegative(),
32
+ outputTokens: z.number().int().nonnegative(),
33
+ totalTokens: z.number().int().nonnegative(),
34
+ cachedInputTokens: z.number().int().nonnegative().optional(),
35
+ audioInputTokens: z.number().int().nonnegative().optional(),
36
+ audioOutputTokens: z.number().int().nonnegative().optional(),
37
+ cachedAudioInputTokens: z.number().int().nonnegative().optional(),
38
+ });
39
+ /** The one transcript payload shape, shared by provider events and harness events. */
40
+ export const TranscriptPayloadSchema = z.object({
41
+ role: z.enum(["user", "assistant"]),
42
+ text: z.string().min(1),
43
+ streamId: z.string().min(1).optional(),
44
+ });
45
+ /** Incremental assistant text as the model streams it; finalized by a `transcript`. */
46
+ export const TranscriptDeltaPayloadSchema = z.object({
47
+ role: z.enum(["user", "assistant"]),
48
+ delta: z.string().min(1),
49
+ streamId: z.string().min(1).optional(),
50
+ });
51
+ /**
52
+ * What a provider adapter yields to the harness. Deliberately minimal: adapters
53
+ * translate vendor wire dialects into these eight shapes, and the harness turns them
54
+ * into HarnessEvents. Anything not expressible here does not exist to the harness.
55
+ */
56
+ export const ProviderEventSchema = z.discriminatedUnion("type", [
57
+ /** Session configuration acknowledged; the model is listening. */
58
+ z.object({ type: z.literal("ready") }),
59
+ /** Model speech as sample-addressed PCM. */
60
+ z.object({ type: z.literal("audio.output"), chunk: AudioChunkSchema }),
61
+ /** Final transcript text for either role. */
62
+ TranscriptPayloadSchema.extend({ type: z.literal("transcript") }),
63
+ /** Incremental assistant text; assembled into a streaming text part. */
64
+ TranscriptDeltaPayloadSchema.extend({ type: z.literal("transcript.delta") }),
65
+ /** The model requested a tool invocation. */
66
+ z.object({ type: z.literal("tool.call"), call: ToolCallSchema }),
67
+ /** Token accounting for one completed response. */
68
+ UsagePayloadSchema.extend({ type: z.literal("usage") }),
69
+ /** Provider fault; unrecoverable errors are followed by `closed`. */
70
+ z.object({
71
+ type: z.literal("error"),
72
+ message: z.string().min(1),
73
+ recoverable: z.boolean(),
74
+ }),
75
+ /** Terminal: the provider connection is gone. Always the last event. */
76
+ z.object({ type: z.literal("closed"), reason: z.string().min(1) }),
77
+ ]);
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ export declare const JsonValueSchema: z.ZodJSONSchema;
3
+ export type JsonValue = z.infer<typeof JsonValueSchema>;
4
+ export declare const ToolCallSchema: z.ZodObject<{
5
+ callId: z.ZodString;
6
+ name: z.ZodString;
7
+ arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
8
+ }, z.core.$strip>;
9
+ export type ToolCall = z.infer<typeof ToolCallSchema>;
10
+ export declare const ToolOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
11
+ type: z.ZodLiteral<"completed">;
12
+ value: z.ZodJSONSchema;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ type: z.ZodLiteral<"accepted_task">;
15
+ taskId: z.ZodString;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ type: z.ZodLiteral<"failed">;
18
+ error: z.ZodString;
19
+ }, z.core.$strip>], "type">;
20
+ export type ToolOutcome = z.infer<typeof ToolOutcomeSchema>;
21
+ export declare const ProviderToolDefinitionSchema: z.ZodObject<{
22
+ name: z.ZodString;
23
+ description: z.ZodString;
24
+ inputSchema: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
25
+ }, z.core.$strip>;
26
+ export type ProviderToolDefinition = z.infer<typeof ProviderToolDefinitionSchema>;
27
+ export declare const ProviderToolResultSchema: z.ZodObject<{
28
+ callId: z.ZodString;
29
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
30
+ type: z.ZodLiteral<"completed">;
31
+ value: z.ZodJSONSchema;
32
+ }, z.core.$strip>, z.ZodObject<{
33
+ type: z.ZodLiteral<"accepted_task">;
34
+ taskId: z.ZodString;
35
+ }, z.core.$strip>, z.ZodObject<{
36
+ type: z.ZodLiteral<"failed">;
37
+ error: z.ZodString;
38
+ }, z.core.$strip>], "type">;
39
+ }, z.core.$strip>;
40
+ export type ProviderToolResult = z.infer<typeof ProviderToolResultSchema>;
41
+ export declare const ProviderContextEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
42
+ type: z.ZodLiteral<"task.progress">;
43
+ taskId: z.ZodString;
44
+ callId: z.ZodString;
45
+ update: z.ZodJSONSchema;
46
+ }, z.core.$strip>, z.ZodObject<{
47
+ type: z.ZodLiteral<"task.completed">;
48
+ taskId: z.ZodString;
49
+ callId: z.ZodString;
50
+ result: z.ZodJSONSchema;
51
+ }, z.core.$strip>, z.ZodObject<{
52
+ type: z.ZodLiteral<"task.failed">;
53
+ taskId: z.ZodString;
54
+ callId: z.ZodString;
55
+ error: z.ZodString;
56
+ }, z.core.$strip>, z.ZodObject<{
57
+ type: z.ZodLiteral<"task.cancelled">;
58
+ taskId: z.ZodString;
59
+ callId: z.ZodString;
60
+ reason: z.ZodString;
61
+ }, z.core.$strip>], "type">;
62
+ export type ProviderContextEvent = z.infer<typeof ProviderContextEventSchema>;
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ export const JsonValueSchema = z.json();
3
+ export const ToolCallSchema = z.object({
4
+ callId: z.string().min(1),
5
+ name: z.string().min(1),
6
+ arguments: z.record(z.string(), z.unknown()),
7
+ });
8
+ export const ToolOutcomeSchema = z.discriminatedUnion("type", [
9
+ z.object({ type: z.literal("completed"), value: JsonValueSchema }),
10
+ z.object({ type: z.literal("accepted_task"), taskId: z.string().min(1) }),
11
+ z.object({ type: z.literal("failed"), error: z.string().min(1) }),
12
+ ]);
13
+ export const ProviderToolDefinitionSchema = z.object({
14
+ name: z.string().regex(/^[A-Za-z0-9_.-]{1,128}$/),
15
+ description: z.string().min(1),
16
+ inputSchema: z.record(z.string(), JsonValueSchema),
17
+ });
18
+ export const ProviderToolResultSchema = z.object({
19
+ callId: z.string().min(1),
20
+ outcome: ToolOutcomeSchema,
21
+ });
22
+ export const ProviderContextEventSchema = z.discriminatedUnion("type", [
23
+ z.object({
24
+ type: z.literal("task.progress"),
25
+ taskId: z.string().min(1),
26
+ callId: z.string().min(1),
27
+ update: JsonValueSchema,
28
+ }),
29
+ z.object({
30
+ type: z.literal("task.completed"),
31
+ taskId: z.string().min(1),
32
+ callId: z.string().min(1),
33
+ result: JsonValueSchema,
34
+ }),
35
+ z.object({
36
+ type: z.literal("task.failed"),
37
+ taskId: z.string().min(1),
38
+ callId: z.string().min(1),
39
+ error: z.string().min(1),
40
+ }),
41
+ z.object({
42
+ type: z.literal("task.cancelled"),
43
+ taskId: z.string().min(1),
44
+ callId: z.string().min(1),
45
+ reason: z.string().min(1),
46
+ }),
47
+ ]);
@@ -0,0 +1,24 @@
1
+ import type { AudioChunk, OutputModality, PlayoutProgress, ProviderCapabilities, ProviderContextEvent, ProviderEvent, ProviderToolDefinition, ProviderToolResult } from "../protocol/index.js";
2
+ export interface CreateProviderSessionOptions {
3
+ sessionId: string;
4
+ signal: AbortSignal;
5
+ instructions?: string;
6
+ /** What the model may produce this session; ["text"] disables audio output. Default: audio. */
7
+ outputModalities?: readonly OutputModality[];
8
+ tools: readonly ProviderToolDefinition[];
9
+ }
10
+ export interface VoiceProviderSession {
11
+ readonly events: AsyncIterable<ProviderEvent>;
12
+ sendAudio(chunk: AudioChunk): Promise<void>;
13
+ /** Injects a typed user message into the conversation; the model responds as usual. */
14
+ sendText(text: string): Promise<void>;
15
+ reportPlayout(progress: PlayoutProgress): Promise<void>;
16
+ sendToolResult(result: ProviderToolResult): Promise<void>;
17
+ sendContext(event: ProviderContextEvent): Promise<void>;
18
+ close(reason?: string): Promise<void>;
19
+ }
20
+ export interface VoiceProvider {
21
+ readonly id: string;
22
+ readonly capabilities: ProviderCapabilities;
23
+ createSession(options: CreateProviderSessionOptions): Promise<VoiceProviderSession>;
24
+ }
@@ -0,0 +1 @@
1
+ export {};