@the-open-engine/zeroshot 6.18.0 → 6.20.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/cli/index.js +2 -1
- package/lib/agent-cli-provider/omp-release.d.ts +15 -0
- package/lib/agent-cli-provider/omp-release.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-release.js +50 -0
- package/lib/agent-cli-provider/omp-release.js.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-protocol.d.ts +38 -0
- package/lib/agent-cli-provider/omp-rpc-protocol.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-protocol.js +267 -0
- package/lib/agent-cli-provider/omp-rpc-protocol.js.map +1 -0
- package/lib/agent-cli-provider/provider-registry.d.ts +16 -1
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +25 -1
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/provider-names.js +5 -0
- package/lib/settings.js +6 -3
- package/lib/setup-plan.js +5 -5
- package/lib/start-cluster.js +3 -3
- package/package.json +1 -1
- package/src/agent/agent-lifecycle.js +4 -4
- package/src/agent/agent-task-executor.js +7 -6
- package/src/agent/pr-verification.js +4 -1
- package/src/agent-cli-provider/omp-release.ts +63 -0
- package/src/agent-cli-provider/omp-rpc-protocol.ts +396 -0
- package/src/agent-cli-provider/provider-registry.ts +29 -1
- package/src/agent-cli-provider/single-agent-runtime.ts +2 -1
- package/src/agent-wrapper.js +3 -3
- package/src/claude-task-runner.js +5 -3
- package/src/config-validator.js +3 -2
- package/src/isolation-manager.js +6 -2
- package/src/orchestrator.js +3 -3
- package/src/preflight.js +2 -1
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { getNumber, getString, isRecord } from './json';
|
|
2
|
+
import { OMP_SUPPORTED_VERSION } from './omp-release';
|
|
3
|
+
|
|
4
|
+
// Purely for traceability/doc; no runtime coupling to omp-release's spawn/install concerns.
|
|
5
|
+
export const OMP_RPC_CONTRACT_RELEASE = OMP_SUPPORTED_VERSION;
|
|
6
|
+
|
|
7
|
+
export interface OmpRpcDecoderLimits {
|
|
8
|
+
readonly maxPhysicalFrameBytes: number;
|
|
9
|
+
readonly maxReassembledFrameBytes: number;
|
|
10
|
+
readonly maxConcurrentReassemblies: number;
|
|
11
|
+
readonly maxChunksPerFrame: number;
|
|
12
|
+
readonly maxInflightReassemblyBytes: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Verified against the pinned OMP release's rpc-frame.ts: MAX_RPC_FRAME_BYTES=1MiB, MAX_RPC_REASSEMBLED_BYTES=64MiB,
|
|
16
|
+
// RPC_CHUNK_PAYLOAD_BYTES=256KiB -> ceil(64MiB/256KiB)=256 chunks max. The real decoder tracks exactly one
|
|
17
|
+
// pending sequence at a time, so maxConcurrentReassemblies/maxInflightReassemblyBytes mirror that ceiling.
|
|
18
|
+
export const DEFAULT_OMP_RPC_DECODER_LIMITS: OmpRpcDecoderLimits = {
|
|
19
|
+
maxPhysicalFrameBytes: 1024 * 1024,
|
|
20
|
+
maxReassembledFrameBytes: 64 * 1024 * 1024,
|
|
21
|
+
maxConcurrentReassemblies: 1,
|
|
22
|
+
maxChunksPerFrame: 256,
|
|
23
|
+
maxInflightReassemblyBytes: 64 * 1024 * 1024,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface OmpRpcInboundFrame {
|
|
27
|
+
readonly type: string;
|
|
28
|
+
readonly [key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface OmpRpcCommand {
|
|
32
|
+
readonly id?: string;
|
|
33
|
+
readonly type: string;
|
|
34
|
+
readonly [key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class OmpRpcProtocolError extends Error {
|
|
38
|
+
readonly code: string;
|
|
39
|
+
|
|
40
|
+
constructor(code: string, message: string) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = 'OmpRpcProtocolError';
|
|
43
|
+
this.code = code;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const KNOWN_PRE_NEGOTIATION_FRAME_TYPES: ReadonlySet<string> = new Set([
|
|
48
|
+
'ready',
|
|
49
|
+
'available_commands_update',
|
|
50
|
+
'response',
|
|
51
|
+
'extension_error',
|
|
52
|
+
'agent_start',
|
|
53
|
+
'agent_end',
|
|
54
|
+
'turn_start',
|
|
55
|
+
'turn_end',
|
|
56
|
+
'message_start',
|
|
57
|
+
'message_update',
|
|
58
|
+
'message_end',
|
|
59
|
+
'tool_execution_start',
|
|
60
|
+
'tool_execution_update',
|
|
61
|
+
'tool_execution_end',
|
|
62
|
+
'auto_compaction_start',
|
|
63
|
+
'auto_compaction_end',
|
|
64
|
+
'auto_retry_start',
|
|
65
|
+
'auto_retry_end',
|
|
66
|
+
'ttsr_triggered',
|
|
67
|
+
'todo_reminder',
|
|
68
|
+
'todo_auto_clear',
|
|
69
|
+
'extension_ui_request',
|
|
70
|
+
'host_tool_call',
|
|
71
|
+
'host_tool_cancel',
|
|
72
|
+
'host_uri_request',
|
|
73
|
+
'host_uri_cancel',
|
|
74
|
+
'prompt_result',
|
|
75
|
+
'command_output',
|
|
76
|
+
'session_info_update',
|
|
77
|
+
'config_update',
|
|
78
|
+
'subagent_lifecycle',
|
|
79
|
+
'subagent_progress',
|
|
80
|
+
'subagent_event',
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
export function classifyOmpRpcFrameType(
|
|
84
|
+
type: string
|
|
85
|
+
): 'known-pre-negotiation' | 'v2-only' | 'unknown' {
|
|
86
|
+
if (type === 'rpc_chunk') return 'v2-only';
|
|
87
|
+
if (KNOWN_PRE_NEGOTIATION_FRAME_TYPES.has(type)) return 'known-pre-negotiation';
|
|
88
|
+
return 'unknown';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function assertNoPreNegotiationRpcChunk(frameType: string, negotiatedV2: boolean): void {
|
|
92
|
+
if (frameType === 'rpc_chunk' && !negotiatedV2) {
|
|
93
|
+
throw new OmpRpcProtocolError(
|
|
94
|
+
'pre-negotiation-rpc-chunk',
|
|
95
|
+
'rpc_chunk frame received before protocol v2 negotiation succeeded.'
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const STRICT_BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
101
|
+
|
|
102
|
+
function decodeStrictBase64(data: unknown): Buffer {
|
|
103
|
+
if (typeof data !== 'string' || data.length === 0 || !STRICT_BASE64_PATTERN.test(data)) {
|
|
104
|
+
throw new OmpRpcProtocolError(
|
|
105
|
+
'invalid-chunk-data',
|
|
106
|
+
"rpc_chunk 'data' must be a non-empty, canonically-padded base64 string."
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const bytes = Buffer.from(data, 'base64');
|
|
110
|
+
if (bytes.toString('base64') !== data) {
|
|
111
|
+
throw new OmpRpcProtocolError(
|
|
112
|
+
'invalid-chunk-data',
|
|
113
|
+
"rpc_chunk 'data' failed a base64 round-trip check (non-canonical encoding)."
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return bytes;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function decodeStrictUtf8(bytes: Buffer): string {
|
|
120
|
+
try {
|
|
121
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
122
|
+
} catch {
|
|
123
|
+
throw new OmpRpcProtocolError(
|
|
124
|
+
'invalid-utf8-in-reassembled-frame',
|
|
125
|
+
'Reassembled rpc_chunk sequence is not valid UTF-8.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function toPhysicalFrame(record: Record<string, unknown>): OmpRpcInboundFrame {
|
|
131
|
+
const type = getString(record, 'type');
|
|
132
|
+
if (type === null) {
|
|
133
|
+
throw new OmpRpcProtocolError(
|
|
134
|
+
'malformed-physical-frame',
|
|
135
|
+
"Physical frame is missing a string 'type' field."
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return { ...record, type };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function toReassembledFrame(record: Record<string, unknown>, chunkId: string): OmpRpcInboundFrame {
|
|
142
|
+
const type = getString(record, 'type');
|
|
143
|
+
if (type === null) {
|
|
144
|
+
throw new OmpRpcProtocolError(
|
|
145
|
+
'malformed-json-in-reassembled-frame',
|
|
146
|
+
`rpc_chunk sequence "${chunkId}" reassembled into an object missing a string 'type' field.`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return { ...record, type };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface ChunkMetadata {
|
|
153
|
+
readonly chunkId: string;
|
|
154
|
+
readonly index: number;
|
|
155
|
+
readonly count: number;
|
|
156
|
+
readonly byteLength: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function readChunkMetadata(
|
|
160
|
+
parsed: Record<string, unknown>,
|
|
161
|
+
limits: OmpRpcDecoderLimits
|
|
162
|
+
): ChunkMetadata {
|
|
163
|
+
const chunkId = getString(parsed, 'chunkId');
|
|
164
|
+
if (chunkId === null || chunkId.length === 0 || chunkId.length > 128) {
|
|
165
|
+
throw new OmpRpcProtocolError(
|
|
166
|
+
'invalid-chunk-metadata',
|
|
167
|
+
"rpc_chunk 'chunkId' must be a non-empty string of at most 128 characters."
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const index = getNumber(parsed, 'index');
|
|
172
|
+
const count = getNumber(parsed, 'count');
|
|
173
|
+
const byteLength = getNumber(parsed, 'byteLength');
|
|
174
|
+
if (
|
|
175
|
+
index === null ||
|
|
176
|
+
!Number.isSafeInteger(index) ||
|
|
177
|
+
index < 0 ||
|
|
178
|
+
count === null ||
|
|
179
|
+
!Number.isSafeInteger(count) ||
|
|
180
|
+
count < 2 ||
|
|
181
|
+
count > limits.maxChunksPerFrame ||
|
|
182
|
+
index >= count ||
|
|
183
|
+
byteLength === null ||
|
|
184
|
+
!Number.isSafeInteger(byteLength) ||
|
|
185
|
+
byteLength <= 0 ||
|
|
186
|
+
byteLength > limits.maxReassembledFrameBytes
|
|
187
|
+
) {
|
|
188
|
+
throw new OmpRpcProtocolError(
|
|
189
|
+
'invalid-chunk-metadata',
|
|
190
|
+
"rpc_chunk has invalid 'index'/'count'/'byteLength' metadata."
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { chunkId, index, count, byteLength };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
interface PendingReassembly {
|
|
198
|
+
readonly chunkId: string;
|
|
199
|
+
readonly count: number;
|
|
200
|
+
readonly byteLength: number;
|
|
201
|
+
nextIndex: number;
|
|
202
|
+
readonly chunks: Buffer[];
|
|
203
|
+
receivedBytes: number;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export class OmpRpcFrameDecoder {
|
|
207
|
+
private readonly limits: OmpRpcDecoderLimits;
|
|
208
|
+
private buffer: Buffer;
|
|
209
|
+
private finished: boolean;
|
|
210
|
+
private readonly pendingReassemblies: Map<string, PendingReassembly>;
|
|
211
|
+
private inflightReassemblyBytes: number;
|
|
212
|
+
|
|
213
|
+
constructor(limits: OmpRpcDecoderLimits) {
|
|
214
|
+
this.limits = limits;
|
|
215
|
+
this.buffer = Buffer.alloc(0);
|
|
216
|
+
this.finished = false;
|
|
217
|
+
this.pendingReassemblies = new Map();
|
|
218
|
+
this.inflightReassemblyBytes = 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
push(chunk: Uint8Array): readonly OmpRpcInboundFrame[] {
|
|
222
|
+
if (this.finished) {
|
|
223
|
+
throw new OmpRpcProtocolError('decoder-finished', 'push() called after finish().');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.buffer = Buffer.concat([this.buffer, Buffer.from(chunk)]);
|
|
227
|
+
const frames: OmpRpcInboundFrame[] = [];
|
|
228
|
+
|
|
229
|
+
for (;;) {
|
|
230
|
+
const newlineIndex = this.buffer.indexOf(0x0a);
|
|
231
|
+
if (newlineIndex === -1) {
|
|
232
|
+
if (this.buffer.byteLength > this.limits.maxPhysicalFrameBytes) {
|
|
233
|
+
throw new OmpRpcProtocolError(
|
|
234
|
+
'physical-frame-too-large',
|
|
235
|
+
`Buffered physical frame exceeds the ${this.limits.maxPhysicalFrameBytes}-byte limit without a terminating newline.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const lineBytes = this.buffer.subarray(0, newlineIndex);
|
|
242
|
+
this.buffer = this.buffer.subarray(newlineIndex + 1);
|
|
243
|
+
if (lineBytes.byteLength + 1 > this.limits.maxPhysicalFrameBytes) {
|
|
244
|
+
throw new OmpRpcProtocolError(
|
|
245
|
+
'physical-frame-too-large',
|
|
246
|
+
`Physical frame of ${lineBytes.byteLength + 1} bytes exceeds the ${this.limits.maxPhysicalFrameBytes}-byte limit.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const frame = this.consumeLine(lineBytes);
|
|
251
|
+
if (frame !== null) frames.push(frame);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return frames;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
finish(): void {
|
|
258
|
+
this.finished = true;
|
|
259
|
+
if (this.buffer.byteLength > 0) {
|
|
260
|
+
throw new OmpRpcProtocolError(
|
|
261
|
+
'incomplete-physical-frame',
|
|
262
|
+
'Stream ended with an unterminated physical frame (no trailing newline).'
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (this.pendingReassemblies.size > 0) {
|
|
266
|
+
throw new OmpRpcProtocolError(
|
|
267
|
+
'incomplete-chunk-sequence',
|
|
268
|
+
'Stream ended with an incomplete rpc_chunk sequence still pending.'
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private consumeLine(lineBytes: Buffer): OmpRpcInboundFrame | null {
|
|
274
|
+
let value: unknown;
|
|
275
|
+
try {
|
|
276
|
+
value = JSON.parse(lineBytes.toString('utf8'));
|
|
277
|
+
} catch {
|
|
278
|
+
throw new OmpRpcProtocolError('malformed-physical-frame', 'Physical frame is not valid JSON.');
|
|
279
|
+
}
|
|
280
|
+
if (!isRecord(value)) {
|
|
281
|
+
throw new OmpRpcProtocolError(
|
|
282
|
+
'malformed-physical-frame',
|
|
283
|
+
'Physical frame must be a JSON object.'
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const type = getString(value, 'type');
|
|
288
|
+
if (type !== 'rpc_chunk') {
|
|
289
|
+
if (this.pendingReassemblies.size > 0) {
|
|
290
|
+
throw new OmpRpcProtocolError(
|
|
291
|
+
'interrupted-chunk-sequence',
|
|
292
|
+
'A non-chunk frame arrived while an rpc_chunk sequence was pending.'
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
return toPhysicalFrame(value);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return this.consumeChunk(value);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private consumeChunk(parsed: Record<string, unknown>): OmpRpcInboundFrame | null {
|
|
302
|
+
const metadata = readChunkMetadata(parsed, this.limits);
|
|
303
|
+
const bytes = decodeStrictBase64(parsed.data);
|
|
304
|
+
|
|
305
|
+
let pending = this.pendingReassemblies.get(metadata.chunkId);
|
|
306
|
+
if (pending === undefined) {
|
|
307
|
+
if (metadata.index !== 0) {
|
|
308
|
+
throw new OmpRpcProtocolError(
|
|
309
|
+
'chunk-sequence-must-start-at-zero',
|
|
310
|
+
`rpc_chunk sequence "${metadata.chunkId}" must begin at index 0.`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (this.pendingReassemblies.size >= this.limits.maxConcurrentReassemblies) {
|
|
314
|
+
throw new OmpRpcProtocolError(
|
|
315
|
+
'interleaved-chunk-sequence',
|
|
316
|
+
`Starting rpc_chunk sequence "${metadata.chunkId}" would exceed the concurrent-reassembly limit of ${this.limits.maxConcurrentReassemblies}.`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
pending = {
|
|
320
|
+
chunkId: metadata.chunkId,
|
|
321
|
+
count: metadata.count,
|
|
322
|
+
byteLength: metadata.byteLength,
|
|
323
|
+
nextIndex: 0,
|
|
324
|
+
chunks: [],
|
|
325
|
+
receivedBytes: 0,
|
|
326
|
+
};
|
|
327
|
+
this.pendingReassemblies.set(metadata.chunkId, pending);
|
|
328
|
+
} else if (
|
|
329
|
+
pending.count !== metadata.count ||
|
|
330
|
+
pending.byteLength !== metadata.byteLength ||
|
|
331
|
+
pending.nextIndex !== metadata.index
|
|
332
|
+
) {
|
|
333
|
+
throw new OmpRpcProtocolError(
|
|
334
|
+
'chunk-sequence-mismatch',
|
|
335
|
+
`rpc_chunk sequence "${metadata.chunkId}" metadata or ordering does not match the tracked sequence.`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
pending.chunks.push(bytes);
|
|
340
|
+
pending.receivedBytes += bytes.byteLength;
|
|
341
|
+
this.inflightReassemblyBytes += bytes.byteLength;
|
|
342
|
+
if (this.inflightReassemblyBytes > this.limits.maxInflightReassemblyBytes) {
|
|
343
|
+
throw new OmpRpcProtocolError(
|
|
344
|
+
'inflight-reassembly-bytes-exceeded',
|
|
345
|
+
`Total in-flight rpc_chunk reassembly bytes exceed the ${this.limits.maxInflightReassemblyBytes}-byte limit.`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (pending.receivedBytes > pending.byteLength) {
|
|
349
|
+
throw new OmpRpcProtocolError(
|
|
350
|
+
'chunk-sequence-exceeds-declared-length',
|
|
351
|
+
`rpc_chunk sequence "${metadata.chunkId}" received more bytes than its declared byteLength.`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
pending.nextIndex += 1;
|
|
355
|
+
if (pending.nextIndex < pending.count) return null;
|
|
356
|
+
|
|
357
|
+
this.pendingReassemblies.delete(metadata.chunkId);
|
|
358
|
+
this.inflightReassemblyBytes -= pending.receivedBytes;
|
|
359
|
+
if (pending.receivedBytes !== pending.byteLength) {
|
|
360
|
+
throw new OmpRpcProtocolError(
|
|
361
|
+
'chunk-sequence-length-mismatch',
|
|
362
|
+
`rpc_chunk sequence "${metadata.chunkId}" completed with ${pending.receivedBytes} bytes but declared ${pending.byteLength}.`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const decodedText = decodeStrictUtf8(Buffer.concat(pending.chunks));
|
|
367
|
+
let reassembled: unknown;
|
|
368
|
+
try {
|
|
369
|
+
reassembled = JSON.parse(decodedText);
|
|
370
|
+
} catch {
|
|
371
|
+
throw new OmpRpcProtocolError(
|
|
372
|
+
'malformed-json-in-reassembled-frame',
|
|
373
|
+
`rpc_chunk sequence "${metadata.chunkId}" reassembled into invalid JSON.`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
if (!isRecord(reassembled)) {
|
|
377
|
+
throw new OmpRpcProtocolError(
|
|
378
|
+
'non-object-reassembled-frame',
|
|
379
|
+
`rpc_chunk sequence "${metadata.chunkId}" reassembled into a non-object JSON value.`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return toReassembledFrame(reassembled, metadata.chunkId);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function encodeOmpRpcCommand(command: OmpRpcCommand, maxFrameBytes: number): Buffer {
|
|
387
|
+
const line = `${JSON.stringify(command)}\n`;
|
|
388
|
+
const byteLength = Buffer.byteLength(line, 'utf8');
|
|
389
|
+
if (byteLength > maxFrameBytes) {
|
|
390
|
+
throw new OmpRpcProtocolError(
|
|
391
|
+
'outbound-frame-too-large',
|
|
392
|
+
`Outbound command of ${byteLength} bytes exceeds the ${maxFrameBytes}-byte limit.`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
return Buffer.from(line, 'utf8');
|
|
396
|
+
}
|
|
@@ -8,6 +8,7 @@ import { opencodeAdapter } from './adapters/opencode';
|
|
|
8
8
|
import { ompAdapter } from './adapters/omp';
|
|
9
9
|
import { piAdapter } from './adapters/pi';
|
|
10
10
|
import { resolveClaudeCommand } from './claude-command';
|
|
11
|
+
import { OMP_INSTALL_COMMAND } from './omp-release';
|
|
11
12
|
import type { ModelLevel, ProviderAdapter, StructuredOutputRecoveryAdapter } from './types';
|
|
12
13
|
|
|
13
14
|
export type ProviderCapabilityState = boolean | 'experimental';
|
|
@@ -71,6 +72,7 @@ export interface ProviderDockerMetadata {
|
|
|
71
72
|
|
|
72
73
|
interface ProviderRegistryEntryBase {
|
|
73
74
|
readonly id: string;
|
|
75
|
+
readonly default: boolean;
|
|
74
76
|
readonly aliases: readonly string[];
|
|
75
77
|
readonly displayName: string;
|
|
76
78
|
readonly binary: string;
|
|
@@ -176,6 +178,7 @@ const kiroAdapter = createAcpAdapter({
|
|
|
176
178
|
export const providerRegistry = [
|
|
177
179
|
{
|
|
178
180
|
id: 'claude',
|
|
181
|
+
default: true,
|
|
179
182
|
aliases: ['anthropic'],
|
|
180
183
|
displayName: 'Claude',
|
|
181
184
|
binary: 'claude',
|
|
@@ -214,6 +217,7 @@ export const providerRegistry = [
|
|
|
214
217
|
},
|
|
215
218
|
{
|
|
216
219
|
id: 'codex',
|
|
220
|
+
default: false,
|
|
217
221
|
aliases: ['openai'],
|
|
218
222
|
displayName: 'Codex',
|
|
219
223
|
binary: 'codex',
|
|
@@ -255,6 +259,7 @@ export const providerRegistry = [
|
|
|
255
259
|
},
|
|
256
260
|
{
|
|
257
261
|
id: 'gateway',
|
|
262
|
+
default: false,
|
|
258
263
|
aliases: [],
|
|
259
264
|
displayName: 'Gateway',
|
|
260
265
|
binary: 'node',
|
|
@@ -303,6 +308,7 @@ export const providerRegistry = [
|
|
|
303
308
|
},
|
|
304
309
|
{
|
|
305
310
|
id: 'gemini',
|
|
311
|
+
default: false,
|
|
306
312
|
aliases: ['google'],
|
|
307
313
|
displayName: 'Gemini',
|
|
308
314
|
binary: 'gemini',
|
|
@@ -340,6 +346,7 @@ export const providerRegistry = [
|
|
|
340
346
|
},
|
|
341
347
|
{
|
|
342
348
|
id: 'opencode',
|
|
349
|
+
default: false,
|
|
343
350
|
aliases: [],
|
|
344
351
|
displayName: 'Opencode',
|
|
345
352
|
binary: 'opencode',
|
|
@@ -380,6 +387,7 @@ export const providerRegistry = [
|
|
|
380
387
|
},
|
|
381
388
|
{
|
|
382
389
|
id: 'pi',
|
|
390
|
+
default: false,
|
|
383
391
|
aliases: [],
|
|
384
392
|
displayName: 'Pi',
|
|
385
393
|
binary: 'pi',
|
|
@@ -419,12 +427,13 @@ export const providerRegistry = [
|
|
|
419
427
|
},
|
|
420
428
|
{
|
|
421
429
|
id: 'omp',
|
|
430
|
+
default: false,
|
|
422
431
|
aliases: [],
|
|
423
432
|
displayName: 'OMP',
|
|
424
433
|
binary: 'omp',
|
|
425
434
|
command: { kind: 'fixed', command: 'omp', args: [] },
|
|
426
435
|
invoke: SPAWN_INVOKE,
|
|
427
|
-
installInstructions:
|
|
436
|
+
installInstructions: OMP_INSTALL_COMMAND,
|
|
428
437
|
authInstructions: 'omp\n/login',
|
|
429
438
|
credentialPaths: ['~/.omp'],
|
|
430
439
|
credentialEnvKeys: ompAdapter.credentialEnvKeys,
|
|
@@ -457,6 +466,7 @@ export const providerRegistry = [
|
|
|
457
466
|
},
|
|
458
467
|
{
|
|
459
468
|
id: 'kiro',
|
|
469
|
+
default: false,
|
|
460
470
|
aliases: [],
|
|
461
471
|
displayName: 'Kiro',
|
|
462
472
|
binary: 'kiro-cli',
|
|
@@ -494,6 +504,7 @@ export const providerRegistry = [
|
|
|
494
504
|
},
|
|
495
505
|
{
|
|
496
506
|
id: 'copilot',
|
|
507
|
+
default: false,
|
|
497
508
|
aliases: [],
|
|
498
509
|
displayName: 'Copilot',
|
|
499
510
|
binary: 'copilot',
|
|
@@ -565,6 +576,23 @@ export const providerAliasMap: Readonly<Record<string, RegistryProviderId>> = Ob
|
|
|
565
576
|
}, {})
|
|
566
577
|
);
|
|
567
578
|
|
|
579
|
+
export function assertExactlyOneDefaultProvider<T extends { id: string; default: boolean }>(
|
|
580
|
+
entries: readonly T[]
|
|
581
|
+
): T['id'] {
|
|
582
|
+
const defaults = entries.filter((e) => e.default);
|
|
583
|
+
const [onlyDefault, ...rest] = defaults;
|
|
584
|
+
if (!onlyDefault || rest.length > 0) {
|
|
585
|
+
throw new Error(
|
|
586
|
+
`Provider registry must declare exactly one default provider; found ${defaults.length}${defaults.length ? ' (' + defaults.map((e) => e.id).join(', ') + ')' : ''}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return onlyDefault.id;
|
|
590
|
+
}
|
|
591
|
+
const DEFAULT_PROVIDER_ID = assertExactlyOneDefaultProvider(providerRegistry);
|
|
592
|
+
export function getDefaultProviderId(): RegistryProviderId {
|
|
593
|
+
return DEFAULT_PROVIDER_ID;
|
|
594
|
+
}
|
|
595
|
+
|
|
568
596
|
export function normalizeProviderName(name: string): RegistryProviderId | string {
|
|
569
597
|
const normalized = name.toLowerCase();
|
|
570
598
|
return providerAliasMap[normalized] ?? name;
|
|
@@ -3,6 +3,7 @@ import { UnsupportedProviderCapabilityError } from './errors';
|
|
|
3
3
|
import { normalizeGatewayBuildOptions, resolveGatewayConfiguration } from './gateway-tools';
|
|
4
4
|
import { isRecord } from './json';
|
|
5
5
|
import {
|
|
6
|
+
getDefaultProviderId,
|
|
6
7
|
getProviderRegistryEntry,
|
|
7
8
|
resolveProviderCommand,
|
|
8
9
|
supportsProviderCapability,
|
|
@@ -414,7 +415,7 @@ function adapterForRuntimeInput(
|
|
|
414
415
|
): ProviderAdapter {
|
|
415
416
|
const configured =
|
|
416
417
|
provider ?? optionalString(settings.defaultProvider, 'settings.defaultProvider');
|
|
417
|
-
return getProviderAdapter(configured ??
|
|
418
|
+
return getProviderAdapter(configured ?? getDefaultProviderId());
|
|
418
419
|
}
|
|
419
420
|
|
|
420
421
|
function runtimeProviderSettings(
|
package/src/agent-wrapper.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
const LogicEngine = require('./logic-engine');
|
|
14
14
|
const { validateAgentConfig } = require('./agent/agent-config');
|
|
15
15
|
const { loadSettings, validateModelAgainstMax, VALID_MODELS } = require('../lib/settings');
|
|
16
|
-
const { normalizeProviderName } = require('../lib/provider-names');
|
|
16
|
+
const { normalizeProviderName, getDefaultProviderId } = require('../lib/provider-names');
|
|
17
17
|
const { getProvider } = require('./providers');
|
|
18
18
|
const { buildContext } = require('./agent/agent-context-builder');
|
|
19
19
|
const { collectQueuedGuidance } = require('./agent/guidance-queue');
|
|
@@ -160,9 +160,9 @@ class AgentWrapper {
|
|
|
160
160
|
this.config.provider ||
|
|
161
161
|
clusterConfig.defaultProvider ||
|
|
162
162
|
settings.defaultProvider ||
|
|
163
|
-
|
|
163
|
+
getDefaultProviderId();
|
|
164
164
|
|
|
165
|
-
return normalizeProviderName(resolved) ||
|
|
165
|
+
return normalizeProviderName(resolved) || getDefaultProviderId();
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
_resolveModelSpec() {
|
|
@@ -9,7 +9,7 @@ const { spawn, spawnSync } = require('child_process');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const TaskRunner = require('./task-runner');
|
|
11
11
|
const { loadSettings } = require('../lib/settings');
|
|
12
|
-
const { normalizeProviderName } = require('../lib/provider-names');
|
|
12
|
+
const { normalizeProviderName, getDefaultProviderId } = require('../lib/provider-names');
|
|
13
13
|
const { getProvider } = require('./providers');
|
|
14
14
|
const { prependWorktreeToolBinToEnv } = require('./worktree-tooling-env');
|
|
15
15
|
const { applyDarwinKeychainBoundaryToEnv } = require('./darwin-keychain-boundary');
|
|
@@ -200,7 +200,9 @@ class ClaudeTaskRunner extends TaskRunner {
|
|
|
200
200
|
} = options;
|
|
201
201
|
|
|
202
202
|
const settings = loadSettings();
|
|
203
|
-
const providerName = normalizeProviderName(
|
|
203
|
+
const providerName = normalizeProviderName(
|
|
204
|
+
provider || settings.defaultProvider || getDefaultProviderId()
|
|
205
|
+
);
|
|
204
206
|
const { providerModule, providerSettings, levelOverrides } = this._getProviderContext(
|
|
205
207
|
providerName,
|
|
206
208
|
settings
|
|
@@ -736,7 +738,7 @@ class ClaudeTaskRunner extends TaskRunner {
|
|
|
736
738
|
rejectCallerSuppliedModelProvenance(options);
|
|
737
739
|
const {
|
|
738
740
|
agentId = 'unknown',
|
|
739
|
-
provider =
|
|
741
|
+
provider = getDefaultProviderId(),
|
|
740
742
|
model = null,
|
|
741
743
|
modelLevel = null,
|
|
742
744
|
modelSpec: explicitModelSpec = null,
|
package/src/config-validator.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
const { loadSettings } = require('../lib/settings');
|
|
15
15
|
const {
|
|
16
16
|
VALID_PROVIDERS,
|
|
17
|
+
getDefaultProviderId,
|
|
17
18
|
normalizeProviderName,
|
|
18
19
|
providerSupportsCapability,
|
|
19
20
|
} = require('../lib/provider-names');
|
|
@@ -1987,8 +1988,8 @@ function resolveProviderName(agent, config, settings) {
|
|
|
1987
1988
|
agent.provider ||
|
|
1988
1989
|
config.defaultProvider ||
|
|
1989
1990
|
settings.defaultProvider ||
|
|
1990
|
-
|
|
1991
|
-
return normalizeProviderName(resolved) ||
|
|
1991
|
+
getDefaultProviderId();
|
|
1992
|
+
return normalizeProviderName(resolved) || getDefaultProviderId();
|
|
1992
1993
|
}
|
|
1993
1994
|
|
|
1994
1995
|
function validateProviderLevel(provider, requestedLevel, minLevel, maxLevel) {
|
package/src/isolation-manager.js
CHANGED
|
@@ -16,7 +16,11 @@ const os = require('os');
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const { loadSettings } = require('../lib/settings');
|
|
18
18
|
const { CLAUDE_AUTH_ENV_VARS, resolveClaudeAuth } = require('../lib/settings/claude-auth');
|
|
19
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
normalizeProviderName,
|
|
21
|
+
getProviderMetadata,
|
|
22
|
+
getDefaultProviderId,
|
|
23
|
+
} = require('../lib/provider-names');
|
|
20
24
|
const {
|
|
21
25
|
MOUNT_PRESETS,
|
|
22
26
|
resolveMounts,
|
|
@@ -236,7 +240,7 @@ class IsolationManager {
|
|
|
236
240
|
|
|
237
241
|
const settings = loadSettings();
|
|
238
242
|
const providerName = normalizeProviderName(
|
|
239
|
-
config.provider || settings.defaultProvider ||
|
|
243
|
+
config.provider || settings.defaultProvider || getDefaultProviderId()
|
|
240
244
|
);
|
|
241
245
|
const containerHome = config.containerHome || settings.dockerContainerHome || '/root';
|
|
242
246
|
|
package/src/orchestrator.js
CHANGED
|
@@ -46,7 +46,7 @@ const { generateName } = require('./name-generator');
|
|
|
46
46
|
const configValidator = require('./config-validator');
|
|
47
47
|
const TemplateResolver = require('./template-resolver');
|
|
48
48
|
const { loadSettings } = require('../lib/settings');
|
|
49
|
-
const { normalizeProviderName } = require('../lib/provider-names');
|
|
49
|
+
const { normalizeProviderName, getDefaultProviderId } = require('../lib/provider-names');
|
|
50
50
|
const { resolveRunPlan } = require('../lib/run-plan');
|
|
51
51
|
const { isProcessRunning } = require('../lib/process-liveness');
|
|
52
52
|
const { getProvider } = require('./providers');
|
|
@@ -313,8 +313,8 @@ class Orchestrator {
|
|
|
313
313
|
clusterConfig.forceProvider ||
|
|
314
314
|
clusterConfig.defaultProvider ||
|
|
315
315
|
settings.defaultProvider ||
|
|
316
|
-
|
|
317
|
-
return normalizeProviderName(resolved) ||
|
|
316
|
+
getDefaultProviderId();
|
|
317
|
+
return normalizeProviderName(resolved) || getDefaultProviderId();
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
/**
|
package/src/preflight.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
const { loadSettings, getClaudeCommand } = require('../lib/settings.js');
|
|
22
22
|
const {
|
|
23
23
|
VALID_PROVIDERS,
|
|
24
|
+
getDefaultProviderId,
|
|
24
25
|
getProviderMetadata,
|
|
25
26
|
normalizeProviderName,
|
|
26
27
|
resolveProviderCommand,
|
|
@@ -597,7 +598,7 @@ async function runPreflight(options = {}) {
|
|
|
597
598
|
};
|
|
598
599
|
}
|
|
599
600
|
const providerName = normalizeProviderName(
|
|
600
|
-
options.provider || settings.defaultProvider ||
|
|
601
|
+
options.provider || settings.defaultProvider || getDefaultProviderId()
|
|
601
602
|
);
|
|
602
603
|
|
|
603
604
|
const providerResult = validateProvider(providerName, options);
|