@the-open-engine/zeroshot 6.6.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ 'use strict';
2
+
3
+ const { cloneJson } = require('./object-utils');
4
+
5
+ const TERMINAL_TOPICS = new Set(['CLUSTER_COMPLETE', 'CLUSTER_FAILED']);
6
+
7
+ function messageKey(message) {
8
+ return message.id ?? `${message.timestamp}:${message.topic}:${message.sender}`;
9
+ }
10
+
11
+ function terminalEventFromMessage(message) {
12
+ const data = message.content?.data || {};
13
+ if (message.topic === 'CLUSTER_COMPLETE') {
14
+ return {
15
+ type: 'complete',
16
+ summary: data.reason || message.content?.text,
17
+ ...(Object.prototype.hasOwnProperty.call(data, 'result') ? { result: data.result } : {}),
18
+ ...(Object.prototype.hasOwnProperty.call(data, 'artifacts')
19
+ ? { artifacts: data.artifacts }
20
+ : {}),
21
+ };
22
+ }
23
+ return {
24
+ type: 'failed',
25
+ summary: data.reason || message.content?.text,
26
+ code: data.code,
27
+ reason: data.workerReason,
28
+ };
29
+ }
30
+
31
+ function inputFromRequest(request, artifactManifest) {
32
+ if (request.source === 'issue') return { issue: request.issue };
33
+ if (request.source === 'prompt') return { text: request.prompt };
34
+ return {
35
+ text:
36
+ 'Execute the task described by this registry-staged, byte-free artifact manifest. ' +
37
+ 'Do not request interactive input.\n' +
38
+ JSON.stringify(artifactManifest),
39
+ };
40
+ }
41
+
42
+ function resolveOrchestrator(options) {
43
+ if (options.orchestrator) return options.orchestrator;
44
+ const Orchestrator = options.Orchestrator || require('../../src/orchestrator');
45
+ return new Orchestrator({
46
+ quiet: true,
47
+ ...(options.storageDir ? { storageDir: options.storageDir } : {}),
48
+ skipLoad: true,
49
+ });
50
+ }
51
+
52
+ class CurrentEngineAdapter {
53
+ constructor(options) {
54
+ this.options = options;
55
+ this.orchestrator = options.orchestrator || null;
56
+ this.startCluster = options.startCluster || null;
57
+ this.resource = null;
58
+ this.startPromise = null;
59
+ this.startSettled = false;
60
+ this.stopPromise = null;
61
+ this.cleanupPromise = null;
62
+ this.allocatedStopPromise = null;
63
+ this.stopRequested = false;
64
+ this.closed = false;
65
+ this.unsubscribe = null;
66
+ this.seen = new Set();
67
+ this.terminalObserved = false;
68
+ this.foldScheduled = false;
69
+ }
70
+
71
+ ensureRuntime() {
72
+ this.orchestrator ||= resolveOrchestrator(this.options);
73
+ this.startCluster ||= require('../start-cluster');
74
+ }
75
+
76
+ loadConfig(profile) {
77
+ const provider = profile.provider;
78
+ if (provider.config) {
79
+ const prepareClusterConfig =
80
+ this.startCluster.prepareClusterConfig || require('../start-cluster').prepareClusterConfig;
81
+ return prepareClusterConfig(
82
+ cloneJson(provider.config),
83
+ provider.settings || {},
84
+ provider.providerOverride || undefined
85
+ );
86
+ }
87
+ return this.startCluster.loadClusterConfig(
88
+ this.orchestrator,
89
+ provider.configPath || this.startCluster.resolveConfigPath(provider.configName),
90
+ provider.settings || {},
91
+ provider.providerOverride || undefined
92
+ );
93
+ }
94
+
95
+ startOptions(profile, clusterId) {
96
+ const provider = profile.provider;
97
+ return this.startCluster.buildTrustedStartOptions({
98
+ clusterId,
99
+ plan: profile.plan,
100
+ options: profile.deployment,
101
+ settings: provider.settings || {},
102
+ providerOverride: provider.providerOverride || undefined,
103
+ forceProvider: provider.forceProvider || undefined,
104
+ });
105
+ }
106
+
107
+ consume(message) {
108
+ if (
109
+ this.terminalObserved ||
110
+ message.cluster_id !== this.resource.clusterId ||
111
+ !TERMINAL_TOPICS.has(message.topic)
112
+ ) {
113
+ return;
114
+ }
115
+ const key = messageKey(message);
116
+ if (this.seen.has(key)) return;
117
+ this.seen.add(key);
118
+ this.terminalObserved = true;
119
+ this.resource.onEvent(terminalEventFromMessage(message));
120
+ }
121
+
122
+ foldDurableMessages() {
123
+ if (!this.resource.messageBus) return;
124
+ for (const message of this.resource.messageBus.getAll(this.resource.clusterId)) {
125
+ this.consume(message);
126
+ }
127
+ }
128
+
129
+ scheduleDurableFold(message) {
130
+ if (message.cluster_id !== this.resource.clusterId || !TERMINAL_TOPICS.has(message.topic)) {
131
+ return;
132
+ }
133
+ if (this.foldScheduled || this.terminalObserved) return;
134
+ this.foldScheduled = true;
135
+ Promise.resolve().then(() => {
136
+ this.foldScheduled = false;
137
+ if (!this.resource || this.terminalObserved) return;
138
+ try {
139
+ this.foldDurableMessages();
140
+ } catch {
141
+ this.resource.onEvent({ type: 'failed', code: 'crash', reason: 'declared_failure' });
142
+ }
143
+ });
144
+ }
145
+
146
+ async start({ request, profile, prepareArtifacts, clusterId, onEvent }) {
147
+ if (this.resource) throw new Error('Engine adapter owns one cluster resource');
148
+ this.ensureRuntime();
149
+ const config = this.loadConfig(profile);
150
+ const artifactInput = request.source === 'artifact';
151
+ if (artifactInput && typeof prepareArtifacts !== 'function') {
152
+ throw new Error('Artifact engine start requires prepareArtifacts()');
153
+ }
154
+ const input = artifactInput
155
+ ? { text: 'Artifact input is being prepared inside the allocated isolation.' }
156
+ : inputFromRequest(request);
157
+ const startOptions = this.startOptions(profile, clusterId);
158
+ let artifactsStaged = !artifactInput;
159
+ const options = artifactInput
160
+ ? {
161
+ ...startOptions,
162
+ async prepareIsolatedInput({ isolation }) {
163
+ const artifactManifest = await prepareArtifacts(isolation);
164
+ if (
165
+ !artifactManifest ||
166
+ typeof artifactManifest !== 'object' ||
167
+ Array.isArray(artifactManifest)
168
+ ) {
169
+ throw new Error('ArtifactResolver returned an invalid staged manifest');
170
+ }
171
+ artifactsStaged = true;
172
+ return inputFromRequest(request, artifactManifest).text;
173
+ },
174
+ }
175
+ : startOptions;
176
+ this.resource = {
177
+ clusterId,
178
+ messageBus: null,
179
+ onEvent,
180
+ shutdownMs: profile.bounds?.shutdownMs || 30_000,
181
+ };
182
+ try {
183
+ this.startPromise = Promise.resolve(this.orchestrator.start(config, input, options));
184
+ const started = await this.startPromise;
185
+ this.resource.clusterId = started.id;
186
+ this.resource.messageBus = started.messageBus;
187
+ this.unsubscribe = started.messageBus.subscribe((message) =>
188
+ this.scheduleDurableFold(message)
189
+ );
190
+ if (this.stopRequested) {
191
+ const stopResult = await this.stopPromise;
192
+ if (!stopResult.effective) await this.stopAllocatedCluster();
193
+ } else {
194
+ onEvent({ type: 'running' });
195
+ }
196
+ this.foldDurableMessages();
197
+ return Object.freeze({ clusterId: started.id, artifactsStaged });
198
+ } finally {
199
+ this.startSettled = true;
200
+ }
201
+ }
202
+
203
+ foldClusterState(cluster) {
204
+ if (!cluster) {
205
+ if (!this.terminalObserved) {
206
+ this.resource.onEvent({ type: 'failed', code: 'crash', reason: 'declared_failure' });
207
+ }
208
+ return 'released';
209
+ }
210
+ if (!this.terminalObserved && ['failed', 'stopped', 'killed'].includes(cluster.state)) {
211
+ this.resource.onEvent({ type: 'failed', code: 'crash', reason: 'declared_failure' });
212
+ } else if (cluster.state === 'running') {
213
+ this.resource.onEvent({ type: 'running' });
214
+ }
215
+ return cluster.state;
216
+ }
217
+
218
+ status() {
219
+ if (!this.resource) return null;
220
+ this.foldDurableMessages();
221
+ const cluster = this.orchestrator.getCluster(this.resource.clusterId);
222
+ if (!cluster && !this.startSettled) {
223
+ return Object.freeze({ clusterId: this.resource.clusterId, state: 'starting' });
224
+ }
225
+ const state = this.foldClusterState(cluster);
226
+ if (!cluster) return Object.freeze({ clusterId: this.resource.clusterId, state });
227
+ return Object.freeze({
228
+ clusterId: this.resource.clusterId,
229
+ state,
230
+ messageCount: cluster.ledger.count({ cluster_id: this.resource.clusterId }),
231
+ pidAliveDiagnostic: this.orchestrator.getStatus(this.resource.clusterId).isZombie !== true,
232
+ });
233
+ }
234
+
235
+ stop() {
236
+ if (!this.resource) throw new Error('Engine adapter has no cluster resource');
237
+ this.stopRequested = true;
238
+ this.stopPromise ||= this.stopResource();
239
+ return this.stopPromise;
240
+ }
241
+ async waitForAllocatedCluster() {
242
+ let cluster = this.orchestrator.getCluster(this.resource.clusterId);
243
+ while (!cluster && !this.startSettled && !this.closed) {
244
+ await new Promise((resolve) => {
245
+ setTimeout(resolve, 10);
246
+ });
247
+ cluster = this.orchestrator.getCluster(this.resource.clusterId);
248
+ }
249
+ return cluster;
250
+ }
251
+ stopAllocatedCluster(cluster = this.orchestrator.getCluster(this.resource.clusterId)) {
252
+ if (!cluster) return Promise.resolve(false);
253
+ if (cluster.state === 'stopped' || cluster.state === 'killed') return Promise.resolve(true);
254
+ this.allocatedStopPromise ||= Promise.resolve()
255
+ .then(() => this.orchestrator.stop(this.resource.clusterId))
256
+ .then(() => true);
257
+ return this.allocatedStopPromise;
258
+ }
259
+ async stopResource() {
260
+ this.cleanupPromise ||= this.waitForAllocatedCluster()
261
+ .then((cluster) => this.stopAllocatedCluster(cluster))
262
+ .catch(() => false)
263
+ .then((effective) => {
264
+ this.close();
265
+ return effective;
266
+ });
267
+ let deadlineHandle;
268
+ const deadline = new Promise((resolve) => {
269
+ deadlineHandle = setTimeout(() => resolve(false), this.resource.shutdownMs);
270
+ });
271
+ const effective = await Promise.race([this.cleanupPromise, deadline]);
272
+ clearTimeout(deadlineHandle);
273
+ return { effective };
274
+ }
275
+
276
+ waitForCleanup() {
277
+ return this.cleanupPromise || Promise.resolve();
278
+ }
279
+
280
+ close() {
281
+ if (this.closed) return;
282
+ this.closed = true;
283
+ this.unsubscribe?.();
284
+ this.unsubscribe = null;
285
+ this.orchestrator?.close();
286
+ }
287
+ }
288
+
289
+ function createCurrentEngineAdapter(options = {}) {
290
+ const adapter = new CurrentEngineAdapter(options);
291
+ return Object.freeze({
292
+ start: adapter.start.bind(adapter),
293
+ status: adapter.status.bind(adapter),
294
+ stop: adapter.stop.bind(adapter),
295
+ waitForCleanup: adapter.waitForCleanup.bind(adapter),
296
+ close: adapter.close.bind(adapter),
297
+ });
298
+ }
299
+
300
+ module.exports = { createCurrentEngineAdapter, inputFromRequest, terminalEventFromMessage };
@@ -0,0 +1,229 @@
1
+ 'use strict';
2
+ const { createLegacyClusterWorker } = require('./index');
3
+ const { validateCommandFrame } = require('./contracts');
4
+ const { DEFAULT_BOUNDS } = require('./profiles');
5
+ const { RELEASE_WORKER, WAIT_FOR_WORKER_CLEANUP } = require('./worker-internals');
6
+
7
+ const ERROR_CODES = Object.freeze({
8
+ SyntaxError: 'MALFORMED_JSON',
9
+ TypeError: 'INVALID_REQUEST',
10
+ });
11
+
12
+ function protocolError(error, fallback = 'WORKER_ERROR') {
13
+ return Object.freeze({
14
+ code: error?.code || ERROR_CODES[error?.name] || fallback,
15
+ message: error instanceof Error ? error.message : String(error),
16
+ });
17
+ }
18
+
19
+ function writeFrame(output, frame) {
20
+ output.write(`${JSON.stringify(frame)}\n`);
21
+ }
22
+
23
+ async function pumpEvents({ id, iterator, state, output, diagnostics }) {
24
+ try {
25
+ for await (const event of iterator) writeFrame(output, { type: 'event', id, event });
26
+ } catch (error) {
27
+ diagnostics.write(`cluster-worker event stream failed: ${protocolError(error).message}\n`);
28
+ } finally {
29
+ state.eventPumps.delete(iterator);
30
+ }
31
+ }
32
+
33
+ function executeCommand(frame, worker, state, streams) {
34
+ if (frame.method === 'start') {
35
+ if (state.startSeen) {
36
+ const error = new Error('Worker process permits exactly one start command');
37
+ error.code = 'DUPLICATE_START';
38
+ throw error;
39
+ }
40
+ state.startSeen = true;
41
+ state.startPromise = Promise.resolve(worker.start(frame.params.request));
42
+ return state.startPromise;
43
+ }
44
+ if (frame.method === 'events') {
45
+ const iterator = worker.events();
46
+ state.eventPumps.add(iterator);
47
+ pumpEvents({ id: frame.id, iterator, state, ...streams });
48
+ return { subscribed: true };
49
+ }
50
+ if (state.startPromise && (frame.method === 'result' || frame.method === 'stop')) {
51
+ const terminal = Promise.race([
52
+ Promise.resolve().then(() => worker.result()),
53
+ state.startPromise.then(
54
+ () => new Promise(() => {}),
55
+ (error) => Promise.reject(error)
56
+ ),
57
+ ]);
58
+ if (frame.method === 'result') return terminal;
59
+ return Promise.race([terminal, Promise.resolve().then(() => worker.stop())]);
60
+ }
61
+ return worker[frame.method]();
62
+ }
63
+
64
+ function createCommandDispatcher({ worker, output, diagnostics }) {
65
+ const state = { eventPumps: new Set(), startSeen: false, startPromise: null };
66
+ const streams = { output, diagnostics };
67
+
68
+ async function dispatch(frame) {
69
+ const id =
70
+ frame && typeof frame === 'object' && !Array.isArray(frame) ? (frame.id ?? null) : null;
71
+ try {
72
+ validateCommandFrame(frame);
73
+ const result = await executeCommand(frame, worker, state, streams);
74
+ writeFrame(output, { type: 'response', id: frame.id, ok: true, result });
75
+ } catch (error) {
76
+ writeFrame(output, { type: 'response', id, ok: false, error: protocolError(error) });
77
+ }
78
+ }
79
+
80
+ function closeEvents() {
81
+ for (const iterator of [...state.eventPumps]) iterator.return();
82
+ }
83
+
84
+ return Object.freeze({
85
+ dispatch,
86
+ closeEvents,
87
+ hasStarted: () => state.startSeen,
88
+ });
89
+ }
90
+
91
+ function listenForFrames({ input, frameBytes, onFrame, onError, onClose }) {
92
+ let chunks = [];
93
+ let bytes = 0;
94
+ let discarding = false;
95
+
96
+ function finishLine() {
97
+ if (discarding) {
98
+ discarding = false;
99
+ chunks = [];
100
+ bytes = 0;
101
+ return;
102
+ }
103
+ let line = Buffer.concat(chunks, bytes);
104
+ if (line.at(-1) === 13) line = line.subarray(0, -1);
105
+ chunks = [];
106
+ bytes = 0;
107
+ if (line.length > 0) onFrame(line.toString('utf8'));
108
+ }
109
+
110
+ input.on('data', (inputChunk) => {
111
+ const chunk = Buffer.isBuffer(inputChunk) ? inputChunk : Buffer.from(inputChunk);
112
+ let offset = 0;
113
+ while (offset < chunk.length) {
114
+ const newline = chunk.indexOf(10, offset);
115
+ const end = newline === -1 ? chunk.length : newline;
116
+ const part = chunk.subarray(offset, end);
117
+ if (!discarding && bytes + part.length > frameBytes) {
118
+ discarding = true;
119
+ chunks = [];
120
+ bytes = 0;
121
+ onError();
122
+ } else if (!discarding && part.length > 0) {
123
+ chunks.push(part);
124
+ bytes += part.length;
125
+ }
126
+ if (newline === -1) break;
127
+ finishLine();
128
+ offset = newline + 1;
129
+ }
130
+ });
131
+ input.once('end', () => {
132
+ if (bytes > 0 || discarding) finishLine();
133
+ onClose();
134
+ });
135
+ input.once('error', onClose);
136
+ }
137
+
138
+ async function stopStartedWorker({ dispatcher, worker, diagnostics }) {
139
+ if (!dispatcher.hasStarted()) return;
140
+ await Promise.resolve(worker.stop()).catch((error) => {
141
+ diagnostics.write(`cluster-worker stop failed: ${protocolError(error).message}\n`);
142
+ });
143
+ await worker[WAIT_FOR_WORKER_CLEANUP]?.();
144
+ }
145
+
146
+ function createDeadline(milliseconds) {
147
+ let handle;
148
+ const promise = new Promise((resolve) => {
149
+ handle = setTimeout(resolve, milliseconds);
150
+ });
151
+ return { promise, cancel: () => clearTimeout(handle) };
152
+ }
153
+
154
+ function runClusterWorkerExecutable(options = {}) {
155
+ const input = options.input || process.stdin;
156
+ const output = options.output || process.stdout;
157
+ const diagnostics = options.diagnostics || process.stderr;
158
+ const worker = options.worker || createLegacyClusterWorker(options.dependencies);
159
+ const frameBytes = options.frameBytes ?? DEFAULT_BOUNDS.frameBytes;
160
+ const shutdownMs = options.shutdownMs ?? DEFAULT_BOUNDS.shutdownMs;
161
+ for (const [name, value] of Object.entries({ frameBytes, shutdownMs })) {
162
+ if (!Number.isSafeInteger(value) || value <= 0) {
163
+ throw new TypeError(`${name} must be a positive safe integer`);
164
+ }
165
+ }
166
+ const dispatcher = createCommandDispatcher({ worker, output, diagnostics });
167
+ let closePromise = null;
168
+
169
+ function boundedStop() {
170
+ if (closePromise) return closePromise;
171
+ closePromise = (async () => {
172
+ dispatcher.closeEvents();
173
+ const deadline = createDeadline(shutdownMs);
174
+ await Promise.race([
175
+ stopStartedWorker({ dispatcher, worker, diagnostics }),
176
+ deadline.promise,
177
+ ]);
178
+ worker[RELEASE_WORKER]?.();
179
+ deadline.cancel();
180
+ })();
181
+ return closePromise;
182
+ }
183
+
184
+ function rejectOversizedFrame() {
185
+ writeFrame(output, {
186
+ type: 'response',
187
+ id: null,
188
+ ok: false,
189
+ error: { code: 'FRAME_TOO_LARGE', message: `Frame exceeds ${frameBytes} UTF-8 bytes` },
190
+ });
191
+ }
192
+
193
+ function acceptLine(line) {
194
+ if (closePromise) return;
195
+ let frame;
196
+ try {
197
+ frame = JSON.parse(line);
198
+ } catch (error) {
199
+ writeFrame(output, {
200
+ type: 'response',
201
+ id: null,
202
+ ok: false,
203
+ error: protocolError(error),
204
+ });
205
+ return;
206
+ }
207
+ dispatcher.dispatch(frame);
208
+ }
209
+
210
+ listenForFrames({
211
+ input,
212
+ frameBytes,
213
+ onFrame: acceptLine,
214
+ onError: rejectOversizedFrame,
215
+ onClose: () => boundedStop(),
216
+ });
217
+
218
+ return Object.freeze({
219
+ close: boundedStop,
220
+ dispatch: dispatcher.dispatch,
221
+ });
222
+ }
223
+
224
+ module.exports = {
225
+ createCommandDispatcher,
226
+ listenForFrames,
227
+ protocolError,
228
+ runClusterWorkerExecutable,
229
+ };