@akagilnc/pi-workflow-roles 0.1.3280 → 0.1.3353

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 (41) hide show
  1. package/README.md +5 -5
  2. package/README.zh-CN.md +5 -5
  3. package/dist/audit-escalation.js +31 -4
  4. package/dist/gatekeeper-role.js +25 -4
  5. package/dist/grok/production-host.js +1167 -862
  6. package/dist/inspector-contracts.js +5 -2
  7. package/dist/notary-contracts.js +16 -5
  8. package/dist/packaged-role-registry.js +0 -7
  9. package/dist/public-cli/main.js +867 -757
  10. package/package.json +1 -1
  11. package/src/audit-escalation.ts +52 -8
  12. package/src/collector-ledger.ts +309 -74
  13. package/src/collector-receipt.ts +0 -1
  14. package/src/collector-role.ts +21 -14
  15. package/src/diarist-mechanical.ts +16 -5
  16. package/src/gatekeeper-role.ts +33 -4
  17. package/src/grok/role-envelope.ts +68 -52
  18. package/src/inspector-contracts.ts +6 -3
  19. package/src/inspector-role.ts +5 -2
  20. package/src/notary-contracts.ts +20 -5
  21. package/src/notary-role.ts +2 -2
  22. package/src/packaged-role-registry.ts +0 -12
  23. package/src/public-cli/cli.ts +50 -125
  24. package/src/public-cli/coder-run.ts +5 -19
  25. package/src/public-cli/collector-run.ts +65 -21
  26. package/src/public-cli/countersign-run.ts +14 -40
  27. package/src/public-cli/doctor-run.ts +50 -6
  28. package/src/public-cli/fixer-run.ts +5 -19
  29. package/src/public-cli/gleaner-left-run.ts +14 -40
  30. package/src/public-cli/inspector-run.ts +51 -7
  31. package/src/public-cli/instruction-seat-run.ts +27 -0
  32. package/src/public-cli/judge-run.ts +11 -36
  33. package/src/public-cli/merger-run.ts +5 -19
  34. package/src/public-cli/notary-run.ts +54 -5
  35. package/src/public-cli/option-definitions.ts +3 -3
  36. package/src/public-cli/post-admission.ts +76 -0
  37. package/src/public-cli/reviewer-run.ts +5 -19
  38. package/src/public-cli/run-lifecycle.ts +342 -107
  39. package/src/public-cli/settlement.ts +134 -71
  40. package/src/role-runtime.ts +18 -1
  41. package/src/submission-ledger.ts +55 -5
package/README.md CHANGED
@@ -38,7 +38,7 @@ ak-role judge --attach ./plan.md "Review this plan." > result.txt
38
38
 
39
39
  Exit status reports lifecycle honesty, not business success: every lawful typed result (including `audit_escalation`) exits zero; a failure without a lawful result exits nonzero, and its Terminal carries the Error Artifact ref and original cause instead of a fabricated receipt.
40
40
 
