@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.
Files changed (61) hide show
  1. package/dist/archivist-record-entry.js +21 -12
  2. package/dist/archivist-record-topology.js +20 -0
  3. package/dist/auditor-dossier-tool.js +67 -76
  4. package/dist/canonical-json.js +66 -69
  5. package/dist/compliance-transport.js +99 -86
  6. package/dist/countersign-contracts.js +23 -25
  7. package/dist/doctor-contracts.js +169 -145
  8. package/dist/engine-detour-tool.js +83 -87
  9. package/dist/engine-detour.js +148 -183
  10. package/dist/evidence-child-executor.js +10 -1
  11. package/dist/exact-utf8.js +11 -10
  12. package/dist/git-object-id.js +10 -5
  13. package/dist/gleaner-left-contracts.js +33 -35
  14. package/dist/grok/production-host.js +229 -118
  15. package/dist/inspector-contracts.js +28 -31
  16. package/dist/institutional-resolution.js +101 -104
  17. package/dist/ledger-session-read.js +241 -0
  18. package/dist/merger-contracts.js +66 -56
  19. package/dist/notary-contracts.js +91 -100
  20. package/dist/open-tool-schema.js +32 -31
  21. package/dist/package-contracts/collector-output.js +56 -43
  22. package/dist/package-contracts/fixer-output.js +52 -50
  23. package/dist/package-contracts/fixer-packet.js +67 -64
  24. package/dist/package-contracts/gatekeeper-output.js +48 -56
  25. package/dist/package-contracts/judge-output.js +23 -20
  26. package/dist/package-contracts/navigator-output.js +35 -40
  27. package/dist/package-contracts/reviewer-output.js +94 -110
  28. package/dist/package-contracts/terminating-infrastructure.js +62 -90
  29. package/dist/package-contracts/worker-output.js +30 -11
  30. package/dist/package-resources/engine-material.js +58 -94
  31. package/dist/public-cli/config.js +420 -484
  32. package/dist/public-cli/main.js +1219 -524
  33. package/dist/public-cli/registry.js +107 -114
  34. package/dist/receipt-delivery-policy.js +74 -76
  35. package/dist/session-opening-materials.js +44 -47
  36. package/dist/sha256.js +5 -3
  37. package/dist/sitian-appender.js +204 -210
  38. package/dist/sitian-contracts.js +15 -19
  39. package/dist/sitian-facade.js +5 -10
  40. package/dist/sitian-reader.js +37 -46
  41. package/dist/stream-idle-guard.js +63 -71
  42. package/dist/ticket-seat-memory.js +244 -0
  43. package/package.json +1 -1
  44. package/scripts/build-package.mjs +41 -0
  45. package/src/analyst-gate-cycles-read.ts +152 -83
  46. package/src/analyst-ledger.ts +170 -43
  47. package/src/archivist-record-entry.ts +32 -11
  48. package/src/archivist-record-topology.ts +39 -0
  49. package/src/evidence-child-executor.ts +14 -1
  50. package/src/grok/production-host.ts +44 -9
  51. package/src/host-contracts.ts +18 -0
  52. package/src/host-transition-prior-native.ts +8 -1
  53. package/src/ledger-session-read.ts +56 -0
  54. package/src/pi/durable-principal.ts +6 -0
  55. package/src/public-cli/inspector-run.ts +21 -10
  56. package/src/public-cli/invocation.ts +4 -20
  57. package/src/public-cli/notary-run.ts +21 -4
  58. package/src/public-cli/post-admission.ts +333 -42
  59. package/src/public-cli/settlement.ts +266 -81
  60. package/src/session-opening-materials.ts +4 -2
  61. package/src/ticket-seat-memory.ts +454 -0
