@akagilnc/pi-workflow-roles 0.1.5068 → 0.1.5096
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 +429 -383
- package/dist/atomic-write.js +13 -19
- package/dist/auditor-soul.js +2 -1
- package/dist/compliance-transport.js +3 -2
- package/dist/countersign-contracts.js +2 -2
- package/dist/doctor-contracts.js +1 -1
- package/dist/gatekeeper-role.js +14 -35
- package/dist/headless-host/production-host.js +461 -415
- package/dist/inspector-contracts.js +5 -2
- package/dist/merger-contracts.js +1 -1
- package/dist/migrate-book-topology.js +49 -33
- package/dist/navigator-attendance.js +28 -143
- package/dist/navigator-invocation-identity.js +2 -0
- package/dist/navigator-public-session.js +13 -1
- package/dist/navigator-work-base.js +113 -0
- package/dist/notary-contracts.js +0 -2
- package/dist/open-tool-schema.js +4 -1
- package/dist/package-contracts/fixer-output.js +1 -1
- package/dist/package-contracts/gatekeeper-output.js +1 -1
- package/dist/package-contracts/judge-output.js +2 -2
- package/dist/package-contracts/navigator-output.js +1 -1
- package/dist/package-contracts/terminating-infrastructure.js +7 -4
- package/dist/packaged-role-registry.js +9 -5
- package/dist/public-cli/countersign-run.js +1 -1
- package/dist/public-cli/invocation.js +6 -3
- package/dist/public-cli/main.js +78 -41
- package/dist/public-cli/settlement.js +20 -1
- package/dist/receipt-delivery-policy.js +9 -2
- package/dist/review-submission.js +19 -8
- package/dist/submission-errors.js +17 -0
- package/dist/submission-ledger.js +18 -6
- package/docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md +9 -0
- package/extensions/role-runtime.ts +0 -27
- package/package.json +2 -1
- package/resources/836-deleted-machine-instruction-inventory.md +3 -3
- package/resources/collector-bot-handbook.md +4 -0
- package/resources/navigator-route-playbook.md +4 -6
- package/scripts/build-package.mjs +4 -0
- package/souls/coder-output-guide.md +1 -0
- package/src/auditor-soul.ts +2 -1
- package/src/collector-role.ts +6 -7
- package/src/collector-tool-schemas.ts +5 -5
- package/src/compliance-transport.ts +3 -2
- package/src/countersign-contracts.ts +2 -2
- package/src/countersign-role.ts +3 -7
- package/src/diarist-role.ts +2 -2
- package/src/doctor-contracts.ts +1 -1
- package/src/gatekeeper-role.ts +15 -36
- package/src/gleaner-left-role.ts +2 -2
- package/src/inspector-contracts.ts +5 -2
- package/src/inspector-role.ts +1 -1
- package/src/judge-role.ts +14 -22
- package/src/merger-contracts.ts +1 -1
- package/src/navigator-attendance.ts +38 -164
- package/src/navigator-invocation-identity.ts +2 -0
- package/src/navigator-public-session.ts +17 -1
- package/src/navigator-role.ts +4 -0
- package/src/navigator-session-contracts.ts +2 -0
- package/src/navigator-work-base.ts +123 -0
- package/src/notary-contracts.ts +0 -4
- package/src/open-tool-schema.ts +9 -4
- package/src/package-contracts/fixer-output.ts +1 -1
- package/src/package-contracts/gatekeeper-output.ts +3 -3
- package/src/package-contracts/judge-output.ts +2 -4
- package/src/package-contracts/navigator-output.ts +1 -1
- package/src/package-contracts/terminating-infrastructure.ts +12 -10
- package/src/packaged-role-registry.ts +5 -1
- package/src/public-cli/countersign-run.ts +1 -1
- package/src/public-cli/invocation.ts +6 -3
- package/src/public-cli/settlement.ts +21 -0
- package/src/receipt-delivery-policy.ts +9 -1
- package/src/review-submission.ts +24 -10
- package/src/reviewer-role.ts +3 -3
- package/src/role-runtime-dependencies.ts +1 -5
- package/src/role-runtime.ts +61 -63
- package/src/secretariat-role.ts +3 -3
- package/src/submission-errors.ts +16 -0
- package/src/submission-gate.ts +2 -1
- package/src/submission-ledger.ts +23 -11
- package/src/worker-role.ts +7 -9
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable work subject and controlling authority for one navigator nest.
|
|
3
|
+
* Settlement user turns keep a path pointer; the navigator system prompt
|
|
4
|
+
* loads the bytes once per agent start from this file (base material).
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
9
|
+
|
|
10
|
+
import { writeFileAtomically } from "./atomic-write.ts";
|
|
11
|
+
|
|
12
|
+
export const NAVIGATOR_WORK_CONTEXT_BASENAME = "work-context.json";
|
|
13
|
+
|
|
14
|
+
export type NavigatorWorkBase = {
|
|
15
|
+
readonly subject: string;
|
|
16
|
+
readonly authority: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function navigatorWorkContextFile(sessionDir: string): string {
|
|
20
|
+
return join(sessionDir, NAVIGATOR_WORK_CONTEXT_BASENAME);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Nest file only: books/.../navigator/<sha256-32>/work-context.json. */
|
|
24
|
+
export function isNavigatorWorkContextFile(path: string): boolean {
|
|
25
|
+
const resolved = resolve(path);
|
|
26
|
+
if (basename(resolved) !== NAVIGATOR_WORK_CONTEXT_BASENAME) return false;
|
|
27
|
+
const nest = dirname(resolved);
|
|
28
|
+
if (!/^[0-9a-f]{32}$/.test(basename(nest))) return false;
|
|
29
|
+
return basename(dirname(nest)) === "navigator";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function canonicalNavigatorWorkContextFile(path: string): string | undefined {
|
|
33
|
+
if (!isNavigatorWorkContextFile(path)) return undefined;
|
|
34
|
+
try {
|
|
35
|
+
const real = realpathSync(path);
|
|
36
|
+
return isNavigatorWorkContextFile(real) ? real : undefined;
|
|
37
|
+
} catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function persistNavigatorWorkBase(
|
|
43
|
+
sessionDir: string,
|
|
44
|
+
body: NavigatorWorkBase,
|
|
45
|
+
): Promise<string | undefined> {
|
|
46
|
+
if (sessionDir.trim() === "" || !existsSync(sessionDir)) return undefined;
|
|
47
|
+
if (body.authority.trim() === "") return undefined;
|
|
48
|
+
const path = navigatorWorkContextFile(sessionDir);
|
|
49
|
+
const next = `${JSON.stringify({ subject: body.subject, authority: body.authority })}\n`;
|
|
50
|
+
try {
|
|
51
|
+
const current = await readFile(path, "utf8");
|
|
52
|
+
if (current === next) return path;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (!(error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
await writeFileAtomically(path, next);
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function readNavigatorWorkBase(path: string): Promise<NavigatorWorkBase | undefined> {
|
|
63
|
+
const canonical = canonicalNavigatorWorkContextFile(path);
|
|
64
|
+
if (canonical === undefined) return undefined;
|
|
65
|
+
let text: string;
|
|
66
|
+
try {
|
|
67
|
+
text = await readFile(canonical, "utf8");
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
let parsed: unknown;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(text);
|
|
77
|
+
} catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return undefined;
|
|
81
|
+
const record = parsed as Record<string, unknown>;
|
|
82
|
+
if (typeof record.subject !== "string" || typeof record.authority !== "string") return undefined;
|
|
83
|
+
if (record.authority.trim() === "") return undefined;
|
|
84
|
+
return { subject: record.subject, authority: record.authority };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function jsonObjectFromPrompt(prompt: string): Record<string, unknown> | undefined {
|
|
88
|
+
const head = prompt.trimStart().split("\n\n", 1)[0] ?? "";
|
|
89
|
+
if (!head.startsWith("{")) return undefined;
|
|
90
|
+
try {
|
|
91
|
+
const parsed: unknown = JSON.parse(head);
|
|
92
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return undefined;
|
|
93
|
+
return parsed as Record<string, unknown>;
|
|
94
|
+
} catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function workContextPathFromPrompt(prompt: string): string | undefined {
|
|
100
|
+
const record = jsonObjectFromPrompt(prompt);
|
|
101
|
+
const path = record?.workContextPath;
|
|
102
|
+
if (typeof path !== "string" || path.trim() === "") return undefined;
|
|
103
|
+
return isNavigatorWorkContextFile(path) ? path : undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Settlement JSON gains the nest pointer. Non-JSON prompts stay unchanged. */
|
|
107
|
+
export function attachWorkContextPointer(text: string, workContextPath: string): string {
|
|
108
|
+
const record = jsonObjectFromPrompt(text);
|
|
109
|
+
if (record === undefined) return text;
|
|
110
|
+
if (typeof record.workContextPath === "string" && record.workContextPath.trim() !== "") return text;
|
|
111
|
+
const head = text.trimStart().split("\n\n", 1)[0] ?? "";
|
|
112
|
+
const rest = text.trimStart().slice(head.length);
|
|
113
|
+
return `${JSON.stringify({ ...record, workContextPath })}${rest}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** System-prompt base for one agent start. Absent or unreadable file yields undefined. */
|
|
117
|
+
export async function loadNavigatorWorkBaseSuffix(prompt: string): Promise<string | undefined> {
|
|
118
|
+
const path = workContextPathFromPrompt(prompt);
|
|
119
|
+
if (path === undefined) return undefined;
|
|
120
|
+
const body = await readNavigatorWorkBase(path);
|
|
121
|
+
if (body === undefined) return undefined;
|
|
122
|
+
return `<work_subject>\n${body.subject}\n</work_subject>\n\n<controlling_authority>\n${body.authority}\n</controlling_authority>`;
|
|
123
|
+
}
|
package/src/notary-contracts.ts
CHANGED
|
@@ -23,10 +23,6 @@ export const NOTARY_TICKET_FLAG = {
|
|
|
23
23
|
},
|
|
24
24
|
} as const;
|
|
25
25
|
|
|
26
|
-
/** Package-owned kickoff only — callers supply zero prompt bytes (ADR 0067 / #448). */
|
|
27
|
-
export const NOTARY_FIXED_KICKOFF =
|
|
28
|
-
"符宝郎案卷已受理;来源 run 定位见会话材料。";
|
|
29
|
-
|
|
30
26
|
export const notaryOutputSchema = reviewSubmissionSchema;
|
|
31
27
|
|
|
32
28
|
export type NotarySourceRunLocator = {
|
package/src/open-tool-schema.ts
CHANGED
|
@@ -12,7 +12,7 @@ function declarationIdentity(schema: TSchema): string {
|
|
|
12
12
|
return JSON.stringify(semantic);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/** Collapse transport variants into one provider-compatible
|
|
15
|
+
/** Collapse transport variants into one provider-compatible open object. Non-three-state tools stay zero-required. */
|
|
16
16
|
export function openToolObjectFromUnion(schema: UnionSchema): TSchema {
|
|
17
17
|
const declarations = new Map<string, TSchema[]>();
|
|
18
18
|
for (const variant of schema.anyOf) {
|
|
@@ -35,10 +35,15 @@ export function openToolObjectFromUnion(schema: UnionSchema): TSchema {
|
|
|
35
35
|
return object;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/** Open a pre-existing object transport schema
|
|
39
|
-
export function openToolObject(
|
|
38
|
+
/** Open a pre-existing object transport schema. Every field stays optional. */
|
|
39
|
+
export function openToolObject(
|
|
40
|
+
schema: TSchema & { properties: Record<string, TSchema> },
|
|
41
|
+
): TSchema {
|
|
40
42
|
const object = Type.Object(
|
|
41
|
-
Object.fromEntries(Object.entries(schema.properties).map(([name, declaration]) => [
|
|
43
|
+
Object.fromEntries(Object.entries(schema.properties).map(([name, declaration]) => [
|
|
44
|
+
name,
|
|
45
|
+
Type.Optional(described(name, declaration)),
|
|
46
|
+
])),
|
|
42
47
|
{ additionalProperties: true },
|
|
43
48
|
);
|
|
44
49
|
(object as unknown as { required: string[] }).required = [];
|
|
@@ -41,7 +41,7 @@ const completedClassResultsSchema = Type.Array(completedClassResultSchema);
|
|
|
41
41
|
// (src/countersign-role.ts) — one shared description across every variant
|
|
42
42
|
// so openToolObjectFromUnion's identical-declaration collapse drops none of it.
|
|
43
43
|
const FIXER_STATUS_DESCRIPTION =
|
|
44
|
-
"planned | completed | refused | unfinished | partially_completed
|
|
44
|
+
"planned | completed | refused | unfinished | partially_completed。unfinished 缺前置或违宪约束致本局未完成时可用,缺待决 owner 决定或答复属缺前置。" as const;
|
|
45
45
|
const fixerOutputVariants = Type.Union([
|
|
46
46
|
Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }) }),
|
|
47
47
|
Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), remainingScope: Type.String({ description: "依法不能完成的工作范围" }), blocker: Type.Unsafe({ ...blockerSchema, description: "合法阻断完成的 blocker" }) }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public Gatekeeper (门下省) terminating receipt contracts (#639).
|
|
3
|
-
* Direct public seat
|
|
3
|
+
* Direct public seat decision shape. status field semantics: dispatch | pass.
|
|
4
4
|
* No usable result is infrastructure failure via public settlement, not a judgment status (#475).
|
|
5
5
|
*/
|
|
6
6
|
import { Type } from "typebox";
|
|
@@ -14,7 +14,7 @@ export const GATEKEEPER_OUTPUT_TOOL_NAME = "ak_gatekeeper_output";
|
|
|
14
14
|
export const gatekeeperDecisionSchema = openToolObject(
|
|
15
15
|
Type.Object({
|
|
16
16
|
status: Type.Unknown({
|
|
17
|
-
description: "dispatch | pass
|
|
17
|
+
description: "dispatch | pass",
|
|
18
18
|
}),
|
|
19
19
|
officer: Type.Unknown({
|
|
20
20
|
description: "status 为 dispatch 时为 inspector | notary",
|
|
@@ -43,7 +43,7 @@ function asStringArray(value: unknown): readonly string[] {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* Project one
|
|
46
|
+
* Project one explicit Gatekeeper decision.
|
|
47
47
|
* No throw on shape — ADR 0055 / 第 0 条: already-submitted params are retained as-is;
|
|
48
48
|
* public-terminal projects non-usable releases via typed failure cause.
|
|
49
49
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Package-owned Judge output leaf — no role registration surface. */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "../review-submission.ts";
|
|
4
|
+
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "../review-submission.ts";
|
|
5
5
|
|
|
6
6
|
export const JUDGE_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
7
7
|
|
|
@@ -39,9 +39,7 @@ export function validateAcceptedJudgeDetails(verdict: unknown): JudgeVerdict {
|
|
|
39
39
|
if (typeof status !== "string") {
|
|
40
40
|
throw new Error("Judge verdict has no execution discriminator");
|
|
41
41
|
}
|
|
42
|
-
if (
|
|
43
|
-
["converged", "continue", "escalate"].includes(status)
|
|
44
|
-
) {
|
|
42
|
+
if (REVIEW_QUEUE_STATUSES.has(status)) {
|
|
45
43
|
return verdict as JudgeVerdict;
|
|
46
44
|
}
|
|
47
45
|
throw new Error("Judge verdict has no execution discriminator");
|
|
@@ -16,7 +16,7 @@ export const navigatorOutputSchema = withTerminatingOutputDeclarations(
|
|
|
16
16
|
openToolObject(
|
|
17
17
|
Type.Object({
|
|
18
18
|
prose: Type.Unknown({
|
|
19
|
-
description: "游奕使散文建议,原样呈现。不要求 candidates/next
|
|
19
|
+
description: "游奕使散文建议,原样呈现。不要求 candidates/next 等结构化字段。",
|
|
20
20
|
}),
|
|
21
21
|
}),
|
|
22
22
|
),
|
|
@@ -28,14 +28,12 @@ export const INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY = "diagnostic" as const;
|
|
|
28
28
|
const infrastructureFailureNested = Type.Object(
|
|
29
29
|
{
|
|
30
30
|
[INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY]: Type.Unknown({
|
|
31
|
-
description:
|
|
32
|
-
"非空基础设施失败诊断字符串。无失败时必须省略整个 infrastructureFailure。形状指引,非 schema 闸。",
|
|
31
|
+
description: "基础设施失败诊断字符串。",
|
|
33
32
|
}),
|
|
34
33
|
},
|
|
35
34
|
{
|
|
36
35
|
additionalProperties: true,
|
|
37
|
-
description:
|
|
38
|
-
"基础设施真实失败声明(如需)。规范形:{ diagnostic: 非空诊断字符串 };无失败时必须省略。形状指引,非 schema 闸。",
|
|
36
|
+
description: "仅基础设施真实失败时出现。",
|
|
39
37
|
},
|
|
40
38
|
);
|
|
41
39
|
// Open nested required so host cannot pure-shape-reject the declaration fragment.
|
|
@@ -50,8 +48,9 @@ const infrastructureFailureDeclarationSchema = Type.Object(
|
|
|
50
48
|
|
|
51
49
|
/**
|
|
52
50
|
* Compose declarations shared by terminating output tools: infrastructure failure
|
|
53
|
-
* and the optional role-asserted ticket identity. Returns an open object (additionalProperties: true
|
|
54
|
-
*
|
|
51
|
+
* and the optional role-asserted ticket identity. Returns an open object (additionalProperties: true)
|
|
52
|
+
* with the base schema's properties plus the shared declaration. Incoming required
|
|
53
|
+
* keys that still exist are kept; every other key stays optional.
|
|
55
54
|
* Static typing is preserved on the base (`as S`), so existing
|
|
56
55
|
* `Static<typeof ...>` derived parameter types are unchanged.
|
|
57
56
|
*/
|
|
@@ -66,8 +65,7 @@ export function withTerminatingOutputDeclarations<
|
|
|
66
65
|
baseProperties?.ticketNumber === undefined
|
|
67
66
|
? {
|
|
68
67
|
ticketNumber: Type.Unknown({
|
|
69
|
-
description:
|
|
70
|
-
"可选本票号:尚未绑定时由角色在既有回执中申报;机械层只记录合法正整数,不从散文推导、不验真。缺失或错形不拒收。",
|
|
68
|
+
description: "可选本票号。尚未绑定时由角色在既有回执中申报。",
|
|
71
69
|
}),
|
|
72
70
|
}
|
|
73
71
|
: {}
|
|
@@ -78,7 +76,11 @@ export function withTerminatingOutputDeclarations<
|
|
|
78
76
|
],
|
|
79
77
|
};
|
|
80
78
|
const object = Type.Object(properties, { additionalProperties: true });
|
|
81
|
-
|
|
79
|
+
const incoming = (schema as { required?: unknown }).required;
|
|
80
|
+
const preserved = Array.isArray(incoming)
|
|
81
|
+
? incoming.filter((key): key is string => typeof key === "string" && Object.hasOwn(properties, key))
|
|
82
|
+
: [];
|
|
83
|
+
(object as unknown as { required: string[] }).required = preserved;
|
|
82
84
|
return object as unknown as S;
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -115,7 +117,7 @@ function isInfrastructureFailureDeclaration(
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
/** Non-empty trimmed diagnostic from the declaration, else undefined. */
|
|
118
|
-
function infrastructureFailureDiagnostic(
|
|
120
|
+
export function infrastructureFailureDiagnostic(
|
|
119
121
|
parameters: unknown,
|
|
120
122
|
): string | undefined {
|
|
121
123
|
if (!isInfrastructureFailureDeclaration(parameters)) return undefined;
|
|
@@ -52,6 +52,7 @@ export const NOTARY_SESSION_MATERIALS = [
|
|
|
52
52
|
"souls/audit-law.md",
|
|
53
53
|
"souls/ticket-law.md",
|
|
54
54
|
"souls/gate-output-guide.md",
|
|
55
|
+
"docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
|
|
55
56
|
] as const;
|
|
56
57
|
|
|
57
58
|
/** Shared by public inspector and gatekeeper-province inspector. */
|
|
@@ -61,6 +62,7 @@ export const INSPECTOR_SESSION_MATERIALS = [
|
|
|
61
62
|
"souls/audit-law.md",
|
|
62
63
|
"souls/quality-law.md",
|
|
63
64
|
"souls/gate-output-guide.md",
|
|
65
|
+
"docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
|
|
64
66
|
] as const;
|
|
65
67
|
|
|
66
68
|
/**
|
|
@@ -75,6 +77,7 @@ export const AUDITOR_PUBLIC_SESSION_MATERIALS = [
|
|
|
75
77
|
"souls/doctor-auditor.md",
|
|
76
78
|
"souls/audit-law.md",
|
|
77
79
|
"souls/quality-law.md",
|
|
80
|
+
"docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
|
|
78
81
|
] as const;
|
|
79
82
|
|
|
80
83
|
/**
|
|
@@ -107,6 +110,7 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
107
110
|
"souls/audit-law.md",
|
|
108
111
|
"souls/quality-law.md",
|
|
109
112
|
"souls/judge-output-guide.md",
|
|
113
|
+
"docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
|
|
110
114
|
],
|
|
111
115
|
},
|
|
112
116
|
{
|
|
@@ -384,7 +388,7 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
384
388
|
activationStage: "load-and-install",
|
|
385
389
|
receiptStatusKey: "status",
|
|
386
390
|
// #924: 公用《票面法》三席同装
|
|
387
|
-
sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "resources/countersign-ticket-issue.md"],
|
|
391
|
+
sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "resources/countersign-ticket-issue.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"],
|
|
388
392
|
},
|
|
389
393
|
{
|
|
390
394
|
role: "secretariat",
|
|
@@ -369,7 +369,7 @@ export async function runCountersignCourtDiaristStation(
|
|
|
369
369
|
for (const ticketNumber of refreshTickets.slice(completedIndex + 1)) {
|
|
370
370
|
const outcome = await invokeCourtDiarist(
|
|
371
371
|
{
|
|
372
|
-
instruction:
|
|
372
|
+
instruction: admitted.instruction,
|
|
373
373
|
projectRoot: admitted.projectRoot,
|
|
374
374
|
failureLabel: `ticket #${ticketNumber}`,
|
|
375
375
|
correlationId: admitted.runId,
|
|
@@ -87,7 +87,6 @@ import { sha256Hex } from "../sha256.ts";
|
|
|
87
87
|
import { rehomeUnboundTicketProvenance } from "../ticket-provenance.ts";
|
|
88
88
|
import { uuidv7 } from "../uuidv7.ts";
|
|
89
89
|
import {
|
|
90
|
-
NOTARY_FIXED_KICKOFF,
|
|
91
90
|
type NotarySourceRunLocator,
|
|
92
91
|
} from "../notary-contracts.ts";
|
|
93
92
|
import {
|
|
@@ -2150,6 +2149,7 @@ type InstructionTransportSource = {
|
|
|
2150
2149
|
readonly baseRevision?: string;
|
|
2151
2150
|
readonly lens?: ReviewerLens;
|
|
2152
2151
|
readonly authorityRefs?: readonly string[];
|
|
2152
|
+
readonly sourceRunPath?: string;
|
|
2153
2153
|
};
|
|
2154
2154
|
|
|
2155
2155
|
function admittedTransportPromptKind(
|
|
@@ -2172,13 +2172,16 @@ export function buildInstructionTransportPrompt(
|
|
|
2172
2172
|
): string {
|
|
2173
2173
|
const kind = admittedTransportPromptKind(admitted);
|
|
2174
2174
|
if (kind === "fixed-kickoff") {
|
|
2175
|
-
|
|
2175
|
+
if (admitted.sourceRunPath === undefined || admitted.sourceRunPath.trim() === "") {
|
|
2176
|
+
throw new Error("fixed-kickoff transport prompt is missing the source run pointer");
|
|
2177
|
+
}
|
|
2178
|
+
return appendEngineSessionMaterial([admitted.sourceRunPath], engineMaterial).join("\n");
|
|
2176
2179
|
}
|
|
2177
2180
|
if (kind === "baseline") {
|
|
2178
2181
|
if (admitted.baseRevision === undefined) {
|
|
2179
2182
|
throw new Error("baseline transport prompt is missing the bound revision");
|
|
2180
2183
|
}
|
|
2181
|
-
const lines = [
|
|
2184
|
+
const lines = [admitted.baseRevision];
|
|
2182
2185
|
if (!admitted.instructionEmpty) {
|
|
2183
2186
|
lines.push("", admitted.instruction);
|
|
2184
2187
|
}
|
|
@@ -99,6 +99,7 @@ import {
|
|
|
99
99
|
isAcceptedPackagedRoleTerminalResult,
|
|
100
100
|
isReceiptSettlementBindingClear,
|
|
101
101
|
NAVIGATOR_INVOCATION_ENTRY,
|
|
102
|
+
NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY,
|
|
102
103
|
parseInvocationMarkerIdentity,
|
|
103
104
|
type InvocationMarkerIdentity,
|
|
104
105
|
} from "../navigator-invocation-identity.ts";
|
|
@@ -2249,8 +2250,28 @@ async function withOptionalGateProjection<
|
|
|
2249
2250
|
return attachEngineDetourToolUsage(next, sessionDirectory, gateContext);
|
|
2250
2251
|
}
|
|
2251
2252
|
|
|
2253
|
+
function routePlaybookFailureMessage(entries: readonly SessionEntry[]): string | undefined {
|
|
2254
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
2255
|
+
const entry = entries[i];
|
|
2256
|
+
if (entry?.type !== "custom" || entry.customType !== NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY) continue;
|
|
2257
|
+
const data = entry.data;
|
|
2258
|
+
if (!isRecord(data) || typeof data.message !== "string" || data.message.trim() === "") return undefined;
|
|
2259
|
+
return data.message;
|
|
2260
|
+
}
|
|
2261
|
+
return undefined;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2252
2264
|
export function extractNavigatorFact(
|
|
2253
2265
|
entries: readonly SessionEntry[],
|
|
2266
|
+
): TerminalNavigatorFact {
|
|
2267
|
+
const fact = extractNavigatorAttendanceFact(entries);
|
|
2268
|
+
if (fact.advisoryDiagnostic !== undefined) return fact;
|
|
2269
|
+
const message = routePlaybookFailureMessage(entries);
|
|
2270
|
+
return message === undefined ? fact : { ...fact, advisoryDiagnostic: message };
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
function extractNavigatorAttendanceFact(
|
|
2274
|
+
entries: readonly SessionEntry[],
|
|
2254
2275
|
): TerminalNavigatorFact {
|
|
2255
2276
|
// Affirmative attendance only. Missing / uncorrelated / unparseable is never no-advice.
|
|
2256
2277
|
// Minimal provenance: latest durable terminal + nearest preceding marker +
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** Shared accepted-receipt delivery budget for role, auditor, and Navigator sessions (#288). */
|
|
2
2
|
export const RECEIPT_DELIVERY_TURN_LIMIT = 2 as const;
|
|
3
|
-
export const RECEIPT_DELIVERY_PROMPT = "本会话尚无已接受的 typed 回执。";
|
|
4
3
|
|
|
5
4
|
export const NO_RECEIPT_LIFECYCLE_ENTRY_TYPE = "ak-no-receipt-lifecycle" as const;
|
|
6
5
|
|
|
@@ -104,6 +103,15 @@ export function createReceiptDeliveryPolicy() {
|
|
|
104
103
|
if (accepted) return "accepted";
|
|
105
104
|
return deliveryTurns < RECEIPT_DELIVERY_TURN_LIMIT ? "request-delivery" : "no-receipt";
|
|
106
105
|
},
|
|
106
|
+
/** Current typed delivery facts. No prose. */
|
|
107
|
+
deliveryState() {
|
|
108
|
+
return {
|
|
109
|
+
terminalToolCalled,
|
|
110
|
+
rejectedReceipts: rejectedReceipts.map((item) => ({ ...item })),
|
|
111
|
+
deliveryTurns,
|
|
112
|
+
acceptedReceipt: false as const,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
107
115
|
facts(binding: { runPointer: string; attemptPointer: string }): NoReceiptLifecycleFacts {
|
|
108
116
|
return noReceiptLifecycleFacts({ terminalToolCalled, rejectedReceipts: [...rejectedReceipts], deliveryTurns, ...binding });
|
|
109
117
|
},
|
package/src/review-submission.ts
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
|
-
/** One shared terminating receipt contract for review officers (#1028). */
|
|
1
|
+
/** One shared terminating receipt contract for review officers (#1028 / #1055). */
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
|
-
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
4
|
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
6
5
|
|
|
7
6
|
export const REVIEW_SUBMISSION_OUTPUT_TOOL_NAME = "ak_submission_output" as const;
|
|
8
7
|
|
|
9
|
-
/**
|
|
8
|
+
/** Single schema copy of the review queue words. Runtime membership reads this set. */
|
|
9
|
+
export const REVIEW_QUEUE_WORDS = ["converged", "continue", "escalate"] as const;
|
|
10
|
+
export type ReviewQueueWord = (typeof REVIEW_QUEUE_WORDS)[number];
|
|
11
|
+
export const REVIEW_QUEUE_STATUSES: ReadonlySet<string> = new Set(REVIEW_QUEUE_WORDS);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* One object root. Codex structured output rejects a root anyOf, and status
|
|
15
|
+
* stays a required three-state enum with no omitted-status failure branch.
|
|
16
|
+
* External-dependency failure while the seat can still submit:
|
|
17
|
+
* docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md
|
|
18
|
+
*/
|
|
10
19
|
export const reviewSubmissionSchema = withTerminatingOutputDeclarations(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
Type.Object({
|
|
21
|
+
status: Type.Union(
|
|
22
|
+
[
|
|
23
|
+
Type.Literal(REVIEW_QUEUE_WORDS[0]),
|
|
24
|
+
Type.Literal(REVIEW_QUEUE_WORDS[1]),
|
|
25
|
+
Type.Literal(REVIEW_QUEUE_WORDS[2]),
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
description: "审核席本轮裁决的判别状态。",
|
|
29
|
+
},
|
|
30
|
+
),
|
|
16
31
|
findings: Type.Optional(Type.Unknown({ description: "审核发现,原样留存" })),
|
|
17
32
|
reason: Type.Optional(Type.Unknown({ description: "上呈理由" })),
|
|
18
33
|
violations: Type.Optional(Type.Unknown({ description: "违规条目" })),
|
|
@@ -22,8 +37,7 @@ export const reviewSubmissionSchema = withTerminatingOutputDeclarations(
|
|
|
22
37
|
note: Type.Optional(Type.Unknown({ description: "裁决附注" })),
|
|
23
38
|
evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
|
|
24
39
|
decisionGate: Type.Optional(Type.Unknown({ description: "需人权威处置的问题与选项;可说明 question、options,原样留存" })),
|
|
25
|
-
}),
|
|
26
|
-
),
|
|
40
|
+
}, { additionalProperties: true }),
|
|
27
41
|
);
|
|
28
42
|
|
|
29
43
|
export type ReviewSubmission = { readonly status?: unknown; readonly [key: string]: unknown };
|
package/src/reviewer-role.ts
CHANGED
|
@@ -29,7 +29,7 @@ const reviewerAmendmentsSchema = Type.Object({
|
|
|
29
29
|
}, {
|
|
30
30
|
additionalProperties: true,
|
|
31
31
|
description:
|
|
32
|
-
"已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens
|
|
32
|
+
"已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。",
|
|
33
33
|
});
|
|
34
34
|
// #836 r16 class 1: diagnostic is LLM/human-read narrative content — no code
|
|
35
35
|
// branches on its length (src/reviewer-role.ts consumer: reviewer content is
|
|
@@ -40,7 +40,7 @@ const reviewerAmendmentsSchema = Type.Object({
|
|
|
40
40
|
// #917 §6: both normal lens verdicts → completed; hard-stop/usage error → refused
|
|
41
41
|
// (report already produced still lands in selected-lens amendments).
|
|
42
42
|
const REVIEWER_STATUS_DESCRIPTION =
|
|
43
|
-
"completed | refused
|
|
43
|
+
"completed | refused。两种正常 lens verdict(completeness / correctness)均 completed;hard-stop 与 usage error 为 refused(已产出报告仍照录进所选 lens amendments)。" as const;
|
|
44
44
|
const reviewerOutputVariants = Type.Union([
|
|
45
45
|
Type.Object({
|
|
46
46
|
status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
|
|
@@ -90,7 +90,7 @@ export function createReviewerRoleRuntime(
|
|
|
90
90
|
|
|
91
91
|
if (!registered) {
|
|
92
92
|
registered = true;
|
|
93
|
-
pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "
|
|
93
|
+
pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "提交御史台终局回执。", promptSnippet: "提交御史台终局回执", parameters: reviewerOutputSchema,
|
|
94
94
|
async execute(_id: string, parameters: unknown): Promise<HostToolResult<unknown>> {
|
|
95
95
|
if (!soul) throw new Error("御史台输入未装载");
|
|
96
96
|
return {
|
|
@@ -7,7 +7,7 @@ import { loadDoctorCase } from "./doctor-evidence.ts";
|
|
|
7
7
|
import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "./navigator-attendance.ts";
|
|
8
8
|
import { loadNavigatorWorkContext } from "./navigator-work-context.ts";
|
|
9
9
|
import { loadNotarySourceRunLocator } from "./notary-source-run.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { type RoleRuntimeDependencies } from "./role-runtime.ts";
|
|
11
11
|
import {
|
|
12
12
|
loadAuditorReferenceMaterialsFromSubjectInput,
|
|
13
13
|
loadAuditorSoulFromSubjectInput,
|
|
@@ -42,7 +42,6 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
42
42
|
// packageRoot is the install root (resources/ lives there). Never resolve via
|
|
43
43
|
// import.meta.url — headless/acp production-host bundles live under dist/*/
|
|
44
44
|
// and would otherwise look for dist/resources/ (#962).
|
|
45
|
-
const navigatorRoutePlaybookPath = join(packageRoot, "resources/navigator-route-playbook.md");
|
|
46
45
|
const collectorHandbookSeedPath = join(packageRoot, "resources/collector-bot-handbook.md");
|
|
47
46
|
const doctorAuditor = createPiDoctorAuditor();
|
|
48
47
|
const navigatorSessionFactory = createNativeNavigatorSessionFactory();
|
|
@@ -72,9 +71,6 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
72
71
|
subject: options.subject,
|
|
73
72
|
authority: options.authority,
|
|
74
73
|
invocationId: options.invocationId,
|
|
75
|
-
loadSoul: () => loadRegisteredRoleSoul("navigator"),
|
|
76
|
-
loadRoutePlaybook: () => readFile(navigatorRoutePlaybookPath, "utf8"),
|
|
77
|
-
loadRoleHelp: async (role) => formatNavigatorRoleHelp(role),
|
|
78
74
|
createSession: navigatorSessionFactory,
|
|
79
75
|
...(options.contextError === undefined ? {} : { contextError: options.contextError }),
|
|
80
76
|
onEvent: options.onEvent,
|