@akagilnc/pi-workflow-roles 0.1.3816 → 0.1.3824

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.
@@ -2712,6 +2712,10 @@ var init_submission_errors = __esm({
2712
2712
  });
2713
2713
 
2714
2714
  // src/submission-correctable-error.ts
2715
+ function mechanicalSubmissionRejectionResumeMessage(code) {
2716
+ if (code === "non-sole-round") return NON_SOLE_ROUND_RESUME_MESSAGE;
2717
+ return code;
2718
+ }
2715
2719
  function isCorrectableSubmissionError(error) {
2716
2720
  return error instanceof CorrectableSubmissionError;
2717
2721
  }
@@ -2736,11 +2740,12 @@ function projectCorrectableExecuteRejection(error) {
2736
2740
  }
2737
2741
  };
2738
2742
  }
2739
- var correctableSubmissionErrorBrand, CorrectableSubmissionError;
2743
+ var NON_SOLE_ROUND_RESUME_MESSAGE, correctableSubmissionErrorBrand, CorrectableSubmissionError;
2740
2744
  var init_submission_correctable_error = __esm({
2741
2745
  "src/submission-correctable-error.ts"() {
2742
2746
  "use strict";
2743
2747
  init_submission_errors();
2748
+ NON_SOLE_ROUND_RESUME_MESSAGE = "\u7EC8\u5C40\u4EA4\u5377\u5E76\u975E\u672C\u8F6E\u552F\u4E00\u5DE5\u5177\u8C03\u7528\u3002";
2744
2749
  correctableSubmissionErrorBrand = /* @__PURE__ */ Symbol("ak-roles.correctable-submission-error");
2745
2750
  CorrectableSubmissionError = class extends Error {
2746
2751
  [correctableSubmissionErrorBrand] = true;
@@ -24413,7 +24418,7 @@ function createPiRoleHostAdapter(pi, options = {}) {
24413
24418
  deliverSubmissionRejection(rejection) {
24414
24419
  pi.sendMessage({
24415
24420
  customType: "ak-role-submission-rejection",
24416
- content: "The terminal submission was rejected because it was not the sole tool call in its turn. Correct the call pattern and resubmit.",
24421
+ content: mechanicalSubmissionRejectionResumeMessage(rejection.code),
24417
24422
  display: true,
24418
24423
  details: rejection
24419
24424
  }, { triggerTurn: true, deliverAs: "followUp" });
@@ -24528,6 +24533,7 @@ var init_adapter = __esm({
24528
24533
  init_oauth_keepalive();
24529
24534
  init_role_runtime();
24530
24535
  init_agent_start_materials();
24536
+ init_submission_correctable_error();
24531
24537
  piContexts = /* @__PURE__ */ new WeakMap();
24532
24538
  }
24533
24539
  });
@@ -26192,7 +26198,7 @@ ${priorNativePaths.join("\n")}` : prepared.prompt;
26192
26198
  if ("failure" in closure) {
26193
26199
  return { code: null, stderr: "", timedOut: false, knownFailure: closure.failure };
26194
26200
  }
26195
- prompt = `The prior terminal submission was rejected (${closure.retry.code}). Resubmit it as the sole terminal tool call. Rejected call ids: ${closure.retry.toolCallIds.join(", ") || "none"}.`;
26201
+ prompt = closure.retry.message;
26196
26202
  }
26197
26203
  return failure("output", "AcpRoundLimit", "round-retry-limit", { sessionId });
26198
26204
  } finally {
@@ -26390,7 +26396,11 @@ async function prepareAcpRoleEnvelope(options) {
26390
26396
  };
26391
26397
  const host = {
26392
26398
  deliverSubmissionRejection(value) {
26393
- rejection = value;
26399
+ rejection = {
26400
+ code: value.code,
26401
+ toolCallIds: value.toolCallIds,
26402
+ message: mechanicalSubmissionRejectionResumeMessage(value.code)
26403
+ };
26394
26404
  },
26395
26405
  capabilities: {
26396
26406
  skillExpansion(prompt) {
@@ -26460,18 +26470,22 @@ async function prepareAcpRoleEnvelope(options) {
26460
26470
  createRoleRuntimeExtension(options.dependencies)(envelope);
26461
26471
  const token = randomUUID7();
26462
26472
  const server = createServer((socket) => serveSocket(socket));
26463
- function rememberProjectedRejection(details, toolCallId) {
26473
+ function rememberProjectedRejection(details, toolCallId, content) {
26464
26474
  if (typeof details !== "object" || details === null) return;
26465
26475
  const record4 = details;
26466
26476
  if (record4.cause === "infrastructure") return;
26467
26477
  if (record4.kind === "role_infrastructure_failure") return;
26468
26478
  const code = typeof record4.code === "string" && record4.code.length > 0 ? record4.code : record4.status === "bounce" || record4.status === "escalate" || record4.status === "no_receipt" ? record4.status : void 0;
26469
26479
  if (code === void 0) return;
26470
- rejection = { code, toolCallIds: [toolCallId] };
26480
+ rejection = {
26481
+ code,
26482
+ toolCallIds: [toolCallId],
26483
+ message: textDiagnostic(content) ?? code
26484
+ };
26471
26485
  }
26472
26486
  function textDiagnostic(content) {
26473
- const text = content.map((part) => part.type === "text" ? part.text : "").join("").trim();
26474
- return text.length > 0 ? text : void 0;
26487
+ const text = content.map((part) => part.type === "text" ? part.text : "").join("");
26488
+ return text.trim().length > 0 ? text : void 0;
26475
26489
  }
26476
26490
  function rememberInfrastructureFailure(details, content) {
26477
26491
  if (infrastructureRoundFailure !== void 0) return;
@@ -26527,7 +26541,7 @@ async function prepareAcpRoleEnvelope(options) {
26527
26541
  sessionEntries.push(toolResultEntry);
26528
26542
  if (projected.isError) {
26529
26543
  rememberInfrastructureFailure(projected.details, projected.content);
26530
- rememberProjectedRejection(projected.details, toolCallId);
26544
+ rememberProjectedRejection(projected.details, toolCallId, projected.content);
26531
26545
  }
26532
26546
  await emit("tool_execution_end", { toolCallId, toolName, isError: projected.isError });
26533
26547
  return projected;
@@ -26718,7 +26732,11 @@ async function prepareAcpRoleEnvelope(options) {
26718
26732
  return { accepted: true };
26719
26733
  }
26720
26734
  if (rejection !== void 0) {
26721
- const retry = { code: rejection.code, toolCallIds: rejection.toolCallIds };
26735
+ const retry = {
26736
+ code: rejection.code,
26737
+ toolCallIds: rejection.toolCallIds,
26738
+ message: rejection.message
26739
+ };
26722
26740
  rejection = void 0;
26723
26741
  return { accepted: false, retry };
26724
26742
  }
@@ -2712,6 +2712,10 @@ var init_submission_errors = __esm({
2712
2712
  });
2713
2713
 
2714
2714
  // src/submission-correctable-error.ts
2715
+ function mechanicalSubmissionRejectionResumeMessage(code) {
2716
+ if (code === "non-sole-round") return NON_SOLE_ROUND_RESUME_MESSAGE;
2717
+ return code;
2718
+ }
2715
2719
  function isCorrectableSubmissionError(error) {
2716
2720
  return error instanceof CorrectableSubmissionError;
2717
2721
  }
@@ -2736,11 +2740,12 @@ function projectCorrectableExecuteRejection(error) {
2736
2740
  }
2737
2741
  };
2738
2742
  }
2739
- var correctableSubmissionErrorBrand, CorrectableSubmissionError;
2743
+ var NON_SOLE_ROUND_RESUME_MESSAGE, correctableSubmissionErrorBrand, CorrectableSubmissionError;
2740
2744
  var init_submission_correctable_error = __esm({
2741
2745
  "src/submission-correctable-error.ts"() {
2742
2746
  "use strict";
2743
2747
  init_submission_errors();
2748
+ NON_SOLE_ROUND_RESUME_MESSAGE = "\u7EC8\u5C40\u4EA4\u5377\u5E76\u975E\u672C\u8F6E\u552F\u4E00\u5DE5\u5177\u8C03\u7528\u3002";
2744
2749
  correctableSubmissionErrorBrand = /* @__PURE__ */ Symbol("ak-roles.correctable-submission-error");
2745
2750
  CorrectableSubmissionError = class extends Error {
2746
2751
  [correctableSubmissionErrorBrand] = true;
@@ -24413,7 +24418,7 @@ function createPiRoleHostAdapter(pi, options = {}) {
24413
24418
  deliverSubmissionRejection(rejection) {
24414
24419
  pi.sendMessage({
24415
24420
  customType: "ak-role-submission-rejection",
24416
- content: "The terminal submission was rejected because it was not the sole tool call in its turn. Correct the call pattern and resubmit.",
24421
+ content: mechanicalSubmissionRejectionResumeMessage(rejection.code),
24417
24422
  display: true,
24418
24423
  details: rejection
24419
24424
  }, { triggerTurn: true, deliverAs: "followUp" });
@@ -24528,6 +24533,7 @@ var init_adapter = __esm({
24528
24533
  init_oauth_keepalive();
24529
24534
  init_role_runtime();
24530
24535
  init_agent_start_materials();
24536
+ init_submission_correctable_error();
24531
24537
  piContexts = /* @__PURE__ */ new WeakMap();
24532
24538
  }
24533
24539
  });
@@ -26086,7 +26092,11 @@ async function prepareAcpRoleEnvelope(options) {
26086
26092
  };
26087
26093
  const host = {
26088
26094
  deliverSubmissionRejection(value) {
26089
- rejection = value;
26095
+ rejection = {
26096
+ code: value.code,
26097
+ toolCallIds: value.toolCallIds,
26098
+ message: mechanicalSubmissionRejectionResumeMessage(value.code)
26099
+ };
26090
26100
  },
26091
26101
  capabilities: {
26092
26102
  skillExpansion(prompt) {
@@ -26156,18 +26166,22 @@ async function prepareAcpRoleEnvelope(options) {
26156
26166
  createRoleRuntimeExtension(options.dependencies)(envelope);
26157
26167
  const token = randomUUID7();
26158
26168
  const server = createServer((socket) => serveSocket(socket));
26159
- function rememberProjectedRejection(details, toolCallId) {
26169
+ function rememberProjectedRejection(details, toolCallId, content) {
26160
26170
  if (typeof details !== "object" || details === null) return;
26161
26171
  const record4 = details;
26162
26172
  if (record4.cause === "infrastructure") return;
26163
26173
  if (record4.kind === "role_infrastructure_failure") return;
26164
26174
  const code = typeof record4.code === "string" && record4.code.length > 0 ? record4.code : record4.status === "bounce" || record4.status === "escalate" || record4.status === "no_receipt" ? record4.status : void 0;
26165
26175
  if (code === void 0) return;
26166
- rejection = { code, toolCallIds: [toolCallId] };
26176
+ rejection = {
26177
+ code,
26178
+ toolCallIds: [toolCallId],
26179
+ message: textDiagnostic(content) ?? code
26180
+ };
26167
26181
  }
26168
26182
  function textDiagnostic(content) {
26169
- const text = content.map((part) => part.type === "text" ? part.text : "").join("").trim();
26170
- return text.length > 0 ? text : void 0;
26183
+ const text = content.map((part) => part.type === "text" ? part.text : "").join("");
26184
+ return text.trim().length > 0 ? text : void 0;
26171
26185
  }
26172
26186
  function rememberInfrastructureFailure(details, content) {
26173
26187
  if (infrastructureRoundFailure !== void 0) return;
@@ -26223,7 +26237,7 @@ async function prepareAcpRoleEnvelope(options) {
26223
26237
  sessionEntries.push(toolResultEntry);
26224
26238
  if (projected.isError) {
26225
26239
  rememberInfrastructureFailure(projected.details, projected.content);
26226
- rememberProjectedRejection(projected.details, toolCallId);
26240
+ rememberProjectedRejection(projected.details, toolCallId, projected.content);
26227
26241
  }
26228
26242
  await emit("tool_execution_end", { toolCallId, toolName, isError: projected.isError });
26229
26243
  return projected;
@@ -26414,7 +26428,11 @@ async function prepareAcpRoleEnvelope(options) {
26414
26428
  return { accepted: true };
26415
26429
  }
26416
26430
  if (rejection !== void 0) {
26417
- const retry = { code: rejection.code, toolCallIds: rejection.toolCallIds };
26431
+ const retry = {
26432
+ code: rejection.code,
26433
+ toolCallIds: rejection.toolCallIds,
26434
+ message: rejection.message
26435
+ };
26418
26436
  rejection = void 0;
26419
26437
  return { accepted: false, retry };
26420
26438
  }
@@ -26937,7 +26955,7 @@ ${priorNativePaths.join("\n")}` : prepared.prompt;
26937
26955
  outcome = { code: null, stderr: spawned.stderr, timedOut: false, knownFailure: closure.failure };
26938
26956
  break;
26939
26957
  }
26940
- prompt = `The prior terminal submission was rejected (${closure.retry.code}). Resubmit it as the sole terminal structured output (or the sole terminating tool call). Rejected call ids: ${closure.retry.toolCallIds.join(", ") || "none"}.`;
26958
+ prompt = closure.retry.message;
26941
26959
  sessionKind = "resume";
26942
26960
  if (attempt === 7) {
26943
26961
  outcome = failure("output", "HeadlessRoundLimit", "round-retry-limit", { sessionId });
@@ -3,6 +3,7 @@ import { requireGatekeeperPass } from "../gatekeeper-pass-envelope.js";
3
3
  import { createOAuthKeepalive } from "../oauth-keepalive.js";
4
4
  import { createRoleRuntimeExtension } from "../role-runtime.js";
5
5
  import { renderAgentStartMaterials } from "../agent-start-materials.js";
6
+ import { mechanicalSubmissionRejectionResumeMessage } from "../submission-correctable-error.js";
6
7
  const piContexts = new WeakMap();
7
8
  /** Pi-only entrypoint adapter around the host-neutral shared composition. */
8
9
  export function createPiRoleRuntimeExtension(dependencies, options = {}) {
@@ -87,7 +88,7 @@ export function createPiRoleHostAdapter(pi, options = {}) {
87
88
  deliverSubmissionRejection(rejection) {
88
89
  pi.sendMessage({
89
90
  customType: "ak-role-submission-rejection",
90
- content: "The terminal submission was rejected because it was not the sole tool call in its turn. Correct the call pattern and resubmit.",
91
+ content: mechanicalSubmissionRejectionResumeMessage(rejection.code),
91
92
  display: true,
92
93
  details: rejection,
93
94
  }, { triggerTurn: true, deliverAs: "followUp" });
@@ -1,4 +1,16 @@
1
1
  import { GatekeeperDecisionError, ParentQueueReaskError, WorkerCommitReminderError, WorkerPrefixReminderError, WorkerUnfinishedReasonReminderError, } from "./submission-errors.js";
2
+ /**
3
+ * Mechanical non-sole resume text — one shared authority for Pi followUp and
4
+ * ACP/headless closeRound retry (#813). Officer bounce/escalate use projected
5
+ * receipt text instead; this string is only the round-shape correction.
6
+ */
7
+ export const NON_SOLE_ROUND_RESUME_MESSAGE = "终局交卷并非本轮唯一工具调用。";
8
+ /** Resume text for mechanical deliverSubmissionRejection codes (no officer receipt). */
9
+ export function mechanicalSubmissionRejectionResumeMessage(code) {
10
+ if (code === "non-sole-round")
11
+ return NON_SOLE_ROUND_RESUME_MESSAGE;
12
+ return code;
13
+ }
2
14
  /** Shared, unforgeable identity for submission errors that the same session may correct. */
3
15
  const correctableSubmissionErrorBrand = Symbol("ak-roles.correctable-submission-error");
4
16
  export class CorrectableSubmissionError extends Error {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3816",
3
+ "version": "0.1.3824",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -28,6 +28,7 @@ import {
28
28
  } from "./role-turn-host.ts";
29
29
  import {
30
30
  isCorrectableExecuteError,
31
+ mechanicalSubmissionRejectionResumeMessage,
31
32
  projectCorrectableExecuteRejection,
32
33
  } from "../submission-correctable-error.ts";
33
34
  import {
@@ -184,7 +185,9 @@ export async function prepareAcpRoleEnvelope(options: {
184
185
  const sessionEntries: Array<Record<string, unknown>> = [];
185
186
  const methodSkills = new Map<string, { path: string; body: string }>();
186
187
  let preferredTools: string[] = [];
187
- let rejection: { readonly code: string; readonly toolCallIds: readonly string[] } | undefined;
188
+ let rejection:
189
+ | { readonly code: string; readonly toolCallIds: readonly string[]; readonly message: string }
190
+ | undefined;
188
191
  /** Typed infrastructure failure for this ACP round; closeRound returns it as knownFailure (#593). */
189
192
  let infrastructureRoundFailure: RoleTurnKnownFailure | undefined;
190
193
  const hostAbort = new AbortController();
@@ -263,7 +266,14 @@ export async function prepareAcpRoleEnvelope(options: {
263
266
  };
264
267
 
265
268
  const host: RoleHost = {
266
- deliverSubmissionRejection(value) { rejection = value; },
269
+ deliverSubmissionRejection(value) {
270
+ // Mechanical round rejection has no officer receipt; shared resume text is the fact (#813).
271
+ rejection = {
272
+ code: value.code,
273
+ toolCallIds: value.toolCallIds,
274
+ message: mechanicalSubmissionRejectionResumeMessage(value.code),
275
+ };
276
+ },
267
277
  capabilities: {
268
278
  skillExpansion(prompt): HostSkillExpansionEvidence | undefined {
269
279
  return buildAcpSkillExpansion(methodSkills, prompt);
@@ -321,7 +331,11 @@ export async function prepareAcpRoleEnvelope(options: {
321
331
  const token = randomUUID();
322
332
  const server = createServer((socket) => serveSocket(socket));
323
333
  /** Correctable non-pass must arm the existing rejection state so closeRound returns retry. */
324
- function rememberProjectedRejection(details: unknown, toolCallId: string): void {
334
+ function rememberProjectedRejection(
335
+ details: unknown,
336
+ toolCallId: string,
337
+ content: ContentPart[],
338
+ ): void {
325
339
  if (typeof details !== "object" || details === null) return;
326
340
  const record = details as Record<string, unknown>;
327
341
  if (record.cause === "infrastructure") return;
@@ -332,14 +346,21 @@ export async function prepareAcpRoleEnvelope(options: {
332
346
  ? record.status
333
347
  : undefined;
334
348
  if (code === undefined) return;
335
- rejection = { code, toolCallIds: [toolCallId] };
349
+ // Officer bounce/escalate text is already the tool_result content (GatekeeperDecisionError
350
+ // message = raw receipt). Adapters resume with this message — never a host-invented line (#813).
351
+ rejection = {
352
+ code,
353
+ toolCallIds: [toolCallId],
354
+ message: textDiagnostic(content) ?? code,
355
+ };
336
356
  }
337
357
  function textDiagnostic(content: ContentPart[]): string | undefined {
358
+ // Keep concatenated text bytes intact for resume relay (#813 online P2):
359
+ // only emptiness uses the trimmed form; do not alter Markdown/spacing.
338
360
  const text = content
339
361
  .map((part) => (part.type === "text" ? part.text : ""))
340
- .join("")
341
- .trim();
342
- return text.length > 0 ? text : undefined;
362
+ .join("");
363
+ return text.trim().length > 0 ? text : undefined;
343
364
  }
344
365
  /** Arm closeRound + abort path with the durable infrastructure failure for this round. */
345
366
  function rememberInfrastructureFailure(
@@ -430,7 +451,7 @@ export async function prepareAcpRoleEnvelope(options: {
430
451
  sessionEntries.push(toolResultEntry);
431
452
  if (projected.isError) {
432
453
  rememberInfrastructureFailure(projected.details, projected.content);
433
- rememberProjectedRejection(projected.details, toolCallId);
454
+ rememberProjectedRejection(projected.details, toolCallId, projected.content);
434
455
  }
435
456
  await emit("tool_execution_end", { toolCallId, toolName, isError: projected.isError });
436
457
  return projected;
@@ -646,7 +667,11 @@ export async function prepareAcpRoleEnvelope(options: {
646
667
  return { accepted: true as const };
647
668
  }
648
669
  if (rejection !== undefined) {
649
- const retry = { code: rejection.code, toolCallIds: rejection.toolCallIds };
670
+ const retry = {
671
+ code: rejection.code,
672
+ toolCallIds: rejection.toolCallIds,
673
+ message: rejection.message,
674
+ };
650
675
  rejection = undefined;
651
676
  return { accepted: false as const, retry };
652
677
  }
@@ -39,7 +39,15 @@ export type AcpPreparedTurn = Readonly<{
39
39
  /** Shared ledger consumes the complete ACP round after session/prompt resolves. */
40
40
  closeRound(): Promise<
41
41
  | { readonly accepted: true }
42
- | { readonly accepted: false; readonly retry: { readonly code: string; readonly toolCallIds: readonly string[] } }
42
+ | {
43
+ readonly accepted: false;
44
+ /** Shared envelope provides officer/correctable text; adapters only deliver it (#813). */
45
+ readonly retry: {
46
+ readonly code: string;
47
+ readonly toolCallIds: readonly string[];
48
+ readonly message: string;
49
+ };
50
+ }
43
51
  | { readonly accepted: false; readonly failure: RoleTurnKnownFailure }
44
52
  >;
45
53
  dispose?(): Promise<void>;
@@ -400,7 +408,8 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
400
408
  if ("failure" in closure) {
401
409
  return { code: null, stderr: "", timedOut: false, knownFailure: closure.failure };
402
410
  }
403
- prompt = `The prior terminal submission was rejected (${closure.retry.code}). Resubmit it as the sole terminal tool call. Rejected call ids: ${closure.retry.toolCallIds.join(", ") || "none"}.`;
411
+ // Shared envelope already owns the officer/correctable text (#813).
412
+ prompt = closure.retry.message;
404
413
  }
405
414
  return failure("output", "AcpRoundLimit", "round-retry-limit", { sessionId });
406
415
  } finally {
@@ -401,8 +401,8 @@ export function createHeadlessRoleTurnHost(config: HeadlessRoleTurnHostConfig):
401
401
  outcome = { code: null, stderr: spawned.stderr, timedOut: false, knownFailure: closure.failure };
402
402
  break;
403
403
  }
404
- // Correctable rejection → resume same session with plain resubmit prompt.
405
- prompt = `The prior terminal submission was rejected (${closure.retry.code}). Resubmit it as the sole terminal structured output (or the sole terminating tool call). Rejected call ids: ${closure.retry.toolCallIds.join(", ") || "none"}.`;
404
+ // Shared envelope already owns the officer/correctable text (#813).
405
+ prompt = closure.retry.message;
406
406
  sessionKind = "resume";
407
407
  if (attempt === 7) {
408
408
  outcome = failure("output", "HeadlessRoundLimit", "round-retry-limit", { sessionId });
package/src/pi/adapter.ts CHANGED
@@ -21,6 +21,7 @@ import type {
21
21
  import { createOAuthKeepalive, type OAuthKeepaliveOptions } from "../oauth-keepalive.ts";
22
22
  import { createRoleRuntimeExtension, type RoleRuntimeDependencies } from "../role-runtime.ts";
23
23
  import { renderAgentStartMaterials } from "../agent-start-materials.ts";
24
+ import { mechanicalSubmissionRejectionResumeMessage } from "../submission-correctable-error.ts";
24
25
 
25
26
  export type PiRoleHostAdapter = RoleEnvelopeHost;
26
27
 
@@ -139,7 +140,7 @@ export function createPiRoleHostAdapter(
139
140
  deliverSubmissionRejection(rejection) {
140
141
  pi.sendMessage({
141
142
  customType: "ak-role-submission-rejection",
142
- content: "The terminal submission was rejected because it was not the sole tool call in its turn. Correct the call pattern and resubmit.",
143
+ content: mechanicalSubmissionRejectionResumeMessage(rejection.code),
143
144
  display: true,
144
145
  details: rejection,
145
146
  }, { triggerTurn: true, deliverAs: "followUp" });
@@ -6,6 +6,20 @@ import {
6
6
  WorkerUnfinishedReasonReminderError,
7
7
  } from "./submission-errors.ts";
8
8
 
9
+ /**
10
+ * Mechanical non-sole resume text — one shared authority for Pi followUp and
11
+ * ACP/headless closeRound retry (#813). Officer bounce/escalate use projected
12
+ * receipt text instead; this string is only the round-shape correction.
13
+ */
14
+ export const NON_SOLE_ROUND_RESUME_MESSAGE =
15
+ "终局交卷并非本轮唯一工具调用。";
16
+
17
+ /** Resume text for mechanical deliverSubmissionRejection codes (no officer receipt). */
18
+ export function mechanicalSubmissionRejectionResumeMessage(code: string): string {
19
+ if (code === "non-sole-round") return NON_SOLE_ROUND_RESUME_MESSAGE;
20
+ return code;
21
+ }
22
+
9
23
  /** Shared, unforgeable identity for submission errors that the same session may correct. */
10
24
  const correctableSubmissionErrorBrand = Symbol("ak-roles.correctable-submission-error");
11
25