@@ -1,27 +1,25 @@
1
- /**
2
- * Public Countersign (给事中) terminating receipt contracts.
3
- * Lawful verdicts: converged (署) | continue (封驳) | escalate (上呈).
4
- * (#572 / ADR 0074) — 原卷保真: the verdict is recognized read-only; no field
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
+ };
@@ -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
- export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
6
- export const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
7
- export const DOCTOR_ACCEPTED_TEXT = "太医署回执已接受";
8
- export const DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT = "太医署回执已接受;审计无回执";
9
- export const DOCTOR_OUTPUT_TOOL_DESCRIPTION = "提交唯一终局单案证词;completed 允许空 findings;runtime 补记派生成本入回执。";
10
- export const DOCTOR_TARGET_KINDS = ["law", "gate", "template", "station", "seat"];
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
- 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 }),
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
- evidenceIds, disposition: Type.Union([Type.Literal("keep"), Type.Literal("thin"), Type.Literal("delete")]),
22
- guardrails: Type.Object({ reproducibleFailure: guardrail, owningSeamOrInvariant: guardrail, deletionOrSimplificationSuffices: guardrail }, { additionalProperties: true }),
23
- 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 }), lastRealBite,
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
- Type.Object({ targetKey: nonblank, observation: nonblank, evidenceIds }, { additionalProperties: false }),
27
- 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
+ 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
- invocations: count, legs: count, modelApiTurns: count, outputTokens: count, toolCalls: count,
32
- retries: Type.Object({ count: Type.Integer({ minimum: 0 }), sources: Type.Array(nonblank), evidence: Type.Literal("literal run-dir naming") }, { additionalProperties: false }),
33
- statuses: Type.Array(Type.Object({ source: nonblank, status: nonblank }, { additionalProperties: false })),
34
- commits: Type.Array(Type.Object({ source: nonblank, commit: nonblank }, { additionalProperties: false })),
35
- sessions: Type.Array(Type.Union([
36
- Type.Object({ source: nonblank, startedAt: nonblank, endedAt: nonblank, wallMilliseconds: Type.Number({ minimum: 0 }), completion: Type.Literal("accepted") }, { additionalProperties: false }),
37
- 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 }),
38
- ])),
39
- outputBytes: Type.Object({ count: Type.Integer({ minimum: 0 }), sources: Type.Array(nonblank), payload: Type.Literal("raw JSONL bytes"), providerWireBytes: Type.Literal("unavailable") }, { additionalProperties: false }),
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
- Type.Object({
43
- status: Type.Literal("completed", { description: "completed — 形状指引,非 schema 闸;允许空 findings;runtime 补记派生成本入回执" }),
44
- case: Type.Unsafe({ ...caseIdentity, description: "留存太医署案身份" }),
45
- findings: Type.Array(finding, { description: "可空或仅含非处方案观察;缺可复用资产或 bounded-bite 证据只排除对应资产处方" }),
46
- }, { additionalProperties: false, description: "单案证词,不要求任何处方或可复用 finding" }),
47
- Type.Object({
48
- status: Type.Literal("refused", { description: "refused — 形状指引,非 schema 闸;仅当证据不足以支撑如实案证词" }),
49
- reason: Type.String({ minLength: 1, description: "证据不足以支撑如实证词的原因" }),
50
- missingEvidence: Type.Array(Type.Object({ need: nonblank, targetKeys: Type.Array(nonblank, { minItems: 1 }) }, { additionalProperties: false }), { minItems: 1, description: "如实证词所需而尚缺的证据" }),
51
- }, { additionalProperties: false, description: "证据不足以支撑如实案证词" }),
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
- export const doctorSubmissionSchema = withInfrastructureFailureDeclaration(openToolObjectFromUnion(doctorSubmissionVariants));
54
- export const doctorOutputSchema = Type.Union([
55
- Type.Object({ status: Type.Literal("completed"), case: caseIdentity, findings: Type.Array(finding), cost }, { additionalProperties: false }),
56
- doctorSubmissionVariants.anyOf[1],
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
- export const doctorEvidenceReadSchema = Type.Object({ evidenceId: Type.String({ minLength: 1, description: "待读留存证据标识" }), offset: Type.Optional(Type.Integer({ minimum: 0, description: "起始字节偏移(从 0 计)" })), limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 4096, description: "返回字节上限" })) }, { additionalProperties: false });
59
- export class DoctorSubmissionContractError extends Error {
60
- name = "DoctorSubmissionContractError";
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) { return typeof value === "object" && value !== null && !Array.isArray(value); }
63
- function read(value, key) { if (!isRecord(value))
64
- return undefined; try {
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
- catch {
68
- return undefined;
69
- } }
70
- export function validateDoctorSubmissionShape(value) {
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
- export function validateRecordedDoctorOutput(value) {
77
- const output = validateDoctorSubmissionShape(value);
78
- const status = read(output, "status");
79
- if (status === "completed" && read(output, "cost") === undefined)
80
- throw new DoctorSubmissionContractError("completed 太医署回执缺少 runtime 持有的 cost 证词");
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
- export class DoctorEvidenceStore {
84
- patient;
85
- entries;
86
- coverage = new Map();
87
- constructor(patient) {
88
- this.patient = patient;
89
- this.entries = new Map(patient.evidence.map((entry) => [entry.id, entry]));
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
- read(evidenceId, offset = 0, limit = 4096) { const entry = this.entries.get(evidenceId); if (!entry)
92
- throw new Error(`证据 ID 未准入:${evidenceId}`); if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 4096)
93
- throw new Error("证据分页参数无效"); if (offset > entry.contentLength)
94
- throw new Error("证据 offset 超出内容"); const end = Math.min(entry.contentLength, offset + limit); const ranges = [...(this.coverage.get(evidenceId) ?? []), [offset, end]].sort((a, b) => a[0] - b[0]); const merged = []; for (const range of ranges) {
95
- const prior = merged.at(-1);
96
- if (prior && range[0] <= prior[1])
97
- prior[1] = Math.max(prior[1], range[1]);
98
- else
99
- merged.push([...range]);
100
- } this.coverage.set(evidenceId, merged); 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 }; }
101
- hasRead(id) { const entry = this.entries.get(id); const ranges = this.coverage.get(id); return !!entry && ranges?.length === 1 && ranges[0][0] === 0 && ranges[0][1] === entry.contentLength; }
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
- export function validateDoctorOutput(value, patient, store) {
105
- const output = validateDoctorSubmissionShape(value);
106
- const lawfulTargets = new Set(["case", ...patient.cost.invocations.sources]);
107
- const assertTarget = (targetKey) => { if (typeof targetKey === "string" && !lawfulTargets.has(targetKey))
108
- throw new DoctorSubmissionContractError(`targetKey 不是合法案目标:${targetKey}`); };
109
- const readCitations = (ids, label) => { if (!Array.isArray(ids))
110
- return; for (const id of ids)
111
- if (typeof id === "string" && (!store.entries.has(id) || !store.hasRead(id)))
112
- throw new DoctorSubmissionContractError(`${label} 须引用已准入/已读证据:${id}`); };
113
- if (read(output, "status") === "refused") {
114
- const missingEvidence = read(output, "missingEvidence");
115
- if (Array.isArray(missingEvidence))
116
- for (const missing of missingEvidence) {
117
- const targets = read(missing, "targetKeys");
118
- if (Array.isArray(targets))
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 { ENGINE_DETOUR_TOOL_NAME, engineDetourFailureDiagnostic, engineNameFromEnv, isEngineDetourFailure, runEngineDetourOnce, } from "./engine-detour.js";
10
- const engineDetourArgsSchema = Type.Object({
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
- minItems: 1,
13
- description: "首项为 PATH 中的可执行文件,其余项为参数。",
14
- }),
15
- }, { additionalProperties: false });
16
- /** Caller/upper-layer cancellation must propagate unchanged. */
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
- if (signal?.aborted === true)
19
- return true;
20
- if (typeof error === "object" &&
21
- error !== null &&
22
- error.name === "AbortError") {
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
- * Build one detour tool definition for a configured engine name.
29
- * `fail` owns host abort (parent) vs throw (evidence child) for tool misuse only.
30
- * Engine process failures (nonzero/empty/spawn) stop via `fail` with their cause.
31
- * Caller AbortSignal cancellation propagates unchanged.
32
- */
33
- export function createEngineDetourToolDefinition(input) {
34
- const engineName = input.engineName;
35
- return {
36
- name: ENGINE_DETOUR_TOOL_NAME,
37
- label: "劳务引擎",
38
- description: `运行一次劳务引擎子进程(engine=${engineName}),stdout 返回本 session。`,
39
- promptSnippet: "运行配置的劳务引擎一次并返回 stdout",
40
- parameters: engineDetourArgsSchema,
41
- async execute(toolCallId, params, signal, _onUpdate, ctx) {
42
- const args = params;
43
- const argv = Array.isArray(args.argv) ? args.argv : [];
44
- if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
45
- input.fail(new Error("劳务引擎 argv 须为非空字符串数组"), toolCallId, ctx);
46
- }
47
- let result;
48
- try {
49
- result = await runEngineDetourOnce({
50
- argv,
51
- cwd: ctx.cwd,
52
- ...(signal === undefined ? {} : { signal }),
53
- });
54
- }
55
- catch (error) {
56
- if (isCallerCancellation(error, signal))
57
- throw error;
58
- const cause = error instanceof Error
59
- ? error
60
- : new Error(String(error).trim() || "劳务引擎 spawn 失败");
61
- input.fail(cause, toolCallId, ctx);
62
- }
63
- if (isEngineDetourFailure(result)) {
64
- input.fail(new Error(engineDetourFailureDiagnostic(result)), toolCallId, ctx);
65
- }
66
- return {
67
- content: [{ type: "text", text: result.stdout }],
68
- details: {
69
- tool: ENGINE_DETOUR_TOOL_NAME,
70
- code: result.code,
71
- stderr: result.stderr,
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
- * Register the engine-generic detour tool once for this process when any role has
79
- * an engine activation signal. Returns whether registration occurred.
80
- */
81
- export function registerEngineDetourTool(roleHost, hostActions) {
82
- const engineName = engineNameFromEnv();
83
- if (engineName === undefined) {
84
- return false;
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
- const definition = createEngineDetourToolDefinition({
87
- engineName,
88
- fail(error, toolCallId, ctx) {
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
+ };