41
- `ak-role resume <runId> [message]` reopens that run under the **current seat table** for model / host / engine — the same resolution as starting a new leg (`--flag` → persistent seat → package default). Standard chain after a role `escalate`s: take the owner ruling and feed it back with `ak-role resume <runId> "<ruling>"` so the same run continues to a terminal. The optional `message` after `runId` is passed through unchanged as the continuation prompt (opaque: not parsed as flags); omit it to use the package resume envelope. Global `--model` / `--host` / `--engine` override the table for that resume only. On a real host switch (live seat host differs from the previous invocation host), prior native records of the previous host are delivered once as context to the target host; same-host resume does not re-inject. Each host writes only its native volume (Pi: `session/session.jsonl`, Grok: `runDirectory/grok-home`), with unified ledger entries recorded in 司天台 (Sitian). Whether to resume is the caller's decision: the command does not require a typed HTTP 429 or a `resumable` state. Unknown run IDs and missing session principals are rejected. Collector, Doctor, Notary, and Inspector remain one-shot. Countersign and Gleaner-Left accept manual resume (#599).
41
+ `ak-role resume <runId> [message]` reopens that run under the **current seat table** for model / host / engine — the same resolution as starting a new leg (`--flag` → persistent seat → package default). Standard chain after a role `escalate`s: take the owner ruling and feed it back with `ak-role resume <runId> "<ruling>"` so the same run continues to a terminal. `[message]` applies only to seats that accept caller instruction: for those seats the optional `message` after `runId` is passed through unchanged as the continuation prompt (opaque: not parsed as flags); omit it to use the package resume envelope. Notary/符宝郎 must omit `message` and derives evidence from the existing source-run/dossier binding. Global `--model` / `--host` / `--engine` override the table for that resume only. On a real host switch (live seat host differs from the previous invocation host), prior native records of the previous host are delivered once as context to the target host; same-host resume does not re-inject. Each host writes only its native volume (Pi: `session/session.jsonl`, Grok: `runDirectory/grok-home`), with unified ledger entries recorded in 司天台 (Sitian). Whether to resume is the caller's decision: the command does not require a typed HTTP 429 or a `resumable` state. Unknown run IDs and missing session principals are rejected. Every callable role accepts manual resume; Countersign and Gleaner-Left gained it in #599, Collector, Doctor, Notary, and Inspector in #633.
42
42
 
43
43
  Judge, coder, fixer, reviewer, and merger also retry a non-lawful LLM call in place (same `runId` and session) up to `autoResumeLimit` times. Unset defaults to 2; `ak-role config set-auto-resume-limit <N>` writes the ceiling (`0` disables). Lawful typed terminals (`accepted`, `audit_escalation`, `no_receipt`) stop immediately. Manual `ak-role resume` stays available.
44
44
 
@@ -90,23 +90,23 @@ ak-role coder apply --attach ./plan.md "Implement the approved slice."
90
90
  # reviewer — fixed-target two-axis review; completed ≠ approved, read the findings
91
91
  ak-role reviewer --base main "Review the branch."
92
92
 
93
- # collector — GitHub PR review evidence; one-shot
93
+ # collector — GitHub PR review evidence
94
94
  ak-role collector --pr 42 --repo owner/repository
95
95
 
96
96
  # fixer — repair the assigned findings
97
97
  ak-role fixer --attach ./findings.md --prerequisites ./prereqs.json "Repair the findings."
98
98
 
99
- # doctor — diagnose one retained case; one-shot
99
+ # doctor — diagnose one retained case
100
100
  ak-role doctor --issue 115 "Diagnose this retained case."
101
101
 
102
102
  # merger — resolve one merge already in conflict (start it with Git's ort first)
103
103
  ak-role merger --project /path/to/worktree "Reconcile the active merge."
104
104
 
105
- # notary — document-fidelity check on one retained source run; one-shot; optional --ticket for court diary
105
+ # notary — document-fidelity check on one retained source run; optional --ticket for court diary
106
106
  ak-role notary --source-run <runId@role|path>
107
107
  ak-role notary --source-run <runId@role|path> --ticket 582
108
108
 
109
- # inspector — direct complexity and test-quality check; one-shot
109
+ # inspector — direct complexity and test-quality check
110
110
  ak-role inspector --attach ./change.patch "Review this material."
111
111
 
112
112
  # gatekeeper — direct Gate province review; dispatch an officer or pass
package/README.zh-CN.md CHANGED
@@ -38,7 +38,7 @@ ak-role judge --attach ./plan.md "Review this plan." > result.txt
38
38
 
39
39
  退出码报的是生命周期诚实,不是业务成败:一切合法 typed 终态(含 `audit_escalation`)退出零;无合法终态的失败退出非零,其 Terminal 携带 Error Artifact 引用与原始原因,不伪造回执。
40
40
 
