@akagilnc/pi-workflow-roles 0.1.3414 → 0.1.3439
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/archivist-record-entry.js +21 -12
- package/dist/archivist-record-topology.js +20 -0
- package/dist/auditor-dossier-tool.js +67 -76
- package/dist/canonical-json.js +66 -69
- package/dist/compliance-transport.js +99 -86
- package/dist/countersign-contracts.js +23 -25
- package/dist/doctor-contracts.js +169 -145
- package/dist/engine-detour-tool.js +83 -87
- package/dist/engine-detour.js +148 -183
- package/dist/evidence-child-executor.js +10 -1
- package/dist/exact-utf8.js +11 -10
- package/dist/git-object-id.js +10 -5
- package/dist/gleaner-left-contracts.js +33 -35
- package/dist/grok/production-host.js +229 -118
- package/dist/inspector-contracts.js +28 -31
- package/dist/institutional-resolution.js +101 -104
- package/dist/ledger-session-read.js +241 -0
- package/dist/merger-contracts.js +66 -56
- package/dist/notary-contracts.js +91 -100
- package/dist/open-tool-schema.js +32 -31
- package/dist/package-contracts/collector-output.js +56 -43
- package/dist/package-contracts/fixer-output.js +52 -50
- package/dist/package-contracts/fixer-packet.js +67 -64
- package/dist/package-contracts/gatekeeper-output.js +48 -56
- package/dist/package-contracts/judge-output.js +23 -20
- package/dist/package-contracts/navigator-output.js +35 -40
- package/dist/package-contracts/reviewer-output.js +94 -110
- package/dist/package-contracts/terminating-infrastructure.js +62 -90
- package/dist/package-contracts/worker-output.js +30 -11
- package/dist/package-resources/engine-material.js +58 -94
- package/dist/public-cli/config.js +420 -484
- package/dist/public-cli/main.js +1219 -524
- package/dist/public-cli/registry.js +107 -114
- package/dist/receipt-delivery-policy.js +74 -76
- package/dist/session-opening-materials.js +44 -47
- package/dist/sha256.js +5 -3
- package/dist/sitian-appender.js +204 -210
- package/dist/sitian-contracts.js +15 -19
- package/dist/sitian-facade.js +5 -10
- package/dist/sitian-reader.js +37 -46
- package/dist/stream-idle-guard.js +63 -71
- package/dist/ticket-seat-memory.js +244 -0
- package/package.json +1 -1
- package/scripts/build-package.mjs +41 -0
- package/src/analyst-gate-cycles-read.ts +152 -83
- package/src/analyst-ledger.ts +170 -43
- package/src/archivist-record-entry.ts +32 -11
- package/src/archivist-record-topology.ts +39 -0
- package/src/evidence-child-executor.ts +14 -1
- package/src/grok/production-host.ts +44 -9
- package/src/host-contracts.ts +18 -0
- package/src/host-transition-prior-native.ts +8 -1
- package/src/ledger-session-read.ts +56 -0
- package/src/pi/durable-principal.ts +6 -0
- package/src/public-cli/inspector-run.ts +21 -10
- package/src/public-cli/invocation.ts +4 -20
- package/src/public-cli/notary-run.ts +21 -4
- package/src/public-cli/post-admission.ts +333 -42
- package/src/public-cli/settlement.ts +266 -81
- package/src/session-opening-materials.ts +4 -2
- package/src/ticket-seat-memory.ts +454 -0
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* is defaulted, rewritten, or dropped (ADR 0055).
|
|
6
|
-
*/
|
|
7
|
-
export const COUNTERSIGN_OUTPUT_TOOL_NAME = "ak_countersign_output";
|
|
8
|
-
export const COUNTERSIGN_ACCEPTED_TEXT = "给事中回执已接受";
|
|
9
|
-
export function validateRecordedCountersignOutput(verdict) {
|
|
10
|
-
if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) {
|
|
11
|
-
throw new Error("Countersign verdict has no execution discriminator");
|
|
12
|
-
}
|
|
13
|
-
let countersignStatus;
|
|
14
|
-
try {
|
|
15
|
-
countersignStatus = verdict.countersignStatus;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
throw new Error("Countersign verdict has no execution discriminator");
|
|
19
|
-
}
|
|
20
|
-
if (typeof countersignStatus !== "string") {
|
|
21
|
-
throw new Error("Countersign verdict has no execution discriminator");
|
|
22
|
-
}
|
|
23
|
-
if (["converged", "continue", "escalate"].includes(countersignStatus)) {
|
|
24
|
-
return verdict;
|
|
25
|
-
}
|
|
1
|
+
const COUNTERSIGN_OUTPUT_TOOL_NAME = "ak_countersign_output";
|
|
2
|
+
const COUNTERSIGN_ACCEPTED_TEXT = "\u7ED9\u4E8B\u4E2D\u56DE\u6267\u5DF2\u63A5\u53D7";
|
|
3
|
+
function validateRecordedCountersignOutput(verdict) {
|
|
4
|
+
if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) {
|
|
26
5
|
throw new Error("Countersign verdict has no execution discriminator");
|
|
6
|
+
}
|
|
7
|
+
let countersignStatus;
|
|
8
|
+
try {
|
|
9
|
+
countersignStatus = verdict.countersignStatus;
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error("Countersign verdict has no execution discriminator");
|
|
12
|
+
}
|
|
13
|
+
if (typeof countersignStatus !== "string") {
|
|
14
|
+
throw new Error("Countersign verdict has no execution discriminator");
|
|
15
|
+
}
|
|
16
|
+
if (["converged", "continue", "escalate"].includes(countersignStatus)) {
|
|
17
|
+
return verdict;
|
|
18
|
+
}
|
|
19
|
+
throw new Error("Countersign verdict has no execution discriminator");
|
|
27
20
|
}
|
|
21
|
+
export {
|
|
22
|
+
COUNTERSIGN_ACCEPTED_TEXT,
|
|
23
|
+
COUNTERSIGN_OUTPUT_TOOL_NAME,
|
|
24
|
+
validateRecordedCountersignOutput
|
|
25
|
+
};
|
package/dist/doctor-contracts.js
CHANGED
|
@@ -2,175 +2,199 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { canonicalJson } from "./canonical-json.js";
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.js";
|
|
4
4
|
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
|
|
6
|
+
const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
|
|
7
|
+
const DOCTOR_ACCEPTED_TEXT = "\u592A\u533B\u7F72\u56DE\u6267\u5DF2\u63A5\u53D7";
|
|
8
|
+
const DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT = "\u592A\u533B\u7F72\u56DE\u6267\u5DF2\u63A5\u53D7\uFF1B\u5BA1\u8BA1\u65E0\u56DE\u6267";
|
|
9
|
+
const DOCTOR_OUTPUT_TOOL_DESCRIPTION = "\u63D0\u4EA4\u552F\u4E00\u7EC8\u5C40\u5355\u6848\u8BC1\u8BCD\uFF1Bcompleted \u5141\u8BB8\u7A7A findings\uFF1Bruntime \u8865\u8BB0\u6D3E\u751F\u6210\u672C\u5165\u56DE\u6267\u3002";
|
|
10
|
+
const DOCTOR_TARGET_KINDS = ["law", "gate", "template", "station", "seat"];
|
|
11
11
|
const nonblank = Type.String({ minLength: 1, pattern: "\\S" });
|
|
12
12
|
const count = Type.Object({ count: Type.Integer({ minimum: 0 }), sources: Type.Array(nonblank) }, { additionalProperties: false });
|
|
13
13
|
const evidenceIds = Type.Array(nonblank, { minItems: 1 });
|
|
14
14
|
const guardrail = Type.Object({ answer: Type.Boolean(), evidenceIds, explanation: nonblank }, { additionalProperties: false });
|
|
15
15
|
const lastRealBite = Type.Union([
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Type.Object({ kind: Type.Literal("actual"), targetKey: nonblank, evidenceId: nonblank }, { additionalProperties: false }),
|
|
17
|
+
Type.Object({ kind: Type.Literal("noRealBite"), targetKey: nonblank, eligibleEvidenceIds: evidenceIds }, { additionalProperties: false })
|
|
18
18
|
]);
|
|
19
19
|
const assetKinds = DOCTOR_TARGET_KINDS;
|
|
20
20
|
const findingBody = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
evidenceIds,
|
|
22
|
+
disposition: Type.Union([Type.Literal("keep"), Type.Literal("thin"), Type.Literal("delete")]),
|
|
23
|
+
guardrails: Type.Object({ reproducibleFailure: guardrail, owningSeamOrInvariant: guardrail, deletionOrSimplificationSuffices: guardrail }, { additionalProperties: true }),
|
|
24
|
+
prescription: Type.Object({ kind: Type.Union([Type.Literal("retain"), Type.Literal("delete"), Type.Literal("simplify"), Type.Literal("patch"), Type.Literal("addMechanism")]), recommendation: nonblank, necessityExplanation: Type.Optional(nonblank) }, { additionalProperties: false }),
|
|
25
|
+
lastRealBite
|
|
24
26
|
};
|
|
25
27
|
const finding = Type.Union([
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
Type.Object({ targetKey: nonblank, observation: nonblank, evidenceIds }, { additionalProperties: false }),
|
|
29
|
+
Type.Object({ targetKey: nonblank, targetKind: Type.Union(assetKinds.map((kind) => Type.Literal(kind))), assetEvidence: Type.Object({ targetKey: nonblank, targetKind: Type.Union(assetKinds.map((kind) => Type.Literal(kind))), evidenceId: nonblank }, { additionalProperties: false }), ...findingBody }, { additionalProperties: false })
|
|
28
30
|
]);
|
|
29
31
|
const caseIdentity = Type.Object({ issueNumber: Type.Integer({ minimum: 1 }), runsPath: nonblank }, { additionalProperties: false });
|
|
30
32
|
const cost = Type.Object({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
invocations: count,
|
|
34
|
+
legs: count,
|
|
35
|
+
modelApiTurns: count,
|
|
36
|
+
outputTokens: count,
|
|
37
|
+
toolCalls: count,
|
|
38
|
+
retries: Type.Object({ count: Type.Integer({ minimum: 0 }), sources: Type.Array(nonblank), evidence: Type.Literal("literal run-dir naming") }, { additionalProperties: false }),
|
|
39
|
+
statuses: Type.Array(Type.Object({ source: nonblank, status: nonblank }, { additionalProperties: false })),
|
|
40
|
+
commits: Type.Array(Type.Object({ source: nonblank, commit: nonblank }, { additionalProperties: false })),
|
|
41
|
+
sessions: Type.Array(Type.Union([
|
|
42
|
+
Type.Object({ source: nonblank, startedAt: nonblank, endedAt: nonblank, wallMilliseconds: Type.Number({ minimum: 0 }), completion: Type.Literal("accepted") }, { additionalProperties: false }),
|
|
43
|
+
Type.Object({ source: nonblank, startedAt: Type.Optional(nonblank), endedAt: Type.Optional(nonblank), wallMilliseconds: Type.Optional(Type.Number({ minimum: 0 })), completion: Type.Literal("incomplete"), degradationReason: Type.Optional(nonblank) }, { additionalProperties: false })
|
|
44
|
+
])),
|
|
45
|
+
outputBytes: Type.Object({ count: Type.Integer({ minimum: 0 }), sources: Type.Array(nonblank), payload: Type.Literal("raw JSONL bytes"), providerWireBytes: Type.Literal("unavailable") }, { additionalProperties: false })
|
|
40
46
|
}, { additionalProperties: false });
|
|
41
47
|
const doctorSubmissionVariants = Type.Union([
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
Type.Object({
|
|
49
|
+
status: Type.Literal("completed", { description: "completed \u2014 \u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\uFF1B\u5141\u8BB8\u7A7A findings\uFF1Bruntime \u8865\u8BB0\u6D3E\u751F\u6210\u672C\u5165\u56DE\u6267" }),
|
|
50
|
+
case: Type.Unsafe({ ...caseIdentity, description: "\u7559\u5B58\u592A\u533B\u7F72\u6848\u8EAB\u4EFD" }),
|
|
51
|
+
findings: Type.Array(finding, { description: "\u53EF\u7A7A\u6216\u4EC5\u542B\u975E\u5904\u65B9\u6848\u89C2\u5BDF\uFF1B\u7F3A\u53EF\u590D\u7528\u8D44\u4EA7\u6216 bounded-bite \u8BC1\u636E\u53EA\u6392\u9664\u5BF9\u5E94\u8D44\u4EA7\u5904\u65B9" })
|
|
52
|
+
}, { additionalProperties: false, description: "\u5355\u6848\u8BC1\u8BCD\uFF0C\u4E0D\u8981\u6C42\u4EFB\u4F55\u5904\u65B9\u6216\u53EF\u590D\u7528 finding" }),
|
|
53
|
+
Type.Object({
|
|
54
|
+
status: Type.Literal("refused", { description: "refused \u2014 \u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\uFF1B\u4EC5\u5F53\u8BC1\u636E\u4E0D\u8DB3\u4EE5\u652F\u6491\u5982\u5B9E\u6848\u8BC1\u8BCD" }),
|
|
55
|
+
reason: Type.String({ minLength: 1, description: "\u8BC1\u636E\u4E0D\u8DB3\u4EE5\u652F\u6491\u5982\u5B9E\u8BC1\u8BCD\u7684\u539F\u56E0" }),
|
|
56
|
+
missingEvidence: Type.Array(Type.Object({ need: nonblank, targetKeys: Type.Array(nonblank, { minItems: 1 }) }, { additionalProperties: false }), { minItems: 1, description: "\u5982\u5B9E\u8BC1\u8BCD\u6240\u9700\u800C\u5C1A\u7F3A\u7684\u8BC1\u636E" })
|
|
57
|
+
}, { additionalProperties: false, description: "\u8BC1\u636E\u4E0D\u8DB3\u4EE5\u652F\u6491\u5982\u5B9E\u6848\u8BC1\u8BCD" })
|
|
52
58
|
]);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const doctorSubmissionSchema = withInfrastructureFailureDeclaration(
|
|
60
|
+
openToolObjectFromUnion(doctorSubmissionVariants)
|
|
61
|
+
);
|
|
62
|
+
const doctorOutputSchema = Type.Union([
|
|
63
|
+
Type.Object({ status: Type.Literal("completed"), case: caseIdentity, findings: Type.Array(finding), cost }, { additionalProperties: false }),
|
|
64
|
+
doctorSubmissionVariants.anyOf[1]
|
|
57
65
|
]);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
const doctorEvidenceReadSchema = Type.Object({ evidenceId: Type.String({ minLength: 1, description: "\u5F85\u8BFB\u7559\u5B58\u8BC1\u636E\u6807\u8BC6" }), offset: Type.Optional(Type.Integer({ minimum: 0, description: "\u8D77\u59CB\u5B57\u8282\u504F\u79FB\uFF08\u4ECE 0 \u8BA1\uFF09" })), limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 4096, description: "\u8FD4\u56DE\u5B57\u8282\u4E0A\u9650" })) }, { additionalProperties: false });
|
|
67
|
+
class DoctorSubmissionContractError extends Error {
|
|
68
|
+
name = "DoctorSubmissionContractError";
|
|
61
69
|
}
|
|
62
|
-
function isRecord(value) {
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
function isRecord(value) {
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
72
|
+
}
|
|
73
|
+
function read(value, key) {
|
|
74
|
+
if (!isRecord(value)) return void 0;
|
|
75
|
+
try {
|
|
65
76
|
return value[key];
|
|
77
|
+
} catch {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
66
80
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const status = read(value, "status");
|
|
72
|
-
if (status !== "completed" && status !== "refused")
|
|
73
|
-
throw new DoctorSubmissionContractError("太医署交卷无已识别的执行状态");
|
|
74
|
-
return value;
|
|
81
|
+
function validateDoctorSubmissionShape(value) {
|
|
82
|
+
const status = read(value, "status");
|
|
83
|
+
if (status !== "completed" && status !== "refused") throw new DoctorSubmissionContractError("\u592A\u533B\u7F72\u4EA4\u5377\u65E0\u5DF2\u8BC6\u522B\u7684\u6267\u884C\u72B6\u6001");
|
|
84
|
+
return value;
|
|
75
85
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return output;
|
|
86
|
+
function validateRecordedDoctorOutput(value) {
|
|
87
|
+
const output = validateDoctorSubmissionShape(value);
|
|
88
|
+
const status = read(output, "status");
|
|
89
|
+
if (status === "completed" && read(output, "cost") === void 0) throw new DoctorSubmissionContractError("completed \u592A\u533B\u7F72\u56DE\u6267\u7F3A\u5C11 runtime \u6301\u6709\u7684 cost \u8BC1\u8BCD");
|
|
90
|
+
return output;
|
|
82
91
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
class DoctorEvidenceStore {
|
|
93
|
+
constructor(patient) {
|
|
94
|
+
this.patient = patient;
|
|
95
|
+
this.entries = new Map(patient.evidence.map((entry) => [entry.id, entry]));
|
|
96
|
+
}
|
|
97
|
+
patient;
|
|
98
|
+
entries;
|
|
99
|
+
coverage = /* @__PURE__ */ new Map();
|
|
100
|
+
read(evidenceId, offset = 0, limit = 4096) {
|
|
101
|
+
const entry = this.entries.get(evidenceId);
|
|
102
|
+
if (!entry) throw new Error(`\u8BC1\u636E ID \u672A\u51C6\u5165\uFF1A${evidenceId}`);
|
|
103
|
+
if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 4096) throw new Error("\u8BC1\u636E\u5206\u9875\u53C2\u6570\u65E0\u6548");
|
|
104
|
+
if (offset > entry.contentLength) throw new Error("\u8BC1\u636E offset \u8D85\u51FA\u5185\u5BB9");
|
|
105
|
+
const end = Math.min(entry.contentLength, offset + limit);
|
|
106
|
+
const ranges = [...this.coverage.get(evidenceId) ?? [], [offset, end]].sort((a, b) => a[0] - b[0]);
|
|
107
|
+
const merged = [];
|
|
108
|
+
for (const range of ranges) {
|
|
109
|
+
const prior = merged.at(-1);
|
|
110
|
+
if (prior && range[0] <= prior[1]) prior[1] = Math.max(prior[1], range[1]);
|
|
111
|
+
else merged.push([...range]);
|
|
90
112
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
readRecord() { return [...this.coverage.keys()].sort().map((evidenceId) => ({ evidenceId, fullyRead: this.hasRead(evidenceId) })); }
|
|
113
|
+
this.coverage.set(evidenceId, merged);
|
|
114
|
+
return { evidenceId, kind: entry.kind, offset, content: entry.content.slice(offset, end), nextOffset: end < entry.contentLength ? end : null, contentLength: entry.contentLength, byteLength: entry.byteLength, sha256: entry.sha256 };
|
|
115
|
+
}
|
|
116
|
+
hasRead(id) {
|
|
117
|
+
const entry = this.entries.get(id);
|
|
118
|
+
const ranges = this.coverage.get(id);
|
|
119
|
+
return !!entry && ranges?.length === 1 && ranges[0][0] === 0 && ranges[0][1] === entry.contentLength;
|
|
120
|
+
}
|
|
121
|
+
readRecord() {
|
|
122
|
+
return [...this.coverage.keys()].sort().map((evidenceId) => ({ evidenceId, fullyRead: this.hasRead(evidenceId) }));
|
|
123
|
+
}
|
|
103
124
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (const target of targets)
|
|
120
|
-
assertTarget(target);
|
|
121
|
-
}
|
|
122
|
-
return output;
|
|
123
|
-
}
|
|
124
|
-
const identity = read(output, "case");
|
|
125
|
-
const issueNumber = read(identity, "issueNumber");
|
|
126
|
-
const runsPath = read(identity, "runsPath");
|
|
127
|
-
if ((issueNumber !== undefined && issueNumber !== patient.identity.issueNumber) || (runsPath !== undefined && runsPath !== patient.identity.runsPath))
|
|
128
|
-
throw new DoctorSubmissionContractError("太医署交卷 case 须等于已激活案身份");
|
|
129
|
-
const findings = read(output, "findings");
|
|
130
|
-
if (!Array.isArray(findings))
|
|
131
|
-
return output;
|
|
132
|
-
for (const finding of findings) {
|
|
133
|
-
const targetKey = read(finding, "targetKey");
|
|
134
|
-
readCitations(read(finding, "evidenceIds"), "finding");
|
|
135
|
-
const assetEvidence = read(finding, "assetEvidence");
|
|
136
|
-
if (!isRecord(assetEvidence)) {
|
|
137
|
-
assertTarget(targetKey);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
const assetTargetKey = read(assetEvidence, "targetKey");
|
|
141
|
-
const assetTargetKind = read(assetEvidence, "targetKind");
|
|
142
|
-
const assetEvidenceId = read(assetEvidence, "evidenceId");
|
|
143
|
-
if (typeof assetTargetKey === "string" && assetTargetKey !== targetKey)
|
|
144
|
-
throw new DoctorSubmissionContractError("类型化资产证据须确立 finding 的 targetKey");
|
|
145
|
-
if (typeof assetTargetKind === "string" && assetTargetKind !== read(finding, "targetKind"))
|
|
146
|
-
throw new DoctorSubmissionContractError("类型化资产证据须确立 finding 的 targetKind");
|
|
147
|
-
if (typeof assetEvidenceId === "string")
|
|
148
|
-
readCitations([assetEvidenceId], "asset evidence");
|
|
149
|
-
const guardrails = read(finding, "guardrails");
|
|
150
|
-
for (const key of ["reproducibleFailure", "owningSeamOrInvariant", "deletionOrSimplificationSuffices"])
|
|
151
|
-
readCitations(read(read(guardrails, key), "evidenceIds"), "guardrail");
|
|
152
|
-
const bite = read(finding, "lastRealBite");
|
|
153
|
-
const biteKind = read(bite, "kind");
|
|
154
|
-
if (biteKind !== "actual" && biteKind !== "noRealBite")
|
|
155
|
-
continue;
|
|
156
|
-
if (read(bite, "targetKey") !== targetKey)
|
|
157
|
-
throw new DoctorSubmissionContractError("lastRealBite 目标不匹配");
|
|
158
|
-
if (biteKind === "actual") {
|
|
159
|
-
const evidenceId = read(bite, "evidenceId");
|
|
160
|
-
const entry = typeof evidenceId === "string" ? store.entries.get(evidenceId) : undefined;
|
|
161
|
-
if (!entry || entry.kind !== "session" || !store.hasRead(entry.id))
|
|
162
|
-
throw new DoctorSubmissionContractError("actual bite 须引用已准入/已读的留存 session");
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
const eligible = patient.evidence.map((entry) => entry.id).sort();
|
|
166
|
-
const ids = read(bite, "eligibleEvidenceIds");
|
|
167
|
-
if (Array.isArray(ids)) {
|
|
168
|
-
const claimed = ids.filter((id) => typeof id === "string").sort();
|
|
169
|
-
if (canonicalJson(claimed) !== canonicalJson(eligible))
|
|
170
|
-
throw new DoctorSubmissionContractError("noRealBite 须证明完整的单案合格证据全集");
|
|
171
|
-
readCitations(eligible, "noRealBite");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
125
|
+
function validateDoctorOutput(value, patient, store) {
|
|
126
|
+
const output = validateDoctorSubmissionShape(value);
|
|
127
|
+
const lawfulTargets = /* @__PURE__ */ new Set(["case", ...patient.cost.invocations.sources]);
|
|
128
|
+
const assertTarget = (targetKey) => {
|
|
129
|
+
if (typeof targetKey === "string" && !lawfulTargets.has(targetKey)) throw new DoctorSubmissionContractError(`targetKey \u4E0D\u662F\u5408\u6CD5\u6848\u76EE\u6807\uFF1A${targetKey}`);
|
|
130
|
+
};
|
|
131
|
+
const readCitations = (ids, label) => {
|
|
132
|
+
if (!Array.isArray(ids)) return;
|
|
133
|
+
for (const id of ids) if (typeof id === "string" && (!store.entries.has(id) || !store.hasRead(id))) throw new DoctorSubmissionContractError(`${label} \u987B\u5F15\u7528\u5DF2\u51C6\u5165/\u5DF2\u8BFB\u8BC1\u636E\uFF1A${id}`);
|
|
134
|
+
};
|
|
135
|
+
if (read(output, "status") === "refused") {
|
|
136
|
+
const missingEvidence = read(output, "missingEvidence");
|
|
137
|
+
if (Array.isArray(missingEvidence)) for (const missing of missingEvidence) {
|
|
138
|
+
const targets = read(missing, "targetKeys");
|
|
139
|
+
if (Array.isArray(targets)) for (const target of targets) assertTarget(target);
|
|
174
140
|
}
|
|
175
141
|
return output;
|
|
142
|
+
}
|
|
143
|
+
const identity = read(output, "case");
|
|
144
|
+
const issueNumber = read(identity, "issueNumber");
|
|
145
|
+
const runsPath = read(identity, "runsPath");
|
|
146
|
+
if (issueNumber !== void 0 && issueNumber !== patient.identity.issueNumber || runsPath !== void 0 && runsPath !== patient.identity.runsPath) throw new DoctorSubmissionContractError("\u592A\u533B\u7F72\u4EA4\u5377 case \u987B\u7B49\u4E8E\u5DF2\u6FC0\u6D3B\u6848\u8EAB\u4EFD");
|
|
147
|
+
const findings = read(output, "findings");
|
|
148
|
+
if (!Array.isArray(findings)) return output;
|
|
149
|
+
for (const finding2 of findings) {
|
|
150
|
+
const targetKey = read(finding2, "targetKey");
|
|
151
|
+
readCitations(read(finding2, "evidenceIds"), "finding");
|
|
152
|
+
const assetEvidence = read(finding2, "assetEvidence");
|
|
153
|
+
if (!isRecord(assetEvidence)) {
|
|
154
|
+
assertTarget(targetKey);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const assetTargetKey = read(assetEvidence, "targetKey");
|
|
158
|
+
const assetTargetKind = read(assetEvidence, "targetKind");
|
|
159
|
+
const assetEvidenceId = read(assetEvidence, "evidenceId");
|
|
160
|
+
if (typeof assetTargetKey === "string" && assetTargetKey !== targetKey) throw new DoctorSubmissionContractError("\u7C7B\u578B\u5316\u8D44\u4EA7\u8BC1\u636E\u987B\u786E\u7ACB finding \u7684 targetKey");
|
|
161
|
+
if (typeof assetTargetKind === "string" && assetTargetKind !== read(finding2, "targetKind")) throw new DoctorSubmissionContractError("\u7C7B\u578B\u5316\u8D44\u4EA7\u8BC1\u636E\u987B\u786E\u7ACB finding \u7684 targetKind");
|
|
162
|
+
if (typeof assetEvidenceId === "string") readCitations([assetEvidenceId], "asset evidence");
|
|
163
|
+
const guardrails = read(finding2, "guardrails");
|
|
164
|
+
for (const key of ["reproducibleFailure", "owningSeamOrInvariant", "deletionOrSimplificationSuffices"]) readCitations(read(read(guardrails, key), "evidenceIds"), "guardrail");
|
|
165
|
+
const bite = read(finding2, "lastRealBite");
|
|
166
|
+
const biteKind = read(bite, "kind");
|
|
167
|
+
if (biteKind !== "actual" && biteKind !== "noRealBite") continue;
|
|
168
|
+
if (read(bite, "targetKey") !== targetKey) throw new DoctorSubmissionContractError("lastRealBite \u76EE\u6807\u4E0D\u5339\u914D");
|
|
169
|
+
if (biteKind === "actual") {
|
|
170
|
+
const evidenceId = read(bite, "evidenceId");
|
|
171
|
+
const entry = typeof evidenceId === "string" ? store.entries.get(evidenceId) : void 0;
|
|
172
|
+
if (!entry || entry.kind !== "session" || !store.hasRead(entry.id)) throw new DoctorSubmissionContractError("actual bite \u987B\u5F15\u7528\u5DF2\u51C6\u5165/\u5DF2\u8BFB\u7684\u7559\u5B58 session");
|
|
173
|
+
} else {
|
|
174
|
+
const eligible = patient.evidence.map((entry) => entry.id).sort();
|
|
175
|
+
const ids = read(bite, "eligibleEvidenceIds");
|
|
176
|
+
if (Array.isArray(ids)) {
|
|
177
|
+
const claimed = ids.filter((id) => typeof id === "string").sort();
|
|
178
|
+
if (canonicalJson(claimed) !== canonicalJson(eligible)) throw new DoctorSubmissionContractError("noRealBite \u987B\u8BC1\u660E\u5B8C\u6574\u7684\u5355\u6848\u5408\u683C\u8BC1\u636E\u5168\u96C6");
|
|
179
|
+
readCitations(eligible, "noRealBite");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return output;
|
|
176
184
|
}
|
|
185
|
+
export {
|
|
186
|
+
DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT,
|
|
187
|
+
DOCTOR_ACCEPTED_TEXT,
|
|
188
|
+
DOCTOR_EVIDENCE_TOOL_NAME,
|
|
189
|
+
DOCTOR_OUTPUT_TOOL_DESCRIPTION,
|
|
190
|
+
DOCTOR_OUTPUT_TOOL_NAME,
|
|
191
|
+
DOCTOR_TARGET_KINDS,
|
|
192
|
+
DoctorEvidenceStore,
|
|
193
|
+
DoctorSubmissionContractError,
|
|
194
|
+
doctorEvidenceReadSchema,
|
|
195
|
+
doctorOutputSchema,
|
|
196
|
+
doctorSubmissionSchema,
|
|
197
|
+
validateDoctorOutput,
|
|
198
|
+
validateDoctorSubmissionShape,
|
|
199
|
+
validateRecordedDoctorOutput
|
|
200
|
+
};
|
|
@@ -1,94 +1,90 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned engine detour tool (#357 T2 / #378 / #380).
|
|
3
|
-
* Registered by shared role-runtime when any role + engine activation signal is present.
|
|
4
|
-
* Evidence-child legs install the same definition via customTools (no spawn in role modules).
|
|
5
|
-
* Engine process failures stop through the host infrastructure-failure seam.
|
|
6
|
-
* Caller AbortSignal cancellation propagates unchanged.
|
|
7
|
-
*/
|
|
8
1
|
import { Type } from "typebox";
|
|
9
|
-
import {
|
|
10
|
-
|
|
2
|
+
import {
|
|
3
|
+
ENGINE_DETOUR_TOOL_NAME,
|
|
4
|
+
engineDetourFailureDiagnostic,
|
|
5
|
+
engineNameFromEnv,
|
|
6
|
+
isEngineDetourFailure,
|
|
7
|
+
runEngineDetourOnce
|
|
8
|
+
} from "./engine-detour.js";
|
|
9
|
+
const engineDetourArgsSchema = Type.Object(
|
|
10
|
+
{
|
|
11
11
|
argv: Type.Array(Type.String({ minLength: 1 }), {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
})
|
|
15
|
-
},
|
|
16
|
-
|
|
12
|
+
minItems: 1,
|
|
13
|
+
description: "\u9996\u9879\u4E3A PATH \u4E2D\u7684\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u5176\u4F59\u9879\u4E3A\u53C2\u6570\u3002"
|
|
14
|
+
})
|
|
15
|
+
},
|
|
16
|
+
{ additionalProperties: false }
|
|
17
|
+
);
|
|
17
18
|
function isCallerCancellation(error, signal) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
19
|
+
if (signal?.aborted === true) return true;
|
|
20
|
+
if (typeof error === "object" && error !== null && error.name === "AbortError") {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
};
|
|
25
|
+
function createEngineDetourToolDefinition(input) {
|
|
26
|
+
const engineName = input.engineName;
|
|
27
|
+
return {
|
|
28
|
+
name: ENGINE_DETOUR_TOOL_NAME,
|
|
29
|
+
label: "\u52B3\u52A1\u5F15\u64CE",
|
|
30
|
+
description: `\u8FD0\u884C\u4E00\u6B21\u52B3\u52A1\u5F15\u64CE\u5B50\u8FDB\u7A0B\uFF08engine=${engineName}\uFF09\uFF0Cstdout \u8FD4\u56DE\u672C session\u3002`,
|
|
31
|
+
promptSnippet: "\u8FD0\u884C\u914D\u7F6E\u7684\u52B3\u52A1\u5F15\u64CE\u4E00\u6B21\u5E76\u8FD4\u56DE stdout",
|
|
32
|
+
parameters: engineDetourArgsSchema,
|
|
33
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
34
|
+
const args = params;
|
|
35
|
+
const argv = Array.isArray(args.argv) ? args.argv : [];
|
|
36
|
+
if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
|
|
37
|
+
input.fail(
|
|
38
|
+
new Error("\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4"),
|
|
39
|
+
toolCallId,
|
|
40
|
+
ctx
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
let result;
|
|
44
|
+
try {
|
|
45
|
+
result = await runEngineDetourOnce({
|
|
46
|
+
argv,
|
|
47
|
+
cwd: ctx.cwd,
|
|
48
|
+
...signal === void 0 ? {} : { signal }
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (isCallerCancellation(error, signal)) throw error;
|
|
52
|
+
const cause = error instanceof Error ? error : new Error(String(error).trim() || "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25");
|
|
53
|
+
input.fail(cause, toolCallId, ctx);
|
|
54
|
+
}
|
|
55
|
+
if (isEngineDetourFailure(result)) {
|
|
56
|
+
input.fail(
|
|
57
|
+
new Error(engineDetourFailureDiagnostic(result)),
|
|
58
|
+
toolCallId,
|
|
59
|
+
ctx
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: result.stdout }],
|
|
64
|
+
details: {
|
|
65
|
+
tool: ENGINE_DETOUR_TOOL_NAME,
|
|
66
|
+
code: result.code,
|
|
67
|
+
stderr: result.stderr
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
73
|
+
function registerEngineDetourTool(roleHost, hostActions) {
|
|
74
|
+
const engineName = engineNameFromEnv();
|
|
75
|
+
if (engineName === void 0) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const definition = createEngineDetourToolDefinition({
|
|
79
|
+
engineName,
|
|
80
|
+
fail(error, toolCallId, ctx) {
|
|
81
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
85
82
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
roleHost.registerTool(definition);
|
|
93
|
-
return true;
|
|
83
|
+
});
|
|
84
|
+
roleHost.registerTool(definition);
|
|
85
|
+
return true;
|
|
94
86
|
}
|
|
87
|
+
export {
|
|
88
|
+
createEngineDetourToolDefinition,
|
|
89
|
+
registerEngineDetourTool
|
|
90
|
+
};
|