@akagilnc/pi-workflow-roles 0.1.5096 → 0.1.5124
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/dist/acp-host/production-host.js +197 -33
- package/dist/headless-host/production-host.js +197 -33
- package/dist/public-cli/cli.js +24 -1
- package/dist/public-cli/instruction-seat-run.js +8 -6
- package/dist/public-cli/main.js +6704 -6540
- package/dist/public-cli/post-admission.js +125 -26
- package/dist/public-cli/settlement.js +28 -4
- package/package.json +1 -1
- package/resources/engines/opencode.md +9 -4
- package/src/public-cli/cli-io.ts +2 -0
- package/src/public-cli/cli.ts +38 -4
- package/src/public-cli/instruction-seat-run.ts +10 -6
- package/src/public-cli/invocation.ts +6 -1
- package/src/public-cli/post-admission.ts +189 -34
- package/src/public-cli/settlement.ts +37 -4
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { writeFile } from "node:fs/promises";
|
|
10
10
|
import { isAbsolute, join, resolve } from "node:path";
|
|
11
|
-
import { buildAutoResumeContinuationPrompt, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
11
|
+
import { buildAutoResumeContinuationPrompt, findRunDirectoryById, readRoleRunState, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
12
12
|
import { CliUsageError } from "./cli-errors.js";
|
|
13
13
|
import { bindAdmittedTicketNumber, buildInstructionTransportPrompt, freezeAttachmentsIntoRun, relocateAdmittedRunToTicket, } from "./invocation.js";
|
|
14
14
|
import { readRecordedSubmissionRows } from "../submission-ledger.js";
|
|
15
15
|
import { pathContainedIn } from "../activation-ledger-topology.js";
|
|
16
16
|
import { pickEngineAxis } from "../package-resources/engine-material.js";
|
|
17
|
-
import { readStoredHostSessionId, resolveHostAwareSessionAvailability } from "../session-identity.js";
|
|
17
|
+
import { readStoredHostSessionId, resolveHostAwareSessionAvailability, } from "../session-identity.js";
|
|
18
18
|
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.js";
|
|
19
19
|
import { isOfficerReviewSeat } from "../packaged-role-registry.js";
|
|
20
20
|
import { deliverCaseDossierAsAttachment } from "./case-dossier-delivery.js";
|
|
@@ -39,7 +39,7 @@ import { projectHostTransitionPriorNative } from "../host-transition-prior-nativ
|
|
|
39
39
|
import { clearCurrentCourt, clearTypedProviderHttpObservation, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, renderResumeCommand, } from "./run-lifecycle.js";
|
|
40
40
|
import { processCancelDiagnostic, processCancelSignalName, } from "./process-cancel.js";
|
|
41
41
|
import { homeFromRunDirectory } from "../activation-ledger-topology.js";
|
|
42
|
-
import { attemptProducedFreshSubmission, classifyPostAdmissionFailure, controlledFailureInputFromResolution, exitCodeForTerminalOutcome, explicitInternalKnownFailureClassificationInput, formatCliDiagnostic, formatTerminalResult, inspectJudgeSession, isLawfulTypedTerminalOutcome, presentFailureTerminal, presentStructuralRejection, resolveAuditedRunnerFailureResolution, resolveControlledFailureResumeObservation, settleFailureTerminalResult, settleHostEndedNoReceipt, attachRecordedSubmissions, } from "./settlement.js";
|
|
42
|
+
import { attemptProducedFreshSubmission, classifyPostAdmissionFailure, controlledFailureInputFromResolution, exitCodeForTerminalOutcome, explicitInternalKnownFailureClassificationInput, formatCliDiagnostic, formatErrorCauseDetail, formatTerminalResult, inspectJudgeSession, isLawfulTypedTerminalOutcome, presentFailureTerminal, publishedFailureErrorPath, presentStructuralRejection, resolveAuditedRunnerFailureResolution, resolveControlledFailureResumeObservation, settleFailureTerminalResult, settleHostEndedNoReceipt, attachRecordedSubmissions, rewritePublishedFailureErrorPath, } from "./settlement.js";
|
|
43
43
|
import {} from "./terminal.js";
|
|
44
44
|
import { ensureRealArtifactsDirectory, persistReturnedRunState, runWithAutoResumeLoop, TurnDispatchedFailure, } from "./auto-resume.js";
|
|
45
45
|
function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
|
|
@@ -56,6 +56,7 @@ function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
if (result.terminal !== undefined) {
|
|
59
|
+
rewritePublishedFailureErrorPath(result.terminal, relocation.oldRunDirectory, relocation.newRunDirectory);
|
|
59
60
|
for (const artifact of result.terminal.artifacts) {
|
|
60
61
|
artifact.path = rewrite(artifact.path);
|
|
61
62
|
}
|
|
@@ -175,7 +176,7 @@ export async function resolveResumeMethodMaterialAdapters(input) {
|
|
|
175
176
|
stderr: "",
|
|
176
177
|
thrown: error,
|
|
177
178
|
...(input.knownCause === undefined ? {} : { knownCause: input.knownCause }),
|
|
178
|
-
}, input.emptyAdapters, input.authority, input.io);
|
|
179
|
+
}, input.emptyAdapters, input.authority, { ...input.io, omitFailureStderrDiagnostic: true });
|
|
179
180
|
return { kind: "terminal", ...terminal };
|
|
180
181
|
}
|
|
181
182
|
}
|
|
@@ -233,15 +234,27 @@ export async function presentControlledFailure(admitted, failureInput, adapters,
|
|
|
233
234
|
if (persistRunState && !failureInput.skipRunStateWrite) {
|
|
234
235
|
await persistReturnedRunState(admitted, authority);
|
|
235
236
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
237
|
+
let publishedErrorPath;
|
|
238
|
+
let terminal;
|
|
239
|
+
try {
|
|
240
|
+
const settled = await settleFailureTerminalResult(admitted, failure, authority, {
|
|
241
|
+
...(resumable
|
|
242
|
+
? { resume: { command: renderResumeCommand(admitted.runId) } }
|
|
243
|
+
: {}),
|
|
244
|
+
...(failureInput.invocationScopeId === undefined ||
|
|
245
|
+
failureInput.invocationScopeId.length === 0
|
|
246
|
+
? {}
|
|
247
|
+
: { invocationScopeId: failureInput.invocationScopeId }),
|
|
248
|
+
onErrorPublished: (path) => { publishedErrorPath = path; },
|
|
249
|
+
});
|
|
250
|
+
terminal = await attachRecordedSubmissions(admitted, settled);
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
if (io.omitFailureStderrDiagnostic && publishedErrorPath !== undefined) {
|
|
254
|
+
writeResumeFailurePointer(io, publishedErrorPath);
|
|
255
|
+
}
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
245
258
|
presentFailureTerminal(terminal, io);
|
|
246
259
|
return {
|
|
247
260
|
exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome),
|
|
@@ -359,8 +372,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
359
372
|
if (relocation !== undefined) {
|
|
360
373
|
projectRelocatedTurnIdentity(request, result, admitted, relocation);
|
|
361
374
|
}
|
|
362
|
-
|
|
363
|
-
|
|
375
|
+
const afterDispatchRelocation = adapters.afterDispatch === undefined
|
|
376
|
+
? undefined
|
|
377
|
+
: await adapters.afterDispatch(admitted, lease);
|
|
378
|
+
if (afterDispatchRelocation !== undefined) {
|
|
379
|
+
projectRelocatedTurnIdentity(request, result, admitted, afterDispatchRelocation);
|
|
380
|
+
}
|
|
364
381
|
return result;
|
|
365
382
|
}
|
|
366
383
|
catch (error) {
|
|
@@ -968,16 +985,24 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
968
985
|
loaded = await input.load(request);
|
|
969
986
|
}
|
|
970
987
|
catch (error) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
988
|
+
const usageError = error instanceof CliUsageError;
|
|
989
|
+
// Unknown usage failures retain the structural fallback. Other load
|
|
990
|
+
// failures keep the CLI's outer handling unless an existing run is found.
|
|
991
|
+
const recorded = await pointExistingRunFailure(input.env.home, request.runId, input.env.principalAuthority, input.io, error);
|
|
992
|
+
if (!recorded) {
|
|
993
|
+
if (usageError) {
|
|
994
|
+
presentStructuralRejection(error, input.io);
|
|
995
|
+
return { exitCode: 2 };
|
|
996
|
+
}
|
|
997
|
+
throw error;
|
|
974
998
|
}
|
|
975
|
-
|
|
999
|
+
return { exitCode: usageError ? 2 : 1 };
|
|
976
1000
|
}
|
|
977
1001
|
let adapters = input.adapters;
|
|
978
1002
|
if (input.afterAdmittedLoad !== undefined) {
|
|
979
1003
|
const prepared = await input.afterAdmittedLoad(loaded.admitted);
|
|
980
1004
|
if (prepared.kind === "terminal") {
|
|
1005
|
+
showResumeErrorPointer(input.io, prepared.exitCode, prepared.terminal);
|
|
981
1006
|
return {
|
|
982
1007
|
exitCode: prepared.exitCode,
|
|
983
1008
|
admitted: prepared.admitted,
|
|
@@ -1116,26 +1141,100 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
1116
1141
|
}),
|
|
1117
1142
|
});
|
|
1118
1143
|
}
|
|
1119
|
-
|
|
1144
|
+
const resumed = await runPostAdmissionManualResume({
|
|
1120
1145
|
admitted: loaded.admitted,
|
|
1121
1146
|
env,
|
|
1122
|
-
io: input.io,
|
|
1147
|
+
io: { ...input.io, omitFailureStderrDiagnostic: true },
|
|
1123
1148
|
adapters,
|
|
1124
1149
|
...(input.effectiveEngine === undefined
|
|
1125
1150
|
? {}
|
|
1126
1151
|
: { effectiveEngine: input.effectiveEngine }),
|
|
1127
1152
|
buildRequestAfterLease,
|
|
1128
1153
|
});
|
|
1154
|
+
showResumeErrorPointer(input.io, resumed.exitCode, resumed.terminal);
|
|
1155
|
+
return resumed;
|
|
1129
1156
|
}
|
|
1130
1157
|
catch (error) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1158
|
+
if (error instanceof TurnDispatchedFailure)
|
|
1159
|
+
throw error;
|
|
1160
|
+
await presentResumeFailurePointer(input.io, error, (failure) => writeResumeDiagnosticFile(loaded.admitted.runDirectory, loaded.admitted.runId, failure, loaded.admitted.role));
|
|
1161
|
+
return { exitCode: error instanceof CliUsageError ? 2 : 1 };
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
async function pointExistingRunFailure(home, runId, authority, io, thrown) {
|
|
1165
|
+
let foundRunDirectory;
|
|
1166
|
+
try {
|
|
1167
|
+
foundRunDirectory = await findRunDirectoryById(home, runId);
|
|
1168
|
+
}
|
|
1169
|
+
catch {
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
if (foundRunDirectory === undefined)
|
|
1173
|
+
return false;
|
|
1174
|
+
const runDirectory = foundRunDirectory;
|
|
1175
|
+
let record;
|
|
1176
|
+
try {
|
|
1177
|
+
record = await readRoleRunState(runDirectory, authority);
|
|
1178
|
+
}
|
|
1179
|
+
catch {
|
|
1180
|
+
await presentResumeFailurePointer(io, thrown, (failure) => writeResumeDiagnosticFile(runDirectory, runId, failure));
|
|
1181
|
+
return true;
|
|
1182
|
+
}
|
|
1183
|
+
if (record === undefined)
|
|
1184
|
+
return false;
|
|
1185
|
+
await presentResumeFailurePointer(io, thrown, (failure) => writeResumeDiagnosticFile(runDirectory, runId, failure, record.role));
|
|
1186
|
+
return true;
|
|
1187
|
+
}
|
|
1188
|
+
export async function presentLocatedResumeFailure(home, runId, authority, io, thrown) {
|
|
1189
|
+
return await pointExistingRunFailure(home, runId, authority, io, thrown);
|
|
1190
|
+
}
|
|
1191
|
+
function writeResumeFailurePointer(io, errorPath) {
|
|
1192
|
+
io.stderr(formatCliDiagnostic(`续跑失败,当次错误记录:${errorPath}`));
|
|
1193
|
+
}
|
|
1194
|
+
export function showResumeErrorPointer(io, exitCode, terminal) {
|
|
1195
|
+
if (exitCode === 0 || terminal === undefined)
|
|
1196
|
+
return;
|
|
1197
|
+
const publishedError = terminal.artifacts.find((artifact) => artifact.kind === "error")?.path
|
|
1198
|
+
?? publishedFailureErrorPath(terminal);
|
|
1199
|
+
if (publishedError !== undefined)
|
|
1200
|
+
writeResumeFailurePointer(io, publishedError);
|
|
1201
|
+
}
|
|
1202
|
+
async function presentResumeFailurePointer(io, thrown, publish) {
|
|
1203
|
+
const failure = classifyPostAdmissionFailure({
|
|
1204
|
+
timedOut: false,
|
|
1205
|
+
code: null,
|
|
1206
|
+
stderr: "",
|
|
1207
|
+
thrown,
|
|
1208
|
+
});
|
|
1209
|
+
try {
|
|
1210
|
+
writeResumeFailurePointer(io, await publish(failure));
|
|
1211
|
+
}
|
|
1212
|
+
catch (publishError) {
|
|
1213
|
+
const cause = {
|
|
1214
|
+
publishError: formatErrorCauseDetail(publishError),
|
|
1215
|
+
};
|
|
1216
|
+
if (thrown instanceof Error && thrown.cause !== undefined) {
|
|
1217
|
+
cause.thrownCause = formatErrorCauseDetail(thrown.cause);
|
|
1135
1218
|
}
|
|
1136
|
-
|
|
1219
|
+
presentStructuralRejection({
|
|
1220
|
+
message: thrown instanceof Error ? thrown.message : String(thrown),
|
|
1221
|
+
cause,
|
|
1222
|
+
}, io);
|
|
1137
1223
|
}
|
|
1138
1224
|
}
|
|
1225
|
+
async function writeResumeDiagnosticFile(runDirectory, runId, failure, role) {
|
|
1226
|
+
const dir = await ensureRealArtifactsDirectory(runDirectory);
|
|
1227
|
+
const path = join(dir, `resume-diagnostic-${randomUUID()}.json`);
|
|
1228
|
+
await writeFile(path, `${JSON.stringify({
|
|
1229
|
+
runId,
|
|
1230
|
+
...(role === undefined ? {} : { role }),
|
|
1231
|
+
diagnostic: failure.diagnostic,
|
|
1232
|
+
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
1233
|
+
...(failure.identity === undefined ? {} : { identity: failure.identity }),
|
|
1234
|
+
...(failure.details === undefined ? {} : { details: failure.details }),
|
|
1235
|
+
}, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
1236
|
+
return path;
|
|
1237
|
+
}
|
|
1139
1238
|
/**
|
|
1140
1239
|
* Shared post-admission one-shot path: folds into runPostAdmissionResumable (#840 / #416).
|
|
1141
1240
|
* All callable roles share the single auto-resume loop.
|
|
@@ -31,6 +31,7 @@ import { SECRETARIAT_COUNTERSIGN_TERMINAL_FACT_KEY, SECRETARIAT_GATE_OFFICER_ENT
|
|
|
31
31
|
import { classifyPackagedRoleTerminalResult, findLatestDurablePackagedRoleTerminal, hasNavigatorInfrastructureFailureBase, isAcceptedPackagedRoleTerminalResult, isReceiptSettlementBindingClear, NAVIGATOR_INVOCATION_ENTRY, NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, parseInvocationMarkerIdentity, } from "../navigator-invocation-identity.js";
|
|
32
32
|
import { NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_TURN_LIMIT, noReceiptLifecycleFacts, parseNoReceiptLifecycleFacts, } from "../receipt-delivery-policy.js";
|
|
33
33
|
import { roleRunArtifactsDirectory } from "../role-run-placement.js";
|
|
34
|
+
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.js";
|
|
34
35
|
import { listSeamOwnedUniqueErrorFacePaths, RUN_TERMINAL_ARTIFACT_FILES, RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS, } from "../run-terminal-artifacts.js";
|
|
35
36
|
import { ensureRunArtifactsDir, homeFromRunDirectory, } from "./invocation.js";
|
|
36
37
|
/** Ledger reads use the run's machine home — not ambient process HOME (child write vs parent settle). */
|
|
@@ -127,7 +128,11 @@ export async function attachRecordedSubmissions(admitted, terminal, scope) {
|
|
|
127
128
|
// pass courtAttemptId here — roleOutcome already carries this-court payloads
|
|
128
129
|
// from sealedLedgerOutcome when scoped; withSubmissions keeps them.
|
|
129
130
|
void scope;
|
|
130
|
-
|
|
131
|
+
const result = withSubmissions(terminal, await recordedSubmissionPayloads(admitted, undefined));
|
|
132
|
+
const errorPath = privateFailureErrorPaths.get(terminal);
|
|
133
|
+
if (errorPath !== undefined)
|
|
134
|
+
privateFailureErrorPaths.set(result, errorPath);
|
|
135
|
+
return result;
|
|
131
136
|
}
|
|
132
137
|
/**
|
|
133
138
|
* Host ended with no recorded submission — lawful no_receipt (exit 0).
|
|
@@ -163,6 +168,18 @@ function coordinatesFromAdmitted(authority, admitted) {
|
|
|
163
168
|
}
|
|
164
169
|
import { coalesceSubmissionRows, exitCodeForTerminalOutcome, formatTerminalResult, isLawfulTypedTerminalOutcome, adviceNavigatorFact, } from "./terminal.js";
|
|
165
170
|
export { exitCodeForTerminalOutcome, formatTerminalResult, isLawfulTypedTerminalOutcome, };
|
|
171
|
+
// A resumable public Terminal omits artifact paths (#108); its actual error
|
|
172
|
+
// publication remains available only to the CLI's resume presentation seam.
|
|
173
|
+
const privateFailureErrorPaths = new WeakMap();
|
|
174
|
+
export function publishedFailureErrorPath(terminal) {
|
|
175
|
+
return privateFailureErrorPaths.get(terminal);
|
|
176
|
+
}
|
|
177
|
+
export function rewritePublishedFailureErrorPath(terminal, oldRunDirectory, newRunDirectory) {
|
|
178
|
+
const errorPath = privateFailureErrorPaths.get(terminal);
|
|
179
|
+
if (errorPath !== undefined) {
|
|
180
|
+
privateFailureErrorPaths.set(terminal, rewriteRunDirectoryPathValue(errorPath, oldRunDirectory, newRunDirectory));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
166
183
|
/**
|
|
167
184
|
* Host stderr as recorded — full bytes, no flood filter, no char clip (#836).
|
|
168
185
|
*/
|
|
@@ -2282,7 +2299,7 @@ async function writeFailureJsonRetainingCause(preferredCandidates, uniqueFallbac
|
|
|
2282
2299
|
error.publicationAttempts = issues;
|
|
2283
2300
|
throw error;
|
|
2284
2301
|
}
|
|
2285
|
-
export async function publishFailureArtifacts(admitted, failure, authority) {
|
|
2302
|
+
export async function publishFailureArtifacts(admitted, failure, authority, onErrorPublished) {
|
|
2286
2303
|
const { sessionDirectory, sessionFile } = coordinatesFromAdmitted(authority, admitted);
|
|
2287
2304
|
const { baseDir, attempt: baseAttempt } = await resolveFailureArtifactsBase(admitted.runDirectory);
|
|
2288
2305
|
const priorIssues = baseAttempt === undefined ? [] : [baseAttempt];
|
|
@@ -2346,6 +2363,7 @@ export async function publishFailureArtifacts(admitted, failure, authority) {
|
|
|
2346
2363
|
...(failure.details === undefined ? {} : { details: failure.details }),
|
|
2347
2364
|
};
|
|
2348
2365
|
const errorWrite = await writeFailureJsonRetainingCause(errorCandidates, uniqueFallbackDirs, "error", errorPayloadBase, priorIssues);
|
|
2366
|
+
onErrorPublished?.(errorWrite.path);
|
|
2349
2367
|
const evidencePayload = {
|
|
2350
2368
|
runId: admitted.runId,
|
|
2351
2369
|
sessionDirectory: sessionDirectory,
|
|
@@ -2437,7 +2455,8 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2437
2455
|
// Exact-session attendance only — never infer no-advice from caller omission.
|
|
2438
2456
|
const navigator = await extractNavigatorFactFromAdmittedSession(sessionFile);
|
|
2439
2457
|
// Private durable artifacts retain the original diagnostic identity (including run ID).
|
|
2440
|
-
const artifacts = await publishFailureArtifacts(admitted, failure, authority);
|
|
2458
|
+
const artifacts = await publishFailureArtifacts(admitted, failure, authority, options.onErrorPublished);
|
|
2459
|
+
const errorPath = artifacts.find((artifact) => artifact.kind === "error")?.path;
|
|
2441
2460
|
const decisiveFacts = {
|
|
2442
2461
|
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
2443
2462
|
diagnostic: failure.diagnostic,
|
|
@@ -2463,12 +2482,15 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2463
2482
|
diagnostic: failure.diagnostic,
|
|
2464
2483
|
decisiveFacts,
|
|
2465
2484
|
};
|
|
2466
|
-
|
|
2485
|
+
const terminal = await withOptionalGateProjection({
|
|
2467
2486
|
roleOutcome,
|
|
2468
2487
|
navigator,
|
|
2469
2488
|
artifacts: [],
|
|
2470
2489
|
resume: options.resume,
|
|
2471
2490
|
}, sessionDirectory, detourGateContext(admitted, options));
|
|
2491
|
+
if (errorPath !== undefined)
|
|
2492
|
+
privateFailureErrorPaths.set(terminal, errorPath);
|
|
2493
|
+
return terminal;
|
|
2472
2494
|
}
|
|
2473
2495
|
const roleOutcome = {
|
|
2474
2496
|
kind: "failure",
|
|
@@ -2499,6 +2521,8 @@ export function presentFailureTerminal(terminal, io) {
|
|
|
2499
2521
|
}
|
|
2500
2522
|
io.stdout(formatTerminalResult(terminal));
|
|
2501
2523
|
if (terminal.roleOutcome.kind === "failure") {
|
|
2524
|
+
if (io.omitFailureStderrDiagnostic)
|
|
2525
|
+
return;
|
|
2502
2526
|
io.stderr(formatFailureStderrDiagnostic({
|
|
2503
2527
|
...(terminal.roleOutcome.cause === undefined ? {} : { cause: terminal.roleOutcome.cause }),
|
|
2504
2528
|
diagnostic: terminal.roleOutcome.diagnostic,
|
package/package.json
CHANGED
|
@@ -10,12 +10,18 @@ parameters.
|
|
|
10
10
|
## Invocation examples (local OpenCode CLI)
|
|
11
11
|
|
|
12
12
|
The machine entrypoint is `opencode`. Run from the role project root.
|
|
13
|
-
Non-interactive labor uses `run` with an explicit model:
|
|
13
|
+
Non-interactive labor uses `run` with `--auto` and an explicit model:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
opencode run -m <MODEL_ID> "YOUR_LABOR_PROMPT"
|
|
16
|
+
opencode run --auto -m <MODEL_ID> "YOUR_LABOR_PROMPT"
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
- `--auto` is required: it auto-approves permissions that are not explicitly
|
|
20
|
+
denied. Without it, any `ask` permission (e.g. `external_directory` when the
|
|
21
|
+
labor reads the case dossier outside the project root) is auto-rejected in a
|
|
22
|
+
headless `run`, and the engine exits after its opening sentence with no labor
|
|
23
|
+
done (upstream anomalyco/opencode#36413).
|
|
24
|
+
|
|
19
25
|
- Model id is not pinned by this note; it comes from the dispatch order
|
|
20
26
|
(owner pool directive), passed verbatim as `provider/model`. **Always
|
|
21
27
|
confirm the current id with `opencode models` first** — provider prefixes
|
|
@@ -23,7 +29,6 @@ opencode run -m <MODEL_ID> "YOUR_LABOR_PROMPT"
|
|
|
23
29
|
GLM lives under `opencode-go/`, e.g. `opencode-go/glm-5.2`; the older
|
|
24
30
|
`zai/glm-5.2` id errors with "Unexpected server error").
|
|
25
31
|
- Plain text output only (never `--format json` for labor — the returned body
|
|
26
|
-
goes back into the seat's context)
|
|
27
|
-
permissions for unattended runs (per official docs).
|
|
32
|
+
goes back into the seat's context).
|
|
28
33
|
- Output goes to stdout; long prompts may be passed via shell heredoc or a
|
|
29
34
|
file read into the argument — follow the installed CLI's actual interface.
|
package/src/public-cli/cli-io.ts
CHANGED
package/src/public-cli/cli.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DurablePrincipalAuthority } from "../host-contracts.ts";
|
|
1
|
+
import type { DurablePrincipalAuthority, RoleTurnHost } from "../host-contracts.ts";
|
|
2
2
|
import { piDurablePrincipalAuthority } from "../pi/durable-principal.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Public ak-role CLI dispatcher (roles / config / layered help / Judge run).
|
|
@@ -49,8 +49,10 @@ import { seatModelOnly } from "./registry.ts";
|
|
|
49
49
|
import { CliUsageError } from "./cli-errors.ts";
|
|
50
50
|
import type { CliIo } from "./cli-io.ts";
|
|
51
51
|
import { runMachineSkillSetup } from "./machine-method-skills.ts";
|
|
52
|
-
import
|
|
53
|
-
|
|
52
|
+
import {
|
|
53
|
+
presentLocatedResumeFailure,
|
|
54
|
+
type PostAdmissionEnv,
|
|
55
|
+
} from "./post-admission.ts";
|
|
54
56
|
import { appendPiSessionCustomEntry } from "../pi/role-turn-host.ts";
|
|
55
57
|
import {
|
|
56
58
|
composeRoleTurnHostAdapters,
|
|
@@ -78,6 +80,7 @@ import {
|
|
|
78
80
|
type PublicRoleOptionOwner,
|
|
79
81
|
type TypedOptionConsumer,
|
|
80
82
|
} from "./option-definitions.ts";
|
|
83
|
+
import { TurnDispatchedFailure } from "./auto-resume.ts";
|
|
81
84
|
import { continueParentAfterChild, runPublicInstructionSeat, runPublicInstructionSeatResume } from "./instruction-seat-run.ts";
|
|
82
85
|
import { courtDiaristEscalated } from "./countersign-run.ts";
|
|
83
86
|
import { runPublicAnalyst } from "./analyst-run.ts";
|
|
@@ -1093,6 +1096,9 @@ export async function runAkRole(
|
|
|
1093
1096
|
env: CliEnv,
|
|
1094
1097
|
): Promise<CliResult> {
|
|
1095
1098
|
const io = env.io ?? defaultIo();
|
|
1099
|
+
let resumeFailureContext:
|
|
1100
|
+
| { readonly home: string; readonly runId: string; readonly authority: DurablePrincipalAuthority }
|
|
1101
|
+
| undefined;
|
|
1096
1102
|
|
|
1097
1103
|
try {
|
|
1098
1104
|
// Select the installed package identity once, before any role-owned Skill,
|
|
@@ -1207,12 +1213,17 @@ export async function runAkRole(
|
|
|
1207
1213
|
// must still exist. Seat and dispatch follow the durable admitted role.
|
|
1208
1214
|
// #471: unique parser owns {runId, message?}; five role paths only consume it.
|
|
1209
1215
|
if (parsed.command === "resume") {
|
|
1216
|
+
const resumeRequest = parseResumeRequest(parsed.args);
|
|
1217
|
+
resumeFailureContext = {
|
|
1218
|
+
home,
|
|
1219
|
+
runId: resumeRequest.runId,
|
|
1220
|
+
authority: env.principalAuthority ?? piDurablePrincipalAuthority,
|
|
1221
|
+
};
|
|
1210
1222
|
const agentDir = resolveAgentDir(env, home);
|
|
1211
1223
|
const cwd = env.cwd ?? process.cwd();
|
|
1212
1224
|
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
1213
1225
|
const credentials =
|
|
1214
1226
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
1215
|
-
const resumeRequest = parseResumeRequest(parsed.args);
|
|
1216
1227
|
const resumeRole = await peekRoleRunRole(home, resumeRequest.runId);
|
|
1217
1228
|
// Missing durable role keeps the judge seat table. The resume entry
|
|
1218
1229
|
// itself is one function; it reads the stored run.
|
|
@@ -1243,6 +1254,11 @@ export async function runAkRole(
|
|
|
1243
1254
|
&& !courtDiaristEscalated(current.terminal.roleOutcome)
|
|
1244
1255
|
) {
|
|
1245
1256
|
const parentRunId = current.admitted.correlationId;
|
|
1257
|
+
resumeFailureContext = {
|
|
1258
|
+
home,
|
|
1259
|
+
runId: parentRunId,
|
|
1260
|
+
authority: env.principalAuthority ?? piDurablePrincipalAuthority,
|
|
1261
|
+
};
|
|
1246
1262
|
const parentRole = await peekRoleRunRole(home, parentRunId);
|
|
1247
1263
|
if (parentRole === undefined) break;
|
|
1248
1264
|
const parentSeat = resolveEffectiveSeat(config, parentRole, credentials, invocationFromParsed(parsed));
|
|
@@ -1287,6 +1303,24 @@ export async function runAkRole(
|
|
|
1287
1303
|
// tokens (including misspelled role names) are structural rejects.
|
|
1288
1304
|
throw new CliUsageError(`unknown command: ${parsed.command}`);
|
|
1289
1305
|
} catch (error) {
|
|
1306
|
+
// A dispatched turn owns its settlement; only pre-dispatch resume failures
|
|
1307
|
+
// fall through to the located-run diagnostic presenter here.
|
|
1308
|
+
if (
|
|
1309
|
+
resumeFailureContext !== undefined
|
|
1310
|
+
&& !(error instanceof TurnDispatchedFailure)
|
|
1311
|
+
&& await presentLocatedResumeFailure(
|
|
1312
|
+
resumeFailureContext.home,
|
|
1313
|
+
resumeFailureContext.runId,
|
|
1314
|
+
resumeFailureContext.authority,
|
|
1315
|
+
io,
|
|
1316
|
+
error,
|
|
1317
|
+
)
|
|
1318
|
+
) {
|
|
1319
|
+
return {
|
|
1320
|
+
exitCode: error instanceof CliUsageError ? 2 : 1,
|
|
1321
|
+
...(error instanceof HostSelectionError ? { hostFailure: error.failure } : {}),
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1290
1324
|
if (error instanceof HostSelectionError) {
|
|
1291
1325
|
io.stderr(formatCliDiagnostic(formatHostSelectionFailure(error.failure)));
|
|
1292
1326
|
return { exitCode: 1, hostFailure: error.failure };
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type AdmittedCountersignInvocation,
|
|
34
34
|
type AdmittedRoleInvocation,
|
|
35
35
|
type PublicSeatParse,
|
|
36
|
+
type RunDirectoryRelocation,
|
|
36
37
|
} from "./invocation.ts";
|
|
37
38
|
import {
|
|
38
39
|
presentControlledFailure,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
runPostAdmissionOneShot,
|
|
42
43
|
runPostAdmissionResumable,
|
|
43
44
|
runPostAdmissionSeatResume,
|
|
45
|
+
showResumeErrorPointer,
|
|
44
46
|
resumeTurnRequestProjectionOptions,
|
|
45
47
|
type PostAdmissionAdapters,
|
|
46
48
|
type PostAdmissionEnv,
|
|
@@ -143,13 +145,13 @@ async function bindAndRelocateDiarist(
|
|
|
143
145
|
admitted: AdmittedRoleInvocation & { role: "diarist" },
|
|
144
146
|
authority: DurablePrincipalAuthority,
|
|
145
147
|
lease?: RunWriterLease,
|
|
146
|
-
): Promise<
|
|
148
|
+
): Promise<RunDirectoryRelocation | undefined> {
|
|
147
149
|
const boardTicket = await readBoardTicketNumber(admitted.runDirectory);
|
|
148
|
-
if (boardTicket === undefined) return;
|
|
150
|
+
if (boardTicket === undefined) return undefined;
|
|
149
151
|
if (admitted.ticketNumber === undefined) {
|
|
150
152
|
await bindAdmittedTicketNumber(admitted, boardTicket);
|
|
151
153
|
}
|
|
152
|
-
await relocateAdmittedRunToTicket(admitted, authority, lease);
|
|
154
|
+
return await relocateAdmittedRunToTicket(admitted, authority, lease);
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
function seatAdapters(
|
|
@@ -176,8 +178,8 @@ function seatAdapters(
|
|
|
176
178
|
await bindAndRelocateDiarist(seat, env.principalAuthority, lease);
|
|
177
179
|
},
|
|
178
180
|
afterDispatch: async (seat: AdmittedRoleInvocation, lease?: RunWriterLease) => {
|
|
179
|
-
if (!isBoardTicketSeat(seat)) return;
|
|
180
|
-
await bindAndRelocateDiarist(seat, env.principalAuthority, lease);
|
|
181
|
+
if (!isBoardTicketSeat(seat)) return undefined;
|
|
182
|
+
return await bindAndRelocateDiarist(seat, env.principalAuthority, lease);
|
|
181
183
|
},
|
|
182
184
|
}
|
|
183
185
|
: {}),
|
|
@@ -824,5 +826,7 @@ export async function continueParentAfterChild(
|
|
|
824
826
|
return { exitCode: 0, admitted, terminal: refresh.terminal };
|
|
825
827
|
}
|
|
826
828
|
}
|
|
827
|
-
|
|
829
|
+
const result = await dispatchAdmitted(admitted, env, { ...io, omitFailureStderrDiagnostic: true });
|
|
830
|
+
showResumeErrorPointer(io, result.exitCode, result.terminal);
|
|
831
|
+
return result;
|
|
828
832
|
}
|
|
@@ -308,6 +308,11 @@ export type AdmittedRoleInvocation =
|
|
|
308
308
|
| AdmittedReviewerInvocation
|
|
309
309
|
| AdmittedMergerInvocation;
|
|
310
310
|
|
|
311
|
+
export type RunDirectoryRelocation = {
|
|
312
|
+
readonly oldRunDirectory: string;
|
|
313
|
+
readonly newRunDirectory: string;
|
|
314
|
+
};
|
|
315
|
+
|
|
311
316
|
/** Persistence projection only — not carried on Admitted (opaque principal owns identity). */
|
|
312
317
|
type RoleInvocationLedgerSource = Pick<
|
|
313
318
|
AdmittedRoleInvocationBase,
|
|
@@ -641,7 +646,7 @@ export async function relocateAdmittedRunToTicket(
|
|
|
641
646
|
admitted: AdmittedRoleInvocation,
|
|
642
647
|
authority: DurablePrincipalAuthority,
|
|
643
648
|
heldLease?: { relocate(runDirectory: string): void },
|
|
644
|
-
): Promise<
|
|
649
|
+
): Promise<RunDirectoryRelocation | undefined> {
|
|
645
650
|
if (admitted.ticketNumber === undefined || !admitted.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) return undefined;
|
|
646
651
|
const oldRunDirectory = admitted.runDirectory;
|
|
647
652
|
const ledgerHome = resolveActivationLedgerHome(homeFromRunDirectory(oldRunDirectory));
|