@akagilnc/pi-workflow-roles 0.1.2521 → 0.1.2653-next.71811ca
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 +129 -39
- package/README.zh-CN.md +128 -39
- package/dist/activation-ledger-topology.js +1 -1
- package/dist/archivist-record-entry.js +47 -26
- package/dist/auditor-dossier-tool.js +3 -1
- package/dist/compliance-transport.js +17 -26
- package/dist/doctor-contracts.js +1 -2
- package/dist/dossier-resolution.js +4 -8
- package/dist/engine-detour-tool.js +33 -8
- package/dist/engine-detour.js +1 -0
- package/dist/evidence-child-executor.js +277 -424
- package/dist/host-contracts.js +22 -0
- package/dist/institutional-resolution.js +88 -0
- package/dist/merger-contracts.js +1 -2
- package/dist/navigator-attendance.js +16 -0
- package/dist/notary-contracts.js +2 -3
- package/dist/package-contracts/fixer-output.js +1 -2
- package/dist/packaged-role-registry.js +102 -9
- package/dist/pi/in-process-session.js +671 -0
- package/dist/public-cli/config.js +7 -5
- package/dist/public-cli/main.js +3767 -4113
- package/dist/reviewer-child-executor.js +6 -1
- package/dist/session-opening-materials.js +10 -38
- package/dist/sitian-appender.js +146 -0
- package/dist/sitian-contracts.js +21 -0
- package/dist/sitian-facade.js +15 -0
- package/dist/sitian-reader.js +52 -0
- package/extensions/role-runtime.ts +9 -7
- package/package.json +5 -5
- package/resources/engines/cursor.md +12 -26
- package/resources/engines/hermes.md +2 -23
- package/resources/engines/opus.md +1 -16
- package/resources/navigator-route-playbook.md +2 -2
- package/scripts/build-package.mjs +0 -1
- package/souls/quality-law.md +1 -1
- package/src/activation-ledger-topology.ts +1 -1
- package/src/archivist-record-entry.ts +57 -45
- package/src/auditor-dossier-tool.ts +4 -2
- package/src/canonical-skill-binding.ts +37 -27
- package/src/collector-role.ts +11 -20
- package/src/collector-tool-schemas.ts +1 -4
- package/src/compliance-transport.ts +19 -44
- package/src/doctor-auditor.ts +1 -5
- package/src/doctor-contracts.ts +1 -4
- package/src/doctor-role.ts +31 -8
- package/src/dossier-resolution.ts +8 -8
- package/src/engine-detour-tool.ts +48 -19
- package/src/engine-detour.ts +3 -0
- package/src/evidence-child-executor.ts +277 -485
- package/src/factory-board.ts +3 -3
- package/src/gatekeeper-role.ts +21 -10
- package/src/host-contracts.ts +386 -0
- package/src/in-process-session.ts +1 -115
- package/src/institutional-resolution.ts +135 -0
- package/src/judge-auditor.ts +1 -5
- package/src/judge-role.ts +37 -51
- package/src/merger-contracts.ts +1 -4
- package/src/merger-role.ts +7 -11
- package/src/navigator-attendance.ts +20 -1
- package/src/notary-contracts.ts +8 -11
- package/src/notary-role.ts +7 -23
- package/src/notary-source-run.ts +4 -3
- package/src/package-contracts/fixer-output.ts +1 -4
- package/src/package-resources/method-skill-binding.ts +10 -34
- package/src/packaged-role-registry.ts +122 -12
- package/src/pi/adapter.ts +192 -0
- package/src/pi/durable-principal.ts +98 -0
- package/src/pi/in-process-session.ts +819 -0
- package/src/pi/known-failure.ts +77 -0
- package/src/pi/pi-normalization.ts +284 -0
- package/src/pi/role-turn-host.ts +459 -0
- package/src/public-cli/auto-resume.ts +42 -44
- package/src/public-cli/cli.ts +171 -257
- package/src/public-cli/coder-run.ts +115 -433
- package/src/public-cli/collector-run.ts +62 -314
- package/src/public-cli/config.ts +7 -5
- package/src/public-cli/doctor-run.ts +41 -52
- package/src/public-cli/fixer-run.ts +122 -427
- package/src/public-cli/invocation.ts +287 -96
- package/src/public-cli/judge-run.ts +92 -422
- package/src/public-cli/merger-run.ts +167 -429
- package/src/public-cli/notary-run.ts +41 -47
- package/src/public-cli/option-definitions.ts +136 -3
- package/src/public-cli/post-admission.ts +497 -0
- package/src/public-cli/public-run-credentials.ts +4 -4
- package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
- package/src/public-cli/reviewer-run.ts +132 -449
- package/src/public-cli/run-lifecycle.ts +339 -327
- package/src/public-cli/settlement.ts +251 -134
- package/src/public-cli/terminal.ts +3 -9
- package/src/public-cli/turn-request.ts +87 -0
- package/src/reviewer-child-executor.ts +9 -1
- package/src/reviewer-role.ts +11 -20
- package/src/role-runtime.ts +129 -78
- package/src/session-opening-materials.ts +25 -39
- package/src/sitian-appender.ts +171 -0
- package/src/sitian-contracts.ts +106 -0
- package/src/sitian-facade.ts +18 -0
- package/src/sitian-reader.ts +62 -0
- package/src/worker-role.ts +42 -40
- package/src/worker-submission-gates.ts +33 -0
- package/dist/archivist-role-run-coordinates.js +0 -28
- package/dist/package-contracts/terminating-infrastructure.js +0 -90
- package/resources/engines/sonnet.md +0 -27
- package/resources/engines/zcode.md +0 -63
- package/src/archivist-role-run-coordinates.ts +0 -45
- package/src/package-contracts/terminating-infrastructure.ts +0 -132
- package/src/public-cli/explicit-internal.ts +0 -427
- package/src/public-cli/one-shot-dispatch.ts +0 -300
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
-
import {
|
|
10
|
-
import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE,
|
|
9
|
+
import {} from "@earendil-works/pi-ai";
|
|
10
|
+
import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, } from "./compliance-transport.js";
|
|
11
|
+
import { auditorRunDirectory } from "./auditor-dossier-tool.js";
|
|
12
|
+
import { sitianReport } from "./sitian-facade.js";
|
|
11
13
|
import { createEngineDetourToolDefinition } from "./engine-detour-tool.js";
|
|
12
14
|
import { engineNameFromEnv } from "./engine-detour.js";
|
|
13
15
|
import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, } from "./package-resources/engine-material.js";
|
|
14
16
|
import { readPackageMaterial } from "./session-opening-materials.js";
|
|
15
|
-
import {
|
|
17
|
+
import {} from "./public-cli/config.js";
|
|
18
|
+
import { readInstitutionalSeatSelection } from "./institutional-resolution.js";
|
|
16
19
|
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
|
|
17
|
-
import { createStreamIdleGuard, isStreamIdleTimeoutError } from "./stream-idle-guard.js";
|
|
18
20
|
import { hasUpstreamErrorTestimony, isNonSuccessHttpStatus, projectConfirmedRemotePayload, } from "./upstream-error-testimony.js";
|
|
19
21
|
/**
|
|
20
22
|
* Shared Standards/Spec evidence-child system materials — path roster only.
|
|
@@ -74,247 +76,33 @@ export async function withInProcessScratch(options, run) {
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
+
* Run every child cleanup, aggregating failures so one throwing cleanup never
|
|
80
|
+
* skips the rest (e.g. handle.close must still run when unsubscribe throws).
|
|
81
|
+
* If a primary failure is supplied and cleanup also failed, the two are combined
|
|
82
|
+
* into an AggregateError; otherwise only the failing branch is surfaced.
|
|
79
83
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const activeModel = options.model ?? options.context.model;
|
|
84
|
-
if (activeModel === undefined)
|
|
85
|
-
throw new Error(`${options.label} requires an active model`);
|
|
86
|
-
const dispatch = await prepareComplianceDispatch(activeModel, options.context, options.label);
|
|
87
|
-
const parentProvider = options.runCompletion === undefined
|
|
88
|
-
? options.context.modelRegistry.getProvider(activeModel.provider)
|
|
89
|
-
: undefined;
|
|
90
|
-
if (parentProvider === undefined && options.runCompletion === undefined) {
|
|
91
|
-
throw new Error(`${options.label} provider not found: ${activeModel.provider}`);
|
|
92
|
-
}
|
|
93
|
-
const runtime = await ModelRuntime.create({
|
|
94
|
-
credentials: new InMemoryCredentialStore(),
|
|
95
|
-
modelsPath: null,
|
|
96
|
-
});
|
|
97
|
-
// Injected completions historically accepted the minimal model exposed by an
|
|
98
|
-
// ExtensionContext. AgentSession crosses ModelRuntime first, so complete the
|
|
99
|
-
// model metadata required by that runtime without changing provider identity.
|
|
100
|
-
const inheritedModel = options.runCompletion === undefined
|
|
101
|
-
? dispatch.model
|
|
102
|
-
: {
|
|
103
|
-
...dispatch.model,
|
|
104
|
-
name: dispatch.model.name ?? dispatch.model.id,
|
|
105
|
-
baseUrl: dispatch.model.baseUrl ?? "",
|
|
106
|
-
reasoning: dispatch.model.reasoning ?? false,
|
|
107
|
-
input: dispatch.model.input ?? ["text"],
|
|
108
|
-
cost: dispatch.model.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
109
|
-
contextWindow: dispatch.model.contextWindow ?? 1,
|
|
110
|
-
maxTokens: dispatch.model.maxTokens ?? 1,
|
|
111
|
-
};
|
|
112
|
-
const state = {
|
|
113
|
-
runtime,
|
|
114
|
-
model: inheritedModel,
|
|
115
|
-
dispatch,
|
|
116
|
-
streamFailure: undefined,
|
|
117
|
-
};
|
|
118
|
-
const abortReason = (signal) => signal.reason ?? new Error(`${options.label} provider stream aborted`);
|
|
119
|
-
async function waitForStream(promise, signal) {
|
|
120
|
-
if (signal.aborted)
|
|
121
|
-
throw abortReason(signal);
|
|
122
|
-
let onAbort;
|
|
84
|
+
async function runChildCleanup(cleanups, primaryFailure, label) {
|
|
85
|
+
let cleanupFailure;
|
|
86
|
+
for (const cleanup of cleanups) {
|
|
123
87
|
try {
|
|
124
|
-
|
|
125
|
-
promise,
|
|
126
|
-
new Promise((_resolve, reject) => {
|
|
127
|
-
onAbort = () => reject(abortReason(signal));
|
|
128
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
129
|
-
}),
|
|
130
|
-
]);
|
|
88
|
+
await cleanup();
|
|
131
89
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
90
|
+
catch (failure) {
|
|
91
|
+
cleanupFailure = cleanupFailure === undefined
|
|
92
|
+
? failure
|
|
93
|
+
: new AggregateError([cleanupFailure, failure], `${label} cleanup failed`, {
|
|
94
|
+
cause: cleanupFailure,
|
|
95
|
+
});
|
|
135
96
|
}
|
|
136
97
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const requestSignal = request?.signal;
|
|
146
|
-
const streamSignal = requestSignal === undefined
|
|
147
|
-
? idle.signal
|
|
148
|
-
: AbortSignal.any([idle.signal, requestSignal]);
|
|
149
|
-
const priorOnResponse = request?.onResponse;
|
|
150
|
-
const inheritedRequest = {
|
|
151
|
-
...(request ?? {}),
|
|
152
|
-
...(dispatch.auth.env === undefined ? {} : { env: dispatch.auth.env }),
|
|
153
|
-
signal: streamSignal,
|
|
154
|
-
onResponse: async (response, model) => {
|
|
155
|
-
if (typeof response?.status === "number")
|
|
156
|
-
observedHttpStatus = response.status;
|
|
157
|
-
await priorOnResponse?.(response, model);
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
if (options.runCompletion !== undefined) {
|
|
161
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
162
|
-
if (streamSignal.aborted)
|
|
163
|
-
throw abortReason(streamSignal);
|
|
164
|
-
const completed = await waitForStream(options.runCompletion(model, options.injectedSystemPrompt === undefined
|
|
165
|
-
? context
|
|
166
|
-
: { ...context, systemPrompt: options.injectedSystemPrompt }, inheritedRequest), streamSignal);
|
|
167
|
-
const response = attachObservedHttpStatus({
|
|
168
|
-
...completed,
|
|
169
|
-
api: model.api,
|
|
170
|
-
provider: model.provider,
|
|
171
|
-
model: model.id,
|
|
172
|
-
}, observedHttpStatus);
|
|
173
|
-
if (response.stopReason === "error" || response.stopReason === "aborted") {
|
|
174
|
-
wrapped.push({ type: "error", reason: response.stopReason, error: response });
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
wrapped.end(response);
|
|
178
|
-
}
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const source = simple
|
|
182
|
-
? parentProvider.streamSimple(model, context, inheritedRequest)
|
|
183
|
-
: parentProvider.stream(model, context, inheritedRequest);
|
|
184
|
-
let sawEvent = false;
|
|
185
|
-
const iterator = source[Symbol.asyncIterator]();
|
|
186
|
-
while (true) {
|
|
187
|
-
const next = await waitForStream(iterator.next(), idle.signal);
|
|
188
|
-
if (next.done)
|
|
189
|
-
break;
|
|
190
|
-
sawEvent = true;
|
|
191
|
-
idle.poke();
|
|
192
|
-
wrapped.push(enrichStreamEvent(next.value, observedHttpStatus));
|
|
193
|
-
}
|
|
194
|
-
const response = attachObservedHttpStatus(await waitForStream(source.result(), idle.signal), observedHttpStatus);
|
|
195
|
-
if (!sawEvent)
|
|
196
|
-
wrapped.end(response);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
if (request?.signal?.aborted) {
|
|
201
|
-
const response = {
|
|
202
|
-
role: "assistant",
|
|
203
|
-
content: [],
|
|
204
|
-
api: model.api,
|
|
205
|
-
provider: model.provider,
|
|
206
|
-
model: model.id,
|
|
207
|
-
usage: emptyUsage(),
|
|
208
|
-
stopReason: "aborted",
|
|
209
|
-
errorMessage: "Auditor session aborted",
|
|
210
|
-
timestamp: Date.now(),
|
|
211
|
-
};
|
|
212
|
-
wrapped.push({ type: "error", reason: "aborted", error: response });
|
|
213
|
-
wrapped.end(response);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const failure = isStreamIdleTimeoutError(idle.signal.reason) ? idle.signal.reason : error;
|
|
217
|
-
if (options.idleRetry === true
|
|
218
|
-
&& isStreamIdleTimeoutError(failure)
|
|
219
|
-
&& attempt < DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES
|
|
220
|
-
&& options.signal?.aborted !== true) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
state.streamFailure = failure;
|
|
224
|
-
const projected = projectStructuredRemote(failure);
|
|
225
|
-
// Prefer structured fields on the thrown failure; fall back to onResponse observation.
|
|
226
|
-
const httpStatus = projected.httpStatus ?? numericHttpStatus(observedHttpStatus);
|
|
227
|
-
const response = {
|
|
228
|
-
role: "assistant",
|
|
229
|
-
content: [],
|
|
230
|
-
api: model.api,
|
|
231
|
-
provider: model.provider,
|
|
232
|
-
model: model.id,
|
|
233
|
-
usage: emptyUsage(),
|
|
234
|
-
stopReason: "error",
|
|
235
|
-
// Preserve the held failure message bytes — do not rewrite.
|
|
236
|
-
errorMessage: failure instanceof Error ? failure.message : String(failure),
|
|
237
|
-
timestamp: Date.now(),
|
|
238
|
-
...(projected.diagnostics === undefined ? {} : { diagnostics: projected.diagnostics }),
|
|
239
|
-
...(httpStatus === undefined ? {} : { status: httpStatus, statusCode: httpStatus }),
|
|
240
|
-
...(projected.body === undefined ? {} : { body: projected.body }),
|
|
241
|
-
...(projected.code === undefined ? {} : { code: projected.code }),
|
|
242
|
-
...(projected.errno === undefined ? {} : { errno: projected.errno }),
|
|
243
|
-
};
|
|
244
|
-
wrapped.push({ type: "error", reason: "error", error: response });
|
|
245
|
-
wrapped.end(response);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
finally {
|
|
249
|
-
idle.dispose();
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
})();
|
|
253
|
-
return wrapped;
|
|
254
|
-
};
|
|
255
|
-
// Provider id must match activeModel.provider so ModelRuntime auth lookup
|
|
256
|
-
// finds this registration when a gate override changes provider (#453).
|
|
257
|
-
const provider = options.idleRetry === true || options.runCompletion !== undefined
|
|
258
|
-
? {
|
|
259
|
-
id: activeModel.provider,
|
|
260
|
-
name: parentProvider?.name ?? options.label,
|
|
261
|
-
auth: {
|
|
262
|
-
apiKey: {
|
|
263
|
-
name: `Inherited ${options.label} authentication`,
|
|
264
|
-
async resolve() {
|
|
265
|
-
const { env, ...auth } = dispatch.auth;
|
|
266
|
-
return {
|
|
267
|
-
auth: {
|
|
268
|
-
...auth,
|
|
269
|
-
...(dispatch.model.baseUrl === undefined ? {} : { baseUrl: dispatch.model.baseUrl }),
|
|
270
|
-
},
|
|
271
|
-
...(env === undefined ? {} : { env }),
|
|
272
|
-
};
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
getModels() { return [inheritedModel]; },
|
|
277
|
-
stream(model, context, request) {
|
|
278
|
-
return createRetriedStream(false, model, context, request);
|
|
279
|
-
},
|
|
280
|
-
streamSimple(model, context, request) {
|
|
281
|
-
return createRetriedStream(true, model, context, request);
|
|
282
|
-
},
|
|
283
|
-
}
|
|
284
|
-
: {
|
|
285
|
-
id: parentProvider.id,
|
|
286
|
-
name: parentProvider.name,
|
|
287
|
-
...(parentProvider.baseUrl === undefined ? {} : { baseUrl: parentProvider.baseUrl }),
|
|
288
|
-
...(parentProvider.headers === undefined ? {} : { headers: parentProvider.headers }),
|
|
289
|
-
auth: {
|
|
290
|
-
apiKey: {
|
|
291
|
-
name: `Inherited ${options.label} authentication`,
|
|
292
|
-
async resolve() {
|
|
293
|
-
return {
|
|
294
|
-
auth: {
|
|
295
|
-
...(dispatch.auth.apiKey === undefined ? {} : { apiKey: dispatch.auth.apiKey }),
|
|
296
|
-
...(dispatch.auth.headers === undefined ? {} : { headers: dispatch.auth.headers }),
|
|
297
|
-
...(dispatch.model.baseUrl === undefined ? {} : { baseUrl: dispatch.model.baseUrl }),
|
|
298
|
-
},
|
|
299
|
-
...(dispatch.auth.env === undefined ? {} : { env: dispatch.auth.env }),
|
|
300
|
-
};
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
getModels() { return [inheritedModel]; },
|
|
305
|
-
stream(model, childContext, streamOptions) {
|
|
306
|
-
return parentProvider.stream(model, childContext, streamOptions);
|
|
307
|
-
},
|
|
308
|
-
streamSimple(model, childContext, streamOptions) {
|
|
309
|
-
return parentProvider.streamSimple(model, childContext, streamOptions);
|
|
310
|
-
},
|
|
311
|
-
};
|
|
312
|
-
runtime.registerNativeProvider(provider);
|
|
313
|
-
// registerNativeProvider fires a background refresh; await it so child
|
|
314
|
-
// AgentSession.prompt sees configured auth without racing void refresh
|
|
315
|
-
// (mock timers / slow CI otherwise stall before the compliance stream).
|
|
316
|
-
await runtime.refresh({ allowNetwork: false });
|
|
317
|
-
return state;
|
|
98
|
+
if (cleanupFailure === undefined)
|
|
99
|
+
return;
|
|
100
|
+
if (primaryFailure !== undefined) {
|
|
101
|
+
throw new AggregateError([primaryFailure, cleanupFailure], `${label} execution and cleanup failed`, { cause: primaryFailure });
|
|
102
|
+
}
|
|
103
|
+
throw new AggregateError([cleanupFailure], `${label} cleanup failed`, {
|
|
104
|
+
cause: cleanupFailure,
|
|
105
|
+
});
|
|
318
106
|
}
|
|
319
107
|
function numericHttpStatus(value) {
|
|
320
108
|
return isNonSuccessHttpStatus(value) ? value : undefined;
|
|
@@ -428,6 +216,23 @@ function classifiedError(error, evidenceChildFailure) {
|
|
|
428
216
|
: evidenceChildFailure;
|
|
429
217
|
return Object.assign(wrapped, { evidenceChildFailure: classification });
|
|
430
218
|
}
|
|
219
|
+
/** Extract the first text diagnostic from a flattened toolResult error `details`. */
|
|
220
|
+
function extractToolResultText(details) {
|
|
221
|
+
if (typeof details !== "object" || details === null)
|
|
222
|
+
return undefined;
|
|
223
|
+
const record = details;
|
|
224
|
+
const content = record.content;
|
|
225
|
+
if (Array.isArray(content)) {
|
|
226
|
+
for (const part of content) {
|
|
227
|
+
if (typeof part === "object" && part !== null) {
|
|
228
|
+
const text = part.text;
|
|
229
|
+
if (typeof text === "string" && text.trim() !== "")
|
|
230
|
+
return text;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
431
236
|
function emptyUsage() {
|
|
432
237
|
return {
|
|
433
238
|
input: 0,
|
|
@@ -452,24 +257,19 @@ function addUsage(total, next) {
|
|
|
452
257
|
}
|
|
453
258
|
export async function executeEvidenceChild(workspace, prompt, context, options = {}) {
|
|
454
259
|
const signal = options.signal;
|
|
260
|
+
const runDirectory = options.runDirectory ?? auditorRunDirectory(context);
|
|
261
|
+
if (runDirectory === undefined) {
|
|
262
|
+
throw new Error("Evidence child requires a run directory carrying the institutional resolution page");
|
|
263
|
+
}
|
|
264
|
+
const selection = await readInstitutionalSeatSelection(runDirectory, "evidenceChild");
|
|
455
265
|
return withInProcessScratch({
|
|
456
266
|
prefix: "ak-evidence-child-",
|
|
457
267
|
...(options.credentialScratchParent === undefined
|
|
458
268
|
? {}
|
|
459
269
|
: { parentDirectory: options.credentialScratchParent }),
|
|
460
270
|
}, async (childConfigDir) => {
|
|
461
|
-
const {
|
|
271
|
+
const { openPiInstitutionalSession } = await import("./pi/in-process-session.js");
|
|
462
272
|
const { createRecordSession } = await import("./archivist-record-entry.js");
|
|
463
|
-
let inherited;
|
|
464
|
-
try {
|
|
465
|
-
inherited = await createInheritedRuntime({
|
|
466
|
-
context,
|
|
467
|
-
label: "Evidence child",
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
throw classifiedError(error, "provider");
|
|
472
|
-
}
|
|
473
273
|
// #378: when labor engine is configured, legs get the same detour tool + material
|
|
474
274
|
// dual-path as the parent seat (ADR 0069 detour-rejoins-main-road).
|
|
475
275
|
const engineName = engineNameFromEnv();
|
|
@@ -495,30 +295,38 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
|
|
|
495
295
|
},
|
|
496
296
|
});
|
|
497
297
|
// No tools allowlist — Pi defaults + unrestricted evidence surface (ADR 0064).
|
|
498
|
-
// Single
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
model: inherited.model,
|
|
503
|
-
thinkingLevel: context.thinkingLevel ?? "off",
|
|
504
|
-
modelRuntime: inherited.runtime,
|
|
505
|
-
systemPrompt: await buildEvidenceChildSystemPrompt(engineMaterial),
|
|
506
|
-
...(engineDetourTool === undefined
|
|
507
|
-
? {}
|
|
508
|
-
: { customTools: [engineDetourTool] }),
|
|
509
|
-
sessionManager: createRecordSession({
|
|
298
|
+
// Single open seam owner: pi/in-process-session.ts.
|
|
299
|
+
let opened;
|
|
300
|
+
try {
|
|
301
|
+
opened = await openPiInstitutionalSession({
|
|
510
302
|
cwd: workspace,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
303
|
+
agentDir: childConfigDir,
|
|
304
|
+
selection,
|
|
305
|
+
systemPrompt: await buildEvidenceChildSystemPrompt(engineMaterial),
|
|
306
|
+
...(engineDetourTool === undefined
|
|
307
|
+
? {}
|
|
308
|
+
: { customTools: [engineDetourTool] }),
|
|
309
|
+
sessionManager: createRecordSession({
|
|
310
|
+
cwd: workspace,
|
|
311
|
+
kind: "evidence-children",
|
|
312
|
+
...(context.sessionManager === undefined ? {} : { parent: context.sessionManager }),
|
|
313
|
+
}),
|
|
314
|
+
...(signal === undefined ? {} : { signal }),
|
|
315
|
+
label: "Evidence child",
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
throw classifiedError(error, "provider");
|
|
320
|
+
}
|
|
321
|
+
const { handle } = opened;
|
|
515
322
|
const usage = emptyUsage();
|
|
516
|
-
const unsubscribe =
|
|
517
|
-
if (event.type === "message_end" && event.
|
|
518
|
-
|
|
323
|
+
const unsubscribe = handle.subscribe((event) => {
|
|
324
|
+
if (event.type === "message_end" && event.role === "assistant") {
|
|
325
|
+
if (event.usage)
|
|
326
|
+
addUsage(usage, event.usage);
|
|
519
327
|
}
|
|
520
328
|
});
|
|
521
|
-
const abortChild = () => {
|
|
329
|
+
const abortChild = () => { handle.abort(); };
|
|
522
330
|
if (signal?.aborted)
|
|
523
331
|
abortChild();
|
|
524
332
|
else
|
|
@@ -526,8 +334,9 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
|
|
|
526
334
|
let primaryFailure;
|
|
527
335
|
try {
|
|
528
336
|
const delivered = prompt;
|
|
337
|
+
let turnResult;
|
|
529
338
|
try {
|
|
530
|
-
await
|
|
339
|
+
turnResult = await handle.prompt(delivered);
|
|
531
340
|
}
|
|
532
341
|
catch (error) {
|
|
533
342
|
if (engineDetourFailure !== undefined) {
|
|
@@ -540,22 +349,23 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
|
|
|
540
349
|
}
|
|
541
350
|
if (signal?.aborted)
|
|
542
351
|
throw new Error("Evidence child was cancelled");
|
|
543
|
-
const lastAssistant =
|
|
544
|
-
.reverse()
|
|
545
|
-
|
|
352
|
+
const lastAssistant = turnResult.messages !== undefined
|
|
353
|
+
? [...turnResult.messages].reverse().find((message) => message?.role === "assistant")
|
|
354
|
+
: undefined;
|
|
546
355
|
// error|aborted assistant stops share the upstream-testimony rule: provider only
|
|
547
356
|
// with direct HTTP/SDK testimony, otherwise existing unknown. child is reserved
|
|
548
357
|
// for real local child/report failures (no assistant / blank report / cleanup).
|
|
549
|
-
if (
|
|
550
|
-
&& (lastAssistant.stopReason === "error" || lastAssistant.stopReason === "aborted")) {
|
|
551
|
-
|
|
358
|
+
if (turnResult.stopReason === "error" || turnResult.stopReason === "aborted"
|
|
359
|
+
|| (lastAssistant?.role === "assistant" && (lastAssistant.stopReason === "error" || lastAssistant.stopReason === "aborted"))) {
|
|
360
|
+
const errMsg = turnResult.errorMessage ?? lastAssistant?.errorMessage ?? "";
|
|
361
|
+
throw classifiedError(new Error(errMsg, { cause: lastAssistant }), lastAssistant && projectStructuredRemote(lastAssistant).hasTestimony ? "provider" : "unknown");
|
|
552
362
|
}
|
|
553
|
-
if (lastAssistant
|
|
363
|
+
if (lastAssistant !== undefined && lastAssistant.role !== "assistant") {
|
|
554
364
|
throw classifiedError(new Error("Evidence child child terminated without a report", {
|
|
555
|
-
cause: lastAssistant ??
|
|
365
|
+
cause: lastAssistant ?? turnResult.messages,
|
|
556
366
|
}), "child");
|
|
557
367
|
}
|
|
558
|
-
const report =
|
|
368
|
+
const report = turnResult.text;
|
|
559
369
|
if (report.trim().length === 0) {
|
|
560
370
|
throw new Error("Evidence child returned a blank child report");
|
|
561
371
|
}
|
|
@@ -567,74 +377,30 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
|
|
|
567
377
|
}
|
|
568
378
|
finally {
|
|
569
379
|
signal?.removeEventListener("abort", abortChild);
|
|
570
|
-
|
|
571
|
-
for (const cleanup of [() => unsubscribe(), () => dispose()]) {
|
|
572
|
-
try {
|
|
573
|
-
cleanup();
|
|
574
|
-
}
|
|
575
|
-
catch (failure) {
|
|
576
|
-
cleanupFailure = cleanupFailure === undefined
|
|
577
|
-
? failure
|
|
578
|
-
: new AggregateError([cleanupFailure, failure], "Reviewer child cleanup failed", {
|
|
579
|
-
cause: cleanupFailure,
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
if (cleanupFailure !== undefined) {
|
|
584
|
-
if (primaryFailure !== undefined) {
|
|
585
|
-
throw new AggregateError([primaryFailure, cleanupFailure], "Reviewer child execution and cleanup failed", { cause: primaryFailure });
|
|
586
|
-
}
|
|
587
|
-
throw new AggregateError([cleanupFailure], "Reviewer child cleanup failed", {
|
|
588
|
-
cause: cleanupFailure,
|
|
589
|
-
});
|
|
590
|
-
}
|
|
380
|
+
await runChildCleanup([() => unsubscribe(), () => handle.close()], primaryFailure, "Reviewer child");
|
|
591
381
|
}
|
|
592
382
|
});
|
|
593
383
|
}
|
|
594
|
-
/**
|
|
595
|
-
*
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
*/
|
|
599
|
-
async function resolveGateSeatModelOptions(context, seat, roleLabel) {
|
|
600
|
-
const selection = resolveGateOfficerModelSelection(await loadPublicCliConfig(), seat);
|
|
601
|
-
if (selection === undefined)
|
|
602
|
-
return {};
|
|
603
|
-
const find = context.modelRegistry.find?.bind(context.modelRegistry);
|
|
604
|
-
if (typeof find !== "function") {
|
|
605
|
-
throw new Error(`${roleLabel} model registry cannot resolve models`);
|
|
606
|
-
}
|
|
607
|
-
const model = find(selection.provider, selection.model);
|
|
608
|
-
if (model === undefined) {
|
|
609
|
-
throw new Error(`${roleLabel} model is unavailable: ${formatModelSpec(selection)}`);
|
|
610
|
-
}
|
|
611
|
-
return {
|
|
612
|
-
model,
|
|
613
|
-
...(selection.thinking === undefined ? {} : { thinkingLevel: selection.thinking }),
|
|
614
|
-
};
|
|
384
|
+
/** Resolution-page seat key for an auditor invocation: province gate seats map
|
|
385
|
+
* to their own page seats; doctor/judge compliance audits use the auditor seat. */
|
|
386
|
+
function auditorSeatKey(gateSeat) {
|
|
387
|
+
return gateSeat ?? "auditor";
|
|
615
388
|
}
|
|
616
389
|
/**
|
|
617
|
-
* Auditor lifecycle via the shared
|
|
390
|
+
* Auditor lifecycle via the shared institutional sub-session adapter.
|
|
618
391
|
* Adapter keeps role label / soul / decision tool / result projection only.
|
|
619
392
|
* No tools allowlist (ADR 0064). Provider-stream idle-only retry (ADR 0059).
|
|
620
393
|
* Durable child session via ADR 0065 archivist entry.
|
|
621
394
|
*/
|
|
622
395
|
export async function executeAuditorChild(options) {
|
|
623
396
|
const { createRecordSession } = await import("./archivist-record-entry.js");
|
|
397
|
+
const runDirectory = options.runDirectory ?? auditorRunDirectory(options.context);
|
|
398
|
+
if (runDirectory === undefined) {
|
|
399
|
+
throw new Error(`${options.roleLabel} requires a run directory carrying the institutional resolution page`);
|
|
400
|
+
}
|
|
401
|
+
const seat = auditorSeatKey(options.gateSeat);
|
|
402
|
+
const selection = await readInstitutionalSeatSelection(runDirectory, seat);
|
|
624
403
|
return withInProcessScratch({ prefix: "ak-auditor-role-" }, async (scratch) => {
|
|
625
|
-
const gate = options.gateSeat === undefined
|
|
626
|
-
? {}
|
|
627
|
-
: await resolveGateSeatModelOptions(options.context, options.gateSeat, options.roleLabel);
|
|
628
|
-
const inherited = await createInheritedRuntime({
|
|
629
|
-
context: options.context,
|
|
630
|
-
label: options.roleLabel,
|
|
631
|
-
idleRetry: true,
|
|
632
|
-
...(gate.model === undefined ? {} : { model: gate.model }),
|
|
633
|
-
...(options.runCompletion === undefined
|
|
634
|
-
? {}
|
|
635
|
-
: { runCompletion: options.runCompletion, injectedSystemPrompt: options.systemPrompt }),
|
|
636
|
-
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
637
|
-
});
|
|
638
404
|
const cwd = options.context.cwd ?? process.cwd();
|
|
639
405
|
let decision;
|
|
640
406
|
let noReceiptLifecycle;
|
|
@@ -656,12 +422,15 @@ export async function executeAuditorChild(options) {
|
|
|
656
422
|
try {
|
|
657
423
|
const result = await options.tool.execute(...args);
|
|
658
424
|
delivery.recordAccepted();
|
|
659
|
-
|
|
425
|
+
const rawDecision = args[1];
|
|
426
|
+
const isMissingArgs = rawDecision === undefined
|
|
427
|
+
|| (typeof rawDecision === "object" && rawDecision !== null && !Array.isArray(rawDecision) && Object.keys(rawDecision).length === 0);
|
|
428
|
+
decision = isMissingArgs ? undefined : rawDecision;
|
|
660
429
|
decisionCallId = args[0];
|
|
661
430
|
decisionToolFailure = undefined;
|
|
662
431
|
decisionToolFailures.delete(args[0]);
|
|
663
432
|
decisionSubmitted = true;
|
|
664
|
-
return result;
|
|
433
|
+
return { ...result, terminate: true };
|
|
665
434
|
}
|
|
666
435
|
catch (error) {
|
|
667
436
|
decisionToolFailure = error;
|
|
@@ -679,18 +448,35 @@ export async function executeAuditorChild(options) {
|
|
|
679
448
|
kind: "auditor-roles",
|
|
680
449
|
...(parentSessionManager === undefined ? {} : { parent: parentSessionManager }),
|
|
681
450
|
});
|
|
682
|
-
// Shared session open — no tools allowlist (ADR 0064).
|
|
683
|
-
|
|
684
|
-
const {
|
|
451
|
+
// Shared session open — no tools allowlist (ADR 0064). Auth resolved
|
|
452
|
+
// child-locally from the explicit seat selection; adapter owns runtime/provider.
|
|
453
|
+
const { openPiInstitutionalSession } = await import("./pi/in-process-session.js");
|
|
454
|
+
const evidenceToolFailures = new Map();
|
|
455
|
+
const wrappedDossierTool = {
|
|
456
|
+
...options.dossierTool,
|
|
457
|
+
label: options.roleLabel,
|
|
458
|
+
async execute(...args) {
|
|
459
|
+
try {
|
|
460
|
+
return await options.dossierTool.execute(...args);
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
evidenceToolFailures.set(args[0], error);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
const opened = await openPiInstitutionalSession({
|
|
685
469
|
cwd,
|
|
686
470
|
agentDir: scratch,
|
|
687
|
-
|
|
688
|
-
thinkingLevel: gate.thinkingLevel ?? options.context.thinkingLevel ?? "off",
|
|
689
|
-
modelRuntime: inherited.runtime,
|
|
471
|
+
selection,
|
|
690
472
|
systemPrompt: options.systemPrompt,
|
|
691
|
-
customTools: [
|
|
473
|
+
customTools: [wrappedDossierTool, tool],
|
|
692
474
|
sessionManager: auditorSessionManager,
|
|
475
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
476
|
+
idleRetry: true,
|
|
477
|
+
label: options.roleLabel,
|
|
693
478
|
});
|
|
479
|
+
const { handle } = opened;
|
|
694
480
|
const binding = {
|
|
695
481
|
version: 1,
|
|
696
482
|
parent: {
|
|
@@ -704,6 +490,17 @@ export async function executeAuditorChild(options) {
|
|
|
704
490
|
// Durable binding is a prerequisite: never observe the provider when its
|
|
705
491
|
// response could not later be tied to the current parent attempt.
|
|
706
492
|
auditorSessionManager.appendCustomEntry(AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, binding);
|
|
493
|
+
try {
|
|
494
|
+
sitianReport({
|
|
495
|
+
level: "event",
|
|
496
|
+
kind: "auditor",
|
|
497
|
+
cwd,
|
|
498
|
+
sessionParent: parentSessionFile,
|
|
499
|
+
payload: { type: AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, ...binding },
|
|
500
|
+
source: "evidence-child-executor",
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
catch { }
|
|
707
504
|
let turns = 0;
|
|
708
505
|
const sessionUsage = emptyUsage();
|
|
709
506
|
let boundaryResponse;
|
|
@@ -712,33 +509,13 @@ export async function executeAuditorChild(options) {
|
|
|
712
509
|
let rejectedDecisionResponse;
|
|
713
510
|
let promptNeighboringFailure;
|
|
714
511
|
let promptDecisionFailures = [];
|
|
715
|
-
const registeredToolNames = new Set(session.getAllTools().map((entry) => entry.name));
|
|
716
|
-
const evidenceToolFailures = new Map();
|
|
717
|
-
for (const name of registeredToolNames) {
|
|
718
|
-
if (name === tool.name)
|
|
719
|
-
continue;
|
|
720
|
-
const definition = session.getToolDefinition(name);
|
|
721
|
-
if (definition === undefined)
|
|
722
|
-
continue;
|
|
723
|
-
const execute = definition.execute.bind(definition);
|
|
724
|
-
definition.execute = async (...args) => {
|
|
725
|
-
try {
|
|
726
|
-
return await execute(...args);
|
|
727
|
-
}
|
|
728
|
-
catch (error) {
|
|
729
|
-
evidenceToolFailures.set(args[0], error);
|
|
730
|
-
throw error;
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
512
|
const findToolFailure = (response) => {
|
|
735
|
-
const callIds = response.content.flatMap((part) => part.type === "toolCall" && part.name !== tool.name
|
|
513
|
+
const callIds = response.content.flatMap((part) => part.type === "toolCall" && part.name !== tool.name ? [part.id] : []);
|
|
736
514
|
for (const callId of callIds) {
|
|
737
515
|
if (evidenceToolFailures.has(callId))
|
|
738
516
|
return evidenceToolFailures.get(callId);
|
|
739
517
|
}
|
|
740
|
-
|
|
741
|
-
return [...session.messages].reverse().find((message) => message.role === "toolResult" && callIdSet.has(message.toolCallId) && message.isError);
|
|
518
|
+
return undefined;
|
|
742
519
|
};
|
|
743
520
|
const drainRejectedDecisionFailures = (response) => {
|
|
744
521
|
for (const part of response.content) {
|
|
@@ -749,36 +526,83 @@ export async function executeAuditorChild(options) {
|
|
|
749
526
|
decisionToolFailures.delete(part.id);
|
|
750
527
|
}
|
|
751
528
|
};
|
|
752
|
-
const
|
|
753
|
-
|
|
529
|
+
const retainedAssistants = [];
|
|
530
|
+
const unsubscribe = handle.subscribe((event) => {
|
|
531
|
+
// Capture evidence-tool (non-decision) failures from the handle's real
|
|
532
|
+
// tool_result events. The old runCompletion path wrapped every child tool's
|
|
533
|
+
// execute to observe failures; through the real handle the adapter forwards
|
|
534
|
+
// tool_execution_end isError. Record the errored tool call id so an adjacent
|
|
535
|
+
// evidence failure can outrank a settled/correctable decision feedback.
|
|
536
|
+
if (event.type === "tool_result" && event.isError === true && event.toolName !== tool.name) {
|
|
537
|
+
// Through the real handle the child flattens a throwing evidence tool
|
|
538
|
+
// into a toolResult error whose content text carries the diagnostic
|
|
539
|
+
// (e.g. "ENOENT: no such file or directory..."). Recover that text so
|
|
540
|
+
// the adjacent evidence failure outranks a settled decision feedback
|
|
541
|
+
// with its original diagnostic, mirroring the pre-migration wrapped
|
|
542
|
+
// execute capture (which held the raw error object).
|
|
543
|
+
const detailText = extractToolResultText(event.details);
|
|
544
|
+
// A native unknown-tool receipt ("Tool <name> not found") is not an
|
|
545
|
+
// evidence-tool failure: the child session has no such tool registered,
|
|
546
|
+
// so its errored toolResult must not short-circuit the auditor (it keeps
|
|
547
|
+
// prompting to the turn limit, mirroring the pre-migration
|
|
548
|
+
// registeredToolNames exclusion in findToolFailure).
|
|
549
|
+
if (detailText !== undefined && /^Tool\s+.+ not found$/.test(detailText.trim()))
|
|
550
|
+
return;
|
|
551
|
+
const failure = detailText === undefined
|
|
552
|
+
? new Error(event.toolName ?? "evidence tool failed")
|
|
553
|
+
: new Error(detailText);
|
|
554
|
+
// Recover the errno code from the flattened diagnostic so an evidence
|
|
555
|
+
// failure's identity (e.g. code "ENOENT") is preserved across the real
|
|
556
|
+
// HTTP round-trip, mirroring the pre-migration raw error object.
|
|
557
|
+
const errno = /^([A-Z_]+):/.exec(detailText ?? "");
|
|
558
|
+
if (errno !== null && errno[1] !== undefined)
|
|
559
|
+
failure.code = errno[1];
|
|
560
|
+
evidenceToolFailures.set(event.toolCallId, failure);
|
|
561
|
+
}
|
|
562
|
+
if (event.type === "message_end" && event.role === "assistant" && boundaryResponse === undefined) {
|
|
754
563
|
turns += 1;
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
564
|
+
if (event.usage)
|
|
565
|
+
addUsage(sessionUsage, event.usage);
|
|
566
|
+
const msg = event.message;
|
|
567
|
+
retainedResponse = msg;
|
|
568
|
+
if (msg) {
|
|
569
|
+
retainedAssistants.push(msg);
|
|
570
|
+
try {
|
|
571
|
+
options.retainResponse?.(msg);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
retentionFailure = error;
|
|
575
|
+
}
|
|
576
|
+
// A tool call in assistant output is only an observation. Preserve its
|
|
577
|
+
// candidate for typed malformed-decision settlement, but the wrapped
|
|
578
|
+
// execute path above is the sole owner of accepted-receipt state; a
|
|
579
|
+
// rejected execution must remain retryable in this same session.
|
|
580
|
+
for (const part of msg.content) {
|
|
581
|
+
if (part.type === "toolCall" && part.name === tool.name) {
|
|
582
|
+
rejectedDecisionResponse = msg;
|
|
583
|
+
if (decision === undefined) {
|
|
584
|
+
decision = (part.arguments === undefined
|
|
585
|
+
|| (typeof part.arguments === "object" && part.arguments !== null
|
|
586
|
+
&& !Array.isArray(part.arguments) && Object.keys(part.arguments).length === 0))
|
|
587
|
+
? undefined
|
|
588
|
+
: part.arguments;
|
|
589
|
+
decisionCallId = part.id;
|
|
590
|
+
// Pi can reject malformed root arguments before invoking execute;
|
|
591
|
+
// that remains the existing unreadable-candidate failure path.
|
|
592
|
+
// A missing root argument reaches the real provider adapter as an
|
|
593
|
+
// empty object after serialization — treat it as missing too so a
|
|
594
|
+
// one-shot typed missing-args settlement does not solicit another turn.
|
|
595
|
+
if (part.arguments === undefined
|
|
596
|
+
|| (typeof part.arguments === "object" && part.arguments !== null
|
|
597
|
+
&& !Array.isArray(part.arguments) && Object.keys(part.arguments).length === 0)) {
|
|
598
|
+
decisionSubmitted = true;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
777
601
|
}
|
|
778
602
|
}
|
|
603
|
+
if (turns >= AUDITOR_TURN_LIMIT || msg.stopReason === "error")
|
|
604
|
+
boundaryResponse = msg;
|
|
779
605
|
}
|
|
780
|
-
if (turns >= AUDITOR_TURN_LIMIT)
|
|
781
|
-
boundaryResponse = event.message;
|
|
782
606
|
}
|
|
783
607
|
if (event.type === "turn_end") {
|
|
784
608
|
if (rejectedDecisionResponse !== undefined) {
|
|
@@ -788,15 +612,16 @@ export async function executeAuditorChild(options) {
|
|
|
788
612
|
if (decisionSubmitted || promptNeighboringFailure !== undefined
|
|
789
613
|
|| (boundaryResponse !== undefined && rejectedDecisionResponse === undefined)
|
|
790
614
|
|| retentionFailure !== undefined) {
|
|
791
|
-
|
|
615
|
+
handle.abort();
|
|
792
616
|
}
|
|
793
617
|
}
|
|
794
618
|
});
|
|
795
|
-
const abort = () => {
|
|
619
|
+
const abort = () => { handle.abort(); };
|
|
796
620
|
if (options.signal?.aborted)
|
|
797
621
|
abort();
|
|
798
622
|
else
|
|
799
623
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
624
|
+
let auditorFailure;
|
|
800
625
|
try {
|
|
801
626
|
try {
|
|
802
627
|
const promptAllowingRejectedDecision = async (prompt) => {
|
|
@@ -806,7 +631,7 @@ export async function executeAuditorChild(options) {
|
|
|
806
631
|
promptDecisionFailures = [];
|
|
807
632
|
let promptFailure;
|
|
808
633
|
try {
|
|
809
|
-
await
|
|
634
|
+
await handle.prompt(prompt);
|
|
810
635
|
}
|
|
811
636
|
catch (error) {
|
|
812
637
|
promptFailure = error;
|
|
@@ -831,6 +656,10 @@ export async function executeAuditorChild(options) {
|
|
|
831
656
|
}
|
|
832
657
|
if (decisionToolFailure !== undefined)
|
|
833
658
|
return;
|
|
659
|
+
if (retentionFailure !== undefined)
|
|
660
|
+
return;
|
|
661
|
+
if (opened.streamFailure !== undefined)
|
|
662
|
+
throw opened.streamFailure;
|
|
834
663
|
if (promptFailure !== undefined)
|
|
835
664
|
throw promptFailure;
|
|
836
665
|
};
|
|
@@ -842,8 +671,8 @@ export async function executeAuditorChild(options) {
|
|
|
842
671
|
promptDecisionFailures = [];
|
|
843
672
|
};
|
|
844
673
|
await promptAllowingRejectedDecision(options.prompt);
|
|
845
|
-
while (!decisionSubmitted && (boundaryResponse === undefined || decisionToolFailure !== undefined)
|
|
846
|
-
&&
|
|
674
|
+
while (!decisionSubmitted && retentionFailure === undefined && (boundaryResponse === undefined || decisionToolFailure !== undefined)
|
|
675
|
+
&& opened.streamFailure === undefined && delivery.nextAction() === "request-delivery") {
|
|
847
676
|
if (decisionToolFailure !== undefined) {
|
|
848
677
|
const failures = promptDecisionFailures.length === 0
|
|
849
678
|
? [decisionToolFailure]
|
|
@@ -865,7 +694,7 @@ export async function executeAuditorChild(options) {
|
|
|
865
694
|
await promptAllowingRejectedDecision(RECEIPT_DELIVERY_PROMPT);
|
|
866
695
|
}
|
|
867
696
|
}
|
|
868
|
-
if (!decisionSubmitted &&
|
|
697
|
+
if (!decisionSubmitted && retentionFailure === undefined && opened.streamFailure === undefined
|
|
869
698
|
&& delivery.nextAction() === "no-receipt") {
|
|
870
699
|
const runPointer = options.context.sessionManager.getSessionFile() ?? options.context.cwd ?? process.cwd();
|
|
871
700
|
const attemptPointer = binding.parent.attemptEntryId ?? binding.parent.sessionId ?? `current:${runPointer}`;
|
|
@@ -875,6 +704,17 @@ export async function executeAuditorChild(options) {
|
|
|
875
704
|
// charged this prompt to the exhausted shared budget.
|
|
876
705
|
decisionToolFailure = undefined;
|
|
877
706
|
auditorSessionManager.appendCustomEntry(NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, facts);
|
|
707
|
+
try {
|
|
708
|
+
sitianReport({
|
|
709
|
+
level: "event",
|
|
710
|
+
kind: "auditor",
|
|
711
|
+
cwd,
|
|
712
|
+
sessionParent: parentSessionFile,
|
|
713
|
+
payload: { type: NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, ...facts },
|
|
714
|
+
source: "evidence-child-executor",
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
catch { }
|
|
878
718
|
// Provenance is granted only after the lifecycle owner persisted the
|
|
879
719
|
// current child record; accepted model arguments can never set it.
|
|
880
720
|
noReceiptLifecycle = facts;
|
|
@@ -883,45 +723,44 @@ export async function executeAuditorChild(options) {
|
|
|
883
723
|
catch (error) {
|
|
884
724
|
if (options.signal?.aborted)
|
|
885
725
|
throw options.signal.reason;
|
|
886
|
-
if (
|
|
887
|
-
throw
|
|
888
|
-
|
|
726
|
+
if (retentionFailure === undefined && opened.streamFailure !== undefined)
|
|
727
|
+
throw opened.streamFailure;
|
|
728
|
+
if (retentionFailure === undefined)
|
|
729
|
+
throw error;
|
|
889
730
|
}
|
|
890
731
|
if (options.signal?.aborted)
|
|
891
732
|
throw options.signal.reason;
|
|
892
|
-
if (
|
|
893
|
-
throw
|
|
733
|
+
if (retentionFailure === undefined && opened.streamFailure !== undefined)
|
|
734
|
+
throw opened.streamFailure;
|
|
894
735
|
if (!decisionSubmitted && decisionToolFailure !== undefined)
|
|
895
736
|
throw decisionToolFailure;
|
|
896
737
|
const relevantResponse = !decisionSubmitted
|
|
897
738
|
? boundaryResponse
|
|
898
|
-
:
|
|
739
|
+
: (retainedResponse && retainedResponse.role === "assistant" && retainedResponse.content.some((part) => part.type === "toolCall" && part.name === tool.name)
|
|
740
|
+
? retainedResponse
|
|
741
|
+
: undefined);
|
|
899
742
|
if (relevantResponse !== undefined) {
|
|
900
743
|
const toolFailure = findToolFailure(relevantResponse);
|
|
901
744
|
if (toolFailure !== undefined)
|
|
902
745
|
throw toolFailure;
|
|
903
746
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
747
|
+
const assistants = [...retainedAssistants].reverse();
|
|
748
|
+
const response = !decisionSubmitted
|
|
749
|
+
? assistants[0]
|
|
750
|
+
: assistants.find((message) => message.content.some((part) => part.type === "toolCall" && part.name === tool.name));
|
|
751
|
+
if (boundaryResponse !== undefined && boundaryResponse.stopReason !== "error" && !decisionSubmitted && noReceiptLifecycle === undefined) {
|
|
907
752
|
const toolNames = boundaryResponse.content.flatMap((part) => part.type === "toolCall" ? [part.name] : []);
|
|
908
753
|
throw new AuditorTurnLimitError(AUDITOR_TURN_LIMIT, turns, {
|
|
909
754
|
stopReason: boundaryResponse.stopReason,
|
|
910
755
|
toolNames,
|
|
911
756
|
});
|
|
912
757
|
}
|
|
913
|
-
const assistants = [...session.messages]
|
|
914
|
-
.reverse()
|
|
915
|
-
.filter((message) => message.role === "assistant");
|
|
916
|
-
const response = !decisionSubmitted
|
|
917
|
-
? assistants[0]
|
|
918
|
-
: assistants.find((message) => message.content.some((part) => part.type === "toolCall" && part.name === tool.name));
|
|
919
758
|
if (response !== undefined) {
|
|
920
759
|
try {
|
|
760
|
+
if (retentionFailure !== undefined)
|
|
761
|
+
throw retentionFailure;
|
|
921
762
|
if (retainedResponse === undefined)
|
|
922
763
|
options.retainResponse?.(response);
|
|
923
|
-
else if (retentionFailure !== undefined)
|
|
924
|
-
throw retentionFailure;
|
|
925
764
|
}
|
|
926
765
|
catch (retentionFailure) {
|
|
927
766
|
if (response.stopReason !== "error")
|
|
@@ -971,7 +810,7 @@ export async function executeAuditorChild(options) {
|
|
|
971
810
|
}),
|
|
972
811
|
},
|
|
973
812
|
};
|
|
974
|
-
|
|
813
|
+
const failureData = {
|
|
975
814
|
version: 1,
|
|
976
815
|
parent: binding.parent,
|
|
977
816
|
failure: {
|
|
@@ -980,15 +819,26 @@ export async function executeAuditorChild(options) {
|
|
|
980
819
|
...(failure.message === "" ? {} : { diagnostic: failure.message }),
|
|
981
820
|
details: failure.details,
|
|
982
821
|
},
|
|
983
|
-
}
|
|
822
|
+
};
|
|
823
|
+
auditorSessionManager.appendCustomEntry(AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, failureData);
|
|
824
|
+
try {
|
|
825
|
+
sitianReport({
|
|
826
|
+
level: "event",
|
|
827
|
+
kind: "auditor",
|
|
828
|
+
cwd,
|
|
829
|
+
sessionParent: parentSessionFile,
|
|
830
|
+
payload: { type: AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, ...failureData },
|
|
831
|
+
source: "evidence-child-executor",
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
catch { }
|
|
984
835
|
throw failure;
|
|
985
836
|
}
|
|
986
837
|
}
|
|
987
838
|
if (response === undefined
|
|
988
839
|
|| response.stopReason === "error"
|
|
989
|
-
|| response.stopReason === "aborted"
|
|
990
|
-
|
|
991
|
-
throw new Error(`${options.roleLabel} exited without a readable decision receipt`);
|
|
840
|
+
|| (!decisionSubmitted && (response.stopReason === "aborted" || decision === undefined))) {
|
|
841
|
+
throw new Error(response?.errorMessage ?? `${options.roleLabel} exited without a readable decision receipt`);
|
|
992
842
|
}
|
|
993
843
|
return {
|
|
994
844
|
decision,
|
|
@@ -996,10 +846,13 @@ export async function executeAuditorChild(options) {
|
|
|
996
846
|
...(noReceiptLifecycle === undefined ? {} : { noReceiptLifecycle }),
|
|
997
847
|
};
|
|
998
848
|
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
auditorFailure = error;
|
|
851
|
+
throw error;
|
|
852
|
+
}
|
|
999
853
|
finally {
|
|
1000
854
|
options.signal?.removeEventListener("abort", abort);
|
|
1001
|
-
unsubscribe();
|
|
1002
|
-
dispose();
|
|
855
|
+
await runChildCleanup([() => unsubscribe(), () => handle.close()], auditorFailure, options.roleLabel);
|
|
1003
856
|
}
|
|
1004
857
|
});
|
|
1005
858
|
}
|