@akagilnc/pi-workflow-roles 0.1.4321 → 0.1.4363
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 +81 -99
- package/dist/doctor-contracts.js +2 -2
- package/dist/headless-host/production-host.js +81 -99
- package/dist/merger-contracts.js +2 -2
- package/dist/migrate-book-topology.js +13 -8
- package/dist/notary-contracts.js +2 -2
- package/dist/package-contracts/auditor-output.js +2 -2
- package/dist/package-contracts/fixer-output.js +2 -2
- package/dist/package-contracts/gatekeeper-output.js +2 -2
- package/dist/package-contracts/navigator-output.js +2 -2
- package/dist/package-contracts/terminating-infrastructure.js +7 -2
- package/dist/public-cli/case-dossier-delivery.js +45 -28
- package/dist/public-cli/main.js +71 -89
- package/dist/public-cli/post-admission.js +41 -41
- package/dist/public-cli/settlement.js +3 -53
- package/dist/sitian-appender.js +21 -4
- package/package.json +1 -1
- package/src/collector-tool-schemas.ts +2 -2
- package/src/countersign-role.ts +2 -2
- package/src/diarist-role.ts +2 -2
- package/src/doctor-contracts.ts +2 -2
- package/src/gleaner-left-role.ts +2 -2
- package/src/inspector-role.ts +2 -2
- package/src/judge-role.ts +2 -2
- package/src/merger-contracts.ts +2 -2
- package/src/notary-contracts.ts +2 -2
- package/src/package-contracts/auditor-output.ts +2 -2
- package/src/package-contracts/fixer-output.ts +2 -2
- package/src/package-contracts/gatekeeper-output.ts +2 -2
- package/src/package-contracts/navigator-output.ts +2 -2
- package/src/package-contracts/terminating-infrastructure.ts +13 -3
- package/src/public-cli/case-dossier-delivery.ts +48 -29
- package/src/public-cli/post-admission.ts +42 -50
- package/src/public-cli/settlement.ts +3 -53
- package/src/reviewer-role.ts +2 -2
- package/src/sitian-appender.ts +36 -3
- package/src/worker-role.ts +2 -2
|
@@ -18,9 +18,11 @@ import {
|
|
|
18
18
|
import { CliUsageError } from "./cli-errors.ts";
|
|
19
19
|
import type { RoleTurnRequestProjectionOptions } from "./turn-request.ts";
|
|
20
20
|
import {
|
|
21
|
+
bindAdmittedTicketNumber,
|
|
21
22
|
buildInstructionTransportPrompt,
|
|
22
23
|
freezeAttachmentsIntoRun,
|
|
23
24
|
} from "./invocation.ts";
|
|
25
|
+
import { readRecordedSubmissionRows } from "../submission-ledger.ts";
|
|
24
26
|
import { pathContainedIn } from "../activation-ledger-topology.ts";
|
|
25
27
|
import { pickEngineAxis } from "../package-resources/engine-material.ts";
|
|
26
28
|
import { resolveHostAwareSessionAvailability } from "../session-identity.ts";
|
|
@@ -29,7 +31,6 @@ import type {
|
|
|
29
31
|
ControlledFailureCause,
|
|
30
32
|
DurablePrincipal,
|
|
31
33
|
DurablePrincipalAuthority,
|
|
32
|
-
RoleTurnContinuation,
|
|
33
34
|
RoleTurnHost,
|
|
34
35
|
RoleTurnKnownFailure,
|
|
35
36
|
RoleTurnRequest,
|
|
@@ -37,10 +38,7 @@ import type {
|
|
|
37
38
|
SessionCustomEntryAppender,
|
|
38
39
|
} from "../host-contracts.ts";
|
|
39
40
|
import { isOfficerReviewSeat } from "../host-contracts.ts";
|
|
40
|
-
import {
|
|
41
|
-
deliverCaseDossierAsAttachment,
|
|
42
|
-
projectCaseDossierPointerSection,
|
|
43
|
-
} from "./case-dossier-delivery.ts";
|
|
41
|
+
import { deliverCaseDossierAsAttachment } from "./case-dossier-delivery.ts";
|
|
44
42
|
|
|
45
43
|
/** Original error bytes, never relabeled — a secondary fact riding beside a classified cause. */
|
|
46
44
|
function describeCaughtError(error: unknown): { name?: string; message: string; code?: string | number } {
|
|
@@ -51,17 +49,6 @@ function describeCaughtError(error: unknown): { name?: string; message: string;
|
|
|
51
49
|
return { message: String(error) };
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
/** Append one system section to a continuation prompt, keeping its kind. */
|
|
55
|
-
function appendContinuationSection(
|
|
56
|
-
continuation: RoleTurnContinuation,
|
|
57
|
-
section: string,
|
|
58
|
-
): RoleTurnContinuation {
|
|
59
|
-
const prompt = `${continuation.prompt}\n\n${section}`;
|
|
60
|
-
return continuation.kind === "initial"
|
|
61
|
-
? { kind: "initial", prompt }
|
|
62
|
-
: { kind: "resume", prompt };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
52
|
/**
|
|
66
53
|
* Nested gate summons (station child) on an officer seat: dialogue content is
|
|
67
54
|
* peer words only (#879). ADR 0081 case dossier still hangs via the existing
|
|
@@ -592,10 +579,32 @@ export async function dispatchPostAdmissionTurn<
|
|
|
592
579
|
*/
|
|
593
580
|
let afterDispatchApplied = false;
|
|
594
581
|
const finishAfterTurn = async (result: DispatchOutcome): Promise<DispatchOutcome> => {
|
|
595
|
-
if (
|
|
582
|
+
if (afterDispatchApplied) return result;
|
|
596
583
|
afterDispatchApplied = true;
|
|
597
584
|
try {
|
|
598
|
-
|
|
585
|
+
// #858: an unbound seat may assert its ticket on the existing receipt.
|
|
586
|
+
// Read the original accepted payload; do not rewrite it, infer from prose,
|
|
587
|
+
// or reject missing/malformed declarations. An existing binding wins.
|
|
588
|
+
if (admitted.ticketNumber === undefined) {
|
|
589
|
+
const rows = await readRecordedSubmissionRows(
|
|
590
|
+
admitted.projectRoot,
|
|
591
|
+
admitted.runId,
|
|
592
|
+
{ home: homeFromRunDirectory(admitted.runDirectory), sessionParent: join(admitted.runDirectory, "session", "session.jsonl") },
|
|
593
|
+
);
|
|
594
|
+
const asserted = rows
|
|
595
|
+
.filter((row) => row.kind === "accepted" && row.role === admitted.role)
|
|
596
|
+
.map((row) => row.accepted)
|
|
597
|
+
.find((payload) => {
|
|
598
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
599
|
+
const value = (payload as { ticketNumber?: unknown }).ticketNumber;
|
|
600
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
601
|
+
});
|
|
602
|
+
const ticketNumber = asserted === undefined
|
|
603
|
+
? undefined
|
|
604
|
+
: (asserted as { ticketNumber: number }).ticketNumber;
|
|
605
|
+
if (ticketNumber !== undefined) await bindAdmittedTicketNumber(admitted, ticketNumber);
|
|
606
|
+
}
|
|
607
|
+
if (adapters.afterDispatch !== undefined) await adapters.afterDispatch(admitted, lease);
|
|
599
608
|
return result;
|
|
600
609
|
} catch (error) {
|
|
601
610
|
const primaryFailure =
|
|
@@ -728,10 +737,11 @@ export async function dispatchPostAdmissionTurn<
|
|
|
728
737
|
|
|
729
738
|
// Turn request is assembled after beforeDispatch so this turn sees whatever it
|
|
730
739
|
// settled — the seat's ticket bind re-projection and any court diarist station
|
|
731
|
-
// writes (#742). Case dossier delivery (ADR 0081 / #709) rides here once
|
|
732
|
-
// every public entry
|
|
733
|
-
//
|
|
734
|
-
//
|
|
740
|
+
// writes (#742). Case dossier delivery (ADR 0081 / #709 / #858) rides here once
|
|
741
|
+
// for every public entry on the existing attachments → readingMaterial face
|
|
742
|
+
// (station-child and ordinary share one seam). Dialogue continuation stays
|
|
743
|
+
// caller/peer opaque — never splice system path sections into user dialogue.
|
|
744
|
+
// No package-resume parallel face or typed resume identity.
|
|
735
745
|
let turnRequest: RoleTurnRequest =
|
|
736
746
|
env.signal === undefined ? request : { ...request, signal: env.signal };
|
|
737
747
|
if (env.stationChild !== undefined) {
|
|
@@ -745,34 +755,16 @@ export async function dispatchPostAdmissionTurn<
|
|
|
745
755
|
if (typeof liveHost === "string" && liveHost.trim() !== "") {
|
|
746
756
|
turnRequest = { ...turnRequest, host: liveHost.trim() };
|
|
747
757
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
runDirectory: admitted.runDirectory,
|
|
759
|
-
});
|
|
760
|
-
} else {
|
|
761
|
-
const dossierSection = await projectCaseDossierPointerSection({
|
|
762
|
-
ticketNumber: admitted.ticketNumber,
|
|
763
|
-
projectRoot: admitted.projectRoot,
|
|
764
|
-
home: env.home,
|
|
765
|
-
});
|
|
766
|
-
if (dossierSection !== undefined) {
|
|
767
|
-
turnRequest = {
|
|
768
|
-
...turnRequest,
|
|
769
|
-
continuation: appendContinuationSection(
|
|
770
|
-
turnRequest.continuation,
|
|
771
|
-
dossierSection,
|
|
772
|
-
),
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
}
|
|
758
|
+
// 0081 non-dialogue face: freeze pointer section under run/attachments/.
|
|
759
|
+
// Seat consumes via loadCaseDossierReadingMaterial → existing agent-start
|
|
760
|
+
// readingMaterial / systemPrompt.materials fold. Caller instruction, empty
|
|
761
|
+
// request, and resume --message stay verbatim.
|
|
762
|
+
await deliverCaseDossierAsAttachment({
|
|
763
|
+
ticketNumber: admitted.ticketNumber,
|
|
764
|
+
projectRoot: admitted.projectRoot,
|
|
765
|
+
home: env.home,
|
|
766
|
+
runDirectory: admitted.runDirectory,
|
|
767
|
+
});
|
|
776
768
|
|
|
777
769
|
// Authoritative host write happens here, at the real dispatch boundary —
|
|
778
770
|
// immediately before the turn actually starts, after every retryable
|
|
@@ -1086,38 +1086,7 @@ async function loadBoundAuditorVolumes(
|
|
|
1086
1086
|
}
|
|
1087
1087
|
const parentId = parentEntries.find((entry) => entry.type === "session")?.id;
|
|
1088
1088
|
if (parentId === undefined) return undefined;
|
|
1089
|
-
const isResumeEnvelopeBytes = (value: unknown): boolean => {
|
|
1090
|
-
if (typeof value !== "string") return false;
|
|
1091
|
-
if (value.length === 0) return true;
|
|
1092
|
-
const nl = value.indexOf("\n");
|
|
1093
|
-
const firstLine = nl === -1 ? value : value.slice(0, nl);
|
|
1094
|
-
const body = firstLine === "" && nl !== -1 ? value.slice(nl + 1) : value;
|
|
1095
|
-
return body.startsWith("本次配置的劳务引擎及其手册:") || body.startsWith("- engine:");
|
|
1096
|
-
};
|
|
1097
|
-
const isResumeEnvelope = (msg: unknown): boolean => {
|
|
1098
|
-
if (!isRecord(msg) || msg.role !== "user") return false;
|
|
1099
|
-
const text = typeof msg.text === "string" ? msg.text : typeof (msg as { content?: unknown }).content === "string" ? (msg as { content: string }).content : undefined;
|
|
1100
|
-
if (isResumeEnvelopeBytes(text)) return true;
|
|
1101
|
-
const content = (msg as { content?: unknown }).content;
|
|
1102
|
-
if (Array.isArray(content)) {
|
|
1103
|
-
return content.some((p) => isRecord(p) && (isResumeEnvelopeBytes(p.text) || isResumeEnvelopeBytes(p.content)));
|
|
1104
|
-
}
|
|
1105
|
-
return false;
|
|
1106
|
-
};
|
|
1107
|
-
let latestParentUserIndex = -1;
|
|
1108
|
-
for (let i = parentEntries.length - 1; i >= 0; i -= 1) {
|
|
1109
|
-
const entry = parentEntries[i];
|
|
1110
|
-
if (entry?.type !== "message" || entry.message?.role !== "user") continue;
|
|
1111
|
-
if (isResumeEnvelope(entry.message)) continue;
|
|
1112
|
-
latestParentUserIndex = i;
|
|
1113
|
-
break;
|
|
1114
|
-
}
|
|
1115
1089
|
const childDirectories = [join(dirname(sessionFile), "auditor-roles")];
|
|
1116
|
-
// Auto-resume seam (owner A): stale check must ignore resume envelope and
|
|
1117
|
-
// prioritize retention. Previous `attemptEntryIndex < latest` discarded the
|
|
1118
|
-
// first attempt's child after resume advanced latest, losing retentionFailure
|
|
1119
|
-
// when retry had no compliance entry. Fix: ignore envelope for staleness and
|
|
1120
|
-
// prefer any valid compliance failure before falling back to primary.
|
|
1121
1090
|
const valid: BoundAuditorVolume[] = [];
|
|
1122
1091
|
let sawAnyDirectory = false;
|
|
1123
1092
|
for (const childDirectory of childDirectories) {
|
|
@@ -1166,10 +1135,6 @@ async function loadBoundAuditorVolumes(
|
|
|
1166
1135
|
typeof bindingParent?.attemptEntryId === "string"
|
|
1167
1136
|
? bindingParent.attemptEntryId
|
|
1168
1137
|
: undefined;
|
|
1169
|
-
const attemptEntryIndex =
|
|
1170
|
-
attemptEntryId === undefined
|
|
1171
|
-
? -1
|
|
1172
|
-
: parentEntries.findIndex((entry) => entry.id === attemptEntryId);
|
|
1173
1138
|
const boundSessionFile =
|
|
1174
1139
|
typeof bindingParent?.sessionFile === "string"
|
|
1175
1140
|
? bindingParent.sessionFile
|
|
@@ -1177,12 +1142,7 @@ async function loadBoundAuditorVolumes(
|
|
|
1177
1142
|
? header.parentSession
|
|
1178
1143
|
: undefined;
|
|
1179
1144
|
if (boundSessionFile !== sessionFile) continue;
|
|
1180
|
-
if (
|
|
1181
|
-
bindingParent !== undefined &&
|
|
1182
|
-
(bindingParent.sessionId !== parentId || attemptEntryIndex < latestParentUserIndex)
|
|
1183
|
-
) {
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1145
|
+
if (bindingParent !== undefined && bindingParent.sessionId !== parentId) continue;
|
|
1186
1146
|
if (bindingParent === undefined && header.parentSession !== sessionFile) continue;
|
|
1187
1147
|
valid.push({
|
|
1188
1148
|
entries: entries.slice(start, end),
|
|
@@ -1190,8 +1150,8 @@ async function loadBoundAuditorVolumes(
|
|
|
1190
1150
|
sessionFile,
|
|
1191
1151
|
...(attemptEntryId === undefined ? {} : { attemptEntryId }),
|
|
1192
1152
|
});
|
|
1193
|
-
// Keep every
|
|
1194
|
-
//
|
|
1153
|
+
// Keep every interval bound to this parent. Auditor payload is relayed as
|
|
1154
|
+
// recorded; code does not expire it from later user-message shape (#858).
|
|
1195
1155
|
}
|
|
1196
1156
|
}
|
|
1197
1157
|
}
|
|
@@ -1254,16 +1214,6 @@ function providerStopFallbackFromAuditorVolumes(
|
|
|
1254
1214
|
return undefined;
|
|
1255
1215
|
}
|
|
1256
1216
|
|
|
1257
|
-
/** Recover a provider stop from the auditor child bound to the current parent attempt. */
|
|
1258
|
-
export async function readBoundAuditorKnownFailure(
|
|
1259
|
-
sessionFile: string,
|
|
1260
|
-
): Promise<RoleTurnKnownFailure | undefined> {
|
|
1261
|
-
const volumes = await loadBoundAuditorVolumes(sessionFile);
|
|
1262
|
-
if (volumes === undefined) return undefined;
|
|
1263
|
-
return complianceFailureFromAuditorVolumes(volumes)
|
|
1264
|
-
?? providerStopFallbackFromAuditorVolumes(volumes);
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1217
|
/** Strong auditor tier only — retained compliance-failure entries, no provider-stop fallback. */
|
|
1268
1218
|
async function readBoundAuditorComplianceFailure(
|
|
1269
1219
|
sessionFile: string,
|
package/src/reviewer-role.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
|
|
6
6
|
import type { AnyCanonicalSkillBinding, CanonicalSkillBinding } from "./canonical-skill-binding.ts";
|
|
7
7
|
export type { CanonicalSkillBinding };
|
|
@@ -43,7 +43,7 @@ const reviewerOutputVariants = Type.Union([
|
|
|
43
43
|
amendments: Type.Optional(reviewerAmendmentsSchema),
|
|
44
44
|
}, { additionalProperties: false }),
|
|
45
45
|
]);
|
|
46
|
-
export const reviewerOutputSchema =
|
|
46
|
+
export const reviewerOutputSchema = withTerminatingOutputDeclarations(
|
|
47
47
|
openToolObjectFromUnion(reviewerOutputVariants),
|
|
48
48
|
);
|
|
49
49
|
export type ReviewerRoleDependencies = {
|
package/src/sitian-appender.ts
CHANGED
|
@@ -210,6 +210,22 @@ type SitianRecordPath = {
|
|
|
210
210
|
readonly ledgerHome: string;
|
|
211
211
|
};
|
|
212
212
|
|
|
213
|
+
/** Sole records leaf under every sitian volume directory. */
|
|
214
|
+
const SITIAN_RECORDS_LEAF = "records.jsonl" as const;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Ticket-provenance under-book paths (docs/dossier-topology.md sole authority).
|
|
218
|
+
* Writer ticket branch and owner-visible shape share this join — one code path.
|
|
219
|
+
*/
|
|
220
|
+
function ticketProvenanceUnderBookPaths(
|
|
221
|
+
ledgerHome: string,
|
|
222
|
+
bookKey: string,
|
|
223
|
+
ticketId: string,
|
|
224
|
+
): { sessionDir: string; recordFile: string } {
|
|
225
|
+
const sessionDir = join(activationBookDirectory(ledgerHome, bookKey), ticketId);
|
|
226
|
+
return { sessionDir, recordFile: join(sessionDir, SITIAN_RECORDS_LEAF) };
|
|
227
|
+
}
|
|
228
|
+
|
|
213
229
|
/** Pure topology owner shared by ambient writes and explicit-home submission reads. */
|
|
214
230
|
export function resolveSitianRecordPathInLedger(
|
|
215
231
|
input: SitianRecordInput,
|
|
@@ -228,13 +244,16 @@ export function resolveSitianRecordPathInLedger(
|
|
|
228
244
|
: undefined;
|
|
229
245
|
|
|
230
246
|
let sessionDir: string;
|
|
247
|
+
let recordFile: string;
|
|
231
248
|
if (ticketNumber !== undefined) {
|
|
232
249
|
// docs/dossier-topology.md: ticket dir holds the unique records.jsonl directly (#900).
|
|
233
|
-
const
|
|
250
|
+
const paths = ticketProvenanceUnderBookPaths(
|
|
234
251
|
ledgerHome,
|
|
235
252
|
resolveBookKeyFromGit(input.cwd ?? process.cwd()),
|
|
253
|
+
ticketNumber,
|
|
236
254
|
);
|
|
237
|
-
sessionDir =
|
|
255
|
+
sessionDir = paths.sessionDir;
|
|
256
|
+
recordFile = paths.recordFile;
|
|
238
257
|
} else {
|
|
239
258
|
if (
|
|
240
259
|
input.sessionParent === undefined
|
|
@@ -244,12 +263,26 @@ export function resolveSitianRecordPathInLedger(
|
|
|
244
263
|
throw new Error("Sitian record ownership requires a parent session inside the ledger home");
|
|
245
264
|
}
|
|
246
265
|
sessionDir = join(dirname(input.sessionParent), category);
|
|
266
|
+
recordFile = join(sessionDir, SITIAN_RECORDS_LEAF);
|
|
247
267
|
}
|
|
248
268
|
|
|
249
|
-
const recordFile = join(sessionDir, "records.jsonl");
|
|
250
269
|
return { sessionDir, recordFile, ledgerHome };
|
|
251
270
|
}
|
|
252
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Owner-visible ticket 起居录 path shape via the writer ticket joins
|
|
274
|
+
* (ticketProvenanceUnderBookPaths). Ledger leaf is the package-owned `.ak-roles`
|
|
275
|
+
* name (ADR 0048); variable slots keep owner labels; no fake-home path reverse.
|
|
276
|
+
*/
|
|
277
|
+
export function projectTicketRecordsPathShape(): string {
|
|
278
|
+
const { recordFile } = ticketProvenanceUnderBookPaths(
|
|
279
|
+
join("~", ".ak-roles"),
|
|
280
|
+
"<簿>",
|
|
281
|
+
"<票号>",
|
|
282
|
+
);
|
|
283
|
+
return recordFile.replace(/\\/g, "/");
|
|
284
|
+
}
|
|
285
|
+
|
|
253
286
|
/** Compute a write destination from ambient ledger topology (ADR 0065). */
|
|
254
287
|
export function resolveSitianRecordPath(input: SitianRecordInput): SitianRecordPath {
|
|
255
288
|
const ledgerHome =
|
package/src/worker-role.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult, HostGatekeeperActions } from "./host-contracts.ts";
|
|
2
2
|
import { Type, type Static } from "typebox";
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
import { CorrectableSubmissionError } from "./submission-correctable-error.ts";
|
|
6
6
|
|
|
7
7
|
import type {
|
|
@@ -73,7 +73,7 @@ const coderOutputVariants = Type.Union([
|
|
|
73
73
|
})),
|
|
74
74
|
}, { additionalProperties: false }),
|
|
75
75
|
]);
|
|
76
|
-
export const coderOutputSchema =
|
|
76
|
+
export const coderOutputSchema = withTerminatingOutputDeclarations(
|
|
77
77
|
openToolObjectFromUnion(coderOutputVariants),
|
|
78
78
|
);
|
|
79
79
|
export type { FixerOutput, CoderOutput };
|