@the-open-engine/zeroshot 6.22.0 → 6.23.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/lib/agent-cli-provider/adapters/omp.d.ts +3 -1
- package/lib/agent-cli-provider/adapters/omp.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/omp.js +252 -269
- package/lib/agent-cli-provider/adapters/omp.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +68 -14
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +5 -3
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/index.d.ts +6 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +12 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/omp-release.d.ts +3 -1
- package/lib/agent-cli-provider/omp-release.d.ts.map +1 -1
- package/lib/agent-cli-provider/omp-release.js +22 -2
- package/lib/agent-cli-provider/omp-release.js.map +1 -1
- package/lib/agent-cli-provider/omp-rpc-bounds.d.ts +7 -0
- package/lib/agent-cli-provider/omp-rpc-bounds.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-bounds.js +27 -0
- package/lib/agent-cli-provider/omp-rpc-bounds.js.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-driver.d.ts +40 -0
- package/lib/agent-cli-provider/omp-rpc-driver.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-driver.js +494 -0
- package/lib/agent-cli-provider/omp-rpc-driver.js.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-events.d.ts +28 -0
- package/lib/agent-cli-provider/omp-rpc-events.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-events.js +264 -0
- package/lib/agent-cli-provider/omp-rpc-events.js.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-protocol.d.ts +1 -1
- package/lib/agent-cli-provider/omp-rpc-protocol.d.ts.map +1 -1
- package/lib/agent-cli-provider/omp-rpc-protocol.js +8 -3
- package/lib/agent-cli-provider/omp-rpc-protocol.js.map +1 -1
- package/lib/agent-cli-provider/omp-rpc-session.d.ts +17 -0
- package/lib/agent-cli-provider/omp-rpc-session.d.ts.map +1 -0
- package/lib/agent-cli-provider/omp-rpc-session.js +6 -0
- package/lib/agent-cli-provider/omp-rpc-session.js.map +1 -0
- package/lib/agent-cli-provider/provider-registry.d.ts +15 -11
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +16 -5
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +13 -11
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/package.json +2 -1
- package/src/agent-cli-provider/adapters/omp.ts +276 -329
- package/src/agent-cli-provider/contract-invoke.ts +86 -15
- package/src/agent-cli-provider/contract-options.ts +5 -3
- package/src/agent-cli-provider/index.ts +13 -0
- package/src/agent-cli-provider/omp-release.ts +24 -1
- package/src/agent-cli-provider/omp-rpc-bounds.ts +28 -0
- package/src/agent-cli-provider/omp-rpc-driver.ts +611 -0
- package/src/agent-cli-provider/omp-rpc-events.ts +318 -0
- package/src/agent-cli-provider/omp-rpc-protocol.ts +8 -3
- package/src/agent-cli-provider/omp-rpc-session.ts +20 -0
- package/src/agent-cli-provider/provider-registry.ts +26 -7
- package/src/agent-cli-provider/types.ts +14 -11
- package/src/command-cleanup-ownership.js +307 -0
- package/src/omp-config-overlay.js +96 -0
- package/src/orchestrator.js +20 -5
- package/src/preflight.js +35 -3
- package/src/watcher-prompt-channel.js +209 -0
- package/task-lib/command-spec-cleanup.js +1 -262
- package/task-lib/provider-helper-runtime.js +6 -0
- package/task-lib/rpc-watcher.js +186 -0
- package/task-lib/runner.js +40 -6
- package/task-lib/watcher-output-runtime.js +32 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { OmpRpcProtocolError, type OmpRpcInboundFrame } from './omp-rpc-protocol';
|
|
2
|
+
import { MAX_NORMALIZED_OUTPUT_BYTES } from './omp-rpc-bounds';
|
|
3
|
+
import {
|
|
4
|
+
getArray,
|
|
5
|
+
getBoolean,
|
|
6
|
+
getNumber,
|
|
7
|
+
getOptionalString,
|
|
8
|
+
getRecord,
|
|
9
|
+
getString,
|
|
10
|
+
isRecord,
|
|
11
|
+
stringifyContent,
|
|
12
|
+
tryParseJson,
|
|
13
|
+
} from './json';
|
|
14
|
+
import type { OutputEvent, ProviderParseResult, ProviderParserState } from './types';
|
|
15
|
+
|
|
16
|
+
export interface OmpRpcEventState {
|
|
17
|
+
lastAssistantText: string;
|
|
18
|
+
lastAssistantThinking: string;
|
|
19
|
+
lastToolId: string | null | undefined;
|
|
20
|
+
normalizedBytes: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createOmpRpcEventState(): OmpRpcEventState {
|
|
24
|
+
return {
|
|
25
|
+
lastAssistantText: '',
|
|
26
|
+
lastAssistantThinking: '',
|
|
27
|
+
lastToolId: undefined,
|
|
28
|
+
normalizedBytes: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function assistantSnapshot(message: Record<string, unknown>): { text: string; thinking: string } {
|
|
33
|
+
if (getString(message, 'role') !== 'assistant') return { text: '', thinking: '' };
|
|
34
|
+
|
|
35
|
+
let text = '';
|
|
36
|
+
let thinking = '';
|
|
37
|
+
for (const item of getArray(message, 'content')) {
|
|
38
|
+
if (!isRecord(item)) continue;
|
|
39
|
+
const type = getString(item, 'type');
|
|
40
|
+
if (type === 'text') {
|
|
41
|
+
text += getString(item, 'text') ?? '';
|
|
42
|
+
} else if (type === 'thinking') {
|
|
43
|
+
thinking += getString(item, 'thinking') ?? '';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { text, thinking };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function snapshotDelta(previous: string, current: string): string | null {
|
|
51
|
+
if (!current || previous === current) return null;
|
|
52
|
+
if (current.startsWith(previous)) return current.slice(previous.length) || null;
|
|
53
|
+
return current;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function chargeBytes(state: OmpRpcEventState, value: unknown): void {
|
|
57
|
+
const byteLength = Buffer.byteLength(stringifyContent(value), 'utf8');
|
|
58
|
+
state.normalizedBytes += byteLength;
|
|
59
|
+
if (state.normalizedBytes > MAX_NORMALIZED_OUTPUT_BYTES) {
|
|
60
|
+
throw new OmpRpcProtocolError(
|
|
61
|
+
'output-bound-exceeded',
|
|
62
|
+
`Normalized OMP RPC output exceeded the ${MAX_NORMALIZED_OUTPUT_BYTES}-byte cap for this task.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function emitText(state: OmpRpcEventState, text: string): OutputEvent {
|
|
68
|
+
chargeBytes(state, text);
|
|
69
|
+
return { type: 'text', text };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function emitThinking(state: OmpRpcEventState, text: string): OutputEvent {
|
|
73
|
+
chargeBytes(state, text);
|
|
74
|
+
return { type: 'thinking', text };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeMessageStart(
|
|
78
|
+
frame: OmpRpcInboundFrame,
|
|
79
|
+
state: OmpRpcEventState
|
|
80
|
+
): readonly OutputEvent[] {
|
|
81
|
+
const message = getRecord(frame, 'message');
|
|
82
|
+
if (message !== null && getString(message, 'role') === 'assistant') {
|
|
83
|
+
state.lastAssistantText = '';
|
|
84
|
+
state.lastAssistantThinking = '';
|
|
85
|
+
}
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeAssistantMessageEvent(
|
|
90
|
+
event: Record<string, unknown>,
|
|
91
|
+
state: OmpRpcEventState
|
|
92
|
+
): readonly OutputEvent[] {
|
|
93
|
+
const type = getString(event, 'type');
|
|
94
|
+
if (type === 'text_delta') {
|
|
95
|
+
const delta = getString(event, 'delta');
|
|
96
|
+
if (!delta) return [];
|
|
97
|
+
state.lastAssistantText += delta;
|
|
98
|
+
return [emitText(state, delta)];
|
|
99
|
+
}
|
|
100
|
+
if (type === 'thinking_delta') {
|
|
101
|
+
const delta = getString(event, 'delta');
|
|
102
|
+
if (!delta) return [];
|
|
103
|
+
state.lastAssistantThinking += delta;
|
|
104
|
+
return [emitThinking(state, delta)];
|
|
105
|
+
}
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function normalizeMessageSnapshot(
|
|
110
|
+
message: Record<string, unknown>,
|
|
111
|
+
state: OmpRpcEventState
|
|
112
|
+
): readonly OutputEvent[] {
|
|
113
|
+
const snapshot = assistantSnapshot(message);
|
|
114
|
+
const events: OutputEvent[] = [];
|
|
115
|
+
|
|
116
|
+
const textDelta = snapshotDelta(state.lastAssistantText, snapshot.text);
|
|
117
|
+
if (textDelta) events.push(emitText(state, textDelta));
|
|
118
|
+
if (snapshot.text) state.lastAssistantText = snapshot.text;
|
|
119
|
+
|
|
120
|
+
const thinkingDelta = snapshotDelta(state.lastAssistantThinking, snapshot.thinking);
|
|
121
|
+
if (thinkingDelta) events.push(emitThinking(state, thinkingDelta));
|
|
122
|
+
if (snapshot.thinking) state.lastAssistantThinking = snapshot.thinking;
|
|
123
|
+
|
|
124
|
+
return events;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function normalizeMessageUpdate(
|
|
128
|
+
frame: OmpRpcInboundFrame,
|
|
129
|
+
state: OmpRpcEventState
|
|
130
|
+
): readonly OutputEvent[] {
|
|
131
|
+
const assistantMessageEvent = getRecord(frame, 'assistantMessageEvent');
|
|
132
|
+
if (assistantMessageEvent !== null) {
|
|
133
|
+
const events = normalizeAssistantMessageEvent(assistantMessageEvent, state);
|
|
134
|
+
if (events.length > 0) return events;
|
|
135
|
+
}
|
|
136
|
+
const message = getRecord(frame, 'message');
|
|
137
|
+
return message === null ? [] : normalizeMessageSnapshot(message, state);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function normalizeMessageEnd(
|
|
141
|
+
frame: OmpRpcInboundFrame,
|
|
142
|
+
state: OmpRpcEventState
|
|
143
|
+
): readonly OutputEvent[] {
|
|
144
|
+
const message = getRecord(frame, 'message');
|
|
145
|
+
return message === null ? [] : normalizeMessageSnapshot(message, state);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function normalizeToolExecutionStart(
|
|
149
|
+
frame: OmpRpcInboundFrame,
|
|
150
|
+
state: OmpRpcEventState
|
|
151
|
+
): readonly OutputEvent[] {
|
|
152
|
+
const toolId = getOptionalString(frame, 'toolCallId');
|
|
153
|
+
state.lastToolId = toolId;
|
|
154
|
+
const input = frame.args ?? {};
|
|
155
|
+
chargeBytes(state, input);
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
type: 'tool_call',
|
|
159
|
+
toolName: getOptionalString(frame, 'toolName'),
|
|
160
|
+
toolId,
|
|
161
|
+
input,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeToolExecutionUpdate(
|
|
167
|
+
frame: OmpRpcInboundFrame,
|
|
168
|
+
state: OmpRpcEventState
|
|
169
|
+
): readonly OutputEvent[] {
|
|
170
|
+
const toolId = getOptionalString(frame, 'toolCallId') ?? state.lastToolId;
|
|
171
|
+
if (toolId !== undefined) state.lastToolId = toolId;
|
|
172
|
+
if (!Object.prototype.hasOwnProperty.call(frame, 'partialResult')) return [];
|
|
173
|
+
chargeBytes(state, frame.partialResult);
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
type: 'tool_result',
|
|
177
|
+
toolId,
|
|
178
|
+
content: frame.partialResult,
|
|
179
|
+
isError: false,
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeToolExecutionEnd(
|
|
185
|
+
frame: OmpRpcInboundFrame,
|
|
186
|
+
state: OmpRpcEventState
|
|
187
|
+
): readonly OutputEvent[] {
|
|
188
|
+
const toolId = getOptionalString(frame, 'toolCallId') ?? state.lastToolId;
|
|
189
|
+
const content = frame.result ?? '';
|
|
190
|
+
chargeBytes(state, content);
|
|
191
|
+
return [
|
|
192
|
+
{
|
|
193
|
+
type: 'tool_result',
|
|
194
|
+
toolId,
|
|
195
|
+
content,
|
|
196
|
+
isError: getBoolean(frame, 'isError') ?? false,
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeTurnEnd(
|
|
202
|
+
frame: OmpRpcInboundFrame,
|
|
203
|
+
state: OmpRpcEventState
|
|
204
|
+
): readonly OutputEvent[] {
|
|
205
|
+
const message = getRecord(frame, 'message');
|
|
206
|
+
const usage = message ? (getRecord(message, 'usage') ?? {}) : {};
|
|
207
|
+
const stopReason = message ? getString(message, 'stopReason') : null;
|
|
208
|
+
const errorMessage = message ? getString(message, 'errorMessage') : null;
|
|
209
|
+
const snapshot = message ? assistantSnapshot(message) : { text: '', thinking: '' };
|
|
210
|
+
const success = stopReason !== 'error' && stopReason !== 'aborted' && !errorMessage;
|
|
211
|
+
const result = success ? snapshot.text || state.lastAssistantText || null : null;
|
|
212
|
+
chargeBytes(state, { result, error: errorMessage ?? stopReason ?? null });
|
|
213
|
+
return [
|
|
214
|
+
{
|
|
215
|
+
type: 'result',
|
|
216
|
+
success,
|
|
217
|
+
result,
|
|
218
|
+
error: success ? null : (errorMessage ?? stopReason ?? 'OMP turn failed'),
|
|
219
|
+
inputTokens: getNumber(usage, 'input') ?? 0,
|
|
220
|
+
outputTokens: getNumber(usage, 'output') ?? 0,
|
|
221
|
+
cacheReadInputTokens: getNumber(usage, 'cacheRead') ?? 0,
|
|
222
|
+
cacheCreationInputTokens: getNumber(usage, 'cacheWrite') ?? 0,
|
|
223
|
+
cost: getRecord(usage, 'cost') ?? null,
|
|
224
|
+
modelUsage: usage,
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Normalize one decoded OMP RPC v2 event frame into zero or more OutputEvents. Only the frame
|
|
231
|
+
* types below produce output; every other known-pre-negotiation frame (agent_start/agent_end,
|
|
232
|
+
* turn_start, ready, response, session_info_update, subagent_*, ...) is protocol/control plumbing
|
|
233
|
+
* the driver consumes directly and must never leak into log/attach/ledger output.
|
|
234
|
+
*/
|
|
235
|
+
export function normalizeOmpRpcFrame(
|
|
236
|
+
frame: OmpRpcInboundFrame,
|
|
237
|
+
state: OmpRpcEventState
|
|
238
|
+
): readonly OutputEvent[] {
|
|
239
|
+
switch (frame.type) {
|
|
240
|
+
case 'message_start':
|
|
241
|
+
return normalizeMessageStart(frame, state);
|
|
242
|
+
case 'message_update':
|
|
243
|
+
return normalizeMessageUpdate(frame, state);
|
|
244
|
+
case 'message_end':
|
|
245
|
+
return normalizeMessageEnd(frame, state);
|
|
246
|
+
case 'tool_execution_start':
|
|
247
|
+
return normalizeToolExecutionStart(frame, state);
|
|
248
|
+
case 'tool_execution_update':
|
|
249
|
+
return normalizeToolExecutionUpdate(frame, state);
|
|
250
|
+
case 'tool_execution_end':
|
|
251
|
+
return normalizeToolExecutionEnd(frame, state);
|
|
252
|
+
case 'turn_end':
|
|
253
|
+
return normalizeTurnEnd(frame, state);
|
|
254
|
+
default:
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Parse one line of the rpc-stdio lane's already-normalized OutputEvent JSON stream (produced
|
|
261
|
+
* from `OmpRpcTaskResult.events` by `contract-invoke.ts` / `rpc-watcher.js`). This is the
|
|
262
|
+
* `ProviderAdapter.parseEvent` counterpart to `normalizeOmpRpcFrame` above: the driver normalizes
|
|
263
|
+
* raw RPC frames to OutputEvent once, and this function is a validating passthrough over that
|
|
264
|
+
* already-normalized shape so generic log/attach tooling built on `adapter.parseEvent` keeps
|
|
265
|
+
* working for the omp provider without re-deriving events from raw frames. Each case rebuilds a
|
|
266
|
+
* literal matching its OutputEvent variant instead of casting the parsed record, so an
|
|
267
|
+
* unexpectedly-shaped line fails closed (returns null) rather than passing through untyped.
|
|
268
|
+
*/
|
|
269
|
+
export function parseNormalizedOmpRpcEventLine(
|
|
270
|
+
line: string,
|
|
271
|
+
_state: ProviderParserState
|
|
272
|
+
): ProviderParseResult {
|
|
273
|
+
const parsed = tryParseJson(line);
|
|
274
|
+
if (!isRecord(parsed)) return null;
|
|
275
|
+
switch (parsed.type) {
|
|
276
|
+
case 'text':
|
|
277
|
+
case 'thinking': {
|
|
278
|
+
const text = getString(parsed, 'text');
|
|
279
|
+
return text === null ? null : { type: parsed.type, text };
|
|
280
|
+
}
|
|
281
|
+
case 'tool_call':
|
|
282
|
+
return {
|
|
283
|
+
type: 'tool_call',
|
|
284
|
+
toolName: getOptionalString(parsed, 'toolName'),
|
|
285
|
+
toolId: getOptionalString(parsed, 'toolId'),
|
|
286
|
+
input: parsed.input,
|
|
287
|
+
};
|
|
288
|
+
case 'tool_result':
|
|
289
|
+
return {
|
|
290
|
+
type: 'tool_result',
|
|
291
|
+
toolId: getOptionalString(parsed, 'toolId'),
|
|
292
|
+
content: parsed.content,
|
|
293
|
+
isError: parsed.isError,
|
|
294
|
+
};
|
|
295
|
+
case 'result': {
|
|
296
|
+
if (typeof parsed.success !== 'boolean') return null;
|
|
297
|
+
const inputTokens = getNumber(parsed, 'inputTokens');
|
|
298
|
+
const outputTokens = getNumber(parsed, 'outputTokens');
|
|
299
|
+
const cacheReadInputTokens = getNumber(parsed, 'cacheReadInputTokens');
|
|
300
|
+
const cacheCreationInputTokens = getNumber(parsed, 'cacheCreationInputTokens');
|
|
301
|
+
return {
|
|
302
|
+
type: 'result',
|
|
303
|
+
success: parsed.success,
|
|
304
|
+
result: parsed.result,
|
|
305
|
+
error: parsed.error,
|
|
306
|
+
cost: parsed.cost,
|
|
307
|
+
duration: parsed.duration,
|
|
308
|
+
...(inputTokens === null ? {} : { inputTokens }),
|
|
309
|
+
...(outputTokens === null ? {} : { outputTokens }),
|
|
310
|
+
...(cacheReadInputTokens === null ? {} : { cacheReadInputTokens }),
|
|
311
|
+
...(cacheCreationInputTokens === null ? {} : { cacheCreationInputTokens }),
|
|
312
|
+
modelUsage: parsed.modelUsage,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
default:
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -218,7 +218,11 @@ export class OmpRpcFrameDecoder {
|
|
|
218
218
|
this.inflightReassemblyBytes = 0;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
// `negotiatedV2` defaults to true so callers that don't care about pre-negotiation gating
|
|
222
|
+
// (e.g. the fixture-replay tests below) see unchanged behavior; the RPC driver passes its
|
|
223
|
+
// live negotiation state so a bare `rpc_chunk` physical frame is rejected before negotiation
|
|
224
|
+
// succeeds, even though the decoder would otherwise buffer it silently pending reassembly.
|
|
225
|
+
push(chunk: Uint8Array, negotiatedV2 = true): readonly OmpRpcInboundFrame[] {
|
|
222
226
|
if (this.finished) {
|
|
223
227
|
throw new OmpRpcProtocolError('decoder-finished', 'push() called after finish().');
|
|
224
228
|
}
|
|
@@ -247,7 +251,7 @@ export class OmpRpcFrameDecoder {
|
|
|
247
251
|
);
|
|
248
252
|
}
|
|
249
253
|
|
|
250
|
-
const frame = this.consumeLine(lineBytes);
|
|
254
|
+
const frame = this.consumeLine(lineBytes, negotiatedV2);
|
|
251
255
|
if (frame !== null) frames.push(frame);
|
|
252
256
|
}
|
|
253
257
|
|
|
@@ -270,7 +274,7 @@ export class OmpRpcFrameDecoder {
|
|
|
270
274
|
}
|
|
271
275
|
}
|
|
272
276
|
|
|
273
|
-
private consumeLine(lineBytes: Buffer): OmpRpcInboundFrame | null {
|
|
277
|
+
private consumeLine(lineBytes: Buffer, negotiatedV2: boolean): OmpRpcInboundFrame | null {
|
|
274
278
|
let value: unknown;
|
|
275
279
|
try {
|
|
276
280
|
value = JSON.parse(lineBytes.toString('utf8'));
|
|
@@ -285,6 +289,7 @@ export class OmpRpcFrameDecoder {
|
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
const type = getString(value, 'type');
|
|
292
|
+
assertNoPreNegotiationRpcChunk(type ?? '', negotiatedV2);
|
|
288
293
|
if (type !== 'rpc_chunk') {
|
|
289
294
|
if (this.pendingReassemblies.size > 0) {
|
|
290
295
|
throw new OmpRpcProtocolError(
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Session-launch types for the OMP RPC v2 driver. This slice launches sessionless
|
|
2
|
+
// (`--no-session`) only; `fresh`/`resume` are typed for Subissue 4's session-flag activation but
|
|
3
|
+
// are unreachable from any caller in this slice.
|
|
4
|
+
|
|
5
|
+
export interface VerifiedOmpPartition {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface VerifiedOmpSessionFile {
|
|
10
|
+
readonly path: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type OmpSessionLaunch =
|
|
14
|
+
| { readonly kind: 'none' }
|
|
15
|
+
| { readonly kind: 'fresh'; readonly partition: VerifiedOmpPartition }
|
|
16
|
+
| {
|
|
17
|
+
readonly kind: 'resume';
|
|
18
|
+
readonly partition: VerifiedOmpPartition;
|
|
19
|
+
readonly file: VerifiedOmpSessionFile;
|
|
20
|
+
};
|
|
@@ -8,7 +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
|
+
import { OMP_AUTH_INSTRUCTIONS, OMP_INSTALL_COMMAND } from './omp-release';
|
|
12
12
|
import type { ModelLevel, ProviderAdapter, StructuredOutputRecoveryAdapter } from './types';
|
|
13
13
|
|
|
14
14
|
export type ProviderCapabilityState = boolean | 'experimental';
|
|
@@ -44,7 +44,15 @@ export interface AcpStdioProviderInvokeSpec {
|
|
|
44
44
|
readonly transport: 'stdio';
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export
|
|
47
|
+
export interface RpcStdioProviderInvokeSpec {
|
|
48
|
+
readonly lane: 'rpc-stdio';
|
|
49
|
+
readonly protocol: 'omp-v2';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ProviderInvokeSpec =
|
|
53
|
+
| SpawnProviderInvokeSpec
|
|
54
|
+
| AcpStdioProviderInvokeSpec
|
|
55
|
+
| RpcStdioProviderInvokeSpec;
|
|
48
56
|
|
|
49
57
|
export type ProviderCommandSpec = FixedProviderCommandSpec | ConfiguredClaudeCommandSpec;
|
|
50
58
|
|
|
@@ -146,6 +154,10 @@ const ACP_STDIO_INVOKE = Object.freeze({
|
|
|
146
154
|
lane: 'acp-stdio',
|
|
147
155
|
transport: 'stdio',
|
|
148
156
|
}) as AcpStdioProviderInvokeSpec;
|
|
157
|
+
const RPC_STDIO_INVOKE = Object.freeze({
|
|
158
|
+
lane: 'rpc-stdio',
|
|
159
|
+
protocol: 'omp-v2',
|
|
160
|
+
}) as RpcStdioProviderInvokeSpec;
|
|
149
161
|
|
|
150
162
|
const kiroAdapter = createAcpAdapter({
|
|
151
163
|
provider: 'kiro',
|
|
@@ -428,22 +440,29 @@ export const providerRegistry = [
|
|
|
428
440
|
{
|
|
429
441
|
id: 'omp',
|
|
430
442
|
default: false,
|
|
431
|
-
aliases: [],
|
|
432
|
-
displayName: 'OMP',
|
|
443
|
+
aliases: ['oh-my-pi'],
|
|
444
|
+
displayName: 'OMP (Oh My Pi)',
|
|
433
445
|
binary: 'omp',
|
|
434
446
|
command: { kind: 'fixed', command: 'omp', args: [] },
|
|
435
|
-
invoke:
|
|
447
|
+
invoke: RPC_STDIO_INVOKE,
|
|
436
448
|
installInstructions: OMP_INSTALL_COMMAND,
|
|
437
|
-
authInstructions:
|
|
449
|
+
authInstructions: OMP_AUTH_INSTRUCTIONS,
|
|
438
450
|
credentialPaths: ['~/.omp'],
|
|
439
451
|
credentialEnvKeys: ompAdapter.credentialEnvKeys,
|
|
440
452
|
settingsFields: [],
|
|
441
453
|
availabilityProbe: 'help-or-version',
|
|
454
|
+
// Written out explicitly rather than spread from STANDARD_CAPABILITIES, which defaults
|
|
455
|
+
// dockerIsolation to true; OMP stays worktree-only until Subissue 6 flips dockerIsolation.
|
|
442
456
|
capabilities: {
|
|
443
|
-
|
|
457
|
+
dockerIsolation: false,
|
|
458
|
+
worktreeIsolation: true,
|
|
444
459
|
mcpServers: false,
|
|
445
460
|
jsonSchema: false,
|
|
461
|
+
streamJson: true,
|
|
462
|
+
thinkingMode: true,
|
|
446
463
|
reasoningEffort: true,
|
|
464
|
+
sessionResume: false,
|
|
465
|
+
webSearch: false,
|
|
447
466
|
},
|
|
448
467
|
docs: {
|
|
449
468
|
label: 'OMP',
|
|
@@ -168,16 +168,16 @@ export interface PiCliFeatures extends BaseCliFeatures {
|
|
|
168
168
|
|
|
169
169
|
export interface OmpCliFeatures extends BaseCliFeatures {
|
|
170
170
|
readonly provider: 'omp';
|
|
171
|
-
readonly
|
|
172
|
-
readonly
|
|
173
|
-
readonly supportsCwd: boolean;
|
|
174
|
-
readonly supportsAutoApprove: boolean;
|
|
171
|
+
readonly supportsRpcMode: boolean;
|
|
172
|
+
readonly supportsConfig: boolean;
|
|
175
173
|
readonly supportsModel: boolean;
|
|
176
174
|
readonly supportsThinking: boolean;
|
|
177
|
-
readonly
|
|
178
|
-
readonly supportsNoSkills: boolean;
|
|
179
|
-
readonly supportsNoRules: boolean;
|
|
175
|
+
readonly supportsApprovalMode: boolean;
|
|
180
176
|
readonly supportsNoTitle: boolean;
|
|
177
|
+
readonly supportsNoSession: boolean;
|
|
178
|
+
readonly supportsSessionDir: boolean;
|
|
179
|
+
readonly supportsResume: boolean;
|
|
180
|
+
readonly versionMatches: boolean;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
export interface CopilotCliFeatures extends BaseCliFeatures {
|
|
@@ -256,11 +256,13 @@ export interface CliFeatureOverrides {
|
|
|
256
256
|
readonly supportsRecoveryIsolation?: boolean;
|
|
257
257
|
readonly supportsNoContextFiles?: boolean;
|
|
258
258
|
readonly supportsNoApprove?: boolean;
|
|
259
|
-
readonly
|
|
260
|
-
readonly
|
|
259
|
+
readonly supportsRpcMode?: boolean;
|
|
260
|
+
readonly supportsConfig?: boolean;
|
|
261
261
|
readonly supportsThinking?: boolean;
|
|
262
|
-
readonly
|
|
262
|
+
readonly supportsApprovalMode?: boolean;
|
|
263
263
|
readonly supportsNoTitle?: boolean;
|
|
264
|
+
readonly supportsSessionDir?: boolean;
|
|
265
|
+
readonly versionMatches?: boolean;
|
|
264
266
|
readonly supportsJsonOutput?: boolean;
|
|
265
267
|
readonly supportsAllowAll?: boolean;
|
|
266
268
|
readonly supportsNoAskUser?: boolean;
|
|
@@ -412,7 +414,8 @@ export type ErrorClassificationKind =
|
|
|
412
414
|
| 'code-retryable'
|
|
413
415
|
| 'permanent-pattern'
|
|
414
416
|
| 'retryable-pattern'
|
|
415
|
-
| 'unknown-retryable'
|
|
417
|
+
| 'unknown-retryable'
|
|
418
|
+
| 'cancelled';
|
|
416
419
|
|
|
417
420
|
export interface ErrorClassification {
|
|
418
421
|
readonly retryable: boolean;
|