@akagilnc/pi-workflow-roles 0.1.5375 → 0.1.5406

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 (79) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/dist/acp-host/production-host.js +6381 -5684
  4. package/dist/archivist-record-pointer.js +38 -0
  5. package/dist/collector-tool-schemas.js +18 -0
  6. package/dist/compliance-transport.js +5 -0
  7. package/dist/diarist-contracts.js +26 -0
  8. package/dist/doctor-auditor.js +1 -0
  9. package/dist/engine-detour-usage.js +6 -6
  10. package/dist/filed-submission.js +28 -0
  11. package/dist/gatekeeper-role.js +6 -10
  12. package/dist/gleaner-left-contracts.js +16 -0
  13. package/dist/headless-host/production-host.js +1644 -947
  14. package/dist/judge-role.js +21 -16
  15. package/dist/migrate-book-topology.js +452 -81
  16. package/dist/navigator-attendance.js +10 -9
  17. package/dist/navigator-invocation-identity.js +18 -0
  18. package/dist/package-contracts/reviewer-output.js +16 -0
  19. package/dist/package-contracts/worker-output.js +18 -2
  20. package/dist/pi/role-turn-host.js +9 -0
  21. package/dist/public-cli/cli.js +1 -0
  22. package/dist/public-cli/instruction-seat-run.js +258 -55
  23. package/dist/public-cli/main.js +1209 -346
  24. package/dist/public-cli/post-admission.js +427 -244
  25. package/dist/public-cli/settlement.js +111 -48
  26. package/dist/public-cli/terminal.js +12 -1
  27. package/dist/public-cli/turn-request.js +3 -0
  28. package/dist/receipt-delivery-policy.js +152 -17
  29. package/dist/role-submission-declarations.js +131 -0
  30. package/dist/secretariat-contracts.js +23 -1
  31. package/dist/submission-errors.js +11 -1
  32. package/dist/submission-gate.js +32 -12
  33. package/package.json +1 -1
  34. package/src/archivist-record-pointer.ts +48 -0
  35. package/src/auditor-role.ts +2 -8
  36. package/src/collector-role.ts +5 -28
  37. package/src/compliance-transport.ts +10 -2
  38. package/src/countersign-role.ts +2 -7
  39. package/src/diarist-contracts.ts +37 -0
  40. package/src/diarist-role.ts +4 -44
  41. package/src/doctor-auditor.ts +3 -0
  42. package/src/doctor-role.ts +16 -11
  43. package/src/engine-detour-usage.ts +7 -6
  44. package/src/external-host-turn-loop.ts +24 -12
  45. package/src/filed-submission.ts +44 -0
  46. package/src/gatekeeper-role.ts +8 -16
  47. package/src/gleaner-left-contracts.ts +26 -1
  48. package/src/gleaner-left-role.ts +4 -32
  49. package/src/host-contracts.ts +7 -0
  50. package/src/inspector-role.ts +2 -7
  51. package/src/judge-role.ts +31 -24
  52. package/src/merger-role.ts +5 -8
  53. package/src/navigator-attendance.ts +17 -12
  54. package/src/navigator-invocation-identity.ts +31 -0
  55. package/src/navigator-role.ts +2 -8
  56. package/src/notary-role.ts +5 -20
  57. package/src/package-contracts/reviewer-output.ts +25 -0
  58. package/src/package-contracts/worker-output.ts +43 -5
  59. package/src/pi/adapter.ts +33 -8
  60. package/src/pi/role-turn-host.ts +7 -0
  61. package/src/public-cli/cli.ts +1 -0
  62. package/src/public-cli/instruction-seat-run.ts +292 -61
  63. package/src/public-cli/post-admission.ts +556 -324
  64. package/src/public-cli/settlement.ts +112 -50
  65. package/src/public-cli/terminal.ts +16 -4
  66. package/src/public-cli/turn-request.ts +10 -1
  67. package/src/receipt-delivery-policy.ts +209 -17
  68. package/src/reviewer-role.ts +7 -35
  69. package/src/role-envelope.ts +5 -1
  70. package/src/role-runtime-dependencies.ts +1 -0
  71. package/src/role-runtime.ts +72 -56
  72. package/src/role-submission-declarations.ts +143 -0
  73. package/src/secretariat-contracts.ts +35 -0
  74. package/src/secretariat-role.ts +4 -40
  75. package/src/submission-correctable-error.ts +5 -2
  76. package/src/submission-errors.ts +12 -1
  77. package/src/submission-gate.ts +41 -12
  78. package/src/worker-role.ts +81 -86
  79. package/src/worker-submission-gates.ts +32 -7
@@ -7,10 +7,8 @@ import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
7
7
  import { GatekeeperDecisionError, receivedDiscriminator, unreadableDiscriminatorNotice } from "./submission-errors.ts";
