@akagilnc/pi-workflow-roles 0.1.4977 → 0.1.4989
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 +460 -462
- package/dist/analyst-gate-cycles-read.js +16 -11
- package/dist/analyst-metric-families/gate-cycles.js +2 -2
- package/dist/audit-escalation.js +19 -4
- package/dist/compliance-transport.js +2 -2
- package/dist/countersign-contracts.js +6 -5
- package/dist/doctor-evidence.js +2 -1
- package/dist/gatekeeper-role.js +22 -43
- package/dist/headless-host/production-host.js +460 -462
- package/dist/inspector-contracts.js +3 -2
- package/dist/migrate-book-topology.js +59 -54
- package/dist/navigator-invocation-identity.js +6 -4
- package/dist/notary-contracts.js +3 -19
- package/dist/package-contracts/auditor-output.js +4 -19
- package/dist/package-contracts/judge-output.js +6 -5
- package/dist/package-contracts/terminating-tools.js +9 -3
- package/dist/packaged-role-registry.js +10 -12
- package/dist/public-cli/countersign-run.js +1 -1
- package/dist/public-cli/main.js +95 -87
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/settlement.js +15 -12
- package/dist/review-submission.js +20 -0
- package/dist/submission-errors.js +2 -2
- package/dist/submission-ledger.js +16 -2
- package/package.json +1 -1
- package/souls/doctor-auditor.md +1 -1
- package/souls/fixer-auditor.md +4 -4
- package/souls/gate-output-guide.md +2 -2
- package/souls/inspector.md +1 -1
- package/souls/judge-auditor.md +2 -2
- package/souls/notary.md +1 -1
- package/souls/quality-law.md +1 -1
- package/src/analyst-gate-cycles-read.ts +17 -9
- package/src/analyst-metric-families/gate-cycles.ts +2 -2
- package/src/audit-escalation.ts +27 -7
- package/src/auditor-role.ts +1 -1
- package/src/compliance-transport.ts +7 -7
- package/src/countersign-contracts.ts +10 -8
- package/src/countersign-role.ts +6 -35
- package/src/doctor-evidence.ts +2 -1
- package/src/doctor-role.ts +1 -2
- package/src/gatekeeper-role.ts +32 -57
- package/src/host-contracts.ts +4 -3
- package/src/inspector-contracts.ts +7 -6
- package/src/inspector-role.ts +3 -20
- package/src/judge-role.ts +31 -51
- package/src/navigator-invocation-identity.ts +16 -4
- package/src/notary-contracts.ts +8 -26
- package/src/notary-role.ts +1 -1
- package/src/package-contracts/auditor-output.ts +6 -27
- package/src/package-contracts/judge-output.ts +10 -8
- package/src/package-contracts/terminating-tools.ts +9 -3
- package/src/packaged-role-registry.ts +12 -13
- package/src/pi/adapter.ts +6 -5
- package/src/public-cli/countersign-run.ts +1 -1
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/settlement.ts +18 -15
- package/src/review-submission.ts +29 -0
- package/src/role-envelope.ts +17 -9
- package/src/role-runtime.ts +32 -15
- package/src/submission-errors.ts +6 -6
- package/src/{gatekeeper-pass-envelope.ts → submission-gate.ts} +29 -16
- package/src/submission-ledger.ts +18 -3
- package/src/ticket-trajectory.ts +9 -4
- package/src/worker-role.ts +19 -5
package/src/host-contracts.ts
CHANGED
|
@@ -349,7 +349,7 @@ type HostGatekeeperSubject = {
|
|
|
349
349
|
| "secretariat_verdict";
|
|
350
350
|
};
|
|
351
351
|
/** Gatekeeper non-pass faces returned to parent (#836 includes transport_failure; never kill leg). */
|
|
352
|
-
type HostGatekeeperNonPass = { readonly status: "
|
|
352
|
+
type HostGatekeeperNonPass = { readonly status: "continue" | "escalate" | "no_receipt" | "transport_failure" } & Record<string, unknown>;
|
|
353
353
|
export type HostSubmissionNonPass =
|
|
354
354
|
| HostGatekeeperNonPass
|
|
355
355
|
| { readonly code: "coder_skill_expansion_evidence_missing" };
|
|
@@ -400,10 +400,10 @@ export interface RoleHost {
|
|
|
400
400
|
setActiveTools(names: string[]): void;
|
|
401
401
|
getActiveTools(): string[];
|
|
402
402
|
/**
|
|
403
|
-
* Shared gate
|
|
403
|
+
* Shared submission gate. On converged/continue returns officer snapshot (receipt + nested
|
|
404
404
|
* runId) for seat public-terminal projection (#969); callers may ignore it.
|
|
405
405
|
*/
|
|
406
|
-
|
|
406
|
+
requireSubmissionGate?(options: {
|
|
407
407
|
context: HostContext;
|
|
408
408
|
subject: HostGatekeeperSubject;
|
|
409
409
|
signal?: AbortSignal;
|
|
@@ -411,6 +411,7 @@ export interface RoleHost {
|
|
|
411
411
|
toolCallId: string;
|
|
412
412
|
submission?: unknown;
|
|
413
413
|
}): Promise<void | {
|
|
414
|
+
readonly status: "converged" | "continue";
|
|
414
415
|
readonly officer: string;
|
|
415
416
|
readonly receipt: unknown;
|
|
416
417
|
readonly runId?: string;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public Inspector (台院) terminating receipt contracts.
|
|
3
|
-
* Lawful explicit releases:
|
|
3
|
+
* Lawful explicit releases: converged | continue | escalate.
|
|
4
4
|
* Dual path: gate-dispatched and independently callable (#568 / ADR 0074).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
8
|
+
|
|
9
|
+
export const INSPECTOR_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
8
10
|
export const INSPECTOR_SOURCE_RUN_FLAG = {
|
|
9
11
|
name: "ak-inspector-source-run",
|
|
10
12
|
definition: {
|
|
@@ -14,8 +16,8 @@ export const INSPECTOR_SOURCE_RUN_FLAG = {
|
|
|
14
16
|
} as const;
|
|
15
17
|
|
|
16
18
|
export type InspectorOutput =
|
|
17
|
-
| { readonly status: "
|
|
18
|
-
| { readonly status: "
|
|
19
|
+
| { readonly status: "converged"; readonly findings?: unknown }
|
|
20
|
+
| { readonly status: "continue"; readonly findings?: unknown }
|
|
19
21
|
| { readonly status: "escalate"; readonly reason?: unknown; readonly findings?: unknown };
|
|
20
22
|
|
|
21
23
|
export function validateRecordedInspectorOutput(value: unknown): InspectorOutput {
|
|
@@ -28,9 +30,8 @@ export function validateRecordedInspectorOutput(value: unknown): InspectorOutput
|
|
|
28
30
|
} catch {
|
|
29
31
|
throw new Error("Inspector output has no execution discriminator");
|
|
30
32
|
}
|
|
31
|
-
if (status === "
|
|
33
|
+
if (status === "converged" || status === "continue" || status === "escalate") {
|
|
32
34
|
return value as InspectorOutput;
|
|
33
35
|
}
|
|
34
36
|
throw new Error("Inspector output has no execution discriminator");
|
|
35
37
|
}
|
|
36
|
-
|
package/src/inspector-role.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Static } from "typebox";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
|
-
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
2
|
+
import { reviewSubmissionSchema } from "./review-submission.ts";
|
|
6
3
|
import {
|
|
7
4
|
INSPECTOR_OUTPUT_TOOL_NAME,
|
|
8
5
|
validateRecordedInspectorOutput,
|
|
@@ -17,21 +14,7 @@ export type { InspectorOutput };
|
|
|
17
14
|
export { validateRecordedInspectorOutput };
|
|
18
15
|
|
|
19
16
|
/** 台院事后察举交卷形状;形状指引,非 schema 闸。 */
|
|
20
|
-
export const inspectorOutputSchema =
|
|
21
|
-
openToolObject(
|
|
22
|
-
Type.Object({
|
|
23
|
-
status: Type.Unknown({
|
|
24
|
-
description: "pass | bounce | escalate — 形状指引,非 schema 闸",
|
|
25
|
-
}),
|
|
26
|
-
findings: Type.Unknown({
|
|
27
|
-
description: "随交卷留存的问题记录",
|
|
28
|
-
}),
|
|
29
|
-
reason: Type.Optional(Type.Unknown({
|
|
30
|
-
description: "status 为 escalate 时的上呈理由",
|
|
31
|
-
})),
|
|
32
|
-
}),
|
|
33
|
-
),
|
|
34
|
-
);
|
|
17
|
+
export const inspectorOutputSchema = reviewSubmissionSchema;
|
|
35
18
|
|
|
36
19
|
export type InspectorOutputParameters = Static<typeof inspectorOutputSchema>;
|
|
37
20
|
|
|
@@ -45,7 +28,7 @@ export type InspectorRuntimeDependencies = {
|
|
|
45
28
|
export const INSPECTOR_TOOL_SPEC = {
|
|
46
29
|
name: INSPECTOR_OUTPUT_TOOL_NAME,
|
|
47
30
|
label: "台院输出",
|
|
48
|
-
description: "台院终局回执,状态为
|
|
31
|
+
description: "台院终局回执,状态为 converged、continue 或 escalate。",
|
|
49
32
|
promptSnippet: "台院终局回执",
|
|
50
33
|
parameters: inspectorOutputSchema,
|
|
51
34
|
} as const;
|
package/src/judge-role.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult, HostGatekeeperActions } from "./host-contracts.ts";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
2
|
+
import type { Static } from "typebox";
|
|
3
|
+
import { reviewSubmissionSchema } from "./review-submission.ts";
|
|
5
4
|
import { GatekeeperDecisionError, ParentQueueReaskError } from "./submission-errors.ts";
|
|
6
|
-
import {
|
|
5
|
+
import { projectGatekeeperEscalation } from "./audit-escalation.ts";
|
|
7
6
|
|
|
8
7
|
import { readableGateItem } from "./readable-gate-item.ts";
|
|
9
8
|
import {
|
|
@@ -14,50 +13,17 @@ import {
|
|
|
14
13
|
|
|
15
14
|
const JUDGE_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
|
|
16
15
|
const JUDGE_STATUS_REASK =
|
|
17
|
-
"
|
|
16
|
+
"status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
|
|
18
17
|
|
|
19
18
|
export { JUDGE_OUTPUT_TOOL_NAME };
|
|
20
19
|
export type { JudgeVerdict };
|
|
21
20
|
|
|
22
21
|
// #836 r16 class 1: fix/classes/note/decisionGate are LLM/human-read narrative
|
|
23
22
|
// content — 符宝郎/审刑院 read the raw receipt, no code branches on their length
|
|
24
|
-
// or nested presence. `
|
|
25
|
-
// queue reads to pick
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
// value before the rawStatus/ParentQueueReaskError check below ever runs.
|
|
29
|
-
export const judgeVerdictSchema = withTerminatingOutputDeclarations(
|
|
30
|
-
Type.Object(
|
|
31
|
-
{
|
|
32
|
-
judgeStatus: Type.Unknown({ description: "converged | continue | escalate — 形状指引,非 schema 闸" }),
|
|
33
|
-
fix: Type.Optional(
|
|
34
|
-
Type.Object(
|
|
35
|
-
{ summary: Type.Optional(Type.String({ description: "continue 时的补救摘要" })) },
|
|
36
|
-
{ additionalProperties: true, description: "continue 时的补救说明" },
|
|
37
|
-
),
|
|
38
|
-
),
|
|
39
|
-
classes: Type.Optional(Type.Array(Type.Object({
|
|
40
|
-
name: Type.Optional(Type.String()),
|
|
41
|
-
owner: Type.Optional(Type.String()),
|
|
42
|
-
boundary: Type.Optional(Type.String()),
|
|
43
|
-
disposition: Type.Optional(Type.String()),
|
|
44
|
-
}, { additionalProperties: true }), { description: "已裁决 finding 类及其 owner 与修理边界" })),
|
|
45
|
-
note: Type.Optional(Type.String({ description: "可选裁决附注" })),
|
|
46
|
-
evidence: Type.Optional(Type.Unknown({ description: "留存的裁决证据" })),
|
|
47
|
-
decisionGate: Type.Optional(
|
|
48
|
-
Type.Object(
|
|
49
|
-
{
|
|
50
|
-
question: Type.Optional(Type.String()),
|
|
51
|
-
options: Type.Optional(Type.Array(Type.String())),
|
|
52
|
-
},
|
|
53
|
-
{ additionalProperties: true, description: "需人权威处置的问题与选项" },
|
|
54
|
-
),
|
|
55
|
-
),
|
|
56
|
-
},
|
|
57
|
-
{ additionalProperties: true },
|
|
58
|
-
),
|
|
59
|
-
);
|
|
60
|
-
(judgeVerdictSchema as unknown as { required: string[] }).required = [];
|
|
23
|
+
// or nested presence. `status` alone is the machine discriminator the
|
|
24
|
+
// queue reads to pick converged/continue/escalate (below).
|
|
25
|
+
// The shared open schema preserves role-specific prose without adding another contract.
|
|
26
|
+
export const judgeVerdictSchema = reviewSubmissionSchema;
|
|
61
27
|
|
|
62
28
|
type JudgeVerdictParameters = Static<typeof judgeVerdictSchema>;
|
|
63
29
|
|
|
@@ -97,12 +63,12 @@ export function createJudgeRoleRuntime(
|
|
|
97
63
|
parameters: judgeVerdictSchema,
|
|
98
64
|
async execute(toolCallId: string, parameters: Static<typeof judgeVerdictSchema>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {
|
|
99
65
|
if (soul === undefined) throw new Error("大理寺职分未装载");
|
|
100
|
-
// #756 queue: read
|
|
66
|
+
// #756 queue: read shared status only — escalate skips gates;
|
|
101
67
|
// unreadable status returns to judge; else 符宝郎内闸 then 审刑院合规.
|
|
102
68
|
const rawStatus =
|
|
103
69
|
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
104
|
-
&& typeof (parameters as Record<string, unknown>).
|
|
105
|
-
? (parameters as Record<string, unknown>).
|
|
70
|
+
&& typeof (parameters as Record<string, unknown>).status === "string"
|
|
71
|
+
? (parameters as Record<string, unknown>).status as string
|
|
106
72
|
: undefined;
|
|
107
73
|
if (rawStatus === undefined || !JUDGE_QUEUE_STATUSES.has(rawStatus)) {
|
|
108
74
|
throw new ParentQueueReaskError(JUDGE_STATUS_REASK);
|
|
@@ -119,10 +85,10 @@ export function createJudgeRoleRuntime(
|
|
|
119
85
|
const verdict = validateVerdict(parameters);
|
|
120
86
|
// Candidate verdict is already on the parent session books as this
|
|
121
87
|
// tool-call leaf (first-record-then-audit; run 019fea05 L61/L62).
|
|
122
|
-
// #753: 符宝郎内闸 —
|
|
123
|
-
let draftPass: { receipt: unknown } | undefined;
|
|
88
|
+
// #753: 符宝郎内闸 — continue returns the raw receipt as a nonterminal tool result.
|
|
89
|
+
let draftPass: { status: "converged" | "continue"; receipt: unknown } | undefined;
|
|
124
90
|
try {
|
|
125
|
-
const obtainedDraftPass = await pi.
|
|
91
|
+
const obtainedDraftPass = await pi.requireSubmissionGate!({
|
|
126
92
|
context: ctx,
|
|
127
93
|
subject: { kind: "judge_draft" },
|
|
128
94
|
...(signal === undefined ? {} : { signal }),
|
|
@@ -132,11 +98,18 @@ export function createJudgeRoleRuntime(
|
|
|
132
98
|
submission: parameters,
|
|
133
99
|
});
|
|
134
100
|
draftPass = obtainedDraftPass || undefined;
|
|
101
|
+
if (draftPass?.status === "continue") {
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text" as const, text: readableGateItem(draftPass.receipt) }],
|
|
104
|
+
details: verdict,
|
|
105
|
+
terminate: false,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
135
108
|
if (draftPass !== undefined) hostActions.bindPriorGatePass(toolCallId, draftPass.receipt);
|
|
136
109
|
// #756: 审刑院合规路径 — same review-queue law as 符宝郎/台院.
|
|
137
|
-
//
|
|
110
|
+
// converged → accept; continue → raw auditor receipt; escalate → pause;
|
|
138
111
|
// not three-state → resume auditor; no round cap; no disposeCompliance mapping.
|
|
139
|
-
const compliancePass = await pi.
|
|
112
|
+
const compliancePass = await pi.requireSubmissionGate!({
|
|
140
113
|
context: ctx,
|
|
141
114
|
subject: { kind: "judge_compliance" },
|
|
142
115
|
...(signal === undefined ? {} : { signal }),
|
|
@@ -145,6 +118,13 @@ export function createJudgeRoleRuntime(
|
|
|
145
118
|
// #879: same parent payload for 审刑院; not recovered from session latest.
|
|
146
119
|
submission: parameters,
|
|
147
120
|
});
|
|
121
|
+
if (compliancePass?.status === "continue") {
|
|
122
|
+
return {
|
|
123
|
+
content: [draftPass, compliancePass].filter((pass) => pass !== undefined).map((pass) => ({ type: "text" as const, text: readableGateItem(pass.receipt) })),
|
|
124
|
+
details: verdict,
|
|
125
|
+
terminate: false,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
148
128
|
return {
|
|
149
129
|
content: [draftPass, compliancePass].filter((pass) => pass !== undefined).map((pass) => ({ type: "text" as const, text: readableGateItem(pass.receipt) })),
|
|
150
130
|
details: verdict,
|
|
@@ -152,7 +132,7 @@ export function createJudgeRoleRuntime(
|
|
|
152
132
|
};
|
|
153
133
|
} catch (error) {
|
|
154
134
|
if (error instanceof GatekeeperDecisionError && error.result.status === "escalate") {
|
|
155
|
-
return
|
|
135
|
+
return projectGatekeeperEscalation(error.result, verdict);
|
|
156
136
|
}
|
|
157
137
|
throw error;
|
|
158
138
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* - marker role/phase/subjectKey must match the terminal + independent expected
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { PACKAGED_ROLE_REGISTRY } from "./packaged-role-registry.ts";
|
|
27
|
+
import { LEGACY_REVIEW_OUTPUT_ROLES, PACKAGED_ROLE_REGISTRY } from "./packaged-role-registry.ts";
|
|
28
28
|
import { isUuidV7, uuidv7 } from "./uuidv7.ts";
|
|
29
29
|
import { workSubjectKeysEqual } from "./work-subject-identity.ts";
|
|
30
30
|
|
|
@@ -113,7 +113,8 @@ export function isNavigatorInfrastructureFailureFact(
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
const PACKAGED_ROLE_OUTPUT_TOOLS: ReadonlyMap<string, string> = new Map(
|
|
116
|
-
PACKAGED_ROLE_REGISTRY.
|
|
116
|
+
[...PACKAGED_ROLE_REGISTRY.filter((entry) => entry.outputTool !== "ak_submission_output")
|
|
117
|
+
.map((entry) => [entry.outputTool, entry.role] as const), ...LEGACY_REVIEW_OUTPUT_ROLES],
|
|
117
118
|
);
|
|
118
119
|
|
|
119
120
|
export type NavigatorInvocationEntryLike = {
|
|
@@ -226,7 +227,7 @@ export function classifyPackagedRoleTerminalResult(
|
|
|
226
227
|
message: PackagedRoleTerminalMessage,
|
|
227
228
|
): PackagedRoleTerminalClassification {
|
|
228
229
|
if (typeof message.toolName !== "string") return { kind: "nonterminal" };
|
|
229
|
-
if (!PACKAGED_ROLE_OUTPUT_TOOLS.has(message.toolName)) return { kind: "nonterminal" };
|
|
230
|
+
if (!PACKAGED_ROLE_OUTPUT_TOOLS.has(message.toolName) && message.toolName !== "ak_submission_output") return { kind: "nonterminal" };
|
|
230
231
|
|
|
231
232
|
// Base identity is enough; durable details may carry typed failure evidence (#475).
|
|
232
233
|
const hasInfraBase = hasNavigatorInfrastructureFailureBase(message.details);
|
|
@@ -285,12 +286,23 @@ function durableTerminalAt(
|
|
|
285
286
|
? entry.message
|
|
286
287
|
: undefined;
|
|
287
288
|
if (typeof message?.toolName !== "string") return undefined;
|
|
288
|
-
const
|
|
289
|
+
const marker = message.toolName === "ak_submission_output"
|
|
290
|
+
? entries.slice(0, index).reverse().find(isInvocationMarkerEntry)
|
|
291
|
+
: undefined;
|
|
292
|
+
const role = message.toolName === "ak_submission_output"
|
|
293
|
+
? parseInvocationMarkerIdentity(marker?.data)?.role ?? "unknown"
|
|
294
|
+
: PACKAGED_ROLE_OUTPUT_TOOLS.get(message.toolName);
|
|
289
295
|
if (role === undefined) return undefined;
|
|
290
296
|
const classification = classifyPackagedRoleTerminalResult(message);
|
|
291
297
|
if (classification.kind !== "accepted" && classification.kind !== "infrastructure") {
|
|
292
298
|
return undefined;
|
|
293
299
|
}
|
|
300
|
+
// A non-error toolResult is only a submitted candidate: the gate may still
|
|
301
|
+
// return continue even when the parent's own status was converged. Current
|
|
302
|
+
// invocations seal via the ledger-owned closure entry. Historical named
|
|
303
|
+
// review tools predate that entry and remain readable as legacy terminals.
|
|
304
|
+
if (entry?.type === "message" && classification.kind === "accepted"
|
|
305
|
+
&& !LEGACY_REVIEW_OUTPUT_ROLES.has(message.toolName)) return undefined;
|
|
294
306
|
return {
|
|
295
307
|
index,
|
|
296
308
|
role,
|
package/src/notary-contracts.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public Notary (符宝郎) terminating receipt contracts.
|
|
3
|
-
* Lawful explicit releases:
|
|
3
|
+
* Lawful explicit releases: converged | continue | escalate.
|
|
4
4
|
* No usable result is infrastructure failure via public settlement, not a judgment status (#475).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME, reviewSubmissionSchema } from "./review-submission.ts";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
10
|
-
|
|
11
|
-
export const NOTARY_OUTPUT_TOOL_NAME = "ak_notary_output";
|
|
8
|
+
export const NOTARY_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
12
9
|
export const NOTARY_SOURCE_RUN_FLAG = {
|
|
13
10
|
name: "ak-notary-source-run",
|
|
14
11
|
definition: {
|
|
@@ -30,21 +27,7 @@ export const NOTARY_TICKET_FLAG = {
|
|
|
30
27
|
export const NOTARY_FIXED_KICKOFF =
|
|
31
28
|
"符宝郎案卷已受理;来源 run 定位见会话材料。";
|
|
32
29
|
|
|
33
|
-
export const notaryOutputSchema =
|
|
34
|
-
openToolObject(
|
|
35
|
-
Type.Object({
|
|
36
|
-
status: Type.Unknown({
|
|
37
|
-
description: "pass | bounce | escalate — 形状指引,非 schema 闸",
|
|
38
|
-
}),
|
|
39
|
-
findings: Type.Unknown({
|
|
40
|
-
description: "string[] findings,随 pass、bounce 或 escalate 留存",
|
|
41
|
-
}),
|
|
42
|
-
reason: Type.Optional(Type.Unknown({
|
|
43
|
-
description: "status 为 escalate 时的上呈理由",
|
|
44
|
-
})),
|
|
45
|
-
}),
|
|
46
|
-
),
|
|
47
|
-
);
|
|
30
|
+
export const notaryOutputSchema = reviewSubmissionSchema;
|
|
48
31
|
|
|
49
32
|
export type NotarySourceRunLocator = {
|
|
50
33
|
readonly runDirectory: string;
|
|
@@ -54,8 +37,8 @@ export type NotarySourceRunLocator = {
|
|
|
54
37
|
|
|
55
38
|
/** Recognition face only — fields retained as submitted (#753, no disposition/findings forge). */
|
|
56
39
|
export type NotaryOutput =
|
|
57
|
-
| { readonly status: "
|
|
58
|
-
| { readonly status: "
|
|
40
|
+
| { readonly status: "converged"; readonly findings?: unknown }
|
|
41
|
+
| { readonly status: "continue"; readonly findings?: unknown }
|
|
59
42
|
| {
|
|
60
43
|
readonly status: "escalate";
|
|
61
44
|
readonly reason?: unknown;
|
|
@@ -67,7 +50,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
67
50
|
}
|
|
68
51
|
|
|
69
52
|
/**
|
|
70
|
-
* Recognize one lawful explicit Notary release (
|
|
53
|
+
* Recognize one lawful explicit Notary release (converged | continue | escalate).
|
|
71
54
|
* #753: no field rewrite — submitted params retained as-is (no disposition forge,
|
|
72
55
|
* no findings array rewrite). Recognition only for recording/settlement callers.
|
|
73
56
|
*/
|
|
@@ -89,7 +72,7 @@ export function retainNotarySubmission(value: unknown): unknown {
|
|
|
89
72
|
}
|
|
90
73
|
|
|
91
74
|
/**
|
|
92
|
-
* Settlement/recording path: only lawful recorded
|
|
75
|
+
* Settlement/recording path: only lawful recorded converged/continue/escalate.
|
|
93
76
|
* Does not gate role admission — callers must not use this to reject a submission.
|
|
94
77
|
*/
|
|
95
78
|
export function validateRecordedNotaryOutput(value: unknown): NotaryOutput {
|
|
@@ -98,4 +81,3 @@ export function validateRecordedNotaryOutput(value: unknown): NotaryOutput {
|
|
|
98
81
|
}
|
|
99
82
|
return value as NotaryOutput;
|
|
100
83
|
}
|
|
101
|
-
|
package/src/notary-role.ts
CHANGED
|
@@ -159,7 +159,7 @@ export function createNotaryRoleRuntime(
|
|
|
159
159
|
pi.registerTool({
|
|
160
160
|
name: NOTARY_OUTPUT_TOOL_NAME,
|
|
161
161
|
label: "符宝郎输出",
|
|
162
|
-
description: "提交引文保真与票面对齐的
|
|
162
|
+
description: "提交引文保真与票面对齐的 converged/continue/escalate 决议。",
|
|
163
163
|
promptSnippet: "提交符宝郎决议",
|
|
164
164
|
parameters: notaryOutputSchema,
|
|
165
165
|
async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext, ): Promise<HostToolResult<unknown>> {
|
|
@@ -1,36 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public Auditor (审刑院) terminating receipt contracts (#675 / #754).
|
|
3
|
-
* Lawful explicit releases:
|
|
3
|
+
* Lawful explicit releases: converged | continue | escalate.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME, reviewSubmissionSchema } from "../review-submission.ts";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
|
|
7
|
+
export const AUDITOR_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
9
8
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
export const auditorOutputSchema = withTerminatingOutputDeclarations(
|
|
13
|
-
openToolObject(
|
|
14
|
-
Type.Object({
|
|
15
|
-
status: Type.Unknown({
|
|
16
|
-
description: "pass | bounce | escalate — 形状指引,非 schema 闸",
|
|
17
|
-
}),
|
|
18
|
-
violations: Type.Optional(Type.Unknown({
|
|
19
|
-
description: "status 为 bounce 时的违规条目",
|
|
20
|
-
})),
|
|
21
|
-
conflicts: Type.Optional(Type.Unknown({
|
|
22
|
-
description: "status 为 escalate 时的冲突",
|
|
23
|
-
})),
|
|
24
|
-
decisionGate: Type.Optional(Type.Unknown({
|
|
25
|
-
description: "status 为 escalate 时的决策闸",
|
|
26
|
-
})),
|
|
27
|
-
}),
|
|
28
|
-
),
|
|
29
|
-
);
|
|
9
|
+
export const auditorOutputSchema = reviewSubmissionSchema;
|
|
30
10
|
|
|
31
11
|
export type AuditorOutput =
|
|
32
|
-
| { readonly status: "
|
|
33
|
-
| { readonly status: "
|
|
12
|
+
| { readonly status: "converged" }
|
|
13
|
+
| { readonly status: "continue"; readonly violations?: unknown }
|
|
34
14
|
| {
|
|
35
15
|
readonly status: "escalate";
|
|
36
16
|
readonly conflicts?: unknown;
|
|
@@ -50,4 +30,3 @@ export function validateRecordedAuditorOutput(value: unknown): AuditorOutput {
|
|
|
50
30
|
if (!isRecord(value)) throw new Error("Auditor output is not an object");
|
|
51
31
|
return value as AuditorOutput;
|
|
52
32
|
}
|
|
53
|
-
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** Package-owned Judge output leaf — no role registration surface. */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "../review-submission.ts";
|
|
5
|
+
|
|
6
|
+
export const JUDGE_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
5
7
|
|
|
6
8
|
export type JudgeClass = {
|
|
7
9
|
name: string;
|
|
@@ -11,16 +13,16 @@ export type JudgeClass = {
|
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export type JudgeVerdict =
|
|
14
|
-
| {
|
|
16
|
+
| { status: "converged"; note?: string; evidence?: unknown }
|
|
15
17
|
| {
|
|
16
|
-
|
|
18
|
+
status: "continue";
|
|
17
19
|
fix: { summary: string };
|
|
18
20
|
classes: JudgeClass[];
|
|
19
21
|
note?: string;
|
|
20
22
|
evidence?: unknown;
|
|
21
23
|
}
|
|
22
24
|
| {
|
|
23
|
-
|
|
25
|
+
status: "escalate";
|
|
24
26
|
decisionGate: { question: string; options: string[] };
|
|
25
27
|
note?: string;
|
|
26
28
|
evidence?: unknown;
|
|
@@ -28,17 +30,17 @@ export type JudgeVerdict =
|
|
|
28
30
|
|
|
29
31
|
export function validateAcceptedJudgeDetails(verdict: unknown): JudgeVerdict {
|
|
30
32
|
if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) throw new Error("Judge verdict has no execution discriminator");
|
|
31
|
-
let
|
|
33
|
+
let status: unknown;
|
|
32
34
|
try {
|
|
33
|
-
|
|
35
|
+
status = (verdict as Record<string, unknown>).status;
|
|
34
36
|
} catch {
|
|
35
37
|
throw new Error("Judge verdict has no execution discriminator");
|
|
36
38
|
}
|
|
37
|
-
if (typeof
|
|
39
|
+
if (typeof status !== "string") {
|
|
38
40
|
throw new Error("Judge verdict has no execution discriminator");
|
|
39
41
|
}
|
|
40
42
|
if (
|
|
41
|
-
["converged", "continue", "escalate"].includes(
|
|
43
|
+
["converged", "continue", "escalate"].includes(status)
|
|
42
44
|
) {
|
|
43
45
|
return verdict as JudgeVerdict;
|
|
44
46
|
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
validateReviewerIntent,
|
|
19
19
|
type ReviewerIntent,
|
|
20
20
|
} from "./reviewer-output.ts";
|
|
21
|
-
import { PACKAGED_ROLE_REGISTRY } from "../packaged-role-registry.ts";
|
|
21
|
+
import { LEGACY_REVIEW_OUTPUT_ROLES, PACKAGED_ROLE_REGISTRY } from "../packaged-role-registry.ts";
|
|
22
22
|
import { CorrectableSubmissionError } from "../submission-correctable-error.ts";
|
|
23
23
|
import { DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput, type DoctorOutput, type DoctorSubmission } from "../doctor-contracts.ts";
|
|
24
24
|
import { GATEKEEPER_OUTPUT_TOOL_NAME, validateRecordedGatekeeperOutput, type GatekeeperDirectOutput } from "./gatekeeper-output.ts";
|
|
@@ -80,7 +80,10 @@ export type {
|
|
|
80
80
|
SecretariatVerdict,
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
export const TERMINATING_TOOL_NAMES =
|
|
83
|
+
export const TERMINATING_TOOL_NAMES = [...new Set([
|
|
84
|
+
...PACKAGED_ROLE_REGISTRY.map((entry) => entry.outputTool),
|
|
85
|
+
...LEGACY_REVIEW_OUTPUT_ROLES.keys(),
|
|
86
|
+
])];
|
|
84
87
|
|
|
85
88
|
export type TerminatingToolName = (typeof TERMINATING_TOOL_NAMES)[number];
|
|
86
89
|
|
|
@@ -152,7 +155,10 @@ export type AcceptedFacts = {
|
|
|
152
155
|
export function acceptedFacts(toolName: TerminatingToolName, details: AcceptedDetails): AcceptedFacts {
|
|
153
156
|
const record = details as Record<string, unknown>;
|
|
154
157
|
const entry = PACKAGED_ROLE_REGISTRY.find((candidate) => candidate.outputTool === toolName);
|
|
155
|
-
const
|
|
158
|
+
const legacyRole = LEGACY_REVIEW_OUTPUT_ROLES.get(toolName);
|
|
159
|
+
const statusKey = legacyRole === "judge" || legacyRole === "countersign"
|
|
160
|
+
? `${legacyRole}Status`
|
|
161
|
+
: entry !== undefined && "receiptStatusKey" in entry ? entry.receiptStatusKey : "status";
|
|
156
162
|
const status = typeof record[statusKey] === "string" ? record[statusKey] : undefined;
|
|
157
163
|
const commitKey = entry !== undefined && "receiptCommitKey" in entry ? entry.receiptCommitKey : undefined;
|
|
158
164
|
const commitWhen = entry !== undefined && "receiptCommitWhen" in entry ? entry.receiptCommitWhen : undefined;
|
|
@@ -17,6 +17,15 @@ import { DIARIST_OUTPUT_TOOL_NAME } from "./diarist-contracts.ts";
|
|
|
17
17
|
import { SECRETARIAT_OUTPUT_TOOL_NAME } from "./secretariat-contracts.ts";
|
|
18
18
|
import { JUDGE_AUDIT_TOOL_NAME } from "./judge-auditor.ts";
|
|
19
19
|
|
|
20
|
+
/** Historical durable session names; these are read aliases, never registered tools. */
|
|
21
|
+
export const LEGACY_REVIEW_OUTPUT_ROLES = new Map<string, string>([
|
|
22
|
+
["ak_judge_output", "judge"],
|
|
23
|
+
["ak_notary_output", "notary"],
|
|
24
|
+
["ak_countersign_output", "countersign"],
|
|
25
|
+
["ak_inspector_output", "inspector"],
|
|
26
|
+
["ak_auditor_output", "auditor"],
|
|
27
|
+
]);
|
|
28
|
+
|
|
20
29
|
/**
|
|
21
30
|
* Success-face fields that still differ by seat. Presence and omission match
|
|
22
31
|
* the former per-seat publishers. Key order is not part of the contract.
|
|
@@ -92,7 +101,7 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
92
101
|
/** Navigator subject is the public admitted instruction when the run is bound. */
|
|
93
102
|
navigatorSubject: "public-instruction",
|
|
94
103
|
activationStage: "load-and-install",
|
|
95
|
-
receiptStatusKey: "
|
|
104
|
+
receiptStatusKey: "status",
|
|
96
105
|
sessionMaterials: [
|
|
97
106
|
"CLAUDE.md",
|
|
98
107
|
"souls/judge.md",
|
|
@@ -374,15 +383,13 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
374
383
|
outputTool: COUNTERSIGN_OUTPUT_TOOL_NAME,
|
|
375
384
|
settlement: "accepted",
|
|
376
385
|
gateStageLabel: "给事中",
|
|
377
|
-
/** Gate queue reads countersignStatus, not the shared three-state status. */
|
|
378
|
-
gateDecision: "countersign-status",
|
|
379
386
|
/** Gate summon carries the parent payload as the instruction and a parent run id. */
|
|
380
387
|
gateSummon: "parent-instruction",
|
|
381
388
|
activationFlags: [
|
|
382
389
|
{ field: "ticketNumber" },
|
|
383
390
|
],
|
|
384
391
|
activationStage: "load-and-install",
|
|
385
|
-
receiptStatusKey: "
|
|
392
|
+
receiptStatusKey: "status",
|
|
386
393
|
// #924: 公用《票面法》三席同装
|
|
387
394
|
sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md"],
|
|
388
395
|
},
|
|
@@ -804,7 +811,7 @@ export function packagedGateStageLabel(role: string): string | undefined {
|
|
|
804
811
|
return record.gateStageLabel;
|
|
805
812
|
}
|
|
806
813
|
|
|
807
|
-
/**
|
|
814
|
+
/** How the gate caller builds one officer summons. Pointer is the inspector face. */
|
|
808
815
|
export type PackagedGateSummon = "source-run" | "subject-source" | "parent-instruction" | "pointer";
|
|
809
816
|
|
|
810
817
|
/** How the gate caller builds one officer summons. Pointer is the inspector face. */
|
|
@@ -813,11 +820,3 @@ export function packagedGateSummon(role: string): PackagedGateSummon {
|
|
|
813
820
|
if (record !== undefined && "gateSummon" in record) return record.gateSummon;
|
|
814
821
|
return "pointer";
|
|
815
822
|
}
|
|
816
|
-
|
|
817
|
-
export function packagedGateDecision(role: string): "countersign-status" | "three-state" {
|
|
818
|
-
const record = packagedRoleMetadata(role);
|
|
819
|
-
if (record !== undefined && "gateDecision" in record && record.gateDecision === "countersign-status") {
|
|
820
|
-
return "countersign-status";
|
|
821
|
-
}
|
|
822
|
-
return "three-state";
|
|
823
|
-
}
|
package/src/pi/adapter.ts
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
type ToolDefinition,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import type { Static, TSchema } from "typebox";
|
|
10
|
-
import type {
|
|
11
|
-
import {
|
|
10
|
+
import type { SubmissionGateNonPassResult, GatekeeperSubject } from "../gatekeeper-role.ts";
|
|
11
|
+
import { requireSubmissionGate } from "../submission-gate.ts";
|
|
12
12
|
import type {
|
|
13
13
|
HostContext,
|
|
14
14
|
HostEventRegistration,
|
|
@@ -85,7 +85,7 @@ export function toPiContext(context: HostContext): ExtensionContext {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/** Gatekeeper receives HostContext directly (#518 §1③) without recovering raw ExtensionContext via WeakMap. */
|
|
88
|
-
function
|
|
88
|
+
function requirePiSubmissionGate(options: {
|
|
89
89
|
context: HostContext;
|
|
90
90
|
subject: GatekeeperSubject;
|
|
91
91
|
signal?: AbortSignal;
|
|
@@ -93,11 +93,12 @@ function requirePiGatekeeperPass(options: {
|
|
|
93
93
|
toolCallId: string;
|
|
94
94
|
submission?: unknown;
|
|
95
95
|
}): Promise<void | {
|
|
96
|
+
readonly status: "converged" | "continue";
|
|
96
97
|
readonly officer: string;
|
|
97
98
|
readonly receipt: unknown;
|
|
98
99
|
readonly runId?: string;
|
|
99
100
|
}> {
|
|
100
|
-
return
|
|
101
|
+
return requireSubmissionGate({
|
|
101
102
|
context: options.context,
|
|
102
103
|
subject: options.subject,
|
|
103
104
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
@@ -203,7 +204,7 @@ export function createPiRoleHostAdapter(
|
|
|
203
204
|
})),
|
|
204
205
|
setActiveTools: (names) => pi.setActiveTools(names),
|
|
205
206
|
getActiveTools: () => pi.getActiveTools(),
|
|
206
|
-
|
|
207
|
+
requireSubmissionGate: requirePiSubmissionGate,
|
|
207
208
|
on(...registration: HostEventRegistration) {
|
|
208
209
|
const context = (value: ExtensionContext) => projectPiContext(value, options.transcriptFromContext);
|
|
209
210
|
if (registration[0] === "before_agent_start") {
|
|
@@ -89,7 +89,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
89
89
|
function isEscalatePayload(payload: unknown): boolean {
|
|
90
90
|
if (!isRecord(payload)) return false;
|
|
91
91
|
return (
|
|
92
|
-
payload.status === "escalate"
|
|
92
|
+
payload.status === "escalate"
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|