@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
|
@@ -672,44 +672,7 @@ async function loadBoundAuditorVolumes(sessionFile) {
|
|
|
672
672
|
const parentId = parentEntries.find((entry) => entry.type === "session")?.id;
|
|
673
673
|
if (parentId === undefined)
|
|
674
674
|
return undefined;
|
|
675
|
-
const isResumeEnvelopeBytes = (value) => {
|
|
676
|
-
if (typeof value !== "string")
|
|
677
|
-
return false;
|
|
678
|
-
if (value.length === 0)
|
|
679
|
-
return true;
|
|
680
|
-
const nl = value.indexOf("\n");
|
|
681
|
-
const firstLine = nl === -1 ? value : value.slice(0, nl);
|
|
682
|
-
const body = firstLine === "" && nl !== -1 ? value.slice(nl + 1) : value;
|
|
683
|
-
return body.startsWith("本次配置的劳务引擎及其手册:") || body.startsWith("- engine:");
|
|
684
|
-
};
|
|
685
|
-
const isResumeEnvelope = (msg) => {
|
|
686
|
-
if (!isRecord(msg) || msg.role !== "user")
|
|
687
|
-
return false;
|
|
688
|
-
const text = typeof msg.text === "string" ? msg.text : typeof msg.content === "string" ? msg.content : undefined;
|
|
689
|
-
if (isResumeEnvelopeBytes(text))
|
|
690
|
-
return true;
|
|
691
|
-
const content = msg.content;
|
|
692
|
-
if (Array.isArray(content)) {
|
|
693
|
-
return content.some((p) => isRecord(p) && (isResumeEnvelopeBytes(p.text) || isResumeEnvelopeBytes(p.content)));
|
|
694
|
-
}
|
|
695
|
-
return false;
|
|
696
|
-
};
|
|
697
|
-
let latestParentUserIndex = -1;
|
|
698
|
-
for (let i = parentEntries.length - 1; i >= 0; i -= 1) {
|
|
699
|
-
const entry = parentEntries[i];
|
|
700
|
-
if (entry?.type !== "message" || entry.message?.role !== "user")
|
|
701
|
-
continue;
|
|
702
|
-
if (isResumeEnvelope(entry.message))
|
|
703
|
-
continue;
|
|
704
|
-
latestParentUserIndex = i;
|
|
705
|
-
break;
|
|
706
|
-
}
|
|
707
675
|
const childDirectories = [join(dirname(sessionFile), "auditor-roles")];
|
|
708
|
-
// Auto-resume seam (owner A): stale check must ignore resume envelope and
|
|
709
|
-
// prioritize retention. Previous `attemptEntryIndex < latest` discarded the
|
|
710
|
-
// first attempt's child after resume advanced latest, losing retentionFailure
|
|
711
|
-
// when retry had no compliance entry. Fix: ignore envelope for staleness and
|
|
712
|
-
// prefer any valid compliance failure before falling back to primary.
|
|
713
676
|
const valid = [];
|
|
714
677
|
let sawAnyDirectory = false;
|
|
715
678
|
for (const childDirectory of childDirectories) {
|
|
@@ -759,9 +722,6 @@ async function loadBoundAuditorVolumes(sessionFile) {
|
|
|
759
722
|
const attemptEntryId = typeof bindingParent?.attemptEntryId === "string"
|
|
760
723
|
? bindingParent.attemptEntryId
|
|
761
724
|
: undefined;
|
|
762
|
-
const attemptEntryIndex = attemptEntryId === undefined
|
|
763
|
-
? -1
|
|
764
|
-
: parentEntries.findIndex((entry) => entry.id === attemptEntryId);
|
|
765
725
|
const boundSessionFile = typeof bindingParent?.sessionFile === "string"
|
|
766
726
|
? bindingParent.sessionFile
|
|
767
727
|
: typeof header.parentSession === "string"
|
|
@@ -769,10 +729,8 @@ async function loadBoundAuditorVolumes(sessionFile) {
|
|
|
769
729
|
: undefined;
|
|
770
730
|
if (boundSessionFile !== sessionFile)
|
|
771
731
|
continue;
|
|
772
|
-
if (bindingParent !== undefined &&
|
|
773
|
-
(bindingParent.sessionId !== parentId || attemptEntryIndex < latestParentUserIndex)) {
|
|
732
|
+
if (bindingParent !== undefined && bindingParent.sessionId !== parentId)
|
|
774
733
|
continue;
|
|
775
|
-
}
|
|
776
734
|
if (bindingParent === undefined && header.parentSession !== sessionFile)
|
|
777
735
|
continue;
|
|
778
736
|
valid.push({
|
|
@@ -781,8 +739,8 @@ async function loadBoundAuditorVolumes(sessionFile) {
|
|
|
781
739
|
sessionFile,
|
|
782
740
|
...(attemptEntryId === undefined ? {} : { attemptEntryId }),
|
|
783
741
|
});
|
|
784
|
-
// Keep every
|
|
785
|
-
//
|
|
742
|
+
// Keep every interval bound to this parent. Auditor payload is relayed as
|
|
743
|
+
// recorded; code does not expire it from later user-message shape (#858).
|
|
786
744
|
}
|
|
787
745
|
}
|
|
788
746
|
}
|
|
@@ -843,14 +801,6 @@ function providerStopFallbackFromAuditorVolumes(volumes) {
|
|
|
843
801
|
}
|
|
844
802
|
return undefined;
|
|
845
803
|
}
|
|
846
|
-
/** Recover a provider stop from the auditor child bound to the current parent attempt. */
|
|
847
|
-
export async function readBoundAuditorKnownFailure(sessionFile) {
|
|
848
|
-
const volumes = await loadBoundAuditorVolumes(sessionFile);
|
|
849
|
-
if (volumes === undefined)
|
|
850
|
-
return undefined;
|
|
851
|
-
return complianceFailureFromAuditorVolumes(volumes)
|
|
852
|
-
?? providerStopFallbackFromAuditorVolumes(volumes);
|
|
853
|
-
}
|
|
854
804
|
/** Strong auditor tier only — retained compliance-failure entries, no provider-stop fallback. */
|
|
855
805
|
async function readBoundAuditorComplianceFailure(sessionFile) {
|
|
856
806
|
const volumes = await loadBoundAuditorVolumes(sessionFile);
|
package/dist/sitian-appender.js
CHANGED
|
@@ -152,25 +152,41 @@ function resolveSitianVolumeCategory(kind) {
|
|
|
152
152
|
}
|
|
153
153
|
return kind;
|
|
154
154
|
}
|
|
155
|
+
const SITIAN_RECORDS_LEAF = "records.jsonl";
|
|
156
|
+
function ticketProvenanceUnderBookPaths(ledgerHome, bookKey, ticketId) {
|
|
157
|
+
const sessionDir = join(activationBookDirectory(ledgerHome, bookKey), ticketId);
|
|
158
|
+
return { sessionDir, recordFile: join(sessionDir, SITIAN_RECORDS_LEAF) };
|
|
159
|
+
}
|
|
155
160
|
function resolveSitianRecordPathInLedger(input, ledgerHome) {
|
|
156
161
|
const category = resolveSitianVolumeCategory(input.kind);
|
|
157
162
|
const ticketNumber = category === "ticket-provenance" ? typeof input.subject === "string" && /^[1-9][0-9]*$/.test(input.subject) ? input.subject : typeof input.subject === "object" && typeof input.subject.ticketNumber === "number" && Number.isSafeInteger(input.subject.ticketNumber) && input.subject.ticketNumber > 0 ? String(input.subject.ticketNumber) : void 0 : void 0;
|
|
158
163
|
let sessionDir;
|
|
164
|
+
let recordFile;
|
|
159
165
|
if (ticketNumber !== void 0) {
|
|
160
|
-
const
|
|
166
|
+
const paths = ticketProvenanceUnderBookPaths(
|
|
161
167
|
ledgerHome,
|
|
162
|
-
resolveBookKeyFromGit(input.cwd ?? process.cwd())
|
|
168
|
+
resolveBookKeyFromGit(input.cwd ?? process.cwd()),
|
|
169
|
+
ticketNumber
|
|
163
170
|
);
|
|
164
|
-
sessionDir =
|
|
171
|
+
sessionDir = paths.sessionDir;
|
|
172
|
+
recordFile = paths.recordFile;
|
|
165
173
|
} else {
|
|
166
174
|
if (input.sessionParent === void 0 || input.sessionParent.length === 0 || !physicallyContainedIn(ledgerHome, input.sessionParent)) {
|
|
167
175
|
throw new Error("Sitian record ownership requires a parent session inside the ledger home");
|
|
168
176
|
}
|
|
169
177
|
sessionDir = join(dirname(input.sessionParent), category);
|
|
178
|
+
recordFile = join(sessionDir, SITIAN_RECORDS_LEAF);
|
|
170
179
|
}
|
|
171
|
-
const recordFile = join(sessionDir, "records.jsonl");
|
|
172
180
|
return { sessionDir, recordFile, ledgerHome };
|
|
173
181
|
}
|
|
182
|
+
function projectTicketRecordsPathShape() {
|
|
183
|
+
const { recordFile } = ticketProvenanceUnderBookPaths(
|
|
184
|
+
join("~", ".ak-roles"),
|
|
185
|
+
"<\u7C3F>",
|
|
186
|
+
"<\u7968\u53F7>"
|
|
187
|
+
);
|
|
188
|
+
return recordFile.replace(/\\/g, "/");
|
|
189
|
+
}
|
|
174
190
|
function resolveSitianRecordPath(input) {
|
|
175
191
|
const ledgerHome = input.home !== void 0 && input.home.length > 0 ? resolveActivationLedgerHome(input.home) : resolveActivationLedgerHomeForPath(input.sessionParent);
|
|
176
192
|
return resolveSitianRecordPathInLedger(input, ledgerHome);
|
|
@@ -210,6 +226,7 @@ function appendSitianRecord(input) {
|
|
|
210
226
|
export {
|
|
211
227
|
S4_SUBMISSION_LEDGER_KINDS,
|
|
212
228
|
appendSitianRecord,
|
|
229
|
+
projectTicketRecordsPathShape,
|
|
213
230
|
resolveSitianRecordPath,
|
|
214
231
|
resolveSitianRecordPathInLedger,
|
|
215
232
|
resolveSitianVolumeCategory
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
3
|
-
import {
|
|
3
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
4
4
|
|
|
5
5
|
// #836 r16 class 3: execute() reads no params for this tool
|
|
6
6
|
// (src/collector-role.ts:431-459) — a closed root only rejects the role for
|
|
@@ -124,7 +124,7 @@ export const collectorOutputBaseSchema = openToolObject(
|
|
|
124
124
|
);
|
|
125
125
|
|
|
126
126
|
/** Runtime owns the observed evidence; the model submits findings and signals sole-final submission. */
|
|
127
|
-
export const collectorOutputArgsSchema =
|
|
127
|
+
export const collectorOutputArgsSchema = withTerminatingOutputDeclarations(
|
|
128
128
|
collectorOutputBaseSchema,
|
|
129
129
|
);
|
|
130
130
|
|
package/src/countersign-role.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Static } from "typebox";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
import {
|
|
6
6
|
COUNTERSIGN_OUTPUT_TOOL_NAME,
|
|
7
7
|
validateRecordedCountersignOutput,
|
|
@@ -22,7 +22,7 @@ export type { CountersignVerdict };
|
|
|
22
22
|
// classifies the recorded value and reasks the countersign itself when it
|
|
23
23
|
// isn't one of the three states — code, not the transport, does that work.
|
|
24
24
|
/** 给事中票庭审读五问的交卷形状(ADR 0074);形状指引,非 schema 闸。 */
|
|
25
|
-
export const countersignVerdictSchema =
|
|
25
|
+
export const countersignVerdictSchema = withTerminatingOutputDeclarations(
|
|
26
26
|
Type.Object(
|
|
27
27
|
{
|
|
28
28
|
countersignStatus: Type.Unknown({ description: "converged | continue | escalate。非三态时请重读后重交,勿改标。" }),
|
package/src/diarist-role.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Static } from "typebox";
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
4
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
6
6
|
import {
|
|
7
7
|
DIARIST_OUTPUT_TOOL_NAME,
|
|
8
8
|
validateRecordedDiaristOutput,
|
|
@@ -20,7 +20,7 @@ export { validateRecordedDiaristOutput };
|
|
|
20
20
|
* 起居郎交卷形状;形状指引,非 schema 闸。
|
|
21
21
|
* #901:交边界(sessions)+ 可选坏行补写(amendments);正文由机械投影。
|
|
22
22
|
*/
|
|
23
|
-
export const diaristOutputSchema =
|
|
23
|
+
export const diaristOutputSchema = withTerminatingOutputDeclarations(
|
|
24
24
|
openToolObject(
|
|
25
25
|
Type.Object({
|
|
26
26
|
status: Type.Unknown({
|
package/src/doctor-contracts.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { canonicalJson } from "./canonical-json.ts";
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
|
|
6
6
|
export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
|
|
7
7
|
export const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
|
|
@@ -87,7 +87,7 @@ const doctorSubmissionVariants = Type.Union([
|
|
|
87
87
|
missingEvidence: Type.Array(Type.Object({ need: nonblank, targetKeys: evidenceIds }, { additionalProperties: true }), { description: "如实证词所需而尚缺的证据" }),
|
|
88
88
|
}, { additionalProperties: false, description: "证据不足以支撑如实案证词" }),
|
|
89
89
|
]);
|
|
90
|
-
export const doctorSubmissionSchema =
|
|
90
|
+
export const doctorSubmissionSchema = withTerminatingOutputDeclarations(
|
|
91
91
|
openToolObjectFromUnion(doctorSubmissionVariants),
|
|
92
92
|
);
|
|
93
93
|
// #836 r16 class 3: action tool — code reads evidenceId to look up the Map entry
|
package/src/gleaner-left-role.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Static } from "typebox";
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
4
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
6
6
|
import {
|
|
7
7
|
GLEANER_LEFT_OUTPUT_TOOL_NAME,
|
|
8
8
|
validateRecordedGleanerLeftOutput,
|
|
@@ -19,7 +19,7 @@ export { validateRecordedGleanerLeftOutput };
|
|
|
19
19
|
// #836 r16 class 1: pointer/statement are LLM/human-read narrative content — no
|
|
20
20
|
// code branches on their presence.
|
|
21
21
|
/** 左拾遗弹章交卷形状;形状指引,非 schema 闸。 */
|
|
22
|
-
export const gleanerLeftOutputSchema =
|
|
22
|
+
export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
|
|
23
23
|
openToolObject(
|
|
24
24
|
Type.Object({
|
|
25
25
|
status: Type.Unknown({
|
package/src/inspector-role.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Static } from "typebox";
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
4
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
6
6
|
import {
|
|
7
7
|
INSPECTOR_ACCEPTED_TEXT,
|
|
8
8
|
INSPECTOR_OUTPUT_TOOL_NAME,
|
|
@@ -19,7 +19,7 @@ export type { InspectorOutput };
|
|
|
19
19
|
export { validateRecordedInspectorOutput };
|
|
20
20
|
|
|
21
21
|
/** 台院事后察举交卷形状;形状指引,非 schema 闸。 */
|
|
22
|
-
export const inspectorOutputSchema =
|
|
22
|
+
export const inspectorOutputSchema = withTerminatingOutputDeclarations(
|
|
23
23
|
openToolObject(
|
|
24
24
|
Type.Object({
|
|
25
25
|
status: Type.Unknown({
|
package/src/judge-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
|
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
import { ParentQueueReaskError } from "./submission-errors.ts";
|
|
6
6
|
|
|
7
7
|
import {
|
|
@@ -25,7 +25,7 @@ export type { JudgeVerdict };
|
|
|
25
25
|
// #836 (ADR 0003 Amendment): judgeStatus kept open like countersignStatus
|
|
26
26
|
// (src/countersign-role.ts) — a closed domain here would reject an unknown
|
|
27
27
|
// value before the rawStatus/ParentQueueReaskError check below ever runs.
|
|
28
|
-
export const judgeVerdictSchema =
|
|
28
|
+
export const judgeVerdictSchema = withTerminatingOutputDeclarations(
|
|
29
29
|
Type.Object(
|
|
30
30
|
{
|
|
31
31
|
judgeStatus: Type.Unknown({ description: "converged | continue | escalate — 形状指引,非 schema 闸" }),
|
package/src/merger-contracts.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import { exactUtf8 } from "./exact-utf8.ts";
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
5
|
|
|
6
6
|
const materialSchema = Type.Object({ bytesBase64: Type.String(), sha256: Type.String() }, { additionalProperties: false });
|
|
7
7
|
const checkSchema = Type.Object({ name: Type.String({ minLength: 1 }), argv: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }) }, { additionalProperties: false });
|
|
@@ -25,7 +25,7 @@ const mergerOutputVariants = Type.Union([
|
|
|
25
25
|
Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), report: Type.String({ description: "如实结果报告" }), mergeCommitId: Type.String({ description: "完成合并 commit object ID" }) }, { additionalProperties: false }),
|
|
26
26
|
Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), diagnosis: Type.String({ description: "合并无法或不应由本席完成的原因(含无进行中合并、无活可干、需新的产品/权力决定)" }), report: Type.String({ description: "如实结果报告" }) }, { additionalProperties: false }),
|
|
27
27
|
]);
|
|
28
|
-
export const mergerOutputSchema =
|
|
28
|
+
export const mergerOutputSchema = withTerminatingOutputDeclarations(openToolObjectFromUnion(mergerOutputVariants));
|
|
29
29
|
|
|
30
30
|
export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer U)[] ? readonly DeepReadonly<U>[] : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T;
|
|
31
31
|
export type MergerMaterial = DeepReadonly<Static<typeof materialSchema>>;
|
package/src/notary-contracts.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Type } from "typebox";
|
|
7
7
|
|
|
8
8
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
10
10
|
|
|
11
11
|
export const NOTARY_OUTPUT_TOOL_NAME = "ak_notary_output";
|
|
12
12
|
export const NOTARY_ACCEPTED_TEXT = "符宝郎回执已接受";
|
|
@@ -31,7 +31,7 @@ export const NOTARY_TICKET_FLAG = {
|
|
|
31
31
|
export const NOTARY_FIXED_KICKOFF =
|
|
32
32
|
"符宝郎案卷已受理;来源 run 定位见会话材料。";
|
|
33
33
|
|
|
34
|
-
export const notaryOutputSchema =
|
|
34
|
+
export const notaryOutputSchema = withTerminatingOutputDeclarations(
|
|
35
35
|
openToolObject(
|
|
36
36
|
Type.Object({
|
|
37
37
|
status: Type.Unknown({
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
|
|
7
7
|
import { openToolObject } from "../open-tool-schema.ts";
|
|
8
|
-
import {
|
|
8
|
+
import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
|
|
9
9
|
|
|
10
10
|
export const AUDITOR_OUTPUT_TOOL_NAME = "ak_auditor_output" as const;
|
|
11
11
|
export const AUDITOR_ACCEPTED_TEXT = "审刑院回执已接受";
|
|
12
12
|
|
|
13
|
-
export const auditorOutputSchema =
|
|
13
|
+
export const auditorOutputSchema = withTerminatingOutputDeclarations(
|
|
14
14
|
openToolObject(
|
|
15
15
|
Type.Object({
|
|
16
16
|
status: Type.Unknown({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import { openToolObjectFromUnion } from "../open-tool-schema.ts";
|
|
3
|
-
import {
|
|
3
|
+
import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
|
|
4
4
|
|
|
5
5
|
export const FIXER_OUTPUT_TOOL_NAME = "ak_fixer_output";
|
|
6
6
|
export const FIXER_ACCEPTED_TEXT = "修内司回执已接受";
|
|
@@ -51,7 +51,7 @@ const fixerOutputVariants = Type.Union([
|
|
|
51
51
|
Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { description: "各类拒绝结算" }) }),
|
|
52
52
|
Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { description: "各类完成或拒绝结算" }), testEvidence: Type.Optional(testEvidenceSchema) }),
|
|
53
53
|
]);
|
|
54
|
-
export const fixerOutputSchema =
|
|
54
|
+
export const fixerOutputSchema = withTerminatingOutputDeclarations(
|
|
55
55
|
openToolObjectFromUnion(fixerOutputVariants),
|
|
56
56
|
);
|
|
57
57
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Type } from "typebox";
|
|
7
7
|
|
|
8
8
|
import { openToolObject } from "../open-tool-schema.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
|
|
10
10
|
|
|
11
11
|
export const GATEKEEPER_OUTPUT_TOOL_NAME = "ak_gatekeeper_output";
|
|
12
12
|
export const GATEKEEPER_ACCEPTED_TEXT = "门下省决议已受理";
|
|
@@ -26,7 +26,7 @@ export const gatekeeperDecisionSchema = openToolObject(
|
|
|
26
26
|
}),
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
export const gatekeeperOutputSchema =
|
|
29
|
+
export const gatekeeperOutputSchema = withTerminatingOutputDeclarations(
|
|
30
30
|
gatekeeperDecisionSchema,
|
|
31
31
|
);
|
|
32
32
|
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
import { Type } from "typebox";
|
|
8
8
|
|
|
9
9
|
import { openToolObject } from "../open-tool-schema.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
|
|
11
11
|
|
|
12
12
|
export const NAVIGATOR_OUTPUT_TOOL_NAME = "ak_navigator_output";
|
|
13
13
|
export const NAVIGATOR_ACCEPTED_TEXT = "游奕使建议已受理";
|
|
14
14
|
|
|
15
|
-
export const navigatorOutputSchema =
|
|
15
|
+
export const navigatorOutputSchema = withTerminatingOutputDeclarations(
|
|
16
16
|
openToolObject(
|
|
17
17
|
Type.Object({
|
|
18
18
|
status: Type.Unknown({
|
|
@@ -49,19 +49,29 @@ const infrastructureFailureDeclarationSchema = Type.Object(
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Compose
|
|
53
|
-
*
|
|
52
|
+
* Compose declarations shared by terminating output tools: infrastructure failure
|
|
53
|
+
* and the optional role-asserted ticket identity. Returns an open object (additionalProperties: true,
|
|
54
54
|
* required: []) with the base schema's properties plus the shared declaration.
|
|
55
55
|
* Static typing is preserved on the base (`as S`), so existing
|
|
56
56
|
* `Static<typeof ...>` derived parameter types are unchanged.
|
|
57
57
|
*/
|
|
58
|
-
export function
|
|
58
|
+
export function withTerminatingOutputDeclarations<
|
|
59
59
|
S extends TSchema & { properties?: Record<string, TSchema> },
|
|
60
60
|
>(schema: S): S {
|
|
61
61
|
const baseProperties = (schema as { properties?: Record<string, TSchema> })
|
|
62
62
|
.properties;
|
|
63
63
|
const properties: Record<string, TSchema> = {
|
|
64
64
|
...(baseProperties ?? {}),
|
|
65
|
+
...(
|
|
66
|
+
baseProperties?.ticketNumber === undefined
|
|
67
|
+
? {
|
|
68
|
+
ticketNumber: Type.Unknown({
|
|
69
|
+
description:
|
|
70
|
+
"可选本票号:尚未绑定时由角色在既有回执中申报;机械层只记录合法正整数,不从散文推导、不验真。缺失或错形不拒收。",
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
: {}
|
|
74
|
+
),
|
|
65
75
|
[INFRASTRUCTURE_FAILURE_DECLARATION_KEY]:
|
|
66
76
|
infrastructureFailureDeclarationSchema.properties[
|
|
67
77
|
INFRASTRUCTURE_FAILURE_DECLARATION_KEY
|
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* #709 公共入口通用递送与 #742
|
|
4
|
-
* 指针输入沿 ADR 0079
|
|
5
|
-
*
|
|
2
|
+
* 系统随案递送起居录路径的中立指针段(ADR 0081:公共入口直接挂案卷,Soul 只说明用途不负责派送;
|
|
3
|
+
* #709 公共入口通用递送与 #742 给事中受理链路同源;#858 告诉 LLM 路径,不另建取号/lookup)。
|
|
4
|
+
* 指针输入沿 ADR 0079(指针是绑定材料由代码精准递送),不把卷宗正文塞进提示词。
|
|
5
|
+
* 只告诉路径:不刷新、不生成、不校验内容、不新增拒收或停工条件、不从 instruction 抽票号。
|
|
6
6
|
* 机器文本仅中立标识材料(ADR 0073),用途说明归角色材料所有。
|
|
7
7
|
*
|
|
8
8
|
* 递送挂载点唯一:`post-admission` 在 beforeDispatch 之后为每个公共入口挂载。
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* systemPrompt.materials fold(#879:对话 instruction
|
|
12
|
-
*
|
|
9
|
+
* 全部公共入口(ordinary 已绑定/未绑定与 station-child)统一走 attachments 冻结 +
|
|
10
|
+
* role-runtime `loadCaseDossierReadingMaterial` → readingMaterial →
|
|
11
|
+
* systemPrompt.materials fold(#879 / #858:对话 instruction、空请求、resume
|
|
12
|
+
* message 保持调用者原文;起居录作独立附件面,不新造 RoleTurnRequest.materials,
|
|
13
|
+
* 不拼进 user-dialogue continuation)。
|
|
14
|
+
*
|
|
15
|
+
* #858: deliver returns typed FrozenAttachment[]; load currently re-derives the
|
|
16
|
+
* freeze leaf under runDirectory because agent-start only has HostContext.runDirectory.
|
|
17
|
+
* Consuming the typed frozenPath across that process boundary needs a design
|
|
18
|
+
* revision of the existing envelope/persistence seam — not a local replica of
|
|
19
|
+
* freezeAttachments' `00-` naming, not a drift throw, not readdir guessing.
|
|
13
20
|
*/
|
|
14
21
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
15
22
|
import { tmpdir } from "node:os";
|
|
16
23
|
import { join } from "node:path";
|
|
17
24
|
|
|
25
|
+
import { projectTicketRecordsPathShape } from "../sitian-facade.ts";
|
|
18
26
|
import { resolveTicketProvenanceVolume } from "../ticket-provenance.ts";
|
|
19
27
|
import {
|
|
20
28
|
freezeAttachmentsIntoRun,
|
|
21
29
|
type FrozenAttachment,
|
|
22
30
|
} from "./invocation.ts";
|
|
23
31
|
|
|
24
|
-
/**
|
|
32
|
+
/** Bound-ticket section heading (presentation only). */
|
|
25
33
|
const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)" as const;
|
|
26
34
|
|
|
35
|
+
/** Unbound path-only heading — no 本票 claim when no typed ticket is on the run. */
|
|
36
|
+
const UNBOUND_DOSSIER_PATH_HEADING = "## 起居录路径(系统随案提供)" as const;
|
|
37
|
+
|
|
27
38
|
/** Stable freeze key under run/attachments/ for station-child 0081 delivery. */
|
|
28
39
|
const CASE_DOSSIER_ATTACH_KEY = "case-dossier" as const;
|
|
29
40
|
|
|
30
41
|
/** Leaf name of the frozen pointer section (content = purpose + paths). */
|
|
31
42
|
const CASE_DOSSIER_ATTACH_FILE = "case-dossier-pointer.md" as const;
|
|
32
43
|
|
|
33
|
-
/** Pointer only — presence/absence is for the role to observe at the path. */
|
|
34
|
-
function describeDossierFile(path: string): string {
|
|
35
|
-
return path;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
44
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* the
|
|
45
|
+
* Neutral 起居录 path pointer for every public entry (ADR 0081 delivery seam).
|
|
46
|
+
* Always tells the canonical path shape so the seat LLM can read the diary
|
|
47
|
+
* itself (陛下 2026-09-15: 只需要告诉llm这个路径). When a typed ticket identity is
|
|
48
|
+
* already on the run, also project the concrete resolved file. Never claims a
|
|
49
|
+
* volume or「本票」exists when unbound; never extracts a ticket from instruction.
|
|
50
|
+
* Under-book shape comes only from sitian writer joins — no local replica.
|
|
43
51
|
*/
|
|
44
52
|
export async function projectCaseDossierPointerSection(input: {
|
|
45
53
|
readonly ticketNumber: number | undefined;
|
|
46
54
|
readonly projectRoot: string;
|
|
47
55
|
readonly home: string;
|
|
48
|
-
}): Promise<string
|
|
49
|
-
if (input.ticketNumber === undefined)
|
|
56
|
+
}): Promise<string> {
|
|
57
|
+
if (input.ticketNumber === undefined) {
|
|
58
|
+
return [
|
|
59
|
+
UNBOUND_DOSSIER_PATH_HEADING,
|
|
60
|
+
"",
|
|
61
|
+
`记录卷宗:${projectTicketRecordsPathShape()}`,
|
|
62
|
+
].join("\n");
|
|
63
|
+
}
|
|
50
64
|
const volume = resolveTicketProvenanceVolume(
|
|
51
65
|
input.ticketNumber,
|
|
52
66
|
input.projectRoot,
|
|
@@ -56,30 +70,30 @@ export async function projectCaseDossierPointerSection(input: {
|
|
|
56
70
|
CASE_DOSSIER_SECTION_HEADING,
|
|
57
71
|
"",
|
|
58
72
|
`票号:#${input.ticketNumber}`,
|
|
59
|
-
`记录卷宗:${
|
|
73
|
+
`记录卷宗:${volume.recordFile}`,
|
|
60
74
|
].join("\n");
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
/**
|
|
64
|
-
* ADR 0081 delivery for
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
78
|
+
* ADR 0081 delivery for every public entry (#879 / #858): freeze the pointer
|
|
79
|
+
* section through the existing attachments seam. Role-runtime loads that freeze
|
|
80
|
+
* via loadCaseDossierReadingMaterial onto readingMaterial; the envelope then
|
|
81
|
+
* folds it into systemPrompt.materials. Caller / peer dialogue instruction stays
|
|
82
|
+
* opaque; never RoleTurnRequest.materials, never user-dialogue continuation.
|
|
83
|
+
* Always freezes the path pointer (canonical shape, or concrete file when a
|
|
84
|
+
* typed ticket is already bound).
|
|
70
85
|
*/
|
|
71
86
|
export async function deliverCaseDossierAsAttachment(input: {
|
|
72
87
|
readonly ticketNumber: number | undefined;
|
|
73
88
|
readonly projectRoot: string;
|
|
74
89
|
readonly home: string;
|
|
75
90
|
readonly runDirectory: string;
|
|
76
|
-
}): Promise<readonly FrozenAttachment[]
|
|
91
|
+
}): Promise<readonly FrozenAttachment[]> {
|
|
77
92
|
const section = await projectCaseDossierPointerSection({
|
|
78
93
|
ticketNumber: input.ticketNumber,
|
|
79
94
|
projectRoot: input.projectRoot,
|
|
80
95
|
home: input.home,
|
|
81
96
|
});
|
|
82
|
-
if (section === undefined) return undefined;
|
|
83
97
|
// Stage in OS temp only — never leave a run-local .case-dossier-stage copy.
|
|
84
98
|
const stagingDir = await mkdtemp(join(tmpdir(), "ak-case-dossier-"));
|
|
85
99
|
try {
|
|
@@ -106,6 +120,11 @@ export type CaseDossierReadingMaterial = {
|
|
|
106
120
|
* Load a previously frozen case-dossier attachment as reading material
|
|
107
121
|
* (existing agent-start / systemPrompt.materials fold — not dialogue prompt,
|
|
108
122
|
* not RoleTurnRequest.materials). Undefined when the run has no such freeze.
|
|
123
|
+
*
|
|
124
|
+
* #858: this join re-derives freezeAttachments' leaf naming because the
|
|
125
|
+
* agent-start handler only receives runDirectory. Replacing it requires
|
|
126
|
+
* threading deliver's typed frozenPath across the existing envelope seam
|
|
127
|
+
* (design revision) — do not copy freeze's `00-` algorithm here again.
|
|
109
128
|
*/
|
|
110
129
|
export async function loadCaseDossierReadingMaterial(
|
|
111
130
|
runDirectory: string,
|