8
8
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
9
9
  import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
10
- import {
11
- GATEKEEPER_OUTPUT_TOOL_NAME,
12
- gatekeeperOutputSchema,
13
- } from "./package-contracts/gatekeeper-output.ts";
10
+ import { GATEKEEPER_OUTPUT_TOOL_NAME } from "./package-contracts/gatekeeper-output.ts";
11
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
14
12
  import type { PublicSummonResult } from "./public-role-summons.ts";
15
13
  import type { TerminalResult } from "./public-cli/terminal.ts";
16
14
  import { runIdFromRunDirectory } from "./run-terminal-artifacts.ts";
@@ -60,8 +58,8 @@ export type GatekeeperResult =
60
58
  | {
61
59
  /**
62
60
  * Accepted reply whose conclusion is not converged|continue|escalate.
63
- * Envelope resumes the officer with plain-language re-ask — never parent-stands,
64
- * never forges continue (#753 / unreadable-conclusion-resume-speaker).
61
+ * Envelope reasks the officer in plain language against the configured
62
+ * ceiling, then keeps this receipt. Never parent-stands, never forges continue.
65
63
  */
66
64
  readonly status: "needs_reask";
67
65
  readonly officer: GateOfficer;
@@ -158,13 +156,7 @@ export type SubmissionGateHostActions = {
158
156
  * registration envelope — src/role-runtime.ts (ADR 0018). Schema authority is
159
157
  * the shared contract module (with infrastructure-failure declaration).
160
158
  */
161
- export const GATEKEEPER_TOOL_SPEC = {
162
- name: GATEKEEPER_OUTPUT_TOOL_NAME,
163
- label: "门下省决议",
164
- description: "门下省终局决议。status 为 dispatch 或 pass。",
165
- promptSnippet: "门下省决议",
166
- parameters: gatekeeperOutputSchema,
167
- } as const;
159
+ export const GATEKEEPER_TOOL_SPEC = roleSubmissionDeclaration("gatekeeper");
168
160
 
169
161
  export type GatekeeperRuntimeDependencies = {
170
162
  loadSoul(): Promise<string>;
@@ -199,9 +191,9 @@ function projectOfficerDecision(
199
191
  ): GatekeeperResult {
200
192
  const receipt = retainedReceipt(decision);
201
193
  const record = isRecord(decision) ? decision : undefined;
202
- const status =
203
- (record !== undefined && typeof record.status === "string" ? record.status : undefined)
204
- ?? fallbackStatus;
194
+ const status = record !== undefined && Object.hasOwn(record, "status")
195
+ ? receivedDiscriminator(record, "status")
196
+ : fallbackStatus;
205
197
  const queueStatus = typeof status === "string" ? reviewQueueStatus(status) : undefined;
206
198
  if (queueStatus === "converged") {
207
199
  return { status: "converged", officer, receipt };
@@ -4,10 +4,36 @@
4
4
  * No bounce / verdict channel (言不为狱). 原卷保真 (ADR 0055).
5
5
  */
6
6
 
7
+ import { Type } from "typebox";
8
+
9
+ import { openToolObject } from "./open-tool-schema.ts";
10
+ import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
7
11
  import { isRecord } from "./unknown-value.ts";
8
12
 
9
13
  export const GLEANER_LEFT_OUTPUT_TOOL_NAME = "ak_gleaner_left_output";
10
14
 
15
+ // #836 r16 class 1: pointer/statement are LLM/human-read narrative content — no
16
+ // code branches on their presence.
17
+ // #1134: `status` alone is the trajectory field (src/gleaner-left-contracts.ts:38
18
+ // recognizes completed to settle); its word rides the description. findings is
19
+ // narrative content declared name + semantic description only — the array type
20
+ // and the nested pointer/statement object shape are deleted, because the package
21
+ // declaration IS the host's pre-dispatch validator. An empty findings list stays
22
+ // a lawful completion; an omitted one is now expressible too.
23
+ export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
24
+ openToolObject(
25
+ Type.Object({
26
+ status: Type.Unknown({
27
+ description: "completed",
28
+ }),
29
+ findings: Type.Unknown({
30
+ description:
31
+ "弹章列表;每条可含 pointer(文件/行指针)与 statement(疑点陈述),原样留存。空列表合法完局;机器不判弹章是否成立。",
32
+ }),
33
+ }),
34
+ ),
35
+ );
36
+
11
37
  /** Internal transport: comparison-base revision for the unanchored merge-candidate diff. */
12
38
  export const GLEANER_LEFT_BASE_FLAG = {
13
39
  name: "ak-gleaner-left-base",
@@ -42,4 +68,3 @@ export function validateRecordedGleanerLeftOutput(value: unknown): GleanerLeftOu
42
68
  }
43
69
  throw new Error("Gleaner-left output has no execution discriminator");
44
70
  }
45
-
@@ -1,42 +1,20 @@
1
1
  import type { Static } from "typebox";
2
- import { Type } from "typebox";
3
2
 
4
- import { openToolObject } from "./open-tool-schema.ts";
5
- import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
6
3
  import {
7
4
  GLEANER_LEFT_OUTPUT_TOOL_NAME,
5
+ gleanerLeftOutputSchema,
8
6
  validateRecordedGleanerLeftOutput,
9
7
  type GleanerLeftOutput,
10
8
  } from "./gleaner-left-contracts.ts";
9
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
11
10
 
12
11
  export {
13
12
  GLEANER_LEFT_OUTPUT_TOOL_NAME,
13
+ gleanerLeftOutputSchema,
14
14
  } from "./gleaner-left-contracts.ts";
15
15
  export type { GleanerLeftOutput };
16
16
  export { validateRecordedGleanerLeftOutput };
17
17
 
18
- // #836 r16 class 1: pointer/statement are LLM/human-read narrative content — no
19
- // code branches on their presence.
20
- // #1134: `status` alone is the trajectory field (src/gleaner-left-contracts.ts:38
21
- // recognizes completed to settle); its word rides the description. findings is
22
- // narrative content declared name + semantic description only — the array type
23
- // and the nested pointer/statement object shape are deleted, because the package
24
- // declaration IS the host's pre-dispatch validator. An empty findings list stays
25
- // a lawful completion; an omitted one is now expressible too.
26
- export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
27
- openToolObject(
28
- Type.Object({
29
- status: Type.Unknown({
30
- description: "completed",
31
- }),
32
- findings: Type.Unknown({
33
- description:
34
- "弹章列表;每条可含 pointer(文件/行指针)与 statement(疑点陈述),原样留存。空列表合法完局;机器不判弹章是否成立。",
35
- }),
36
- }),
37
- ),
38
- );
39
-
40
18
  export type GleanerLeftOutputParameters = Static<typeof gleanerLeftOutputSchema>;
41
19
 
42
20
  export type GleanerLeftRuntimeDependencies = {
@@ -46,10 +24,4 @@ export type GleanerLeftRuntimeDependencies = {
46
24
  /**
47
25
  * 决定工具规格。生命周期装配归注册信封 owner——src/role-runtime.ts(ADR 0018)。
48
26
  */
49
- export const GLEANER_LEFT_TOOL_SPEC = {
50
- name: GLEANER_LEFT_OUTPUT_TOOL_NAME,
51
- label: "左拾遗输出",
52
- description: "左拾遗弹章。",
53
- promptSnippet: "左拾遗弹章",
54
- parameters: gleanerLeftOutputSchema,
55
- } as const;
27
+ export const GLEANER_LEFT_TOOL_SPEC = roleSubmissionDeclaration("gleaner-left");
@@ -198,6 +198,13 @@ export type RoleTurnRequest = {
198
198
  readonly host?: string;
199
199
  /** Station child role run (#840): omit automatic navigator attendance. */
200
200
  readonly stationChild?: boolean;
201
+ /**
202
+ * #1132: the effective delivery-request ceiling resolved once by the caller
203
+ * from the single configured `autoResumeLimit` value and projected here, so
204
+ * the AK execution seam and the host adapter's own re-ask loop share one
205
+ * number. Absent = package default. Never re-read from disk downstream.
206
+ */
207
+ readonly deliveryRequestLimit?: number;
201
208
  };
202
209
 
203
210
  /** Turn result — only fields upper layers currently consume. */
@@ -1,5 +1,6 @@
1
1
  import type { Static } from "typebox";
2
2
  import { reviewSubmissionSchema } from "./review-submission.ts";
3
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
3
4
  import {
4
5
  INSPECTOR_OUTPUT_TOOL_NAME,
5
6
  validateRecordedInspectorOutput,
@@ -25,10 +26,4 @@ export type InspectorRuntimeDependencies = {
25
26
  /**
26
27
  * 决定工具规格。生命周期装配归注册信封 owner——src/role-runtime.ts(ADR 0018)。
27
28
  */
28
- export const INSPECTOR_TOOL_SPEC = {
29
- name: INSPECTOR_OUTPUT_TOOL_NAME,
30
- label: "台院输出",
31
- description: "台院终局回执,状态为 converged、continue 或 escalate。",
32
- promptSnippet: "台院终局回执",
33
- parameters: inspectorOutputSchema,
34
- } as const;
29
+ export const INSPECTOR_TOOL_SPEC = roleSubmissionDeclaration("inspector");
package/src/judge-role.ts CHANGED
@@ -1,15 +1,13 @@
1
- import {
2
- type RoleHost,
3
- type HostContext,
4
- type HostToolResult,
5
- } from "./host-contracts.ts";
6
- import type { Static } from "typebox";
7
- import { reviewSubmissionSchema } from "./review-submission.ts";
1
+ import { type RoleHost } from "./host-contracts.ts";
2
+ import { REVIEW_QUEUE_STATUSES, reviewSubmissionSchema, type ReviewQueueWord } from "./review-submission.ts";
3
+ import { registerFiledSubmissionTool } from "./filed-submission.ts";
4
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
8
5
  import { type GatekeeperSubject } from "./gatekeeper-role.ts";
9
6
  import {
10
7
  JUDGE_OUTPUT_TOOL_NAME,
11
8
  type JudgeVerdict,
12
9
  } from "./package-contracts/judge-output.ts";
10
+ import type { TerminalResult } from "./public-cli/terminal.ts";
13
11
 
14
12
  export { JUDGE_OUTPUT_TOOL_NAME };
15
13
  export type { JudgeVerdict };
@@ -29,31 +27,50 @@ export async function runJudgeGates(input: {
29
27
  readonly receipt?: unknown;
30
28
  readonly runId?: string;
31
29
  readonly runDirectory?: string;
30
+ readonly terminal?: TerminalResult;
32
31
  } | void>;
33
32
  }): Promise<{
34
- readonly status: "converged" | "continue" | "escalate";
33
+ readonly status: ReviewQueueWord | "needs_reask";
35
34
  readonly passes: readonly {
36
35
  readonly subject: GatekeeperSubject;
37
- readonly status: "converged" | "continue" | "escalate";
36
+ readonly status: ReviewQueueWord | "needs_reask";
38
37
  readonly receipt: unknown;
39
38
  readonly runId?: string;
40
39
  readonly runDirectory?: string;
40
+ readonly terminal?: TerminalResult;
41
41
  }[];
42
42
  }> {
43
43
  const passes: {
44
44
  subject: GatekeeperSubject;
45
- status: "converged" | "continue" | "escalate";
45
+ status: ReviewQueueWord | "needs_reask";
46
46
  receipt: unknown;
47
47
  runId?: string;
48
48
  runDirectory?: string;
49
+ terminal?: TerminalResult;
49
50
  }[] = [];
50
51
  for (const subject of JUDGE_GATES) {
51
52
  if (await input.gateAlreadyConverged(subject)) continue;
52
53
  const pass = await input.runGate(subject);
53
- const status = pass?.status;
54
- if (pass === undefined || (status !== "converged" && status !== "continue" && status !== "escalate")) {
54
+ const received = pass?.status;
55
+ if (pass === undefined || (
56
+ typeof received !== "string" || (!REVIEW_QUEUE_STATUSES.has(received) && received !== "needs_reask")
57
+ )) {
55
58
  throw new Error("judge gate returned no conclusion");
56
59
  }
60
+ const status = received as ReviewQueueWord | "needs_reask";
61
+ if (status === "needs_reask") {
62
+ return {
63
+ status,
64
+ passes: [{
65
+ subject,
66
+ status,
67
+ receipt: pass.receipt,
68
+ ...(pass.runId === undefined ? {} : { runId: pass.runId }),
69
+ ...(pass.runDirectory === undefined ? {} : { runDirectory: pass.runDirectory }),
70
+ ...(pass.terminal === undefined ? {} : { terminal: pass.terminal }),
71
+ }],
72
+ };
73
+ }
57
74
  passes.push({
58
75
  subject,
59
76
  status,
@@ -90,18 +107,8 @@ export function createJudgeRoleRuntime(
90
107
  if (soul.length === 0) throw new Error("Judge soul is empty");
91
108
  if (!lifecycleRegistered) {
92
109
  lifecycleRegistered = true;
93
- pi.registerTool({
94
- name: JUDGE_OUTPUT_TOOL_NAME,
95
- label: "大理寺输出",
96
- description: "提交大理寺终局判词;交卷调用结束后,由公开调用接缝按判词状态执行适用审核。",
97
- promptSnippet: "提交大理寺终局判词",
98
- parameters: judgeVerdictSchema,
99
- async execute(toolCallId: string, parameters: Static<typeof judgeVerdictSchema>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {
100
- if (soul === undefined) throw new Error("大理寺职分未装载");
101
- // ADR 0003: record the original receipt and end the tool call first.
102
- // The public seam reads status and chooses the next route.
103
- return { content: [], details: parameters, terminate: true };
104
- },
110
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("judge"), {
111
+ readyError: () => (soul === undefined ? "大理寺职分未装载" : undefined),
105
112
  });
106
113
  pi.on("before_agent_start", (event) => {
107
114
  if (soul === undefined) throw new Error("大理寺职分未装载");
@@ -1,10 +1,11 @@
1
- import type { RoleHost, HostContext } from "./host-contracts.ts";
1
+ import type { RoleHost } from "./host-contracts.ts";
2
2
  import {
3
3
  MERGER_OUTPUT_TOOL_NAME,
4
- mergerOutputSchema,
5
4
  validateMergerInput,
6
5
  type MergerInput,
7
6
  } from "./merger-contracts.ts";
7
+ import { registerFiledSubmissionTool } from "./filed-submission.ts";
8
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
8
9
  import { exactUtf8 } from "./exact-utf8.ts";
9
10
 
10
11
  export { MERGER_OUTPUT_TOOL_NAME };
@@ -35,12 +36,8 @@ export function createMergerRoleRuntime(pi: RoleHost, dependencies: MergerRoleDe
35
36
  activation = { soul, input };
36
37
  if (!registered) {
37
38
  registered = true;
38
- pi.registerTool({
39
- name: MERGER_OUTPUT_TOOL_NAME, label: "合并输出", description: "提交合并结果;输出分支为 completed 与 escalate。", promptSnippet: "提交合并结果", parameters: mergerOutputSchema,
40
- async execute(_id: string, params: unknown, _signal: AbortSignal | undefined, _update: unknown, _ctx: HostContext) {
41
- if (!activation) throw new Error("校书郎未激活");
42
- return { content: [], details: params, terminate: true as const };
43
- },
39
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("merger"), {
40
+ readyError: () => (activation === undefined ? "校书郎未激活" : undefined),
44
41
  });
45
42
  pi.on("before_agent_start", (event) => {
46
43
  if (!activation) throw new Error("校书郎未激活");
@@ -59,7 +59,10 @@ export {
59
59
  export { createNativeNavigatorSessionFactory };
60
60
  export { resolveNavigatorSeatSelection };
61
61
  import { issueRoot, subjectPath } from "./work-subject-identity.ts";
62
- import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE } from "./receipt-delivery-policy.ts";
62
+ import {
63
+ createReceiptDeliveryPolicy,
64
+ NO_RECEIPT_LIFECYCLE_ENTRY_TYPE,
65
+ } from "./receipt-delivery-policy.ts";
63
66
  import { navigatorProseFromUnknown } from "./package-contracts/navigator-output.ts";
64
67
  import { persistNavigatorWorkBase } from "./navigator-work-base.ts";
65
68
 
@@ -156,6 +159,8 @@ export type NavigatorAttendanceOptions = {
156
159
  contextError?: unknown;
157
160
  /** Exact principal owned by shared role lifecycle; attendance never overrides it. */
158
161
  invocationId?: string;
162
+ /** Effective ceiling already resolved for this turn (#1132). */
163
+ deliveryRequestLimit?: number;
159
164
  onEvent: (event: NavigatorEvent, report: NavigatorReport) => void | Promise<void>;
160
165
  };
161
166
 
@@ -416,11 +421,12 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
416
421
  try {
417
422
  try {
418
423
  if (disposed) throw navigatorUnavailableError("session", new Error("Navigator attendance was disposed"));
419
- const delivery = createReceiptDeliveryPolicy();
424
+ // #1132: the ceiling this turn already resolved. Absent = package default.
425
+ const delivery = createReceiptDeliveryPolicy(options.deliveryRequestLimit);
420
426
  // Production prose arrives only via nested summon → prepare tool.execute
421
427
  // (navigator-public-session). No assistant-entry harvest — entries() is
422
428
  // archivist custom-only on the wired factory (#959).
423
- const promptAllowingRejectedPrepare = async (text: string, deliveryRequest: boolean) => {
429
+ const promptAllowingRejectedPrepare = async (text: string) => {
424
430
  await persistNavigatorWorkBase(activeSession.recordPointer(), { subject, authority });
425
431
  const entryStart = activeSession.entries().length;
426
432
  prepareBatchRejected = false;
@@ -440,31 +446,30 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
440
446
  // ineligible for publication before the correction turn starts.
441
447
  output = undefined;
442
448
  prepareBatchRejected = true;
449
+ // The correction prompt below is the one spend for this rejection.
443
450
  delivery.recordRejected(rejectedReason);
444
451
  return;
445
452
  }
446
453
  if (promptFailure !== undefined) throw promptFailure;
447
454
  const sessionNoReceipt = activeSession.noReceipt?.();
448
455
  if (sessionNoReceipt !== undefined) {
449
- // This session already settled without an accepted receipt on its own
450
- // budget; one more prompt opens an independent summon, not a delivery
451
- // request on the settled session (#675).
456
+ // This session already settled without an accepted receipt. Keep the
457
+ // larger issued count and stop; a nested zero must not wipe prompts
458
+ // this layer already sent (#675 / #1132).
452
459
  delivery.recordNestedNoReceipt(sessionNoReceipt);
453
460
  return;
454
461
  }
455
- if (deliveryRequest && output === undefined) delivery.recordDeliveryRequest();
456
462
  };
457
- await promptAllowingRejectedPrepare(request, false);
463
+ await promptAllowingRejectedPrepare(request);
458
464
  // Bound output only: correction after rejected prepare. Early ready-wait
459
465
  // does not 催交 final advice (owner: prepare then wait for settlement feed).
460
466
  if (boundSettlement !== undefined) {
461
467
  while (output === undefined && prepareBatchRejected && delivery.nextAction() === "request-delivery") {
462
- await promptAllowingRejectedPrepare(JSON.stringify(delivery.deliveryState()), true);
468
+ delivery.recordDeliveryRequest();
469
+ await promptAllowingRejectedPrepare(JSON.stringify(delivery.deliveryState()));
463
470
  }
464
471
  if (output === undefined && delivery.nextAction() === "request-delivery") {
465
- while (delivery.nextAction() === "request-delivery") {
466
- delivery.recordDeliveryRequest();
467
- }
472
+ delivery.closeBudget();
468
473
  }
469
474
  if (output === undefined && delivery.nextAction() === "no-receipt" && activeSession.providerFailure?.() === undefined) {
470
475
  const facts = delivery.facts({ runPointer: activeSession.recordPointer(), attemptPointer: invocationId });
@@ -214,6 +214,37 @@ export type PackagedRoleTerminalMessage = {
214
214
  readonly details?: unknown;
215
215
  };
216
216
 
217
+ /**
218
+ * Durable rejection of a packaged output tool.
219
+ * Unknown tools and infrastructure failures are not receipt rejections.
220
+ * Empty text is still a rejection. Undefined means "not this fact".
221
+ */
222
+ export function packagedRoleOutputRejectionReason(message: {
223
+ readonly toolName?: unknown;
224
+ readonly isError?: unknown;
225
+ readonly details?: unknown;
226
+ readonly content?: unknown;
227
+ }): string | undefined {
228
+ if (typeof message.toolName !== "string") return undefined;
229
+ if (!PACKAGED_ROLE_OUTPUT_TOOLS.has(message.toolName) && message.toolName !== "ak_submission_output") {
230
+ return undefined;
231
+ }
232
+ if (message.isError !== true) return undefined;
233
+ if (classifyPackagedRoleTerminalResult(message).kind !== "nonterminal") return undefined;
234
+ const content = message.content;
235
+ if (typeof content === "string") return content.trim();
236
+ if (!Array.isArray(content)) return "";
237
+ return content.map((part) => {
238
+ if (
239
+ isRecord(part) && part.type === "text"
240
+ && typeof part.text === "string"
241
+ ) {
242
+ return part.text;
243
+ }
244
+ return "";
245
+ }).join("").trim();
246
+ }
247
+
217
248
  /**
218
249
  * One shared typed terminal classifier for packaged role output toolResults.
219
250
  * Consumed by lifecycle principal completion, publicNavigatorSettlement, and
@@ -1,16 +1,10 @@
1
- import { NAVIGATOR_OUTPUT_TOOL_NAME, navigatorOutputSchema } from "./package-contracts/navigator-output.ts";
1
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
2
2
 
3
3
  /**
4
4
  * 决定工具规格。生命周期装配归注册信封 owner——src/role-runtime.ts(ADR 0018)。
5
5
  * #959: navigator speaks free-form prose; tool is a lifecycle vehicle only.
6
6
  */
7
- export const NAVIGATOR_TOOL_SPEC = {
8
- name: NAVIGATOR_OUTPUT_TOOL_NAME,
9
- label: "游奕使建议",
10
- description: "游奕使终局回执:散文建议,原样呈现。",
11
- promptSnippet: "游奕使建议",
12
- parameters: navigatorOutputSchema,
13
- } as const;
7
+ export const NAVIGATOR_TOOL_SPEC = roleSubmissionDeclaration("navigator");
14
8
 
15
9
  export type NavigatorRuntimeDependencies = {
16
10
  loadSoul(): Promise<string>;
@@ -1,4 +1,6 @@
1
- import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
1
+ import type { RoleHost, HostContext } from "./host-contracts.ts";
2
+ import { registerFiledSubmissionTool } from "./filed-submission.ts";
3
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
2
4
  /**
3
5
  * Public Notary role runtime — direct officer seat (not through Gatekeeper province).
4
6
  * Caller supplies only a source-run locator; Notary self-fetches authoritative materials.
@@ -10,7 +12,6 @@ import {
10
12
  NOTARY_OUTPUT_TOOL_NAME,
11
13
  NOTARY_SOURCE_RUN_FLAG,
12
14
  NOTARY_TICKET_FLAG,
13
- notaryOutputSchema,
14
15
  type NotarySourceRunLocator,
15
16
  } from "./notary-contracts.ts";
16
17
 
@@ -156,24 +157,8 @@ export function createNotaryRoleRuntime(
156
157
 
157
158
  if (!registered) {
158
159
  registered = true;
159
- pi.registerTool({
160
- name: NOTARY_OUTPUT_TOOL_NAME,
161
- label: "符宝郎输出",
162
- description: "提交引文保真与票面对齐的 converged/continue/escalate 决议。",
163
- promptSnippet: "提交符宝郎决议",
164
- parameters: notaryOutputSchema,
165
- async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext, ): Promise<HostToolResult<unknown>> {
166
- if (activation === undefined) {
167
- throw new Error("符宝郎未激活");
168
- }
169
- // #753: handler only records — params as submitted, no findings/disposition rewrite.
170
- // #541 infra declaration + sole-final barrier are ledger-owned (#575).
171
- return {
172
- content: [],
173
- details: parameters,
174
- terminate: true as const,
175
- };
176
- },
160
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("notary"), {
161
+ readyError: () => (activation === undefined ? "符宝郎未激活" : undefined),
177
162
  });
178
163
  // Evidence assembly only (ADR 0018): soul + bound projection.
179
164
  // Ticket value is envelope-admitted at activate — not re-read from flags here.
@@ -1,7 +1,32 @@
1
1
  /** Package-owned Reviewer intent — original role payload only (#836 删 8). */
2
2
 
3
+ import { Type } from "typebox";
4
+
5
+ import { openToolObject } from "../open-tool-schema.ts";
6
+ import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
7
+
3
8
  export const REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
4
9
 
10
+ const REVIEWER_AMENDMENTS_DESCRIPTION =
11
+ "已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略(completeness / correctness)。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。" as const;
12
+ // #1134: `status` alone is the trajectory field (#917 §6 two normal lens verdicts →
13
+ // completed; hard-stop / usage error → refused); its words ride the description.
14
+ // amendments/diagnostic are LLM/human-read narrative content returned as
15
+ // submitted — declaration keeps name + semantic description only, no nested
16
+ // object shape, no string type, no required constraint.
17
+ const REVIEWER_STATUS_DESCRIPTION =
18
+ "completed | refused。两种正常 lens verdict(completeness / correctness)均 completed;hard-stop 与 usage error 为 refused(已产出报告仍照录进所选 lens amendments)。" as const;
19
+ const reviewerOutputObject = Type.Object({
20
+ status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
21
+ amendments: Type.Unknown({ description: REVIEWER_AMENDMENTS_DESCRIPTION }),
22
+ diagnostic: Type.Unknown({
23
+ description: "拒绝诊断说明;hard-stop/usage error 原因进此字段,已产出报告另照录 amendments",
24
+ }),
25
+ });
26
+ export const reviewerOutputSchema = withTerminatingOutputDeclarations(
27
+ openToolObject(reviewerOutputObject),
28
+ );
29
+
5
30
  /** Seat-owned per-lens finding body text (completeness / correctness). */
6
31
  export type ReviewerAmendments = Readonly<
7
32
  Partial<Record<"completeness" | "correctness", string>>
@@ -1,19 +1,39 @@
1
1
  /** Package-owned independent Coder and Fixer output leaves. */
2
2
 
3
- export {
3
+ import { Type } from "typebox";
4
+
5
+ import { openToolObject } from "../open-tool-schema.ts";
6
+ import {
4
7
  FIXER_OUTPUT_TOOL_NAME,
5
8
  fixerOutputSchema,
9
+ PARTIALLY_COMPLETED_DEFINITION,
6
10
  validateFixerOutput,
11
+ type FixerBlocker,
12
+ type FixerClassResult,
13
+ type FixerOutput,
14
+ type FixerPhase,
7
15
  } from "./fixer-output.ts";
16
+ import { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.ts";
17
+ import type { FixerInvocationInput, FixerPrerequisite } from "./fixer-packet.ts";
18
+ import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
19
+
20
+ export {
21
+ FIXER_OUTPUT_TOOL_NAME,
22
+ fixerOutputSchema,
23
+ validateFixerOutput,
24
+ fixerPrerequisiteSchema,
25
+ fixerPrerequisitesSchema,
26
+ parseFixerPrerequisites,
27
+ validateFixerPrerequisites,
28
+ };
8
29
  export type {
9
30
  FixerBlocker,
10
31
  FixerClassResult,
11
32
  FixerOutput,
12
33
  FixerPhase,
13
- } from "./fixer-output.ts";
14
- export { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.ts";
15
- export type { FixerInvocationInput, FixerPrerequisite } from "./fixer-packet.ts";
16
- import { validateFixerOutput, type FixerOutput } from "./fixer-output.ts";
34
+ FixerInvocationInput,
35
+ FixerPrerequisite,
36
+ };
17
37
 
18
38
  export const CODER_OUTPUT_TOOL_NAME = "ak_coder_output";
19
39
  export type WorkerRoleLabel = "Coder" | "Fixer";
@@ -31,3 +51,21 @@ export function validateAcceptedCoderDetails(output: unknown): CoderOutput {
31
51
  export function validateAcceptedWorkerDetails(output: unknown, roleLabel: WorkerRoleLabel = "Coder"): WorkerOutput {
32
52
  return roleLabel === "Fixer" ? validateFixerOutput(output) : validateAcceptedCoderDetails(output);
33
53
  }
54
+
55
+ // status 的合法词写在 description。WORKER_DONE_STATUSES 只让 completed /
56
+ // partially_completed 进入提交闸。unfinished 且未见理由说明时,运行时同 run 催全
57
+ // (ADR 0050);理由在不在不按 JSON 类型判,也不在派发前用长度拒收。
58
+ // report / remainingScope / reason 的声明只留字段名和语义。
59
+ const CODER_STATUS_DESCRIPTION =
60
+ `planned | completed | refused | partially_completed | unfinished。unfinished:缺前置或违宪约束致本局未完成。${PARTIALLY_COMPLETED_DEFINITION}` as const;
61
+ const coderOutputObject = Type.Object({
62
+ status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
63
+ report: Type.Unknown({ description: "如实结果报告" }),
64
+ remainingScope: Type.Unknown({ description: "本局后剩余工作" }),
65
+ reason: Type.Unknown({
66
+ description: "阻断原因:缺前置或违宪约束。缺待决 owner 决定或答复属缺前置。",
67
+ }),
68
+ });
69
+ export const coderOutputSchema = withTerminatingOutputDeclarations(
70
+ openToolObject(coderOutputObject),
71
+ );
package/src/pi/adapter.ts CHANGED
@@ -18,6 +18,12 @@ import type {
18
18
  import { createOAuthKeepalive, type OAuthKeepaliveOptions } from "../oauth-keepalive.ts";
19
19
  import { createRoleRuntimeExtension, type RoleRuntimeDependencies } from "../role-runtime.ts";
20
20
  import { renderAgentStartMaterials } from "../agent-start-materials.ts";
21
+ import { projectCorrectableExecuteRejection } from "../submission-correctable-error.ts";
22
+ import {
23
+ WorkerCommitReminderError,
24
+ WorkerPrefixReminderError,
25
+ WorkerUnfinishedReasonReminderError,
26
+ } from "../submission-errors.ts";
21
27
  import { readUserDialogueStdin } from "../user-dialogue-stdin.ts";
22
28
 
23
29
  export type PiRoleHostAdapter = RoleEnvelopeHost;
@@ -106,14 +112,33 @@ function toPiToolDefinition<S extends TSchema, D>(
106
112
  description: tool.description,
107
113
  ...(tool.promptSnippet === undefined ? {} : { promptSnippet: tool.promptSnippet }),
108
114
  parameters: tool.parameters,
109
- execute: async (toolCallId, params, signal, update, context) =>
110
- toPiResult(await tool.execute(
111
- toolCallId,
112
- params as Static<S>,
113
- signal,
114
- update === undefined ? undefined : (result) => update(toPiResult(result)),
115
- projectContext(context),
116
- )),
115
+ execute: async (toolCallId, params, signal, update, context) => {
116
+ try {
117
+ return toPiResult(await tool.execute(
118
+ toolCallId,
119
+ params as Static<S>,
120
+ signal,
121
+ update === undefined ? undefined : (result) => update(toPiResult(result)),
122
+ projectContext(context),
123
+ ));
124
+ } catch (error) {
125
+ // The worker gate owns these one-shot reminders. Returning them as
126
+ // isError keeps the code on the tool result so催交 can record the
127
+ // fact without spending the reminder itself. Other correctable
128
+ // errors stay native throws.
129
+ if (!(
130
+ error instanceof WorkerUnfinishedReasonReminderError
131
+ || error instanceof WorkerCommitReminderError
132
+ || error instanceof WorkerPrefixReminderError
133
+ )) throw error;
134
+ const projected = projectCorrectableExecuteRejection(error);
135
+ return {
136
+ content: [{ type: "text" as const, text: projected.diagnostic }],
137
+ details: projected.details as D,
138
+ isError: true as const,
139
+ };
140
+ }
141
+ },
117
142
  };
118
143
  }
119
144