@akagilnc/pi-workflow-roles 0.1.3644 → 0.1.3652
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 +3089 -2187
- package/dist/activation-ledger-session.js +105 -0
- package/dist/activation-ledger.js +139 -0
- package/dist/activation-trace.js +33 -0
- package/dist/agent-start-materials.js +11 -0
- package/dist/audit-escalation.js +5 -5
- package/dist/auditor-role.js +12 -0
- package/dist/canonical-skill-binding.js +64 -0
- package/dist/collector-identity.js +151 -0
- package/dist/collector-receipt.js +95 -0
- package/dist/collector-role.js +441 -0
- package/dist/compliance-transport.js +11 -57
- package/dist/countersign-role.js +28 -0
- package/dist/diarist-contracts.js +0 -12
- package/dist/diarist-role.js +29 -0
- package/dist/doctor-contracts.js +0 -2
- package/dist/doctor-role.js +74 -0
- package/dist/fixer-bash-seatbelt.js +13 -0
- package/dist/gatekeeper-pass-envelope.js +72 -0
- package/dist/gleaner-left-contracts.js +0 -7
- package/dist/gleaner-left-role.js +26 -0
- package/dist/inspector-contracts.js +0 -4
- package/dist/inspector-role.js +29 -0
- package/dist/judge-role.js +106 -0
- package/dist/merger-role.js +66 -0
- package/dist/navigator-public-session.js +1 -5
- package/dist/navigator-role.js +12 -0
- package/dist/notary-contracts.js +0 -18
- package/dist/notary-role.js +137 -0
- package/dist/oauth-keepalive.js +158 -0
- package/dist/package-contracts/auditor-output.js +0 -13
- package/dist/package-contracts/gatekeeper-output.js +0 -10
- package/dist/package-contracts/judge-output.js +0 -2
- package/dist/package-contracts/navigator-output.js +0 -4
- package/dist/package-contracts/terminating-tools.js +2 -16
- package/dist/packaged-role-registry.js +1 -18
- package/dist/pi/adapter.js +211 -0
- package/dist/pi/role-turn-host.js +0 -2
- package/dist/public-cli/instruction-seat-run.js +2 -6
- package/dist/public-cli/invocation.js +0 -7
- package/dist/public-cli/main.js +138 -735
- package/dist/public-cli/option-definitions.js +0 -14
- package/dist/public-cli/registry.js +1 -2
- package/dist/public-cli/run-lifecycle.js +1 -3
- package/dist/public-cli/settlement.js +66 -537
- package/dist/public-role-summons.js +0 -14
- package/dist/reviewer-child-executor.js +163 -70
- package/dist/reviewer-construction.js +2 -2
- package/dist/reviewer-role.js +159 -0
- package/dist/reviewer-settlement.js +66 -0
- package/dist/role-runtime.js +1394 -0
- package/dist/stderr-jsonl.js +26 -0
- package/dist/tool-execution-observation.js +141 -0
- package/dist/worker-role.js +343 -0
- package/dist/worker-submission-gates.js +305 -0
- package/extensions/role-runtime.ts +1 -2
- package/package.json +1 -1
- package/src/acp-host/production-host.ts +0 -1
- package/src/audit-escalation.ts +9 -9
- package/src/compliance-transport.ts +28 -94
- package/src/diarist-contracts.ts +0 -12
- package/src/doctor-contracts.ts +0 -1
- package/src/doctor-role.ts +2 -2
- package/src/gleaner-left-contracts.ts +0 -9
- package/src/host-contracts.ts +0 -2
- package/src/inspector-contracts.ts +0 -8
- package/src/judge-role.ts +4 -5
- package/src/navigator-public-session.ts +4 -5
- package/src/notary-contracts.ts +0 -18
- package/src/package-contracts/auditor-output.ts +0 -11
- package/src/package-contracts/gatekeeper-output.ts +0 -11
- package/src/package-contracts/judge-output.ts +0 -1
- package/src/package-contracts/navigator-output.ts +0 -3
- package/src/package-contracts/terminating-tools.ts +1 -19
- package/src/packaged-role-registry.ts +1 -19
- package/src/pi/role-turn-host.ts +0 -2
- package/src/public-cli/cli.ts +1 -5
- package/src/public-cli/instruction-seat-run.ts +5 -14
- package/src/public-cli/invocation.ts +3 -21
- package/src/public-cli/option-definitions.ts +0 -16
- package/src/public-cli/registry.ts +1 -2
- package/src/public-cli/run-lifecycle.ts +0 -6
- package/src/public-cli/settlement.ts +58 -572
- package/src/public-cli/terminal.ts +0 -1
- package/src/public-role-summons.ts +0 -15
- package/src/reviewer-child-executor.ts +199 -98
- package/src/reviewer-construction.ts +3 -3
- package/src/role-runtime.ts +1 -33
- package/dist/package-contracts/evidence-child-output.js +0 -34
- package/souls/evidence-child.md +0 -1
- package/src/evidence-child-role.ts +0 -22
- package/src/package-contracts/evidence-child-output.ts +0 -51
|
@@ -198,20 +198,6 @@ async function summonPublicRole(options) {
|
|
|
198
198
|
);
|
|
199
199
|
break;
|
|
200
200
|
}
|
|
201
|
-
case "evidence-child": {
|
|
202
|
-
const [{ runPublicInstructionSeat }, { parseEvidenceChildArgv }] = await Promise.all([
|
|
203
|
-
import("./public-cli/instruction-seat-run.js"),
|
|
204
|
-
import("./public-cli/invocation.js")
|
|
205
|
-
]);
|
|
206
|
-
result = await runPublicInstructionSeat(
|
|
207
|
-
options.argv,
|
|
208
|
-
env,
|
|
209
|
-
io,
|
|
210
|
-
"evidence-child",
|
|
211
|
-
parseEvidenceChildArgv
|
|
212
|
-
);
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
201
|
case "navigator": {
|
|
216
202
|
const [{ runPublicInstructionSeat }, { parseNavigatorArgv }] = await Promise.all([
|
|
217
203
|
import("./public-cli/instruction-seat-run.js"),
|
|
@@ -1,4 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEngineDetourToolDefinition } from "./engine-detour-tool.js";
|
|
2
|
+
import { engineNameFromEnv } from "./engine-detour.js";
|
|
3
|
+
import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, } from "./package-resources/engine-material.js";
|
|
4
|
+
import { readPackageMaterial } from "./session-opening-materials.js";
|
|
5
|
+
import { hasUpstreamErrorTestimony, isNonSuccessHttpStatus } from "./upstream-error-testimony.js";
|
|
6
|
+
/** Path roster only — cadence prose stays in owner material (ADR 0073). */
|
|
7
|
+
const AXIS_SUBSESSION_MATERIALS = ["souls/quality-law.md"];
|
|
8
|
+
async function buildAxisSystemPrompt(engineMaterial) {
|
|
9
|
+
const materials = [];
|
|
10
|
+
for (const relativePath of AXIS_SUBSESSION_MATERIALS) {
|
|
11
|
+
materials.push(await readPackageMaterial(relativePath));
|
|
12
|
+
}
|
|
13
|
+
return appendEngineSessionMaterial(materials, engineMaterial).join("\n");
|
|
14
|
+
}
|
|
15
|
+
function classifiedError(error, evidenceChildFailure) {
|
|
16
|
+
const diagnostic = typeof error === "object" && error !== null && typeof error.errorMessage === "string"
|
|
17
|
+
? error.errorMessage
|
|
18
|
+
: error === undefined ? "" : String(error);
|
|
19
|
+
const wrapped = error instanceof Error
|
|
20
|
+
? error
|
|
21
|
+
: Object.assign(new Error(diagnostic, { cause: error }), { evidenceChildOriginal: error });
|
|
22
|
+
const classification = "evidenceChildFailure" in wrapped
|
|
23
|
+
? wrapped.evidenceChildFailure
|
|
24
|
+
: evidenceChildFailure;
|
|
25
|
+
return Object.assign(wrapped, { evidenceChildFailure: classification });
|
|
26
|
+
}
|
|
27
|
+
/** Read envelope-projected testimony on an assistant/stop node — no private walker copy. */
|
|
28
|
+
function nodeHasUpstreamTestimony(node) {
|
|
29
|
+
if (typeof node !== "object" || node === null)
|
|
30
|
+
return false;
|
|
31
|
+
const record = node;
|
|
32
|
+
const httpStatus = isNonSuccessHttpStatus(record.statusCode)
|
|
33
|
+
? record.statusCode
|
|
34
|
+
: isNonSuccessHttpStatus(record.status)
|
|
35
|
+
? record.status
|
|
36
|
+
: isNonSuccessHttpStatus(record.httpStatus)
|
|
37
|
+
? record.httpStatus
|
|
38
|
+
: undefined;
|
|
39
|
+
const diagnostics = Array.isArray(record.diagnostics) && record.diagnostics.length > 0
|
|
40
|
+
? record.diagnostics
|
|
41
|
+
: undefined;
|
|
42
|
+
return hasUpstreamErrorTestimony({
|
|
43
|
+
...(httpStatus === undefined ? {} : { httpStatus }),
|
|
44
|
+
...(diagnostics === undefined ? {} : { diagnostics }),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Parent seat model for the axis sub-session (pre-#675 parent-effective inheritance). Pi only. */
|
|
48
|
+
async function parentSelection(context) {
|
|
49
|
+
const { toPiContext } = await import("./pi/adapter.js");
|
|
50
|
+
let pi;
|
|
51
|
+
try {
|
|
52
|
+
pi = toPiContext(context);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw classifiedError(new Error("Reviewer Standards/Spec sub-session requires pi host context (other hosts: subagent not in this ticket)", { cause: error }), "child");
|
|
56
|
+
}
|
|
57
|
+
if (pi.model === undefined) {
|
|
58
|
+
throw classifiedError(new Error("Reviewer axis sub-session requires parent model"), "child");
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
provider: pi.model.provider,
|
|
62
|
+
model: pi.model.id,
|
|
63
|
+
...(pi.thinkingLevel === undefined ? {} : { thinking: String(pi.thinkingLevel) }),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
2
66
|
/**
|
|
3
67
|
* Single conversion at the Reviewer adapter boundary: shared child classifications
|
|
4
68
|
* become Reviewer failure classifications without a second error taxonomy.
|
|
@@ -12,39 +76,17 @@ export function projectSharedChildFailure(error) {
|
|
|
12
76
|
}
|
|
13
77
|
return error;
|
|
14
78
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
return "unknown";
|
|
79
|
+
function emptyUsage() {
|
|
80
|
+
return {
|
|
81
|
+
input: 0,
|
|
82
|
+
output: 0,
|
|
83
|
+
cacheRead: 0,
|
|
84
|
+
cacheWrite: 0,
|
|
85
|
+
totalTokens: 0,
|
|
86
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
87
|
+
};
|
|
26
88
|
}
|
|
27
|
-
|
|
28
|
-
const outcome = summoned.terminal?.roleOutcome;
|
|
29
|
-
if (outcome === undefined) {
|
|
30
|
-
throw Object.assign(new Error(`Evidence-child public summon produced no terminal (exit ${summoned.exitCode})`), { evidenceChildFailure: "child" });
|
|
31
|
-
}
|
|
32
|
-
if (outcome.kind === "failure") {
|
|
33
|
-
throw Object.assign(new Error(outcome.diagnostic), {
|
|
34
|
-
evidenceChildFailure: failureClassification(outcome.cause),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (outcome.kind === "accepted") {
|
|
38
|
-
// No field-presence reject (ADR 0055 / #675). Keep original accepted bytes:
|
|
39
|
-
// report key when present, otherwise the whole decisiveFacts body.
|
|
40
|
-
const facts = outcome.decisiveFacts;
|
|
41
|
-
if (Object.hasOwn(facts, "report"))
|
|
42
|
-
return facts.report;
|
|
43
|
-
return facts;
|
|
44
|
-
}
|
|
45
|
-
throw Object.assign(new Error("Evidence-child public summon returned no accepted report body"), { evidenceChildFailure: "child" });
|
|
46
|
-
}
|
|
47
|
-
/** Reviewer evidence leg via the public evidence-child activation path (#675). */
|
|
89
|
+
/** Reviewer Standards/Spec leg via pi in-process sub-session (#744). */
|
|
48
90
|
export async function executeReviewerChild(workspace, leg, context, options = {}) {
|
|
49
91
|
try {
|
|
50
92
|
if (options.signal?.aborted) {
|
|
@@ -52,47 +94,98 @@ export async function executeReviewerChild(workspace, leg, context, options = {}
|
|
|
52
94
|
evidenceChildFailure: "child",
|
|
53
95
|
});
|
|
54
96
|
}
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Hard path resolve: fail loud — no packageMachineHome fallback (#604 / #675).
|
|
68
|
-
home = homeFromRunDirectory(runDirectory);
|
|
69
|
-
}
|
|
70
|
-
return summonPublicRole({
|
|
71
|
-
role: "evidence-child",
|
|
72
|
-
argv: nextArgv,
|
|
73
|
-
cwd: workspace,
|
|
74
|
-
...(home === undefined ? {} : { home }),
|
|
75
|
-
...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
|
|
76
|
-
...(childSignal === undefined ? {} : { signal: childSignal }),
|
|
97
|
+
const signal = options.signal;
|
|
98
|
+
const selection = await parentSelection(context);
|
|
99
|
+
const { openPiInProcessSession } = await import("./pi/in-process-session.js");
|
|
100
|
+
const { createRecordSession } = await import("./archivist-record-entry.js");
|
|
101
|
+
const engineName = engineNameFromEnv();
|
|
102
|
+
const engineMaterial = engineName === undefined
|
|
103
|
+
? undefined
|
|
104
|
+
: options.packageRoot === undefined || options.packageRoot.trim() === ""
|
|
105
|
+
? Object.freeze({ name: engineName })
|
|
106
|
+
: engineSessionMaterialFromOptions({
|
|
107
|
+
engine: engineName,
|
|
108
|
+
packageRoot: options.packageRoot,
|
|
77
109
|
});
|
|
110
|
+
let engineDetourFailure;
|
|
111
|
+
const engineDetourTool = engineName === undefined
|
|
112
|
+
? undefined
|
|
113
|
+
: createEngineDetourToolDefinition({
|
|
114
|
+
engineName,
|
|
115
|
+
fail(error) {
|
|
116
|
+
engineDetourFailure ??= error instanceof Error ? error : new Error(String(error));
|
|
117
|
+
throw engineDetourFailure;
|
|
118
|
+
},
|
|
78
119
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
120
|
+
let opened;
|
|
121
|
+
try {
|
|
122
|
+
// No agentDir: envelope owns scratch via credentialScratchParent (ADR 0018).
|
|
123
|
+
opened = await openPiInProcessSession({
|
|
124
|
+
cwd: workspace,
|
|
125
|
+
selection,
|
|
126
|
+
systemPrompt: await buildAxisSystemPrompt(engineMaterial),
|
|
127
|
+
...(options.credentialScratchParent === undefined
|
|
128
|
+
? {}
|
|
129
|
+
: { credentialScratchParent: options.credentialScratchParent }),
|
|
130
|
+
...(engineDetourTool === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: { customTools: [engineDetourTool] }),
|
|
133
|
+
sessionManager: createRecordSession({
|
|
134
|
+
cwd: workspace,
|
|
135
|
+
kind: "evidence-children",
|
|
136
|
+
parent: context.sessionManager,
|
|
137
|
+
}),
|
|
138
|
+
...(signal === undefined ? {} : { signal }),
|
|
139
|
+
label: "Reviewer axis sub-session",
|
|
83
140
|
});
|
|
84
141
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw classifiedError(error, "provider");
|
|
144
|
+
}
|
|
145
|
+
const { handle } = opened;
|
|
146
|
+
try {
|
|
147
|
+
const delivered = leg.prompt;
|
|
148
|
+
let turnResult;
|
|
149
|
+
try {
|
|
150
|
+
turnResult = await handle.prompt(String(delivered));
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (engineDetourFailure !== undefined) {
|
|
154
|
+
throw classifiedError(engineDetourFailure, "child");
|
|
155
|
+
}
|
|
156
|
+
throw classifiedError(error, "provider");
|
|
157
|
+
}
|
|
158
|
+
if (engineDetourFailure !== undefined) {
|
|
159
|
+
throw classifiedError(engineDetourFailure, "child");
|
|
160
|
+
}
|
|
161
|
+
if (signal?.aborted)
|
|
162
|
+
throw new Error("Reviewer axis sub-session was cancelled");
|
|
163
|
+
const lastAssistant = turnResult.messages !== undefined
|
|
164
|
+
? [...turnResult.messages].reverse().find((message) => typeof message === "object" && message !== null && message.role === "assistant")
|
|
165
|
+
: undefined;
|
|
166
|
+
if (turnResult.stopReason === "error" || turnResult.stopReason === "aborted"
|
|
167
|
+
|| (typeof lastAssistant === "object" && lastAssistant !== null
|
|
168
|
+
&& (lastAssistant.stopReason === "error"
|
|
169
|
+
|| lastAssistant.stopReason === "aborted"))) {
|
|
170
|
+
const errMsg = turnResult.errorMessage
|
|
171
|
+
?? (typeof lastAssistant === "object" && lastAssistant !== null
|
|
172
|
+
? String(lastAssistant.errorMessage ?? "")
|
|
173
|
+
: "");
|
|
174
|
+
// Envelope already attaches observed HTTP status onto error/aborted assistants.
|
|
175
|
+
const hasTestimony = nodeHasUpstreamTestimony(lastAssistant)
|
|
176
|
+
|| nodeHasUpstreamTestimony(opened.streamFailure);
|
|
177
|
+
throw classifiedError(new Error(errMsg, { cause: lastAssistant }), hasTestimony ? "provider" : "unknown");
|
|
178
|
+
}
|
|
179
|
+
const report = turnResult.text;
|
|
180
|
+
if (report.trim().length === 0) {
|
|
181
|
+
throw new Error("Reviewer axis sub-session returned a blank report");
|
|
182
|
+
}
|
|
183
|
+
const usage = turnResult.usage ?? emptyUsage();
|
|
184
|
+
return { report, usage, prompt: delivered };
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await handle.close();
|
|
188
|
+
}
|
|
96
189
|
}
|
|
97
190
|
catch (error) {
|
|
98
191
|
throw projectSharedChildFailure(error);
|
|
@@ -18,7 +18,7 @@ export function reviewerAxisMethodAdapter(axis) {
|
|
|
18
18
|
].join("\n");
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Spec-only
|
|
21
|
+
* Spec-only axis material carrier for durable authority references.
|
|
22
22
|
* Exact values preserved; no prose extraction and no Standards/parent injection.
|
|
23
23
|
*/
|
|
24
24
|
export function reviewerAuthorityRefsMaterial(authorityRefs) {
|
|
@@ -72,7 +72,7 @@ export function constructReviewerDispatch(input) {
|
|
|
72
72
|
: [{ axis: "standards" }];
|
|
73
73
|
const legs = axes.map((x) => {
|
|
74
74
|
const parts = [common, reviewerAxisMethodAdapter(x.axis)];
|
|
75
|
-
// Spec
|
|
75
|
+
// Spec axis only — never Standards or a parent replacement Spec leg.
|
|
76
76
|
if (x.axis === "spec") {
|
|
77
77
|
if (specFetchedMaterial !== undefined) {
|
|
78
78
|
parts.push(reviewerFetchedSpecMaterial(specFetchedMaterial));
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { openToolObjectFromUnion } from "./open-tool-schema.js";
|
|
3
|
+
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
|
|
4
|
+
import {} from "./reviewer-construction.js";
|
|
5
|
+
import { createReviewerDispatcher } from "./reviewer-dispatch.js";
|
|
6
|
+
import { ReviewerDispatchExecutionError } from "./reviewer-agent.js";
|
|
7
|
+
import { createReviewerExecutionLedger, projectAcceptedDispatch, projectReviewerDispatchOutcome } from "./reviewer-execution-ledger.js";
|
|
8
|
+
import { assembleRuntimeReviewerReceipt } from "./reviewer-settlement.js";
|
|
9
|
+
import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, validateReviewerIntent } from "./package-contracts/reviewer-output.js";
|
|
10
|
+
export { REVIEWER_OUTPUT_TOOL_NAME };
|
|
11
|
+
export const AGENT_TOOL_NAME = "Agent";
|
|
12
|
+
const reviewerAmendmentsSchema = Type.Object({
|
|
13
|
+
standards: Type.Optional(Type.String({ description: "相对 Standards 子报告的增量:增 finding、撤回或事实更正" })),
|
|
14
|
+
spec: Type.Optional(Type.String({ description: "相对 Spec 子报告的增量:增 finding、撤回或事实更正" })),
|
|
15
|
+
}, { additionalProperties: true, description: "相对子报告的可选轴增量;非替代报告。无增量的轴可省略。" });
|
|
16
|
+
const reviewerOutputVariants = Type.Union([
|
|
17
|
+
Type.Object({
|
|
18
|
+
status: Type.Literal("completed", { description: "completed — 形状指引,非 schema 闸" }),
|
|
19
|
+
amendments: Type.Optional(reviewerAmendmentsSchema),
|
|
20
|
+
}, { additionalProperties: false }),
|
|
21
|
+
Type.Object({
|
|
22
|
+
status: Type.Literal("refused", { description: "refused — 形状指引,非 schema 闸" }),
|
|
23
|
+
diagnostic: Type.String({ minLength: 1, description: "拒绝诊断说明" }),
|
|
24
|
+
amendments: Type.Optional(reviewerAmendmentsSchema),
|
|
25
|
+
}, { additionalProperties: false }),
|
|
26
|
+
]);
|
|
27
|
+
export const reviewerOutputSchema = withInfrastructureFailureDeclaration(openToolObjectFromUnion(reviewerOutputVariants));
|
|
28
|
+
/**
|
|
29
|
+
* Reviewer behavior runtime: label, soul, evidence tools, decision tool, projection.
|
|
30
|
+
* No flag registration/decoding and no agent_start prompt lifecycle (ADR 0018 / envelope).
|
|
31
|
+
* Reviewer-side 审刑院 gate retired (#495 S6 / 风闻奏事); accept on typed validate only.
|
|
32
|
+
*/
|
|
33
|
+
export function createReviewerRoleRuntime(pi, dependencies, hostActions) {
|
|
34
|
+
let soul;
|
|
35
|
+
let binding;
|
|
36
|
+
let reader;
|
|
37
|
+
let dispatcher;
|
|
38
|
+
let registered = false;
|
|
39
|
+
let fixedBaseRevision;
|
|
40
|
+
let acceptedDispatch;
|
|
41
|
+
const ledger = createReviewerExecutionLedger();
|
|
42
|
+
return {
|
|
43
|
+
async activate(_ctx, admitted) {
|
|
44
|
+
soul = (await dependencies.loadSoul()).trim();
|
|
45
|
+
if (!soul)
|
|
46
|
+
throw new Error("Reviewer soul is empty");
|
|
47
|
+
// Behavior layer receives frozen admitted inputs only — no pi.getFlag.
|
|
48
|
+
fixedBaseRevision = admitted.baseRevision;
|
|
49
|
+
const reviewScopeKeys = admitted.reviewScopeKeys;
|
|
50
|
+
const authorityRefs = admitted.authorityRefs;
|
|
51
|
+
const ticketNumber = admitted.ticketNumber;
|
|
52
|
+
const loaded = await dependencies.loadCanonicalSkillBinding("code-review");
|
|
53
|
+
if (loaded.name !== "code-review")
|
|
54
|
+
throw new Error("Canonical Skill binding loader returned tdd for code-review");
|
|
55
|
+
binding = loaded;
|
|
56
|
+
reader = await dependencies.createPinnedGitReader();
|
|
57
|
+
acceptedDispatch = undefined;
|
|
58
|
+
const executeAndProjectDispatch = async (execution, invocation) => {
|
|
59
|
+
const dispatch = acceptedDispatch;
|
|
60
|
+
if (dispatch === undefined || dispatch.identity !== execution.identity)
|
|
61
|
+
throw new Error("Reviewer execution lacks accepted construction evidence");
|
|
62
|
+
const { context, signal } = invocation;
|
|
63
|
+
ledger.append({ source: "reviewer-agent", type: "dispatch-started", dispatchIdentity: execution.identity, cardinality: execution.legs.length });
|
|
64
|
+
try {
|
|
65
|
+
const result = await dependencies.runDispatch(execution, { context, ...(signal === undefined ? {} : { signal }) });
|
|
66
|
+
projectReviewerDispatchOutcome(ledger, dispatch, result);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof ReviewerDispatchExecutionError) {
|
|
71
|
+
try {
|
|
72
|
+
projectReviewerDispatchOutcome(ledger, dispatch, error.outcome);
|
|
73
|
+
}
|
|
74
|
+
catch (mismatch) {
|
|
75
|
+
throw ledger.recordInfrastructureFailure(mismatch);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
throw ledger.recordInfrastructureFailure(error);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
dispatcher = createReviewerDispatcher({
|
|
83
|
+
canonicalSkill: binding.snapshot.raw,
|
|
84
|
+
reader,
|
|
85
|
+
...(reviewScopeKeys === undefined ? {} : { reviewScopeKeys }),
|
|
86
|
+
...(authorityRefs === undefined ? {} : { authorityRefs }),
|
|
87
|
+
...(ticketNumber === undefined ? {} : { ticketNumber }),
|
|
88
|
+
...(dependencies.fetchIssue === undefined ? {} : { fetchIssue: dependencies.fetchIssue }),
|
|
89
|
+
decisionEvidence(decision) {
|
|
90
|
+
try {
|
|
91
|
+
if (decision.disposition === "accepted") {
|
|
92
|
+
ledger.append(projectAcceptedDispatch(decision.dispatch));
|
|
93
|
+
acceptedDispatch = decision.dispatch;
|
|
94
|
+
}
|
|
95
|
+
else
|
|
96
|
+
ledger.append({ source: "reviewer-dispatch", type: "rejected", identity: decision.identity, violations: decision.violations, started: false });
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw ledger.recordInfrastructureFailure(error);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
run: executeAndProjectDispatch,
|
|
103
|
+
});
|
|
104
|
+
if (!registered) {
|
|
105
|
+
registered = true;
|
|
106
|
+
pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "Standards/Spec 评审腿由 runtime 以取证子会话代跑,本席收腿报告后交薄回执。", promptSnippet: "提交御史台终局回执", parameters: reviewerOutputSchema,
|
|
107
|
+
async execute(id, parameters, _signal, _update, toolCtx) {
|
|
108
|
+
if (!soul || !binding)
|
|
109
|
+
throw new Error("御史台输入未装载");
|
|
110
|
+
const output = validateReviewerIntent(parameters);
|
|
111
|
+
let record;
|
|
112
|
+
try {
|
|
113
|
+
record = ledger.recordForAudit(output.status);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error?.fatalReviewerInfrastructure)
|
|
117
|
+
hostActions.failInfrastructure(error, toolCtx, id);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
const candidate = assembleRuntimeReviewerReceipt({
|
|
121
|
+
intent: output,
|
|
122
|
+
record,
|
|
123
|
+
canonicalSkillText: binding.snapshot.raw,
|
|
124
|
+
});
|
|
125
|
+
try {
|
|
126
|
+
await dependencies.shutdownAgent?.();
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
content: [{ type: "text", text: REVIEWER_ACCEPTED_TEXT }],
|
|
133
|
+
details: candidate,
|
|
134
|
+
terminate: true,
|
|
135
|
+
};
|
|
136
|
+
} });
|
|
137
|
+
// Skill invocation transform + agent_start expansion/prompt lifecycle: shared envelope (ADR 0018).
|
|
138
|
+
pi.on("session_shutdown", async () => { try {
|
|
139
|
+
await dependencies.shutdownAgent?.();
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
throw ledger.recordInfrastructureFailure(error);
|
|
143
|
+
} });
|
|
144
|
+
}
|
|
145
|
+
const activatedSoul = soul;
|
|
146
|
+
const activatedBase = fixedBaseRevision;
|
|
147
|
+
const activatedBinding = binding;
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
dispatcher,
|
|
150
|
+
fixedBaseRevision: activatedBase,
|
|
151
|
+
soul: activatedSoul,
|
|
152
|
+
skillBinding: activatedBinding,
|
|
153
|
+
getSpecDisposition() {
|
|
154
|
+
return acceptedDispatch?.specDisposition;
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function freeze(value) {
|
|
2
|
+
if (Array.isArray(value))
|
|
3
|
+
return Object.freeze(value.map(freeze));
|
|
4
|
+
if (value !== null && typeof value === "object")
|
|
5
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, freeze(item)])));
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
function receiptPrompt(prompt) {
|
|
9
|
+
return Object.freeze({ text: prompt });
|
|
10
|
+
}
|
|
11
|
+
export function assembleRuntimeReviewerReceipt(input) {
|
|
12
|
+
const reports = {};
|
|
13
|
+
const outcomes = {};
|
|
14
|
+
for (const axis of ["standards", "spec"]) {
|
|
15
|
+
const result = input.record.results[axis];
|
|
16
|
+
if (result === undefined)
|
|
17
|
+
continue;
|
|
18
|
+
if (result.status === "successful") {
|
|
19
|
+
outcomes[axis] = {
|
|
20
|
+
status: "successful",
|
|
21
|
+
prompt: receiptPrompt(result.prompt),
|
|
22
|
+
workspaceDisposition: result.workspaceDisposition,
|
|
23
|
+
};
|
|
24
|
+
reports[axis] = { text: result.report };
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
outcomes[axis] = {
|
|
28
|
+
status: "failed",
|
|
29
|
+
prompt: receiptPrompt(result.prompt),
|
|
30
|
+
workspaceDisposition: result.workspaceDisposition,
|
|
31
|
+
failure: result.failure,
|
|
32
|
+
diagnostic: result.diagnostic,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const accepted = input.record.accepted;
|
|
37
|
+
const skillText = accepted?.input.canonicalSkill ?? input.canonicalSkillText;
|
|
38
|
+
// Seat-owned amendments sit beside reports; never rewrite runtime-owned child bytes.
|
|
39
|
+
const amendments = input.intent.amendments;
|
|
40
|
+
return freeze({
|
|
41
|
+
version: 2,
|
|
42
|
+
status: input.intent.status,
|
|
43
|
+
...(input.intent.status === "refused" ? { diagnostic: input.intent.diagnostic } : {}),
|
|
44
|
+
...(accepted === undefined ? {} : {
|
|
45
|
+
acceptedBatch: {
|
|
46
|
+
identity: accepted.identity,
|
|
47
|
+
legs: accepted.legs.map(({ axis, prompt }) => ({ axis, prompt: receiptPrompt(prompt) })),
|
|
48
|
+
},
|
|
49
|
+
// Honest Spec skipped/missing notation when independent discovery confirmed absence.
|
|
50
|
+
...(accepted.specDisposition === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { specDisposition: accepted.specDisposition }),
|
|
53
|
+
// Self-fetch bytes + source annotation retained for audit (#343 fetch-then-store).
|
|
54
|
+
...(accepted.specFetchedMaterial === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { specFetchedMaterial: accepted.specFetchedMaterial }),
|
|
57
|
+
}),
|
|
58
|
+
reports,
|
|
59
|
+
...(amendments === undefined ? {} : { amendments }),
|
|
60
|
+
outcomes,
|
|
61
|
+
identities: {
|
|
62
|
+
canonicalSkill: { text: skillText },
|
|
63
|
+
...(accepted === undefined ? {} : { construction: { recipe: accepted.recipe }, target: accepted.target }),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|