@h1v35/hivex 0.2.0 → 0.2.2
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/README.md +55 -163
- package/docs/CONTEXT.md +20 -36
- package/docs/README.md +6 -12
- package/docs/adr/0003-independent-bun-installation.md +5 -19
- package/docs/adr/0010-practical-knowledge-assistance.md +28 -81
- package/docs/adr/0011-shared-knowledge-and-selective-history.md +16 -43
- package/docs/guidelines/engineering.md +74 -0
- package/docs/procedures/self-hosted-runner.md +7 -0
- package/package.json +32 -11
- package/skills/hivex/SKILL.md +28 -92
- package/skills/hivex/references/markdown.md +12 -42
- package/src/cli/diagnostic.ts +21 -11
- package/src/cli.ts +46 -36
- package/src/documents.ts +502 -320
- package/src/errors.ts +8 -6
- package/src/implementation.ts +185 -87
- package/src/ingestion-units.ts +107 -64
- package/src/knowledge-maintenance.ts +35 -22
- package/src/knowledge-model.ts +386 -268
- package/src/knowledge-serialization.ts +239 -0
- package/src/knowledge-snapshot.ts +100 -77
- package/src/knowledge-store.ts +634 -453
- package/src/knowledge.ts +1001 -758
- package/src/markdown.ts +107 -45
- package/src/model/connection.ts +134 -76
- package/src/model/failure.ts +46 -23
- package/src/model/invoke.ts +346 -166
- package/src/model/profile.ts +201 -103
- package/src/model/rpc-error.ts +21 -0
- package/src/model/server.ts +151 -82
- package/src/model/thread.ts +24 -14
- package/src/model/transcript.ts +87 -46
- package/src/ordering.ts +9 -0
- package/src/retrieval/lexical.ts +64 -41
- package/src/review.ts +83 -55
- package/src/runtime.d.ts +4 -0
- package/src/snapshot-command.ts +82 -43
- package/src/source-relocation.ts +222 -0
- package/docs/engineering.md +0 -174
package/src/model/invoke.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { mkdtempSync, readdirSync, realpathSync, rmSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { startServer } from './server.ts';
|
|
6
|
-
import { knowledgeTurn
|
|
7
|
-
import { captureTranscript, type Usage } from './transcript.ts';
|
|
6
|
+
import { knowledgeTurn } from './profile.ts';
|
|
8
7
|
import { startKnowledgeThread } from './thread.ts';
|
|
9
8
|
import { failureDiagnostic, ServerAdmissionFailure } from './failure.ts';
|
|
9
|
+
import { captureTranscript } from './transcript.ts';
|
|
10
|
+
import type { ProfileEvidence } from './profile.ts';
|
|
11
|
+
import type { Usage } from './transcript.ts';
|
|
10
12
|
|
|
11
13
|
export type NativeProcessStarted = (nativeProcessId: number) => void;
|
|
12
14
|
|
|
13
|
-
export
|
|
15
|
+
export interface InvocationOptions {
|
|
14
16
|
binary: string;
|
|
15
17
|
prompt: string;
|
|
16
18
|
schema: Record<string, unknown>;
|
|
17
19
|
deadlineMilliseconds: number;
|
|
18
20
|
onNativeProcessStarted?: NativeProcessStarted;
|
|
19
|
-
}
|
|
20
|
-
export
|
|
21
|
+
}
|
|
22
|
+
export interface InvocationReport {
|
|
21
23
|
outcome: string;
|
|
22
24
|
code?: string;
|
|
23
25
|
deadlineMilliseconds: number;
|
|
@@ -32,22 +34,35 @@ export type InvocationReport = {
|
|
|
32
34
|
admission?: ProfileEvidence & { launchPolicyHash: string };
|
|
33
35
|
diagnostic?: Record<string, unknown>;
|
|
34
36
|
nativeProcessId?: number;
|
|
35
|
-
}
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
class
|
|
38
|
-
|
|
39
|
+
class ModelAbortError extends Error {
|
|
40
|
+
name = 'ModelAbortError';
|
|
41
|
+
readonly kind: 'cancelled' | 'timeout';
|
|
42
|
+
|
|
43
|
+
constructor(kind: 'cancelled' | 'timeout', options?: ErrorOptions) {
|
|
44
|
+
super('', options);
|
|
45
|
+
this.kind = kind;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
39
48
|
|
|
40
|
-
async
|
|
49
|
+
const completedWithin = async (options: {
|
|
41
50
|
captured: ReturnType<typeof captureTranscript>;
|
|
42
51
|
server: Awaited<ReturnType<typeof startServer>>;
|
|
43
52
|
milliseconds: number;
|
|
44
53
|
signal?: AbortSignal;
|
|
45
|
-
}) {
|
|
54
|
+
}) => {
|
|
46
55
|
const expired = Promise.withResolvers<never>();
|
|
47
|
-
const cancelled = () =>
|
|
56
|
+
const cancelled = (): void => {
|
|
57
|
+
expired.reject(new ModelAbortError('cancelled'));
|
|
58
|
+
};
|
|
48
59
|
options.signal?.addEventListener('abort', cancelled, { once: true });
|
|
49
|
-
if (options.signal?.aborted)
|
|
50
|
-
|
|
60
|
+
if (options.signal?.aborted === true) {
|
|
61
|
+
cancelled();
|
|
62
|
+
}
|
|
63
|
+
const timer = setTimeout(() => {
|
|
64
|
+
expired.reject(new ModelAbortError('timeout'));
|
|
65
|
+
}, options.milliseconds);
|
|
51
66
|
try {
|
|
52
67
|
return await Promise.race([
|
|
53
68
|
options.captured.done.promise,
|
|
@@ -58,208 +73,373 @@ async function completedWithin(options: {
|
|
|
58
73
|
clearTimeout(timer);
|
|
59
74
|
options.signal?.removeEventListener('abort', cancelled);
|
|
60
75
|
}
|
|
61
|
-
}
|
|
76
|
+
};
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
const isInterruptedTurn = function isInterruptedTurn(
|
|
79
|
+
end: Awaited<ReturnType<typeof completedWithin>>,
|
|
80
|
+
threadId: string,
|
|
81
|
+
turnId: string
|
|
82
|
+
) {
|
|
83
|
+
return end.threadId === threadId && end.turn.id === turnId && end.turn.status === 'interrupted';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const isInterruptRequestAccepted = async (options: {
|
|
65
87
|
server: Awaited<ReturnType<typeof startServer>>;
|
|
66
88
|
threadId: string;
|
|
67
89
|
turnId: string;
|
|
68
|
-
}) {
|
|
90
|
+
}) => {
|
|
69
91
|
try {
|
|
70
92
|
await options.server.rpc.request(
|
|
71
93
|
'turn/interrupt',
|
|
72
94
|
{ threadId: options.threadId, turnId: options.turnId },
|
|
73
|
-
{ timeoutMilliseconds: 5000 }
|
|
95
|
+
{ timeoutMilliseconds: 5000 }
|
|
74
96
|
);
|
|
97
|
+
return true;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const isInterrupted = async (options: {
|
|
104
|
+
captured: ReturnType<typeof captureTranscript>;
|
|
105
|
+
server: Awaited<ReturnType<typeof startServer>>;
|
|
106
|
+
threadId: string;
|
|
107
|
+
turnId: string;
|
|
108
|
+
}) => {
|
|
109
|
+
const isInterruptAccepted = await isInterruptRequestAccepted(options);
|
|
110
|
+
if (!isInterruptAccepted) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
75
114
|
const end = await completedWithin({
|
|
76
115
|
captured: options.captured,
|
|
77
|
-
server: options.server,
|
|
78
116
|
milliseconds: 5000,
|
|
117
|
+
server: options.server,
|
|
79
118
|
});
|
|
80
|
-
return (
|
|
81
|
-
end.threadId === options.threadId &&
|
|
82
|
-
end.turn.id === options.turnId &&
|
|
83
|
-
end.turn.status === 'interrupted'
|
|
84
|
-
);
|
|
119
|
+
return isInterruptedTurn(end, options.threadId, options.turnId);
|
|
85
120
|
} catch {
|
|
86
121
|
return false;
|
|
87
122
|
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const turnStartResponse = z.looseObject({
|
|
126
|
+
turn: z.looseObject({ id: z.string() }),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
interface RunTurnOptions extends InvocationOptions {
|
|
130
|
+
captured: ReturnType<typeof captureTranscript>;
|
|
131
|
+
server: Awaited<ReturnType<typeof startServer>>;
|
|
132
|
+
signal: AbortSignal;
|
|
133
|
+
threadId: string;
|
|
134
|
+
workspace: string;
|
|
88
135
|
}
|
|
89
136
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
137
|
+
interface InvocationResult {
|
|
138
|
+
value: unknown;
|
|
139
|
+
report: InvocationReport;
|
|
140
|
+
retry: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const createInvocationResult = (
|
|
144
|
+
value: unknown,
|
|
145
|
+
report: InvocationReport,
|
|
146
|
+
shouldRetry: boolean
|
|
147
|
+
): InvocationResult => {
|
|
148
|
+
const valueField = { value };
|
|
149
|
+
const reportField = { report };
|
|
150
|
+
const retryField = { retry: shouldRetry };
|
|
151
|
+
return { ...valueField, ...reportField, ...retryField };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const failureCode = (options: { isCancelled: boolean; isTimeout: boolean }) => {
|
|
155
|
+
if (options.isCancelled) {
|
|
156
|
+
return 'MODEL_CANCELLED';
|
|
157
|
+
}
|
|
158
|
+
return options.isTimeout ? 'MODEL_TIMEOUT' : 'MODEL_PROTOCOL_FAILED';
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const completeTurn = async (options: RunTurnOptions & { begin: number; turnId: string }) => {
|
|
162
|
+
if (options.signal.aborted) {
|
|
163
|
+
throw new ModelAbortError('cancelled');
|
|
164
|
+
}
|
|
165
|
+
const remaining = options.deadlineMilliseconds - (performance.now() - options.begin);
|
|
166
|
+
if (remaining <= 0) {
|
|
167
|
+
throw new ModelAbortError('timeout');
|
|
168
|
+
}
|
|
169
|
+
const end = await completedWithin({
|
|
170
|
+
captured: options.captured,
|
|
171
|
+
milliseconds: remaining,
|
|
172
|
+
server: options.server,
|
|
173
|
+
signal: options.signal,
|
|
174
|
+
});
|
|
175
|
+
if (
|
|
176
|
+
end.threadId !== options.threadId ||
|
|
177
|
+
end.turn.id !== options.turnId ||
|
|
178
|
+
end.turn.status !== 'completed'
|
|
179
|
+
) {
|
|
180
|
+
throw new Error('Model completion was not established');
|
|
181
|
+
}
|
|
182
|
+
options.captured.assertValid({
|
|
183
|
+
threadId: options.threadId,
|
|
184
|
+
turnId: options.turnId,
|
|
185
|
+
});
|
|
186
|
+
const final = options.captured.items.at(-1);
|
|
187
|
+
const text = final?.item.text;
|
|
188
|
+
if (
|
|
189
|
+
text === undefined ||
|
|
190
|
+
text === '' ||
|
|
191
|
+
final?.threadId !== options.threadId ||
|
|
192
|
+
final.turnId !== options.turnId
|
|
193
|
+
) {
|
|
194
|
+
throw new Error('Structured model output is missing');
|
|
195
|
+
}
|
|
196
|
+
if (readdirSync(options.workspace).length !== 0) {
|
|
197
|
+
throw new Error('Knowledge workspace was mutated');
|
|
198
|
+
}
|
|
199
|
+
return text;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const runTurn = async (options: RunTurnOptions) => {
|
|
99
203
|
const { server, captured, threadId } = options;
|
|
100
204
|
const begin = performance.now();
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
205
|
+
const inputTextType = { type: 'text' };
|
|
206
|
+
const inputText = { text: options.prompt };
|
|
207
|
+
const turnInput = {
|
|
208
|
+
...inputTextType,
|
|
209
|
+
...inputText,
|
|
210
|
+
};
|
|
211
|
+
const turnThread = { threadId };
|
|
212
|
+
const turnInputParameter = { input: [turnInput] };
|
|
213
|
+
const turnOutputSchema = { outputSchema: options.schema };
|
|
214
|
+
const turnParameters = {
|
|
215
|
+
...turnThread,
|
|
216
|
+
...knowledgeTurn,
|
|
217
|
+
...turnInputParameter,
|
|
218
|
+
...turnOutputSchema,
|
|
219
|
+
};
|
|
220
|
+
let accepted: z.infer<typeof turnStartResponse> | null;
|
|
221
|
+
try {
|
|
222
|
+
const response = await server.rpc.request('turn/start', turnParameters, {
|
|
223
|
+
timeoutMilliseconds: Math.min(options.deadlineMilliseconds, 30_000),
|
|
224
|
+
});
|
|
225
|
+
accepted = turnStartResponse.parse(response);
|
|
226
|
+
} catch {
|
|
227
|
+
accepted = null;
|
|
228
|
+
}
|
|
229
|
+
if (accepted === null) {
|
|
230
|
+
const reportOutcome = { outcome: 'failed' };
|
|
231
|
+
const reportCode = { code: 'MODEL_START_UNCONFIRMED' };
|
|
232
|
+
const reportTurnAccepted = { turnAccepted: 'unknown' as const };
|
|
233
|
+
const reportThreadId = { threadId };
|
|
234
|
+
const reportDeadlineMilliseconds = {
|
|
120
235
|
deadlineMilliseconds: options.deadlineMilliseconds,
|
|
121
|
-
usage: null,
|
|
122
236
|
};
|
|
123
|
-
|
|
237
|
+
const reportUsage = { usage: null };
|
|
238
|
+
const report: InvocationReport = {
|
|
239
|
+
...reportOutcome,
|
|
240
|
+
...reportCode,
|
|
241
|
+
...reportTurnAccepted,
|
|
242
|
+
...reportThreadId,
|
|
243
|
+
...reportDeadlineMilliseconds,
|
|
244
|
+
...reportUsage,
|
|
245
|
+
};
|
|
246
|
+
return createInvocationResult(null, report, false);
|
|
124
247
|
}
|
|
125
248
|
const turnId = accepted.turn.id;
|
|
126
249
|
try {
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
captured.assertValid({ threadId, turnId });
|
|
134
|
-
const final = captured.items.at(-1);
|
|
135
|
-
if (!final || final.threadId !== threadId || final.turnId !== turnId || !final.item.text)
|
|
136
|
-
throw new Error('Structured model output is missing');
|
|
137
|
-
if (readdirSync(options.workspace).length) throw new Error('Knowledge workspace was mutated');
|
|
138
|
-
const report: InvocationReport = {
|
|
139
|
-
outcome: 'completed',
|
|
140
|
-
threadId,
|
|
141
|
-
turnId,
|
|
142
|
-
turnAccepted: 'confirmed',
|
|
250
|
+
const value = await completeTurn({ ...options, begin, turnId });
|
|
251
|
+
const reportOutcome = { outcome: 'completed' };
|
|
252
|
+
const reportThreadId = { threadId };
|
|
253
|
+
const reportTurnId = { turnId };
|
|
254
|
+
const reportTurnAccepted = { turnAccepted: 'confirmed' as const };
|
|
255
|
+
const reportDeadlineMilliseconds = {
|
|
143
256
|
deadlineMilliseconds: options.deadlineMilliseconds,
|
|
144
|
-
usage: captured.measured({ threadId, turnId }),
|
|
145
257
|
};
|
|
146
|
-
|
|
147
|
-
} catch (error) {
|
|
148
|
-
const confirmed = await interrupt({ ...options, turnId });
|
|
149
|
-
const timeout = error instanceof ModelTimeout;
|
|
150
|
-
const cancelled = error instanceof ModelCancelled;
|
|
258
|
+
const reportUsage = { usage: captured.measured({ threadId, turnId }) };
|
|
151
259
|
const report: InvocationReport = {
|
|
152
|
-
|
|
153
|
-
|
|
260
|
+
...reportOutcome,
|
|
261
|
+
...reportThreadId,
|
|
262
|
+
...reportTurnId,
|
|
263
|
+
...reportTurnAccepted,
|
|
264
|
+
...reportDeadlineMilliseconds,
|
|
265
|
+
...reportUsage,
|
|
266
|
+
};
|
|
267
|
+
return createInvocationResult(value, report, false);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
const isInterruptionConfirmed = await isInterrupted({
|
|
270
|
+
...options,
|
|
154
271
|
turnId,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
272
|
+
});
|
|
273
|
+
const isTimeout = error instanceof ModelAbortError && error.kind === 'timeout';
|
|
274
|
+
const isCancelled = error instanceof ModelAbortError && error.kind === 'cancelled';
|
|
275
|
+
const reportOutcome = {
|
|
276
|
+
outcome: isTimeout ? 'timeout' : 'failed',
|
|
277
|
+
};
|
|
278
|
+
const reportThreadId = { threadId };
|
|
279
|
+
const reportTurnId = { turnId };
|
|
280
|
+
const reportCode = { code: failureCode({ isCancelled, isTimeout }) };
|
|
281
|
+
const reportInterruption = {
|
|
282
|
+
interruption: isInterruptionConfirmed ? 'confirmed' : 'unconfirmed',
|
|
283
|
+
};
|
|
284
|
+
const reportTurnAccepted = { turnAccepted: 'confirmed' as const };
|
|
285
|
+
const reportDeadlineMilliseconds = {
|
|
158
286
|
deadlineMilliseconds: options.deadlineMilliseconds,
|
|
159
|
-
usage: captured.measured({ threadId, turnId }),
|
|
160
287
|
};
|
|
161
|
-
|
|
288
|
+
const reportUsage = { usage: captured.measured({ threadId, turnId }) };
|
|
289
|
+
const report: InvocationReport = {
|
|
290
|
+
...reportOutcome,
|
|
291
|
+
...reportThreadId,
|
|
292
|
+
...reportTurnId,
|
|
293
|
+
...reportCode,
|
|
294
|
+
...reportInterruption,
|
|
295
|
+
...reportTurnAccepted,
|
|
296
|
+
...reportDeadlineMilliseconds,
|
|
297
|
+
...reportUsage,
|
|
298
|
+
};
|
|
299
|
+
return createInvocationResult(null, report, isTimeout && isInterruptionConfirmed);
|
|
162
300
|
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const captureFailure = async <Value>(promise: Promise<Value>) => {
|
|
304
|
+
try {
|
|
305
|
+
return { value: await promise };
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return { error };
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
interface InvocationResource {
|
|
312
|
+
server?: Awaited<ReturnType<typeof startServer>>;
|
|
313
|
+
result: InvocationResult;
|
|
163
314
|
}
|
|
164
315
|
|
|
165
|
-
export async
|
|
166
|
-
const
|
|
316
|
+
export const invokeModel = async (options: InvocationOptions) => {
|
|
317
|
+
const currentTime = new Date();
|
|
318
|
+
const startedAt = currentTime.toISOString();
|
|
167
319
|
const began = performance.now();
|
|
168
|
-
const workspace = realpathSync(mkdtempSync(join(tmpdir(), 'hivex-model-')));
|
|
320
|
+
const workspace = realpathSync(mkdtempSync(path.join(tmpdir(), 'hivex-model-')));
|
|
169
321
|
const captured = captureTranscript();
|
|
170
322
|
const controller = new AbortController();
|
|
171
|
-
const cancel = () =>
|
|
323
|
+
const cancel = (): void => {
|
|
324
|
+
controller.abort();
|
|
325
|
+
};
|
|
172
326
|
process.on('SIGINT', cancel);
|
|
173
327
|
process.on('SIGTERM', cancel);
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
328
|
+
const initialReportOutcome = { outcome: 'failed' };
|
|
329
|
+
const initialReportCode = { code: 'MODEL_ADMISSION_FAILED' };
|
|
330
|
+
const initialReportDeadlineMilliseconds = {
|
|
177
331
|
deadlineMilliseconds: options.deadlineMilliseconds,
|
|
178
|
-
usage: null,
|
|
179
332
|
};
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
333
|
+
const initialReportUsage = { usage: null };
|
|
334
|
+
const initialReport: InvocationReport = {
|
|
335
|
+
...initialReportOutcome,
|
|
336
|
+
...initialReportCode,
|
|
337
|
+
...initialReportDeadlineMilliseconds,
|
|
338
|
+
...initialReportUsage,
|
|
339
|
+
};
|
|
340
|
+
const resource: InvocationResource = {
|
|
341
|
+
result: createInvocationResult(null, initialReport, false),
|
|
342
|
+
};
|
|
343
|
+
const execution = await captureFailure(
|
|
344
|
+
(async () => {
|
|
345
|
+
const server = await startServer({
|
|
346
|
+
binary: options.binary,
|
|
347
|
+
interaction: () => {
|
|
348
|
+
throw new Error('Knowledge execution cannot request interaction');
|
|
349
|
+
},
|
|
350
|
+
notification: captured.notification,
|
|
351
|
+
signal: controller.signal,
|
|
352
|
+
workspace,
|
|
353
|
+
});
|
|
354
|
+
resource.server = server;
|
|
355
|
+
if (options.onNativeProcessStarted !== undefined) {
|
|
356
|
+
options.onNativeProcessStarted(server.pid);
|
|
357
|
+
}
|
|
358
|
+
const threadId = await startKnowledgeThread({
|
|
359
|
+
rpc: server.rpc,
|
|
360
|
+
signal: controller.signal,
|
|
361
|
+
workspace,
|
|
362
|
+
});
|
|
363
|
+
controller.signal.throwIfAborted();
|
|
364
|
+
resource.result = await runTurn({
|
|
365
|
+
...options,
|
|
366
|
+
captured,
|
|
367
|
+
server,
|
|
368
|
+
signal: controller.signal,
|
|
369
|
+
threadId,
|
|
370
|
+
workspace,
|
|
371
|
+
});
|
|
372
|
+
})()
|
|
373
|
+
);
|
|
374
|
+
if ('error' in execution) {
|
|
375
|
+
const { error } = execution;
|
|
376
|
+
if (controller.signal.aborted) {
|
|
377
|
+
initialReport.code = 'MODEL_CANCELLED';
|
|
378
|
+
}
|
|
211
379
|
initialReport.diagnostic = failureDiagnostic(error);
|
|
212
380
|
if (error instanceof ServerAdmissionFailure) {
|
|
213
381
|
initialReport.cleanup = error.cleanup;
|
|
214
382
|
initialReport.nativeProcessId = error.processId;
|
|
215
383
|
initialReport.admission = error.admission;
|
|
216
384
|
}
|
|
217
|
-
resource.result =
|
|
218
|
-
}
|
|
219
|
-
|
|
385
|
+
resource.result = createInvocationResult(null, initialReport, false);
|
|
386
|
+
}
|
|
387
|
+
const cleanup = await captureFailure(
|
|
388
|
+
(async () => {
|
|
220
389
|
await resource.server?.stop();
|
|
221
|
-
rmSync(workspace, {
|
|
222
|
-
resource.result.report.cleanup =
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
390
|
+
rmSync(workspace, { force: true, recursive: true });
|
|
391
|
+
resource.result.report.cleanup =
|
|
392
|
+
resource.server === undefined
|
|
393
|
+
? (resource.result.report.cleanup ?? 'not-observed')
|
|
394
|
+
: 'confirmed';
|
|
395
|
+
})()
|
|
396
|
+
);
|
|
397
|
+
if ('error' in cleanup) {
|
|
398
|
+
const cleanupReportOutcome = { outcome: 'failed' };
|
|
399
|
+
const cleanupReportCode = { code: 'MODEL_CLEANUP_FAILED' };
|
|
400
|
+
const cleanupReportCleanup = { cleanup: 'failed' as const };
|
|
401
|
+
const cleanupReport = {
|
|
402
|
+
...resource.result.report,
|
|
403
|
+
...cleanupReportOutcome,
|
|
404
|
+
...cleanupReportCode,
|
|
405
|
+
...cleanupReportCleanup,
|
|
406
|
+
};
|
|
407
|
+
resource.result = createInvocationResult(null, cleanupReport, false);
|
|
408
|
+
}
|
|
409
|
+
process.off('SIGINT', cancel);
|
|
410
|
+
process.off('SIGTERM', cancel);
|
|
411
|
+
const { report } = resource.result;
|
|
412
|
+
const { threadId, turnId } = report;
|
|
413
|
+
if (threadId !== undefined && threadId !== '' && turnId !== undefined && turnId !== '') {
|
|
414
|
+
report.usage = captured.measured({ threadId, turnId });
|
|
239
415
|
}
|
|
240
|
-
const report = resource.result.report;
|
|
241
|
-
if (report.threadId && report.turnId)
|
|
242
|
-
report.usage = captured.measured({ threadId: report.threadId, turnId: report.turnId });
|
|
243
416
|
if (captured.invalid && report.outcome === 'completed') {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
417
|
+
const protocolReportOutcome = { outcome: 'failed' };
|
|
418
|
+
const protocolReportCode = { code: 'MODEL_PROTOCOL_FAILED' };
|
|
419
|
+
const protocolReport = {
|
|
420
|
+
...report,
|
|
421
|
+
...protocolReportOutcome,
|
|
422
|
+
...protocolReportCode,
|
|
248
423
|
};
|
|
424
|
+
resource.result = createInvocationResult(null, protocolReport, false);
|
|
249
425
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
...resource.result.report,
|
|
254
|
-
admission: resource.server?.admission ?? resource.result.report.admission,
|
|
255
|
-
nativeProcessId: resource.server?.pid ?? resource.result.report.nativeProcessId,
|
|
256
|
-
startedAt,
|
|
257
|
-
durationMilliseconds: Math.round(performance.now() - began),
|
|
258
|
-
},
|
|
426
|
+
const finalReport = resource.result.report;
|
|
427
|
+
const finalAdmission = {
|
|
428
|
+
admission: resource.server?.admission ?? finalReport.admission,
|
|
259
429
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
430
|
+
const finalNativeProcessId = {
|
|
431
|
+
nativeProcessId: resource.server?.pid ?? finalReport.nativeProcessId,
|
|
432
|
+
};
|
|
433
|
+
const finalStartedAt = { startedAt };
|
|
434
|
+
const finalDurationMilliseconds = {
|
|
435
|
+
durationMilliseconds: Math.round(performance.now() - began),
|
|
436
|
+
};
|
|
437
|
+
const reportWithMetadata = {
|
|
438
|
+
...finalReport,
|
|
439
|
+
...finalAdmission,
|
|
440
|
+
...finalNativeProcessId,
|
|
441
|
+
...finalStartedAt,
|
|
442
|
+
...finalDurationMilliseconds,
|
|
443
|
+
};
|
|
444
|
+
return { ...resource.result, report: reportWithMetadata };
|
|
445
|
+
};
|