@harapter/adapter-dsh 0.1.1
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/LICENSE +201 -0
- package/README.md +262 -0
- package/dist/adapter.d.ts +36 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +1065 -0
- package/dist/adapter.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +93 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +708 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +53 -0
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { ExtensionRegistry, HarnessError, assertSessionOwnership, providerSessionId, runId, } from '@harapter/core';
|
|
5
|
+
import { JsonRpcRemoteError, JsonRpcStdioTransport, JsonRpcTransportError, } from '@harapter/transport-jsonrpc-stdio';
|
|
6
|
+
import { DSH_NOTIFICATION_EXTENSION, DSH_PROVIDER_ID, DSH_SESSION_COMPATIBILITY_REF, dshCompatibilityIdentity, mapDshSessionEvent, parseDshInitializeResponse, parseDshPromptResponse, parseDshSessionEventNotification, parseDshStatusNotification, parseDshSubagentFinished, parseDshSubagentStarted, prepareDshPrompt, redactDshEvent, validateDshSessionInput, } from './protocol.js';
|
|
7
|
+
const descriptor = {
|
|
8
|
+
providerId: DSH_PROVIDER_ID,
|
|
9
|
+
displayName: 'DeepSeek Harness SDK Runtime',
|
|
10
|
+
connectionKinds: ['process'],
|
|
11
|
+
documentationUrl: 'https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/sdk',
|
|
12
|
+
};
|
|
13
|
+
const defaultMaxRunEvents = 128;
|
|
14
|
+
const maximumRunEvents = 4_096;
|
|
15
|
+
const defaultShutdownTimeoutMs = 1_000;
|
|
16
|
+
const childTerminationTimeoutMs = 2_000;
|
|
17
|
+
const maximumTimerMilliseconds = 2_147_483_647;
|
|
18
|
+
/** Create a fresh DeepSeek Harness SDK Runtime Adapter factory. */
|
|
19
|
+
export function createDshProviderFactory() {
|
|
20
|
+
return {
|
|
21
|
+
descriptor: () => ({
|
|
22
|
+
...descriptor,
|
|
23
|
+
connectionKinds: [...descriptor.connectionKinds],
|
|
24
|
+
}),
|
|
25
|
+
connect: async (profile) => connectDsh(profile),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function connectDsh(profile) {
|
|
29
|
+
validateProfile(profile);
|
|
30
|
+
const options = connectionOptions(profile.providerOptions, profile);
|
|
31
|
+
let transport;
|
|
32
|
+
try {
|
|
33
|
+
transport = await spawnTransport(profile, options.transport);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw mapError(error, profile, 'spawn', true);
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const response = await transport.request('initialize', options.initialize);
|
|
40
|
+
const runtime = parseDshInitializeResponse(response);
|
|
41
|
+
return new DshClient(snapshotProfile(profile), transport, runtime, options.initialize, options.maxRunEvents, options.shutdownTimeoutMs);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
await transport.close().catch(() => undefined);
|
|
45
|
+
throw mapError(error, profile, 'initialize', true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class DshClient {
|
|
49
|
+
profile;
|
|
50
|
+
transport;
|
|
51
|
+
runtime;
|
|
52
|
+
initialize;
|
|
53
|
+
maxRunEvents;
|
|
54
|
+
shutdownTimeoutMs;
|
|
55
|
+
extensionRegistry = new ExtensionRegistry(DSH_PROVIDER_ID);
|
|
56
|
+
nativeClient;
|
|
57
|
+
notificationListeners = new Set();
|
|
58
|
+
unknownListeners = new Set();
|
|
59
|
+
activeRun;
|
|
60
|
+
closePromise;
|
|
61
|
+
closed = false;
|
|
62
|
+
runSerial = 0;
|
|
63
|
+
sessionSerial = 0;
|
|
64
|
+
constructor(profile, transport, runtime, initialize, maxRunEvents, shutdownTimeoutMs) {
|
|
65
|
+
this.profile = profile;
|
|
66
|
+
this.transport = transport;
|
|
67
|
+
this.runtime = runtime;
|
|
68
|
+
this.initialize = initialize;
|
|
69
|
+
this.maxRunEvents = maxRunEvents;
|
|
70
|
+
this.shutdownTimeoutMs = shutdownTimeoutMs;
|
|
71
|
+
const observer = Object.freeze({
|
|
72
|
+
onNotification: (listener) => {
|
|
73
|
+
this.notificationListeners.add(listener);
|
|
74
|
+
return () => {
|
|
75
|
+
this.notificationListeners.delete(listener);
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
this.extensionRegistry.register({
|
|
80
|
+
name: DSH_NOTIFICATION_EXTENSION,
|
|
81
|
+
providerId: DSH_PROVIDER_ID,
|
|
82
|
+
displayName: 'DeepSeek Harness notification observer',
|
|
83
|
+
description: 'Bounded, redacted SDK runtime notifications.',
|
|
84
|
+
documentationUrl: 'https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sdk/protocol/README.md',
|
|
85
|
+
stability: 'experimental',
|
|
86
|
+
}, observer);
|
|
87
|
+
this.nativeClient = Object.freeze({
|
|
88
|
+
runtimeIdentity: this.runtimeIdentity(),
|
|
89
|
+
request: (method, params, options) => this.nativeRequest(method, params, options),
|
|
90
|
+
notify: (method, params) => this.nativeNotify(method, params),
|
|
91
|
+
onUnknownEvent: (listener) => {
|
|
92
|
+
this.unknownListeners.add(listener);
|
|
93
|
+
return () => {
|
|
94
|
+
this.unknownListeners.delete(listener);
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
void this.pump().catch(() => undefined);
|
|
99
|
+
}
|
|
100
|
+
descriptor() {
|
|
101
|
+
return Promise.resolve({
|
|
102
|
+
providerId: DSH_PROVIDER_ID,
|
|
103
|
+
profileId: this.profile.profileId,
|
|
104
|
+
displayName: this.profile.displayName,
|
|
105
|
+
connectionKind: 'process',
|
|
106
|
+
runtime: {
|
|
107
|
+
name: this.runtime.name,
|
|
108
|
+
version: this.runtime.version,
|
|
109
|
+
protocol: 'JSON-RPC 2.0 over stdio JSONL',
|
|
110
|
+
protocolVersion: 'current',
|
|
111
|
+
},
|
|
112
|
+
compatibility: 'experimental',
|
|
113
|
+
warnings: [
|
|
114
|
+
{
|
|
115
|
+
code: 'pre_release_upstream_protocol',
|
|
116
|
+
message: 'The DeepSeek Harness SDK protocol does not negotiate a compatibility version.',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
capabilities() {
|
|
122
|
+
return Promise.resolve(this.capabilityManifest());
|
|
123
|
+
}
|
|
124
|
+
createSession(input = {}) {
|
|
125
|
+
return Promise.resolve().then(() => {
|
|
126
|
+
this.assertOpen();
|
|
127
|
+
validateDshSessionInput(input, pathToFileURL(this.initialize.cwd).href);
|
|
128
|
+
const sessionId = providerSessionId(`harapter-dsh-session-${String(++this.sessionSerial)}`);
|
|
129
|
+
return new DshSession(this, sessionId);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
resumeSession(ref) {
|
|
133
|
+
return Promise.resolve().then(() => {
|
|
134
|
+
this.assertOpen();
|
|
135
|
+
assertSessionOwnership(ref, DSH_PROVIDER_ID, this.profile.profileId);
|
|
136
|
+
throw new HarnessError('unsupported_capability', 'DeepSeek Harness SDK Sessions cannot be resumed through the current protocol.', {
|
|
137
|
+
retryable: false,
|
|
138
|
+
providerId: DSH_PROVIDER_ID,
|
|
139
|
+
profileId: this.profile.profileId,
|
|
140
|
+
details: { capability: 'session.resume' },
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
extensions() {
|
|
145
|
+
return this.extensionRegistry;
|
|
146
|
+
}
|
|
147
|
+
native(guard) {
|
|
148
|
+
const value = this.nativeClient;
|
|
149
|
+
return guard !== undefined && !guard(value) ? undefined : value;
|
|
150
|
+
}
|
|
151
|
+
close() {
|
|
152
|
+
this.closePromise ??= this.closeOnce(true);
|
|
153
|
+
return this.closePromise;
|
|
154
|
+
}
|
|
155
|
+
sessionRef(sessionId) {
|
|
156
|
+
return {
|
|
157
|
+
providerId: DSH_PROVIDER_ID,
|
|
158
|
+
profileId: this.profile.profileId,
|
|
159
|
+
providerSessionId: sessionId,
|
|
160
|
+
compatibilityRef: DSH_SESSION_COMPATIBILITY_REF,
|
|
161
|
+
providerState: {
|
|
162
|
+
createdRuntimeVersion: this.runtime.version,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
capabilityManifest() {
|
|
167
|
+
return dshCapabilities(this.profile, this.runtimeIdentity());
|
|
168
|
+
}
|
|
169
|
+
hasActiveRun(sessionId) {
|
|
170
|
+
return (this.activeRun !== undefined &&
|
|
171
|
+
(sessionId === undefined || this.activeRun.ref().sessionId === sessionId));
|
|
172
|
+
}
|
|
173
|
+
async startRun(sessionId, input, options = {}) {
|
|
174
|
+
this.assertOpen();
|
|
175
|
+
if (this.activeRun !== undefined) {
|
|
176
|
+
throw new HarnessError('run_conflict', 'DeepSeek Harness allows one active Harapter Run per connection.', {
|
|
177
|
+
retryable: false,
|
|
178
|
+
providerId: DSH_PROVIDER_ID,
|
|
179
|
+
profileId: this.profile.profileId,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const contentBlocks = prepareDshPrompt(input, options);
|
|
183
|
+
const run = new DshRun({
|
|
184
|
+
providerId: DSH_PROVIDER_ID,
|
|
185
|
+
profileId: this.profile.profileId,
|
|
186
|
+
sessionId,
|
|
187
|
+
runId: runId(`dsh-run-${String(++this.runSerial)}`),
|
|
188
|
+
}, this.maxRunEvents, options.timeoutMs, (reason) => {
|
|
189
|
+
this.abortConnection(reason);
|
|
190
|
+
}, (terminal) => {
|
|
191
|
+
if (this.activeRun === terminal)
|
|
192
|
+
this.activeRun = undefined;
|
|
193
|
+
});
|
|
194
|
+
this.activeRun = run;
|
|
195
|
+
try {
|
|
196
|
+
const response = await this.transport.request('session/prompt', {
|
|
197
|
+
sessionId,
|
|
198
|
+
contentBlocks,
|
|
199
|
+
});
|
|
200
|
+
const messageId = parseDshPromptResponse(response);
|
|
201
|
+
run.acknowledge(messageId);
|
|
202
|
+
return run;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
const failure = mapError(error, this.profile, 'session/prompt', false, this.transport);
|
|
206
|
+
if (this.activeRun === run)
|
|
207
|
+
this.activeRun = undefined;
|
|
208
|
+
run.discardBeforeReturn();
|
|
209
|
+
if (!(error instanceof JsonRpcRemoteError)) {
|
|
210
|
+
this.abortConnection('prompt_outcome_unknown');
|
|
211
|
+
}
|
|
212
|
+
throw failure;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
respond(sessionId, _requestId, _response) {
|
|
216
|
+
return Promise.resolve().then(() => {
|
|
217
|
+
this.assertOpen();
|
|
218
|
+
throw new HarnessError('unsupported_capability', 'DeepSeek Harness does not expose host interaction responses in the current SDK protocol.', {
|
|
219
|
+
retryable: false,
|
|
220
|
+
providerId: DSH_PROVIDER_ID,
|
|
221
|
+
profileId: this.profile.profileId,
|
|
222
|
+
details: {
|
|
223
|
+
capability: 'interaction.provider',
|
|
224
|
+
sessionId: String(sessionId),
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async pump() {
|
|
230
|
+
try {
|
|
231
|
+
for await (const message of this.transport.incoming()) {
|
|
232
|
+
if (message.kind === 'request')
|
|
233
|
+
await this.handleRequest(message);
|
|
234
|
+
else
|
|
235
|
+
this.handleNotification(message.method, message.params);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
if (!this.closed)
|
|
240
|
+
this.abortConnection('transport_ended');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async handleRequest(message) {
|
|
244
|
+
const raw = redactDshEvent(message.method, message.params);
|
|
245
|
+
this.emitNotification(raw);
|
|
246
|
+
this.emitUnknown(raw);
|
|
247
|
+
await this.transport.respondError(message.id, {
|
|
248
|
+
code: -32_601,
|
|
249
|
+
message: 'Harapter does not implement Provider-initiated requests.',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
handleNotification(method, params) {
|
|
253
|
+
const raw = redactDshEvent(method, params);
|
|
254
|
+
this.emitNotification(raw);
|
|
255
|
+
try {
|
|
256
|
+
if (method === 'session.event') {
|
|
257
|
+
this.handleSessionEvent(raw, params);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (method === 'session.status') {
|
|
261
|
+
this.handleSessionStatus(raw, params);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (method === 'subagent.started') {
|
|
265
|
+
this.handleSubagentStarted(raw, params);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (method === 'subagent.finished') {
|
|
269
|
+
this.handleSubagentFinished(raw, params);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this.emitUnknown(raw);
|
|
273
|
+
this.activeRun?.receive({ kind: 'raw', event: raw });
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
this.failProtocol(error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
handleSessionEvent(raw, params) {
|
|
280
|
+
const parsed = parseDshSessionEventNotification(params);
|
|
281
|
+
const run = this.activeRun;
|
|
282
|
+
if (run?.ownsRootSession(parsed.sessionId) === true) {
|
|
283
|
+
run.receive({ kind: 'event', event: parsed.event });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (run?.ownsChildSession(parsed.sessionId) === true) {
|
|
287
|
+
this.emitUnknown(raw);
|
|
288
|
+
run.receive({ kind: 'raw', event: raw });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
this.emitUnknown(raw);
|
|
292
|
+
}
|
|
293
|
+
handleSessionStatus(raw, params) {
|
|
294
|
+
const parsed = parseDshStatusNotification(params);
|
|
295
|
+
const run = this.activeRun;
|
|
296
|
+
if (run?.ownsRootSession(parsed.sessionId) === true) {
|
|
297
|
+
run.receive({ kind: 'status', status: parsed.status });
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.emitUnknown(raw);
|
|
301
|
+
if (run?.ownsChildSession(parsed.sessionId) === true) {
|
|
302
|
+
run.receive({ kind: 'raw', event: raw });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
handleSubagentStarted(raw, params) {
|
|
306
|
+
const parsed = parseDshSubagentStarted(params);
|
|
307
|
+
const run = this.activeRun;
|
|
308
|
+
if (run?.hasPromptCorrelation() === true &&
|
|
309
|
+
run.ownsSession(parsed.parentSessionId)) {
|
|
310
|
+
run.registerChild(parsed.parentSessionId, parsed.childSessionId);
|
|
311
|
+
run.receive({ kind: 'raw', event: raw });
|
|
312
|
+
}
|
|
313
|
+
this.emitUnknown(raw);
|
|
314
|
+
}
|
|
315
|
+
handleSubagentFinished(raw, params) {
|
|
316
|
+
const parsed = parseDshSubagentFinished(params);
|
|
317
|
+
const run = this.activeRun;
|
|
318
|
+
if (run?.ownsChildSession(parsed.childSessionId) === true) {
|
|
319
|
+
run.finishChild(parsed.parentSessionId, parsed.childSessionId);
|
|
320
|
+
run.receive({ kind: 'raw', event: raw });
|
|
321
|
+
}
|
|
322
|
+
this.emitUnknown(raw);
|
|
323
|
+
}
|
|
324
|
+
emitNotification(event) {
|
|
325
|
+
emitToListeners(this.notificationListeners, event);
|
|
326
|
+
}
|
|
327
|
+
emitUnknown(event) {
|
|
328
|
+
emitToListeners(this.unknownListeners, event);
|
|
329
|
+
}
|
|
330
|
+
async nativeRequest(method, params, options = {}) {
|
|
331
|
+
this.assertOpen();
|
|
332
|
+
return this.transport.request(method, params, options);
|
|
333
|
+
}
|
|
334
|
+
async nativeNotify(method, params) {
|
|
335
|
+
this.assertOpen();
|
|
336
|
+
return this.transport.notify(method, params);
|
|
337
|
+
}
|
|
338
|
+
failProtocol(error) {
|
|
339
|
+
const failure = error instanceof HarnessError
|
|
340
|
+
? error
|
|
341
|
+
: new HarnessError('provider_api_incompatible', 'DeepSeek Harness emitted an incompatible notification.', {
|
|
342
|
+
retryable: false,
|
|
343
|
+
providerId: DSH_PROVIDER_ID,
|
|
344
|
+
profileId: this.profile.profileId,
|
|
345
|
+
});
|
|
346
|
+
this.activeRun?.failProtocol(failure.code);
|
|
347
|
+
this.abortConnection('protocol_incompatible');
|
|
348
|
+
}
|
|
349
|
+
abortConnection(reason) {
|
|
350
|
+
if (this.closed)
|
|
351
|
+
return;
|
|
352
|
+
this.closed = true;
|
|
353
|
+
this.activeRun?.abortConnection(reason);
|
|
354
|
+
this.activeRun = undefined;
|
|
355
|
+
void this.transport.close().catch(() => undefined);
|
|
356
|
+
}
|
|
357
|
+
async closeOnce(graceful) {
|
|
358
|
+
if (!this.closed) {
|
|
359
|
+
this.closed = true;
|
|
360
|
+
this.activeRun?.abortConnection('client_closed');
|
|
361
|
+
this.activeRun = undefined;
|
|
362
|
+
}
|
|
363
|
+
if (graceful && this.transport.isOpen()) {
|
|
364
|
+
await this.transport
|
|
365
|
+
.request('shutdown', undefined, {
|
|
366
|
+
timeoutMs: this.shutdownTimeoutMs,
|
|
367
|
+
})
|
|
368
|
+
.catch(() => undefined);
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
await this.transport.close();
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
throw mapError(error, this.profile, 'close', false, this.transport);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
runtimeIdentity() {
|
|
378
|
+
return dshCompatibilityIdentity(this.runtime.version);
|
|
379
|
+
}
|
|
380
|
+
assertOpen() {
|
|
381
|
+
if (this.closed) {
|
|
382
|
+
throw new HarnessError('connection_aborted', 'DeepSeek Harness SDK Runtime is closed.', {
|
|
383
|
+
retryable: false,
|
|
384
|
+
providerId: DSH_PROVIDER_ID,
|
|
385
|
+
profileId: this.profile.profileId,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
class DshSession {
|
|
391
|
+
client;
|
|
392
|
+
sessionId;
|
|
393
|
+
closed = false;
|
|
394
|
+
constructor(client, sessionId) {
|
|
395
|
+
this.client = client;
|
|
396
|
+
this.sessionId = sessionId;
|
|
397
|
+
}
|
|
398
|
+
ref() {
|
|
399
|
+
return this.client.sessionRef(this.sessionId);
|
|
400
|
+
}
|
|
401
|
+
capabilities() {
|
|
402
|
+
return Promise.resolve(this.client.capabilityManifest());
|
|
403
|
+
}
|
|
404
|
+
async start(input, options) {
|
|
405
|
+
this.assertOpen();
|
|
406
|
+
return this.client.startRun(this.sessionId, input, options);
|
|
407
|
+
}
|
|
408
|
+
async respond(requestId, response) {
|
|
409
|
+
this.assertOpen();
|
|
410
|
+
return this.client.respond(this.sessionId, requestId, response);
|
|
411
|
+
}
|
|
412
|
+
close() {
|
|
413
|
+
if (this.closed)
|
|
414
|
+
return Promise.resolve();
|
|
415
|
+
if (this.client.hasActiveRun(this.sessionId)) {
|
|
416
|
+
return Promise.reject(new HarnessError('run_conflict', 'Cannot close a DeepSeek Harness Session with an active Run.', {
|
|
417
|
+
retryable: false,
|
|
418
|
+
providerId: DSH_PROVIDER_ID,
|
|
419
|
+
profileId: this.ref().profileId,
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
this.closed = true;
|
|
423
|
+
return Promise.resolve();
|
|
424
|
+
}
|
|
425
|
+
assertOpen() {
|
|
426
|
+
if (this.closed) {
|
|
427
|
+
throw new HarnessError('session_not_found', 'DeepSeek Harness Session is closed.', {
|
|
428
|
+
retryable: false,
|
|
429
|
+
providerId: DSH_PROVIDER_ID,
|
|
430
|
+
profileId: this.ref().profileId,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
class DshRun {
|
|
436
|
+
reference;
|
|
437
|
+
maxRunEvents;
|
|
438
|
+
abortOwnerConnection;
|
|
439
|
+
onTerminal;
|
|
440
|
+
childParents = new Map();
|
|
441
|
+
eventQueue;
|
|
442
|
+
pending = [];
|
|
443
|
+
settlement;
|
|
444
|
+
timeout;
|
|
445
|
+
correlationStarted = false;
|
|
446
|
+
finalMessage;
|
|
447
|
+
finalResult;
|
|
448
|
+
lastEventSequence;
|
|
449
|
+
messageId;
|
|
450
|
+
resolveSettlement;
|
|
451
|
+
sequence = 0;
|
|
452
|
+
terminal;
|
|
453
|
+
terminalCount = 0;
|
|
454
|
+
usage;
|
|
455
|
+
constructor(reference, maxRunEvents, timeoutMs, abortOwnerConnection, onTerminal) {
|
|
456
|
+
this.reference = reference;
|
|
457
|
+
this.maxRunEvents = maxRunEvents;
|
|
458
|
+
this.abortOwnerConnection = abortOwnerConnection;
|
|
459
|
+
this.onTerminal = onTerminal;
|
|
460
|
+
validateRunTimeout(timeoutMs);
|
|
461
|
+
this.eventQueue = new EventQueue(maxRunEvents);
|
|
462
|
+
this.settlement = new Promise((resolveSettlement) => {
|
|
463
|
+
this.resolveSettlement = resolveSettlement;
|
|
464
|
+
});
|
|
465
|
+
this.timeout =
|
|
466
|
+
timeoutMs === undefined
|
|
467
|
+
? undefined
|
|
468
|
+
: setTimeout(() => {
|
|
469
|
+
this.abortOwnerConnection('local_timeout');
|
|
470
|
+
}, timeoutMs);
|
|
471
|
+
this.timeout?.unref();
|
|
472
|
+
this.emit({ type: 'run.started', data: {} });
|
|
473
|
+
}
|
|
474
|
+
ref() {
|
|
475
|
+
return { ...this.reference };
|
|
476
|
+
}
|
|
477
|
+
events() {
|
|
478
|
+
return this.eventQueue.iterable();
|
|
479
|
+
}
|
|
480
|
+
cancel() {
|
|
481
|
+
if (this.isTerminal())
|
|
482
|
+
return Promise.resolve({ mode: 'already_terminal' });
|
|
483
|
+
return Promise.reject(new HarnessError('unsupported_capability', 'DeepSeek Harness does not expose native Run cancellation in the current SDK protocol.', {
|
|
484
|
+
retryable: false,
|
|
485
|
+
providerId: DSH_PROVIDER_ID,
|
|
486
|
+
profileId: this.reference.profileId,
|
|
487
|
+
details: { capability: 'run.cancel' },
|
|
488
|
+
}));
|
|
489
|
+
}
|
|
490
|
+
result() {
|
|
491
|
+
return this.settlement;
|
|
492
|
+
}
|
|
493
|
+
ownsSession(sessionId) {
|
|
494
|
+
return this.ownsRootSession(sessionId) || this.ownsChildSession(sessionId);
|
|
495
|
+
}
|
|
496
|
+
hasPromptCorrelation() {
|
|
497
|
+
return this.correlationStarted;
|
|
498
|
+
}
|
|
499
|
+
ownsChildSession(sessionId) {
|
|
500
|
+
return this.childParents.has(sessionId);
|
|
501
|
+
}
|
|
502
|
+
registerChild(parentSessionId, childSessionId) {
|
|
503
|
+
if (!this.ownsSession(parentSessionId) ||
|
|
504
|
+
this.ownsSession(childSessionId)) {
|
|
505
|
+
throw new HarnessError('provider_api_incompatible', 'DeepSeek Harness emitted an incompatible subagent relationship.', { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
506
|
+
}
|
|
507
|
+
this.childParents.set(childSessionId, parentSessionId);
|
|
508
|
+
}
|
|
509
|
+
finishChild(parentSessionId, childSessionId) {
|
|
510
|
+
if (this.childParents.get(childSessionId) !== parentSessionId) {
|
|
511
|
+
throw new HarnessError('provider_api_incompatible', 'DeepSeek Harness emitted an incompatible subagent completion.', { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
512
|
+
}
|
|
513
|
+
const removed = new Set([childSessionId]);
|
|
514
|
+
let changed = true;
|
|
515
|
+
while (changed) {
|
|
516
|
+
changed = false;
|
|
517
|
+
for (const [candidate, parent] of this.childParents) {
|
|
518
|
+
if (removed.has(parent) && !removed.has(candidate)) {
|
|
519
|
+
removed.add(candidate);
|
|
520
|
+
changed = true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
for (const sessionId of removed)
|
|
525
|
+
this.childParents.delete(sessionId);
|
|
526
|
+
}
|
|
527
|
+
acknowledge(messageId) {
|
|
528
|
+
if (this.isTerminal())
|
|
529
|
+
return;
|
|
530
|
+
this.messageId = messageId;
|
|
531
|
+
const pending = this.pending.splice(0);
|
|
532
|
+
for (const notification of pending)
|
|
533
|
+
this.process(notification);
|
|
534
|
+
}
|
|
535
|
+
receive(notification) {
|
|
536
|
+
if (this.isTerminal())
|
|
537
|
+
return;
|
|
538
|
+
if (this.messageId === undefined) {
|
|
539
|
+
if (this.pending.length >= this.maxRunEvents - 1) {
|
|
540
|
+
this.abortOwnerConnection('event_buffer_overflow');
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
this.pending.push(notification);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.process(notification);
|
|
547
|
+
}
|
|
548
|
+
failProtocol(reason) {
|
|
549
|
+
if (this.isTerminal())
|
|
550
|
+
return;
|
|
551
|
+
this.finish({ status: 'failed', providerResult: { reason } }, 'run.failed');
|
|
552
|
+
}
|
|
553
|
+
abortConnection(reason) {
|
|
554
|
+
if (this.isTerminal())
|
|
555
|
+
return;
|
|
556
|
+
this.finish({ status: 'connection_aborted', providerResult: { reason } }, 'connection.aborted');
|
|
557
|
+
}
|
|
558
|
+
discardBeforeReturn() {
|
|
559
|
+
if (this.timeout !== undefined)
|
|
560
|
+
clearTimeout(this.timeout);
|
|
561
|
+
this.childParents.clear();
|
|
562
|
+
this.pending.length = 0;
|
|
563
|
+
this.eventQueue.close();
|
|
564
|
+
}
|
|
565
|
+
isTerminal() {
|
|
566
|
+
return this.finalResult !== undefined;
|
|
567
|
+
}
|
|
568
|
+
process(notification) {
|
|
569
|
+
if (notification.kind === 'raw') {
|
|
570
|
+
if (this.correlationStarted) {
|
|
571
|
+
this.emit({
|
|
572
|
+
type: 'provider',
|
|
573
|
+
data: { method: notification.event.method },
|
|
574
|
+
providerEventType: notification.event.method,
|
|
575
|
+
raw: notification.event,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (notification.kind === 'status') {
|
|
581
|
+
if (notification.status === 'idle' && this.correlationStarted) {
|
|
582
|
+
this.finishAtIdle();
|
|
583
|
+
}
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (this.correlationStarted &&
|
|
587
|
+
(this.lastEventSequence === undefined ||
|
|
588
|
+
notification.event.seq !== this.lastEventSequence + 1)) {
|
|
589
|
+
this.failProtocol('provider_api_incompatible');
|
|
590
|
+
this.abortOwnerConnection('protocol_incompatible');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (!this.correlationStarted) {
|
|
594
|
+
if (notification.event.type !== 'agent/inbox/spliced')
|
|
595
|
+
return;
|
|
596
|
+
const mapping = mapDshSessionEvent(notification.event);
|
|
597
|
+
if (mapping.insertedMessageIds.includes(this.messageId ?? '')) {
|
|
598
|
+
if (mapping.insertedMessageCount !== 1 ||
|
|
599
|
+
mapping.insertedMessageIds.length !== 1 ||
|
|
600
|
+
mapping.insertedMessageIds[0] !== this.messageId) {
|
|
601
|
+
this.failProtocol('ambiguous_prompt_receipt');
|
|
602
|
+
this.abortOwnerConnection('protocol_incompatible');
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.correlationStarted = true;
|
|
606
|
+
this.lastEventSequence = notification.event.seq;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
for (const event of mapping.events)
|
|
612
|
+
this.emit(event);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const mapping = mapDshSessionEvent(notification.event);
|
|
616
|
+
if (mapping.insertedMessageCount > 0) {
|
|
617
|
+
this.failProtocol('competing_prompt');
|
|
618
|
+
this.abortOwnerConnection('protocol_incompatible');
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
this.lastEventSequence = notification.event.seq;
|
|
622
|
+
if (mapping.terminal !== undefined) {
|
|
623
|
+
this.terminalCount += 1;
|
|
624
|
+
this.terminal ??= mapping.terminal;
|
|
625
|
+
}
|
|
626
|
+
for (const event of mapping.events)
|
|
627
|
+
this.emit(event);
|
|
628
|
+
}
|
|
629
|
+
emit(mapped) {
|
|
630
|
+
if (this.isTerminal())
|
|
631
|
+
return;
|
|
632
|
+
if (mapped.finalMessage !== undefined) {
|
|
633
|
+
this.finalMessage = mapped.finalMessage;
|
|
634
|
+
}
|
|
635
|
+
if (mapped.usage !== undefined)
|
|
636
|
+
this.usage = mapped.usage;
|
|
637
|
+
if (!this.eventQueue.push(this.portableEvent(mapped))) {
|
|
638
|
+
this.abortOwnerConnection('event_buffer_overflow');
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
finishAtIdle() {
|
|
642
|
+
if (this.terminalCount !== 1 || this.terminal === undefined) {
|
|
643
|
+
this.finish({
|
|
644
|
+
status: 'failed',
|
|
645
|
+
providerResult: {
|
|
646
|
+
reason: this.terminalCount === 0
|
|
647
|
+
? 'missing_terminal_reason'
|
|
648
|
+
: 'duplicate_terminal_reason',
|
|
649
|
+
},
|
|
650
|
+
}, 'run.failed');
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const terminal = this.terminal;
|
|
654
|
+
const result = {
|
|
655
|
+
...terminal.result,
|
|
656
|
+
...(terminal.result.status === 'completed' &&
|
|
657
|
+
this.finalMessage !== undefined
|
|
658
|
+
? { finalMessage: this.finalMessage }
|
|
659
|
+
: {}),
|
|
660
|
+
...(this.usage === undefined ? {} : { usage: this.usage }),
|
|
661
|
+
};
|
|
662
|
+
this.finish(result, terminal.valid ? terminal.eventType : 'run.failed');
|
|
663
|
+
}
|
|
664
|
+
finish(result, type) {
|
|
665
|
+
if (this.isTerminal())
|
|
666
|
+
return;
|
|
667
|
+
this.finalResult = result;
|
|
668
|
+
if (this.timeout !== undefined)
|
|
669
|
+
clearTimeout(this.timeout);
|
|
670
|
+
this.childParents.clear();
|
|
671
|
+
this.eventQueue.pushTerminal(this.portableEvent({ type, data: result }));
|
|
672
|
+
this.eventQueue.close();
|
|
673
|
+
this.onTerminal(this);
|
|
674
|
+
this.resolveSettlement(result);
|
|
675
|
+
}
|
|
676
|
+
portableEvent(mapped) {
|
|
677
|
+
const sequence = this.sequence++;
|
|
678
|
+
return {
|
|
679
|
+
id: `${this.reference.runId}:event:${String(sequence)}`,
|
|
680
|
+
type: mapped.type,
|
|
681
|
+
providerId: this.reference.providerId,
|
|
682
|
+
profileId: this.reference.profileId,
|
|
683
|
+
sessionId: this.reference.sessionId,
|
|
684
|
+
runId: this.reference.runId,
|
|
685
|
+
sequence,
|
|
686
|
+
timestamp: new Date().toISOString(),
|
|
687
|
+
data: mapped.data,
|
|
688
|
+
...(mapped.providerEventType === undefined
|
|
689
|
+
? {}
|
|
690
|
+
: { providerEventType: mapped.providerEventType }),
|
|
691
|
+
...(mapped.raw === undefined ? {} : { raw: mapped.raw }),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
ownsRootSession(sessionId) {
|
|
695
|
+
return this.reference.sessionId === sessionId;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
class EventQueue {
|
|
699
|
+
capacity;
|
|
700
|
+
values = [];
|
|
701
|
+
closed = false;
|
|
702
|
+
consumed = false;
|
|
703
|
+
waiter;
|
|
704
|
+
constructor(capacity) {
|
|
705
|
+
this.capacity = capacity;
|
|
706
|
+
}
|
|
707
|
+
push(event) {
|
|
708
|
+
if (this.closed)
|
|
709
|
+
return false;
|
|
710
|
+
if (this.waiter !== undefined) {
|
|
711
|
+
const waiter = this.waiter;
|
|
712
|
+
this.waiter = undefined;
|
|
713
|
+
waiter({ done: false, value: event });
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
if (this.values.length >= this.capacity - 1)
|
|
717
|
+
return false;
|
|
718
|
+
this.values.push(event);
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
pushTerminal(event) {
|
|
722
|
+
if (this.waiter !== undefined) {
|
|
723
|
+
const waiter = this.waiter;
|
|
724
|
+
this.waiter = undefined;
|
|
725
|
+
waiter({ done: false, value: event });
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
this.values.push(event);
|
|
729
|
+
}
|
|
730
|
+
close() {
|
|
731
|
+
this.closed = true;
|
|
732
|
+
}
|
|
733
|
+
iterable() {
|
|
734
|
+
if (this.consumed) {
|
|
735
|
+
return {
|
|
736
|
+
[Symbol.asyncIterator]: () => ({
|
|
737
|
+
next: () => Promise.reject(new HarnessError('run_conflict', 'DeepSeek Harness Run events already have a consumer.', { retryable: false, providerId: DSH_PROVIDER_ID })),
|
|
738
|
+
}),
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
this.consumed = true;
|
|
742
|
+
return {
|
|
743
|
+
[Symbol.asyncIterator]: () => ({
|
|
744
|
+
next: () => this.next(),
|
|
745
|
+
}),
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
next() {
|
|
749
|
+
const event = this.values.shift();
|
|
750
|
+
if (event !== undefined) {
|
|
751
|
+
return Promise.resolve({ done: false, value: event });
|
|
752
|
+
}
|
|
753
|
+
if (this.closed)
|
|
754
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
755
|
+
if (this.waiter !== undefined) {
|
|
756
|
+
return Promise.reject(new HarnessError('run_conflict', 'A DeepSeek Harness event read is already pending.', { retryable: false, providerId: DSH_PROVIDER_ID }));
|
|
757
|
+
}
|
|
758
|
+
return new Promise((resolveNext) => {
|
|
759
|
+
this.waiter = resolveNext;
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async function spawnTransport(profile, options) {
|
|
764
|
+
if (profile.connection.kind !== 'process')
|
|
765
|
+
throw profileInvalid(profile);
|
|
766
|
+
const child = spawn(profile.connection.command, [...(profile.connection.args ?? [])], {
|
|
767
|
+
cwd: profile.connection.cwd,
|
|
768
|
+
shell: false,
|
|
769
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
770
|
+
});
|
|
771
|
+
await processStarted(child);
|
|
772
|
+
try {
|
|
773
|
+
return new JsonRpcStdioTransport({
|
|
774
|
+
...options,
|
|
775
|
+
emitJsonRpcVersion: true,
|
|
776
|
+
readable: child.stdout,
|
|
777
|
+
writable: child.stdin,
|
|
778
|
+
cleanup: () => terminateChild(child),
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
await terminateChild(child);
|
|
783
|
+
throw error;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function processStarted(child) {
|
|
787
|
+
return new Promise((resolveStarted, rejectStarted) => {
|
|
788
|
+
const onSpawn = () => {
|
|
789
|
+
child.off('error', onError);
|
|
790
|
+
child.on('error', () => undefined);
|
|
791
|
+
resolveStarted();
|
|
792
|
+
};
|
|
793
|
+
const onError = (error) => {
|
|
794
|
+
child.off('spawn', onSpawn);
|
|
795
|
+
rejectStarted(error);
|
|
796
|
+
};
|
|
797
|
+
child.once('spawn', onSpawn);
|
|
798
|
+
child.once('error', onError);
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
async function terminateChild(child) {
|
|
802
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
803
|
+
return;
|
|
804
|
+
child.kill();
|
|
805
|
+
if (await waitForChildExit(child, childTerminationTimeoutMs))
|
|
806
|
+
return;
|
|
807
|
+
child.kill('SIGKILL');
|
|
808
|
+
if (await waitForChildExit(child, childTerminationTimeoutMs))
|
|
809
|
+
return;
|
|
810
|
+
throw new Error('DeepSeek Harness child process did not exit after forced termination.');
|
|
811
|
+
}
|
|
812
|
+
function waitForChildExit(child, timeoutMs) {
|
|
813
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
814
|
+
return Promise.resolve(true);
|
|
815
|
+
}
|
|
816
|
+
return new Promise((resolveExit) => {
|
|
817
|
+
let settled = false;
|
|
818
|
+
const onExit = () => {
|
|
819
|
+
finish(true);
|
|
820
|
+
};
|
|
821
|
+
const timer = setTimeout(() => {
|
|
822
|
+
finish(false);
|
|
823
|
+
}, timeoutMs);
|
|
824
|
+
function finish(exited) {
|
|
825
|
+
if (settled)
|
|
826
|
+
return;
|
|
827
|
+
settled = true;
|
|
828
|
+
clearTimeout(timer);
|
|
829
|
+
child.off('exit', onExit);
|
|
830
|
+
resolveExit(exited);
|
|
831
|
+
}
|
|
832
|
+
child.once('exit', onExit);
|
|
833
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
834
|
+
finish(true);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
function dshCapabilities(profile, runtimeIdentity) {
|
|
838
|
+
const native = { mode: 'native', source: 'schema' };
|
|
839
|
+
const unsupported = {
|
|
840
|
+
mode: 'unsupported',
|
|
841
|
+
source: 'schema',
|
|
842
|
+
};
|
|
843
|
+
return {
|
|
844
|
+
providerId: DSH_PROVIDER_ID,
|
|
845
|
+
profileId: profile.profileId,
|
|
846
|
+
capabilities: {
|
|
847
|
+
'session.create': native,
|
|
848
|
+
'session.resume': unsupported,
|
|
849
|
+
'session.fork': unsupported,
|
|
850
|
+
'session.close': {
|
|
851
|
+
mode: 'adapter_controlled',
|
|
852
|
+
reason: 'Closing a Harapter Session only closes its local handle.',
|
|
853
|
+
source: 'configuration',
|
|
854
|
+
},
|
|
855
|
+
'run.stream': native,
|
|
856
|
+
'run.cancel': unsupported,
|
|
857
|
+
'run.timeout': {
|
|
858
|
+
mode: 'adapter_controlled',
|
|
859
|
+
reason: 'A local timeout aborts the owning runtime connection.',
|
|
860
|
+
source: 'configuration',
|
|
861
|
+
},
|
|
862
|
+
'connection.abort': {
|
|
863
|
+
mode: 'adapter_controlled',
|
|
864
|
+
source: 'configuration',
|
|
865
|
+
},
|
|
866
|
+
'input.text': native,
|
|
867
|
+
'input.image': unsupported,
|
|
868
|
+
'input.file': unsupported,
|
|
869
|
+
'interaction.approval': unsupported,
|
|
870
|
+
'interaction.user_input': unsupported,
|
|
871
|
+
'interaction.provider': unsupported,
|
|
872
|
+
'event.raw': { mode: 'adapter_controlled', source: 'configuration' },
|
|
873
|
+
'native.client': native,
|
|
874
|
+
},
|
|
875
|
+
observedAt: new Date().toISOString(),
|
|
876
|
+
runtimeIdentity,
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function connectionOptions(value, profile) {
|
|
880
|
+
const options = value ?? {};
|
|
881
|
+
const allowed = new Set([
|
|
882
|
+
'maxBufferedMessages',
|
|
883
|
+
'maxMessageBytes',
|
|
884
|
+
'maxPendingInboundRequests',
|
|
885
|
+
'maxPendingRequests',
|
|
886
|
+
'maxPendingWrites',
|
|
887
|
+
'maxRunEvents',
|
|
888
|
+
'maxTokens',
|
|
889
|
+
'model',
|
|
890
|
+
'provider',
|
|
891
|
+
'reasoningEffort',
|
|
892
|
+
'requestTimeoutMs',
|
|
893
|
+
'shutdownTimeoutMs',
|
|
894
|
+
]);
|
|
895
|
+
if (Object.keys(options).some((key) => !allowed.has(key))) {
|
|
896
|
+
throw profileInvalid(profile);
|
|
897
|
+
}
|
|
898
|
+
const provider = nonEmptyProfileString(options['provider'], 'provider');
|
|
899
|
+
const model = nonEmptyProfileString(options['model'], 'model');
|
|
900
|
+
const transport = {
|
|
901
|
+
emitJsonRpcVersion: true,
|
|
902
|
+
};
|
|
903
|
+
for (const name of [
|
|
904
|
+
'maxBufferedMessages',
|
|
905
|
+
'maxMessageBytes',
|
|
906
|
+
'maxPendingInboundRequests',
|
|
907
|
+
'maxPendingRequests',
|
|
908
|
+
'maxPendingWrites',
|
|
909
|
+
]) {
|
|
910
|
+
if (options[name] !== undefined) {
|
|
911
|
+
transport[name] = positiveProfileInteger(options[name], name);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (options['requestTimeoutMs'] !== undefined) {
|
|
915
|
+
transport['requestTimeoutMs'] = positiveProfileTimer(options['requestTimeoutMs'], 'requestTimeoutMs');
|
|
916
|
+
}
|
|
917
|
+
const reasoningEffort = options['reasoningEffort'] === undefined
|
|
918
|
+
? undefined
|
|
919
|
+
: nonEmptyProfileString(options['reasoningEffort'], 'reasoningEffort');
|
|
920
|
+
const maxTokens = options['maxTokens'] === undefined
|
|
921
|
+
? undefined
|
|
922
|
+
: positiveProfileInteger(options['maxTokens'], 'maxTokens');
|
|
923
|
+
const cwd = resolve(profile.connection.kind === 'process'
|
|
924
|
+
? (profile.connection.cwd ?? process.cwd())
|
|
925
|
+
: process.cwd());
|
|
926
|
+
return {
|
|
927
|
+
initialize: {
|
|
928
|
+
cwd,
|
|
929
|
+
provider,
|
|
930
|
+
model,
|
|
931
|
+
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
932
|
+
...(maxTokens === undefined ? {} : { maxTokens }),
|
|
933
|
+
},
|
|
934
|
+
maxRunEvents: runEventCapacity(options['maxRunEvents']),
|
|
935
|
+
shutdownTimeoutMs: options['shutdownTimeoutMs'] === undefined
|
|
936
|
+
? defaultShutdownTimeoutMs
|
|
937
|
+
: positiveProfileTimer(options['shutdownTimeoutMs'], 'shutdownTimeoutMs'),
|
|
938
|
+
transport,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function runEventCapacity(value) {
|
|
942
|
+
if (value === undefined)
|
|
943
|
+
return defaultMaxRunEvents;
|
|
944
|
+
const capacity = positiveProfileInteger(value, 'maxRunEvents');
|
|
945
|
+
if (capacity < 2) {
|
|
946
|
+
throw new HarnessError('profile_invalid', 'DeepSeek Harness maxRunEvents must reserve a terminal event.', { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
947
|
+
}
|
|
948
|
+
if (capacity > maximumRunEvents) {
|
|
949
|
+
throw new HarnessError('profile_invalid', `DeepSeek Harness maxRunEvents cannot exceed ${String(maximumRunEvents)}.`, { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
950
|
+
}
|
|
951
|
+
return capacity;
|
|
952
|
+
}
|
|
953
|
+
function validateProfile(profile) {
|
|
954
|
+
if (profile.providerId !== DSH_PROVIDER_ID ||
|
|
955
|
+
profile.connection.kind !== 'process' ||
|
|
956
|
+
profile.connection.ownership !== 'adapter' ||
|
|
957
|
+
profile.connection.command.length === 0 ||
|
|
958
|
+
profile.connection.envRefs !== undefined) {
|
|
959
|
+
throw profileInvalid(profile);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function profileInvalid(profile) {
|
|
963
|
+
return new HarnessError('profile_invalid', 'DeepSeek Harness requires an adapter-owned process Profile, provider and model options, and no unresolved Secret references.', {
|
|
964
|
+
retryable: false,
|
|
965
|
+
providerId: DSH_PROVIDER_ID,
|
|
966
|
+
profileId: profile.profileId,
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
function nonEmptyProfileString(value, label) {
|
|
970
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
971
|
+
throw new HarnessError('profile_invalid', `DeepSeek Harness ${label} must be a non-empty string.`, { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
972
|
+
}
|
|
973
|
+
return value;
|
|
974
|
+
}
|
|
975
|
+
function positiveProfileInteger(value, label) {
|
|
976
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
|
|
977
|
+
throw new HarnessError('profile_invalid', `DeepSeek Harness ${label} must be a positive integer.`, { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
978
|
+
}
|
|
979
|
+
return value;
|
|
980
|
+
}
|
|
981
|
+
function positiveProfileTimer(value, label) {
|
|
982
|
+
const timeout = positiveProfileInteger(value, label);
|
|
983
|
+
if (timeout > maximumTimerMilliseconds) {
|
|
984
|
+
throw new HarnessError('profile_invalid', `DeepSeek Harness ${label} exceeds the supported timer range.`, { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
985
|
+
}
|
|
986
|
+
return timeout;
|
|
987
|
+
}
|
|
988
|
+
function validateRunTimeout(timeoutMs) {
|
|
989
|
+
if (timeoutMs === undefined)
|
|
990
|
+
return;
|
|
991
|
+
if (!Number.isSafeInteger(timeoutMs) ||
|
|
992
|
+
timeoutMs <= 0 ||
|
|
993
|
+
timeoutMs > maximumTimerMilliseconds) {
|
|
994
|
+
throw new HarnessError('invalid_request', 'DeepSeek Harness Run timeoutMs must be a positive supported timer value.', { retryable: false, providerId: DSH_PROVIDER_ID });
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function snapshotProfile(profile) {
|
|
998
|
+
return {
|
|
999
|
+
...profile,
|
|
1000
|
+
connection: {
|
|
1001
|
+
...profile.connection,
|
|
1002
|
+
...(profile.connection.kind === 'process' && profile.connection.args
|
|
1003
|
+
? { args: [...profile.connection.args] }
|
|
1004
|
+
: {}),
|
|
1005
|
+
},
|
|
1006
|
+
...(profile.providerOptions === undefined
|
|
1007
|
+
? {}
|
|
1008
|
+
: { providerOptions: { ...profile.providerOptions } }),
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function emitToListeners(listeners, event) {
|
|
1012
|
+
for (const listener of [...listeners]) {
|
|
1013
|
+
try {
|
|
1014
|
+
listener(structuredClone(event));
|
|
1015
|
+
}
|
|
1016
|
+
catch {
|
|
1017
|
+
// Provider observers cannot break lifecycle processing.
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function mapError(error, profile, phase, connecting = false, transport) {
|
|
1022
|
+
if (error instanceof HarnessError)
|
|
1023
|
+
return error;
|
|
1024
|
+
if (error instanceof JsonRpcRemoteError) {
|
|
1025
|
+
const remote = error.getRemoteError();
|
|
1026
|
+
return new HarnessError(remote.code === -32_601 ? 'provider_api_incompatible' : 'provider_error', `DeepSeek Harness rejected ${phase}.`, {
|
|
1027
|
+
retryable: false,
|
|
1028
|
+
providerId: DSH_PROVIDER_ID,
|
|
1029
|
+
profileId: profile.profileId,
|
|
1030
|
+
providerCode: String(remote.code),
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
if (error instanceof JsonRpcTransportError) {
|
|
1034
|
+
const code = error.code === 'request_timeout'
|
|
1035
|
+
? 'timeout'
|
|
1036
|
+
: connecting
|
|
1037
|
+
? 'connection_failed'
|
|
1038
|
+
: transport?.isOpen() === false
|
|
1039
|
+
? 'connection_aborted'
|
|
1040
|
+
: 'provider_error';
|
|
1041
|
+
return new HarnessError(code, `DeepSeek Harness ${phase} did not complete.`, {
|
|
1042
|
+
retryable: error.code === 'request_timeout' ||
|
|
1043
|
+
error.code === 'capacity_exceeded',
|
|
1044
|
+
providerId: DSH_PROVIDER_ID,
|
|
1045
|
+
profileId: profile.profileId,
|
|
1046
|
+
providerCode: error.code,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
const systemCode = typeof error === 'object' && error !== null && 'code' in error
|
|
1050
|
+
? error.code
|
|
1051
|
+
: undefined;
|
|
1052
|
+
if (connecting && systemCode === 'ENOENT') {
|
|
1053
|
+
return new HarnessError('runtime_not_found', 'The configured DeepSeek Harness runtime was not found.', {
|
|
1054
|
+
retryable: false,
|
|
1055
|
+
providerId: DSH_PROVIDER_ID,
|
|
1056
|
+
profileId: profile.profileId,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
return new HarnessError(connecting ? 'connection_failed' : 'provider_error', `DeepSeek Harness ${phase} failed.`, {
|
|
1060
|
+
retryable: false,
|
|
1061
|
+
providerId: DSH_PROVIDER_ID,
|
|
1062
|
+
profileId: profile.profileId,
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
//# sourceMappingURL=adapter.js.map
|