41
- `ak-role resume <runId> [message]` 按**现行席位表**的 model / host / engine 续跑该次运行——与新起角色腿同一解析(调用旗 → 席位持久 → 包默认)。角色 `escalate`(直通御前)后拿到 owner 裁定,标准续跑是 `ak-role resume <runId> "<裁定>"`——把裁定喂回同一 run,角色继续走到终局。`runId` 后可选的 `message` 原样作为续跑 prompt(opaque:不进全局旗标语法);省略则用包自带 resume envelope。全局 `--model` / `--host` / `--engine` 仅覆盖本次 resume。真实换宿主时(现行席位 host 与上一次 invocation host 不同),将前序宿主原生卷宗一次性作为 context 交付目标宿主;同宿主续跑不重复注入。各宿主仅直写自身原生卷宗(Pi:`session/session.jsonl`,Grok:`runDirectory/grok-home`),统一账目归入司天台。要不要续跑由调用者决定:不再要求 typed HTTP 429,也不要求 `resumable` 状态。未知 run ID、session 主体不在则拒绝。通进司、太医署、符宝郎、察院仍为一次性,无 resume;给事中、左拾遗可手动 resume(#599)。
41
+ `ak-role resume <runId> [message]` 按**现行席位表**的 model / host / engine 续跑该次运行——与新起角色腿同一解析(调用旗 → 席位持久 → 包默认)。角色 `escalate`(直通御前)后拿到 owner 裁定,标准续跑是 `ak-role resume <runId> "<裁定>"`——把裁定喂回同一 run,角色继续走到终局。`[message]` 只适用于接收 caller instruction 的席位:对这些席位,`runId` 后可选的 `message` 原样作为续跑 prompt(opaque:不进全局旗标语法);省略则用包自带 resume envelope。Notary/符宝郎必须省略 `message`,仅从既有 source-run/案卷绑定自取证。全局 `--model` / `--host` / `--engine` 仅覆盖本次 resume。真实换宿主时(现行席位 host 与上一次 invocation host 不同),将前序宿主原生卷宗一次性作为 context 交付目标宿主;同宿主续跑不重复注入。各宿主仅直写自身原生卷宗(Pi:`session/session.jsonl`,Grok:`runDirectory/grok-home`),统一账目归入司天台。要不要续跑由调用者决定:不再要求 typed HTTP 429,也不要求 `resumable` 状态。未知 run ID、session 主体不在则拒绝。所有可调用角色均可手动 resume:给事中、左拾遗始于 #599,通进司、太医署、符宝郎、察院始于 #633。
42
42
 
43
43
  大理寺、将作监、修内司、御史台、校书郎在单次调用内对非 lawful LLM 终态原地续跑(同一 `runId` 与 session),次数上限为 `autoResumeLimit`。缺键默认 2;`ak-role config set-auto-resume-limit <N>` 写入(`0` 关闭自动续)。lawful typed 终态(`accepted` / `audit_escalation` / `no_receipt`)立即停止。手动 `ak-role resume` 仍可用。
44
44
 
@@ -84,23 +84,23 @@ ak-role coder apply --attach ./plan.md "Implement the approved slice."
84
84
  # 御史台——固定目标双轴察举;completed ≠ 准行,findings 在 Terminal 里
85
85
  ak-role reviewer --base main "Review the branch."
86
86
 
87
- # 通进司——GitHub PR 收证;一次性
87
+ # 通进司——GitHub PR 收证
88
88
  ak-role collector --pr 42 --repo owner/repository
89
89
 
90
90
  # 修内司——缮修所指 findings
91
91
  ak-role fixer --attach ./findings.md --prerequisites ./prereqs.json "Repair the findings."
92
92
 
93
- # 太医署——单案诊断;一次性
93
+ # 太医署——单案诊断
94
94
  ak-role doctor --issue 115 "Diagnose this retained case."
95
95
 
96
96
  # 校书郎——调和已在冲突的 merge(先用 Git ort 起动)
97
97
  ak-role merger --project /path/to/worktree "Reconcile the active merge."
98
98
 
99
- # 符宝郎——文书核验一份留存 source run;一次性;可选 --ticket 调起居录
99
+ # 符宝郎——文书核验一份留存 source run;可选 --ticket 调起居录
100
100
  ak-role notary --source-run <runId@role|path>
101
101
  ak-role notary --source-run <runId@role|path> --ticket 582
102
102
 
103
- # 察院——直调复杂度与测试质量两轴;一次性
103
+ # 察院——直调复杂度与测试质量两轴
104
104
  ak-role inspector --attach ./change.patch "Review this material."
105
105
 
106
106
  # 门下省——直调省审:派官或放行
@@ -22,15 +22,29 @@ export function buildAuditEscalationResult(decision, deliveredOutput) {
22
22
  if (Object.hasOwn(decision, "decisionGate")) {
23
23
  auditOwned.auditDecisionGate = decision.decisionGate;
24
24
  }
25
+ if (Object.hasOwn(decision, "reason") && decision.reason !== undefined) {
26
+ auditOwned.reason = decision.reason;
27
+ }
28
+ if (Object.hasOwn(decision, "officer") && decision.officer !== undefined) {
29
+ auditOwned.officer = decision.officer;
30
+ }
31
+ if (Object.hasOwn(decision, "findings") && decision.findings !== undefined) {
32
+ auditOwned.findings = decision.findings;
33
+ }
25
34
  const deliveredFields = deliveredOutput !== undefined &&
26
35
  deliveredOutput !== null &&
27
36
  typeof deliveredOutput === "object" &&
28
37
  !Array.isArray(deliveredOutput)
29
38
  ? { ...deliveredOutput }
30
39
  : {};
31
- // Role output cannot fill an absent audit-owned field.
40
+ // Role output cannot fill an absent audit/officer-owned field.
32
41
  delete deliveredFields.conflicts;
33
42
  delete deliveredFields.auditDecisionGate;
43
+ if (Object.hasOwn(decision, "officer")) {
44
+ delete deliveredFields.reason;
45
+ delete deliveredFields.officer;
46
+ delete deliveredFields.findings;
47
+ }
34
48
  const result = {
35
49
  ...deliveredFields,
36
50
  ...auditOwned,
@@ -44,11 +58,24 @@ export function isAuditEscalationProjection(value) {
44
58
  return false;
45
59
  return AUDIT_ESCALATION_LIVE_REGISTRY.has(value);
46
60
  }
47
- function humanDecisionText(result) {
48
- const lines = ["Human decision required: compliance audit escalation."];
61
+ function humanDecisionText(decision, result) {
62
+ const officer = Object.hasOwn(decision, "officer")
63
+ ? decision.officer
64
+ : undefined;
65
+ const lines = [
66
+ officer === undefined
67
+ ? "Human decision required: compliance audit escalation."
68
+ : `Human decision required: ${officer} escalation.`,
69
+ ];
70
+ if (officer !== undefined && typeof result.reason === "string") {
71
+ lines.push(`Reason: ${result.reason}`);
72
+ }
49
73
  if (Array.isArray(result.conflicts)) {
50
74
  lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${conflict}`));
51
75
  }
76
+ if (officer !== undefined && Array.isArray(result.findings) && result.findings.length > 0) {
77
+ lines.push("Findings:", ...result.findings.map((finding) => `- ${finding}`));
78
+ }
52
79
  const gate = result.auditDecisionGate;
53
80
  if (gate !== null && typeof gate === "object" && !Array.isArray(gate)) {
54
81
  const record = gate;
@@ -63,7 +90,7 @@ function humanDecisionText(result) {
63
90
  export function projectAuditEscalation(decision, deliveredOutput) {
64
91
  const details = buildAuditEscalationResult(decision, deliveredOutput);
65
92
  return {
66
- content: [{ type: "text", text: humanDecisionText(details) }],
93
+ content: [{ type: "text", text: humanDecisionText(decision, details) }],
67
94
  details,
68
95
  terminate: true,
69
96
  ...(decision.usage === undefined ? {} : { usage: decision.usage }),
@@ -19,11 +19,20 @@ function gateSeatLabel(stage) {
19
19
  }
20
20
  }
21
21
  export { GatekeeperDecisionError } from "./submission-errors.js";
22
+ export class GatekeeperEscalationError extends Error {
23
+ gatekeeper;
24
+ constructor(gatekeeper) {
25
+ super(`门下省${gateSeatLabel(gatekeeper.officer)}上呈`);
26
+ this.name = "GatekeeperEscalationError";
27
+ this.gatekeeper = gatekeeper;
28
+ }
29
+ }
22
30
  // Unknown fields so wrong types/spellings still reach projection (ADR 0055/0057; 仓第 0 条).
23
31
  // Opening goes through the sole openToolObject owner — no parallel transport helper.
24
32
  const officerDecisionSchema = openToolObject(Type.Object({
25
- status: Type.Unknown({ description: "pass | bounce — 形状指引,非 schema 闸" }),
26
- findings: Type.Unknown({ description: "string[] findings,随 pass 或 bounce 留存" }),
33
+ status: Type.Unknown({ description: "pass | bounce | escalate — 形状指引,非 schema 闸" }),
34
+ reason: Type.Optional(Type.Unknown({ description: "status 为 escalate 时的理由" })),
35
+ findings: Type.Unknown({ description: "string[] findings,随 pass、bounce 或 escalate 留存" }),
27
36
  }));
28
37
  /**
29
38
  * Direct-seat decision tool spec (#639). Lifecycle assembly stays on the
@@ -52,7 +61,7 @@ function subjectTool(subject) {
52
61
  export function createOfficerDecisionTool(name) {
53
62
  return {
54
63
  name,
55
- description: "提交一份 typed pass/bounce 决议。",
64
+ description: "提交一份 typed pass/bounce/escalate 决议。",
56
65
  parameters: officerDecisionSchema,
57
66
  async execute(_id, args) { return result(`已收 ${String(args?.status)}`, args); },
58
67
  };
@@ -101,7 +110,7 @@ function noUsableReleaseFailure(stage, decision) {
101
110
  return {
102
111
  status: "transport_failure",
103
112
  stage,
104
- reason: stage === "gatekeeper" ? "decision 无显式 dispatch" : "decision 无显式 pass/bounce",
113
+ reason: stage === "gatekeeper" ? "decision 无显式 dispatch" : "decision 无显式 pass/bounce/escalate",
105
114
  submission: retainedSubmission(decision),
106
115
  };
107
116
  }
@@ -143,6 +152,15 @@ function projectOfficerDecision(officer, decision) {
143
152
  findings: asStringArray(record.findings),
144
153
  };
145
154
  }
155
+ if (record.status === "escalate") {
156
+ return {
157
+ status: "escalate",
158
+ officer,
159
+ ...(Object.hasOwn(record, "reason") ? { reason: record.reason } : {}),
160
+ findings: record.findings,
161
+ submission: retainedSubmission(decision),
162
+ };
163
+ }
146
164
  return noUsableReleaseFailure(officer, decision);
147
165
  }
148
166
  export async function runGatekeeper(options) {
@@ -212,6 +230,9 @@ export async function requireGatekeeperPass(options) {
212
230
  error.submission = gatekeeper.submission;
213
231
  options.hostActions.failInfrastructure(error, options.context, options.toolCallId);
214
232
  }
233
+ if (gatekeeper.status === "escalate") {
234
+ throw new GatekeeperEscalationError(gatekeeper);
235
+ }
215
236
  // Envelope owns the execute→tool_result bridge; this module only projects + throws.
216
237
  options.hostActions.bindSubmissionNonPass(options.toolCallId, gatekeeper);
217
238
  throw new GatekeeperDecisionError(gatekeeper);