@akagilnc/pi-workflow-roles 0.1.3682 → 0.1.3685
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 +25 -7
- package/dist/audit-escalation.js +4 -3
- package/dist/doctor-role.js +2 -1
- package/dist/gatekeeper-role.js +3 -2
- package/dist/judge-role.js +2 -1
- package/dist/public-cli/main.js +10 -0
- package/dist/readable-gate-item.js +16 -0
- package/dist/submission-errors.js +4 -1
- package/package.json +1 -1
- package/src/audit-escalation.ts +4 -3
- package/src/doctor-role.ts +2 -1
- package/src/gatekeeper-role.ts +3 -2
- package/src/judge-role.ts +2 -1
- package/src/readable-gate-item.ts +20 -0
- package/src/submission-errors.ts +4 -1
|
@@ -2228,6 +2228,19 @@ var init_reviewer_output = __esm({
|
|
|
2228
2228
|
}
|
|
2229
2229
|
});
|
|
2230
2230
|
|
|
2231
|
+
// src/readable-gate-item.ts
|
|
2232
|
+
function readableGateItem(value) {
|
|
2233
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
2234
|
+
}
|
|
2235
|
+
function joinReadableGateItems(items, separator = "; ") {
|
|
2236
|
+
return items.map(readableGateItem).join(separator);
|
|
2237
|
+
}
|
|
2238
|
+
var init_readable_gate_item = __esm({
|
|
2239
|
+
"src/readable-gate-item.ts"() {
|
|
2240
|
+
"use strict";
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
|
|
2231
2244
|
// src/audit-escalation.ts
|
|
2232
2245
|
function buildAuditEscalationResult(decision, deliveredOutput) {
|
|
2233
2246
|
const auditOwned = {
|
|
@@ -2276,17 +2289,17 @@ function humanDecisionText(decision, result) {
|
|
|
2276
2289
|
lines.push(`Reason: ${result.reason}`);
|
|
2277
2290
|
}
|
|
2278
2291
|
if (Array.isArray(result.conflicts)) {
|
|
2279
|
-
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${conflict}`));
|
|
2292
|
+
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${readableGateItem(conflict)}`));
|
|
2280
2293
|
}
|
|
2281
2294
|
if (officer !== void 0 && Array.isArray(result.findings) && result.findings.length > 0) {
|
|
2282
|
-
lines.push("Findings:", ...result.findings.map((finding2) => `- ${finding2}`));
|
|
2295
|
+
lines.push("Findings:", ...result.findings.map((finding2) => `- ${readableGateItem(finding2)}`));
|
|
2283
2296
|
}
|
|
2284
2297
|
const gate = result.auditDecisionGate;
|
|
2285
2298
|
if (gate !== null && typeof gate === "object" && !Array.isArray(gate)) {
|
|
2286
2299
|
const record4 = gate;
|
|
2287
2300
|
if (typeof record4.question === "string") lines.push(`Question: ${record4.question}`);
|
|
2288
2301
|
if (Array.isArray(record4.options)) {
|
|
2289
|
-
lines.push("Options:", ...record4.options.map((option) => `- ${option}`));
|
|
2302
|
+
lines.push("Options:", ...record4.options.map((option) => `- ${readableGateItem(option)}`));
|
|
2290
2303
|
}
|
|
2291
2304
|
}
|
|
2292
2305
|
return lines.join("\n");
|
|
@@ -2338,6 +2351,7 @@ var AUDIT_ESCALATION_KIND, AUDIT_ESCALATION_LIVE_REGISTRY;
|
|
|
2338
2351
|
var init_audit_escalation = __esm({
|
|
2339
2352
|
"src/audit-escalation.ts"() {
|
|
2340
2353
|
"use strict";
|
|
2354
|
+
init_readable_gate_item();
|
|
2341
2355
|
AUDIT_ESCALATION_KIND = "audit_escalation";
|
|
2342
2356
|
AUDIT_ESCALATION_LIVE_REGISTRY = /* @__PURE__ */ new WeakSet();
|
|
2343
2357
|
}
|
|
@@ -2346,7 +2360,7 @@ var init_audit_escalation = __esm({
|
|
|
2346
2360
|
// src/submission-errors.ts
|
|
2347
2361
|
function gatekeeperNonPassMessage(result) {
|
|
2348
2362
|
if (result.status === "bounce") {
|
|
2349
|
-
const findings = result.findings.length === 0 ? "\uFF08\u65E0 findings\uFF09" : result.findings
|
|
2363
|
+
const findings = result.findings.length === 0 ? "\uFF08\u65E0 findings\uFF09" : joinReadableGateItems(result.findings);
|
|
2350
2364
|
return `\u95E8\u4E0B\u7701\u6253\u56DE\u91CD\u5199\uFF0Cfindings\uFF1A${findings}`;
|
|
2351
2365
|
}
|
|
2352
2366
|
if (result.status === "unreadable") {
|
|
@@ -2358,6 +2372,7 @@ var GatekeeperDecisionError, WorkerCommitReminderError, WorkerPrefixReminderErro
|
|
|
2358
2372
|
var init_submission_errors = __esm({
|
|
2359
2373
|
"src/submission-errors.ts"() {
|
|
2360
2374
|
"use strict";
|
|
2375
|
+
init_readable_gate_item();
|
|
2361
2376
|
GatekeeperDecisionError = class extends Error {
|
|
2362
2377
|
result;
|
|
2363
2378
|
constructor(result) {
|
|
@@ -9834,7 +9849,7 @@ function failureReason(error) {
|
|
|
9834
9849
|
}
|
|
9835
9850
|
function asStringArray3(value) {
|
|
9836
9851
|
if (!Array.isArray(value)) return [];
|
|
9837
|
-
return value.map(
|
|
9852
|
+
return value.map(readableGateItem);
|
|
9838
9853
|
}
|
|
9839
9854
|
function isRecord13(value) {
|
|
9840
9855
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -9973,6 +9988,7 @@ var init_gatekeeper_role = __esm({
|
|
|
9973
9988
|
"use strict";
|
|
9974
9989
|
init_auditor_dossier_tool();
|
|
9975
9990
|
init_submission_errors();
|
|
9991
|
+
init_readable_gate_item();
|
|
9976
9992
|
init_inspector_contracts();
|
|
9977
9993
|
init_gatekeeper_output();
|
|
9978
9994
|
init_shape_unreadable_failure();
|
|
@@ -20071,7 +20087,7 @@ function createDoctorRoleRuntime(pi, dependencies, host) {
|
|
|
20071
20087
|
const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony;
|
|
20072
20088
|
const acceptedDetails = details;
|
|
20073
20089
|
return disposeComplianceDecision(audit, { pass: (usage) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true, ...usage === void 0 ? {} : { usage } }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true, ...usageProjection }), bounce: (violations) => {
|
|
20074
|
-
throw new Error(`\u592A\u533B\u7F72\u56DE\u6267\u8FDD soul\uFF1A${violations
|
|
20090
|
+
throw new Error(`\u592A\u533B\u7F72\u56DE\u6267\u8FDD soul\uFF1A${joinReadableGateItems(violations)}`);
|
|
20075
20091
|
}, escalate: (result) => result }, acceptedDetails);
|
|
20076
20092
|
} });
|
|
20077
20093
|
pi.on("before_agent_start", (event) => {
|
|
@@ -20102,6 +20118,7 @@ var init_doctor_role = __esm({
|
|
|
20102
20118
|
"use strict";
|
|
20103
20119
|
init_audit_escalation();
|
|
20104
20120
|
init_compliance_transport();
|
|
20121
|
+
init_readable_gate_item();
|
|
20105
20122
|
init_dossier_resolution();
|
|
20106
20123
|
init_doctor_contracts();
|
|
20107
20124
|
init_sitian_facade();
|
|
@@ -20780,7 +20797,7 @@ function createJudgeRoleRuntime(pi, dependencies, hostActions) {
|
|
|
20780
20797
|
}),
|
|
20781
20798
|
bounce: (violations) => {
|
|
20782
20799
|
throw new Error(
|
|
20783
|
-
`\u5927\u7406\u5BFA\u56DE\u6267\u8FDD soul\uFF1A${violations
|
|
20800
|
+
`\u5927\u7406\u5BFA\u56DE\u6267\u8FDD soul\uFF1A${joinReadableGateItems(violations)}`
|
|
20784
20801
|
);
|
|
20785
20802
|
},
|
|
20786
20803
|
escalate: (result) => result
|
|
@@ -20810,6 +20827,7 @@ var init_judge_role = __esm({
|
|
|
20810
20827
|
"use strict";
|
|
20811
20828
|
init_host_contracts();
|
|
20812
20829
|
init_audit_escalation();
|
|
20830
|
+
init_readable_gate_item();
|
|
20813
20831
|
init_terminating_infrastructure();
|
|
20814
20832
|
init_judge_output();
|
|
20815
20833
|
judgeVerdictSchema = withInfrastructureFailureDeclaration(
|
package/dist/audit-escalation.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readableGateItem } from "./readable-gate-item.js";
|
|
1
2
|
export const AUDIT_ESCALATION_KIND = "audit_escalation";
|
|
2
3
|
// Live Navigator settlement may consume only the projection produced by this
|
|
3
4
|
// owner. Its private WeakSet cannot be authored by role output; persisted/
|
|
@@ -71,10 +72,10 @@ function humanDecisionText(decision, result) {
|
|
|
71
72
|
lines.push(`Reason: ${result.reason}`);
|
|
72
73
|
}
|
|
73
74
|
if (Array.isArray(result.conflicts)) {
|
|
74
|
-
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${conflict}`));
|
|
75
|
+
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${readableGateItem(conflict)}`));
|
|
75
76
|
}
|
|
76
77
|
if (officer !== undefined && Array.isArray(result.findings) && result.findings.length > 0) {
|
|
77
|
-
lines.push("Findings:", ...result.findings.map((finding) => `- ${finding}`));
|
|
78
|
+
lines.push("Findings:", ...result.findings.map((finding) => `- ${readableGateItem(finding)}`));
|
|
78
79
|
}
|
|
79
80
|
const gate = result.auditDecisionGate;
|
|
80
81
|
if (gate !== null && typeof gate === "object" && !Array.isArray(gate)) {
|
|
@@ -82,7 +83,7 @@ function humanDecisionText(decision, result) {
|
|
|
82
83
|
if (typeof record.question === "string")
|
|
83
84
|
lines.push(`Question: ${record.question}`);
|
|
84
85
|
if (Array.isArray(record.options)) {
|
|
85
|
-
lines.push("Options:", ...record.options.map((option) => `- ${option}`));
|
|
86
|
+
lines.push("Options:", ...record.options.map((option) => `- ${readableGateItem(option)}`));
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
return lines.join("\n");
|
package/dist/doctor-role.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { disposeComplianceDecision } from "./audit-escalation.js";
|
|
2
2
|
import { ComplianceResponseRetentionError } from "./compliance-transport.js";
|
|
3
|
+
import { joinReadableGateItems } from "./readable-gate-item.js";
|
|
3
4
|
import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.js";
|
|
4
5
|
import { DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT, DOCTOR_ACCEPTED_TEXT, DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_DESCRIPTION, DOCTOR_OUTPUT_TOOL_NAME, DoctorEvidenceStore, doctorEvidenceReadSchema, doctorSubmissionSchema, validateDoctorOutput } from "./doctor-contracts.js";
|
|
5
6
|
import { sitianReport } from "./sitian-facade.js";
|
|
@@ -57,7 +58,7 @@ export function createDoctorRoleRuntime(pi, dependencies, host) {
|
|
|
57
58
|
}
|
|
58
59
|
catch (error) {
|
|
59
60
|
host.failInfrastructure(error, ctx, id);
|
|
60
|
-
} const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision(audit, { pass: (usage) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${violations
|
|
61
|
+
} const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision(audit, { pass: (usage) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text", text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${joinReadableGateItems(violations)}`); }, escalate: (result) => result }, acceptedDetails); } });
|
|
61
62
|
pi.on("before_agent_start", (event) => { if (!activation)
|
|
62
63
|
throw new Error("太医署未激活"); const catalog = { version: activation.patient.version, identity: activation.patient.identity, admittedMetrics: { provenance: "由留存 session 字节推导,封入受理回执。", cost: activation.patient.cost }, lawfulTargetKeys: ["case", ...activation.patient.cost.invocations.sources], evidence: activation.patient.evidence.map(({ id, kind, sha256, byteLength, contentLength }) => ({ id, kind, sha256, byteLength, contentLength })) }; return { systemPrompt: `${event.systemPrompt}\n\n<doctor_soul>\n${activation.soul}\n</doctor_soul>\n\n<doctor_case>\n${JSON.stringify(catalog)}\n</doctor_case>` }; });
|
|
63
64
|
}
|
package/dist/gatekeeper-role.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { auditorRunDirectory, persistGateSubmissionCandidate, } from "./auditor-dossier-tool.js";
|
|
2
2
|
import { GatekeeperDecisionError } from "./submission-errors.js";
|
|
3
|
+
import { readableGateItem } from "./readable-gate-item.js";
|
|
3
4
|
import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.js";
|
|
4
5
|
import { GATEKEEPER_OUTPUT_TOOL_NAME, gatekeeperDecisionSchema, gatekeeperOutputSchema, } from "./package-contracts/gatekeeper-output.js";
|
|
5
6
|
import { retainedShapeUnreadable } from "./shape-unreadable-failure.js";
|
|
@@ -52,8 +53,8 @@ function asStringArray(value) {
|
|
|
52
53
|
if (!Array.isArray(value))
|
|
53
54
|
return [];
|
|
54
55
|
// Officer findings may be structured objects (category/law/evidence); the
|
|
55
|
-
// parent role must see them verbatim, not an empty list (#750
|
|
56
|
-
return value.map(
|
|
56
|
+
// parent role must see them verbatim, not an empty list (#750 / #775).
|
|
57
|
+
return value.map(readableGateItem);
|
|
57
58
|
}
|
|
58
59
|
/** Serializable stand-in when the child tool call had no arguments object. */
|
|
59
60
|
export const MISSING_ARGUMENTS_SUBMISSION = Object.freeze({ missing: "arguments" });
|
package/dist/judge-role.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stringEnum } from "./host-contracts.js";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { disposeComplianceDecision } from "./audit-escalation.js";
|
|
4
|
+
import { joinReadableGateItems } from "./readable-gate-item.js";
|
|
4
5
|
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
|
|
5
6
|
import { JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT, JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, validateAcceptedJudgeDetails, } from "./package-contracts/judge-output.js";
|
|
6
7
|
export { JUDGE_OUTPUT_TOOL_NAME };
|
|
@@ -85,7 +86,7 @@ export function createJudgeRoleRuntime(pi, dependencies, hostActions) {
|
|
|
85
86
|
...usageProjection,
|
|
86
87
|
}),
|
|
87
88
|
bounce: (violations) => {
|
|
88
|
-
throw new Error(`大理寺回执违 soul:${violations
|
|
89
|
+
throw new Error(`大理寺回执违 soul:${joinReadableGateItems(violations)}`);
|
|
89
90
|
},
|
|
90
91
|
escalate: (result) => result,
|
|
91
92
|
},
|
package/dist/public-cli/main.js
CHANGED
|
@@ -16687,6 +16687,13 @@ var init_run_ticket_number = __esm({
|
|
|
16687
16687
|
}
|
|
16688
16688
|
});
|
|
16689
16689
|
|
|
16690
|
+
// src/readable-gate-item.ts
|
|
16691
|
+
var init_readable_gate_item = __esm({
|
|
16692
|
+
"src/readable-gate-item.ts"() {
|
|
16693
|
+
"use strict";
|
|
16694
|
+
}
|
|
16695
|
+
});
|
|
16696
|
+
|
|
16690
16697
|
// src/audit-escalation.ts
|
|
16691
16698
|
function isAuditEscalationResult(value) {
|
|
16692
16699
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -16698,6 +16705,7 @@ var AUDIT_ESCALATION_KIND;
|
|
|
16698
16705
|
var init_audit_escalation = __esm({
|
|
16699
16706
|
"src/audit-escalation.ts"() {
|
|
16700
16707
|
"use strict";
|
|
16708
|
+
init_readable_gate_item();
|
|
16701
16709
|
AUDIT_ESCALATION_KIND = "audit_escalation";
|
|
16702
16710
|
}
|
|
16703
16711
|
});
|
|
@@ -16706,6 +16714,7 @@ var init_audit_escalation = __esm({
|
|
|
16706
16714
|
var init_submission_errors = __esm({
|
|
16707
16715
|
"src/submission-errors.ts"() {
|
|
16708
16716
|
"use strict";
|
|
16717
|
+
init_readable_gate_item();
|
|
16709
16718
|
}
|
|
16710
16719
|
});
|
|
16711
16720
|
|
|
@@ -22411,6 +22420,7 @@ var init_gatekeeper_role = __esm({
|
|
|
22411
22420
|
"use strict";
|
|
22412
22421
|
init_auditor_dossier_tool();
|
|
22413
22422
|
init_submission_errors();
|
|
22423
|
+
init_readable_gate_item();
|
|
22414
22424
|
init_inspector_contracts();
|
|
22415
22425
|
init_gatekeeper_output();
|
|
22416
22426
|
init_shape_unreadable_failure();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-visible relay of one gate bounce/finding item (#750 / #775).
|
|
3
|
+
*
|
|
4
|
+
* Strings stay as-is. Any other value is JSON-serialized so structured content
|
|
5
|
+
* (article/reason/evidence, category/law/evidence, …) is not lost as
|
|
6
|
+
* `[object Object]` when joined into parent-seat text.
|
|
7
|
+
*
|
|
8
|
+
* Presentation only: does not legislate carrier format for LLM-to-LLM traffic.
|
|
9
|
+
*/
|
|
10
|
+
export function readableGateItem(value) {
|
|
11
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
12
|
+
}
|
|
13
|
+
/** Join gate items into one parent-visible line. */
|
|
14
|
+
export function joinReadableGateItems(items, separator = "; ") {
|
|
15
|
+
return items.map(readableGateItem).join(separator);
|
|
16
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { joinReadableGateItems } from "./readable-gate-item.js";
|
|
1
2
|
function gatekeeperNonPassMessage(result) {
|
|
2
3
|
if (result.status === "bounce") {
|
|
3
|
-
const findings = result.findings.length === 0
|
|
4
|
+
const findings = result.findings.length === 0
|
|
5
|
+
? "(无 findings)"
|
|
6
|
+
: joinReadableGateItems(result.findings);
|
|
4
7
|
return `门下省打回重写,findings:${findings}`;
|
|
5
8
|
}
|
|
6
9
|
if (result.status === "unreadable") {
|
package/package.json
CHANGED
package/src/audit-escalation.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Usage } from "@earendil-works/pi-ai";
|
|
|
3
3
|
import type {
|
|
4
4
|
ComplianceDecision,
|
|
5
5
|
} from "./compliance-transport.ts";
|
|
6
|
+
import { readableGateItem } from "./readable-gate-item.ts";
|
|
6
7
|
|
|
7
8
|
export const AUDIT_ESCALATION_KIND = "audit_escalation" as const;
|
|
8
9
|
|
|
@@ -126,17 +127,17 @@ function humanDecisionText(
|
|
|
126
127
|
lines.push(`Reason: ${result.reason}`);
|
|
127
128
|
}
|
|
128
129
|
if (Array.isArray(result.conflicts)) {
|
|
129
|
-
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${conflict}`));
|
|
130
|
+
lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${readableGateItem(conflict)}`));
|
|
130
131
|
}
|
|
131
132
|
if (officer !== undefined && Array.isArray(result.findings) && result.findings.length > 0) {
|
|
132
|
-
lines.push("Findings:", ...result.findings.map((finding) => `- ${finding}`));
|
|
133
|
+
lines.push("Findings:", ...result.findings.map((finding) => `- ${readableGateItem(finding)}`));
|
|
133
134
|
}
|
|
134
135
|
const gate = result.auditDecisionGate;
|
|
135
136
|
if (gate !== null && typeof gate === "object" && !Array.isArray(gate)) {
|
|
136
137
|
const record = gate as Record<string, unknown>;
|
|
137
138
|
if (typeof record.question === "string") lines.push(`Question: ${record.question}`);
|
|
138
139
|
if (Array.isArray(record.options)) {
|
|
139
|
-
lines.push("Options:", ...record.options.map((option) => `- ${option}`));
|
|
140
|
+
lines.push("Options:", ...record.options.map((option) => `- ${readableGateItem(option)}`));
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
return lines.join("\n");
|
package/src/doctor-role.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
2
2
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
3
3
|
import { ComplianceResponseRetentionError, type ComplianceDecision } from "./compliance-transport.ts";
|
|
4
|
+
import { joinReadableGateItems } from "./readable-gate-item.ts";
|
|
4
5
|
import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.ts";
|
|
5
6
|
import { DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT, DOCTOR_ACCEPTED_TEXT, DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_DESCRIPTION, DOCTOR_OUTPUT_TOOL_NAME, DoctorEvidenceStore, doctorEvidenceReadSchema, doctorSubmissionSchema, validateDoctorOutput, type DoctorCase } from "./doctor-contracts.ts";
|
|
6
7
|
|
|
@@ -35,7 +36,7 @@ export function createDoctorRoleRuntime(pi: RoleHost, dependencies: DoctorRoleDe
|
|
|
35
36
|
let activation: { soul: string; patient: DoctorCase; store: DoctorEvidenceStore } | undefined; let registered = false; pi.registerFlag(DOCTOR_CASE_FLAG.name, DOCTOR_CASE_FLAG.definition);
|
|
36
37
|
return { async activate() { const path = pi.getFlag(DOCTOR_CASE_FLAG.name); if (typeof path !== "string" || !path.trim()) throw new Error("Doctor requires --ak-doctor-case"); const soul = (await dependencies.loadSoul()).trim(); if (!soul) throw new Error("Doctor soul is empty"); const patient = await dependencies.loadCase(path); activation = { soul, patient, store: new DoctorEvidenceStore(patient) };
|
|
37
38
|
if (!registered) { registered = true; pi.registerTool({ name: DOCTOR_EVIDENCE_TOOL_NAME, label: "太医署证据", description: "分页读取留存的 Pi session 字节。", parameters: doctorEvidenceReadSchema, async execute(_id: string, params: { evidenceId: string; offset?: number; limit?: number }) { if (!activation) throw new Error("太医署未激活"); const details = activation.store.read(params.evidenceId, params.offset, params.limit); return { content: [{ type: "text" as const, text: JSON.stringify(details) }], details }; } });
|
|
38
|
-
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "太医署输出", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id: string, params: unknown, signal: AbortSignal | undefined, _update: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> { if (!activation) throw new Error("太医署未激活"); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendCandidate(ctx, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision<HostToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true as const, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${violations
|
|
39
|
+
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "太医署输出", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id: string, params: unknown, signal: AbortSignal | undefined, _update: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> { if (!activation) throw new Error("太医署未激活"); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendCandidate(ctx, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision<HostToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true as const, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${joinReadableGateItems(violations)}`); }, escalate: (result) => result }, acceptedDetails); } });
|
|
39
40
|
pi.on("before_agent_start", (event) => { if (!activation) throw new Error("太医署未激活"); const catalog = { version: activation.patient.version, identity: activation.patient.identity, admittedMetrics: { provenance: "由留存 session 字节推导,封入受理回执。", cost: activation.patient.cost }, lawfulTargetKeys: ["case", ...activation.patient.cost.invocations.sources], evidence: activation.patient.evidence.map(({ id, kind, sha256, byteLength, contentLength }) => ({ id, kind, sha256, byteLength, contentLength })) }; return { systemPrompt: `${event.systemPrompt}\n\n<doctor_soul>\n${activation.soul}\n</doctor_soul>\n\n<doctor_case>\n${JSON.stringify(catalog)}\n</doctor_case>` }; }); }
|
|
40
41
|
const required = [DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_NAME]; const names = pi.getAllTools().map((tool) => tool.name); for (const name of required) if (names.filter((item) => item === name).length !== 1) throw new Error(`Doctor required tool collision or missing: ${name}`); pi.setActiveTools(required); const active = pi.getActiveTools?.() ?? required; if (active.length !== 2 || !required.every((name) => active.includes(name))) throw new Error("Doctor active tool narrowing failed"); } };
|
|
41
42
|
}
|
package/src/gatekeeper-role.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./auditor-dossier-tool.ts";
|
|
8
8
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
9
9
|
import { GatekeeperDecisionError } from "./submission-errors.ts";
|
|
10
|
+
import { readableGateItem } from "./readable-gate-item.ts";
|
|
10
11
|
import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
|
|
11
12
|
import {
|
|
12
13
|
GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
@@ -147,8 +148,8 @@ function failureReason(error: unknown): string {
|
|
|
147
148
|
function asStringArray(value: unknown): readonly string[] {
|
|
148
149
|
if (!Array.isArray(value)) return [];
|
|
149
150
|
// Officer findings may be structured objects (category/law/evidence); the
|
|
150
|
-
// parent role must see them verbatim, not an empty list (#750
|
|
151
|
-
return value.map(
|
|
151
|
+
// parent role must see them verbatim, not an empty list (#750 / #775).
|
|
152
|
+
return value.map(readableGateItem);
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
/** Serializable stand-in when the child tool call had no arguments object. */
|
package/src/judge-role.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Type, type Static } from "typebox";
|
|
|
4
4
|
|
|
5
5
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
6
6
|
import type { ComplianceDecision } from "./compliance-transport.ts";
|
|
7
|
+
import { joinReadableGateItems } from "./readable-gate-item.ts";
|
|
7
8
|
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
8
9
|
|
|
9
10
|
import {
|
|
@@ -136,7 +137,7 @@ export function createJudgeRoleRuntime(
|
|
|
136
137
|
}),
|
|
137
138
|
bounce: (violations) => {
|
|
138
139
|
throw new Error(
|
|
139
|
-
`大理寺回执违 soul:${violations
|
|
140
|
+
`大理寺回执违 soul:${joinReadableGateItems(violations)}`,
|
|
140
141
|
);
|
|
141
142
|
},
|
|
142
143
|
escalate: (result) => result,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-visible relay of one gate bounce/finding item (#750 / #775).
|
|
3
|
+
*
|
|
4
|
+
* Strings stay as-is. Any other value is JSON-serialized so structured content
|
|
5
|
+
* (article/reason/evidence, category/law/evidence, …) is not lost as
|
|
6
|
+
* `[object Object]` when joined into parent-seat text.
|
|
7
|
+
*
|
|
8
|
+
* Presentation only: does not legislate carrier format for LLM-to-LLM traffic.
|
|
9
|
+
*/
|
|
10
|
+
export function readableGateItem(value: unknown): string {
|
|
11
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Join gate items into one parent-visible line. */
|
|
15
|
+
export function joinReadableGateItems(
|
|
16
|
+
items: readonly unknown[],
|
|
17
|
+
separator = "; ",
|
|
18
|
+
): string {
|
|
19
|
+
return items.map(readableGateItem).join(separator);
|
|
20
|
+
}
|
package/src/submission-errors.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { GatekeeperNonPassResult } from "./gatekeeper-role.ts";
|
|
2
|
+
import { joinReadableGateItems } from "./readable-gate-item.ts";
|
|
2
3
|
|
|
3
4
|
function gatekeeperNonPassMessage(result: GatekeeperNonPassResult): string {
|
|
4
5
|
if (result.status === "bounce") {
|
|
5
|
-
const findings = result.findings.length === 0
|
|
6
|
+
const findings = result.findings.length === 0
|
|
7
|
+
? "(无 findings)"
|
|
8
|
+
: joinReadableGateItems(result.findings);
|
|
6
9
|
return `门下省打回重写,findings:${findings}`;
|
|
7
10
|
}
|
|
8
11
|
if (result.status === "unreadable") {
|