@akagilnc/pi-workflow-roles 0.1.3648 → 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 +2919 -1750
- 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/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/countersign-role.js +28 -0
- package/dist/diarist-role.js +29 -0
- 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-role.js +26 -0
- package/dist/inspector-role.js +29 -0
- package/dist/judge-role.js +106 -0
- package/dist/merger-role.js +66 -0
- package/dist/navigator-role.js +12 -0
- package/dist/notary-role.js +137 -0
- package/dist/oauth-keepalive.js +158 -0
- 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 +94 -191
- 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 +0 -10
- 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/host-contracts.ts +0 -2
- 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 +0 -32
- 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 -31
- package/souls/evidence-child.md +0 -1
- package/src/evidence-child-role.ts +0 -22
- package/src/package-contracts/evidence-child-output.ts +0 -46
|
@@ -347,10 +347,6 @@ const AUDITOR_OPTIONS = [
|
|
|
347
347
|
},
|
|
348
348
|
},
|
|
349
349
|
];
|
|
350
|
-
const EVIDENCE_CHILD_OPTIONS = [
|
|
351
|
-
bindOwner("evidence-child", SHARED_PROJECT_SEMANTICS),
|
|
352
|
-
bindOwner("evidence-child", SHARED_ATTACH_SEMANTICS),
|
|
353
|
-
];
|
|
354
350
|
const COUNTERSIGN_OPTIONS = [
|
|
355
351
|
bindOwner("countersign", SHARED_PROJECT_SEMANTICS),
|
|
356
352
|
bindOwner("countersign", SHARED_ATTACH_SEMANTICS),
|
|
@@ -709,7 +705,6 @@ export const PUBLIC_OPTION_TABLE = {
|
|
|
709
705
|
gatekeeper: GATEKEEPER_OPTIONS,
|
|
710
706
|
navigator: NAVIGATOR_OPTIONS,
|
|
711
707
|
auditor: AUDITOR_OPTIONS,
|
|
712
|
-
"evidence-child": EVIDENCE_CHILD_OPTIONS,
|
|
713
708
|
diarist: DIARIST_OPTIONS,
|
|
714
709
|
analyst: ANALYST_OPTIONS,
|
|
715
710
|
};
|
|
@@ -729,7 +724,6 @@ export const PUBLIC_ROLE_OPTION_OWNERS = [
|
|
|
729
724
|
"gatekeeper",
|
|
730
725
|
"navigator",
|
|
731
726
|
"auditor",
|
|
732
|
-
"evidence-child",
|
|
733
727
|
"diarist",
|
|
734
728
|
"analyst",
|
|
735
729
|
];
|
|
@@ -1032,14 +1026,6 @@ const ROLE_COMMAND_HELP = {
|
|
|
1032
1026
|
'ak-role auditor --subject doctor --source-run 01abc…@doctor "审:太医候选是否合规。"',
|
|
1033
1027
|
],
|
|
1034
1028
|
},
|
|
1035
|
-
"evidence-child": {
|
|
1036
|
-
command: "evidence-child",
|
|
1037
|
-
summary: "Direct evidence-child fact gathering; submit one report.",
|
|
1038
|
-
usage: ["ak-role evidence-child [options] [instruction]"],
|
|
1039
|
-
examples: [
|
|
1040
|
-
'ak-role evidence-child "查:本工作树 diff 与票面是否一致。"',
|
|
1041
|
-
],
|
|
1042
|
-
},
|
|
1043
1029
|
diarist: {
|
|
1044
1030
|
command: "diarist",
|
|
1045
1031
|
summary: "Direct Diarist (起居郎) pass: gather and organize this case's decision basis into its 起居录.",
|
|
@@ -66,9 +66,8 @@ const STARTUP_CANDIDATES = {
|
|
|
66
66
|
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "medium" },
|
|
67
67
|
{ provider: "xai", model: "grok-4.5", thinking: "high" }
|
|
68
68
|
],
|
|
69
|
-
// #675: auditor
|
|
69
|
+
// #675: auditor public seat — no package startup; caller configures.
|
|
70
70
|
auditor: [],
|
|
71
|
-
"evidence-child": [],
|
|
72
71
|
// #708 `diarist-seat-default`: owner-set initial value; changed in the seat
|
|
73
72
|
// table like any other seat. No engine axis, package-default host.
|
|
74
73
|
diarist: [{ provider: "xai", model: "grok-4.5", thinking: "medium" }]
|
|
@@ -191,7 +191,6 @@ async function readRoleRunStateDisk(runDirectory) {
|
|
|
191
191
|
record.role !== "gatekeeper" &&
|
|
192
192
|
record.role !== "navigator" &&
|
|
193
193
|
record.role !== "auditor" &&
|
|
194
|
-
record.role !== "evidence-child" &&
|
|
195
194
|
record.role !== "diarist") {
|
|
196
195
|
return undefined;
|
|
197
196
|
}
|
|
@@ -1251,8 +1250,7 @@ export async function loadResumableInstructionSeatRun(home, runId, authority) {
|
|
|
1251
1250
|
const loaded = await loadResumableRunRecord(home, runId, authority);
|
|
1252
1251
|
if (loaded.run.role !== "gatekeeper"
|
|
1253
1252
|
&& loaded.run.role !== "navigator"
|
|
1254
|
-
&& loaded.run.role !== "auditor"
|
|
1255
|
-
&& loaded.run.role !== "evidence-child") {
|
|
1253
|
+
&& loaded.run.role !== "auditor") {
|
|
1256
1254
|
throw new CliUsageError(`role run ${runId} belongs to ${loaded.run.role}, not an instruction seat`);
|
|
1257
1255
|
}
|
|
1258
1256
|
const admitted = {
|
|
@@ -36,7 +36,6 @@ import { INSPECTOR_OUTPUT_TOOL_NAME, } from "../inspector-contracts.js";
|
|
|
36
36
|
import { GATEKEEPER_OUTPUT_TOOL_NAME, } from "../package-contracts/gatekeeper-output.js";
|
|
37
37
|
import { NAVIGATOR_OUTPUT_TOOL_NAME, } from "../package-contracts/navigator-output.js";
|
|
38
38
|
import { AUDITOR_OUTPUT_TOOL_NAME, } from "../package-contracts/auditor-output.js";
|
|
39
|
-
import { EVIDENCE_CHILD_OUTPUT_TOOL_NAME, } from "../package-contracts/evidence-child-output.js";
|
|
40
39
|
import { observePackagedMethodSkillInvocation, } from "../package-resources/method-skill.js";
|
|
41
40
|
import { classifyPackagedRoleTerminalResult, findLatestDurablePackagedRoleTerminal, hasNavigatorInfrastructureFailureBase, isAcceptedPackagedRoleTerminalResult, isReceiptSettlementBindingClear, NAVIGATOR_INVOCATION_ENTRY, parseInvocationMarkerIdentity, } from "../navigator-invocation-identity.js";
|
|
42
41
|
import { NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, parseNoReceiptLifecycleFacts } from "../receipt-delivery-policy.js";
|
|
@@ -2332,15 +2331,6 @@ async function settleLawfulAuditorTerminalResult(admitted, authority, scope) {
|
|
|
2332
2331
|
export async function trySettleAuditorTerminalResult(admitted, authority, scope) {
|
|
2333
2332
|
return settleLawfulAuditorTerminalResult(admitted, authority, scope);
|
|
2334
2333
|
}
|
|
2335
|
-
async function settleLawfulEvidenceChildTerminalResult(admitted, authority, scope) {
|
|
2336
|
-
return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
|
|
2337
|
-
role: "evidence-child",
|
|
2338
|
-
toolName: EVIDENCE_CHILD_OUTPUT_TOOL_NAME,
|
|
2339
|
-
}, scope);
|
|
2340
|
-
}
|
|
2341
|
-
export async function trySettleEvidenceChildTerminalResult(admitted, authority, scope) {
|
|
2342
|
-
return settleLawfulEvidenceChildTerminalResult(admitted, authority, scope);
|
|
2343
|
-
}
|
|
2344
2334
|
/** Try to settle a lawful Coder Terminal; undefined only for genuine absence. */
|
|
2345
2335
|
export async function trySettleCoderTerminalResult(admitted, authority, options = {}) {
|
|
2346
2336
|
return settleLawfulCoderTerminalResult(admitted, authority, options);
|
|
@@ -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
|
+
}
|