pi-subagents 0.62.0 → 0.64.0

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 (57) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/docs/agents.md +5 -4
  3. package/docs/configuration.md +28 -2
  4. package/docs/models.md +5 -5
  5. package/docs/observability.md +4 -1
  6. package/docs/tool-reference.md +2 -2
  7. package/docs/watchdog.md +92 -114
  8. package/package.json +1 -1
  9. package/skills/pi-subagents/references/execution-controls.md +4 -3
  10. package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  11. package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
  12. package/src/agents/agent-management.ts +22 -4
  13. package/src/agents/agents.ts +107 -125
  14. package/src/api/shared-types.ts +3 -0
  15. package/src/extension/config.ts +20 -0
  16. package/src/inspectors/herdr/inspector-runner.ts +19 -13
  17. package/src/runs/background/active-async-capacity.ts +0 -1
  18. package/src/runs/background/async-execution.ts +56 -7
  19. package/src/runs/background/async-resume.ts +3 -0
  20. package/src/runs/background/async-status.ts +18 -2
  21. package/src/runs/background/notify.ts +54 -3
  22. package/src/runs/background/run-status.ts +22 -2
  23. package/src/runs/background/subagent-runner.ts +53 -3
  24. package/src/runs/background/wait-completions.ts +13 -0
  25. package/src/runs/foreground/execution.ts +7 -1
  26. package/src/runs/foreground/subagent-executor.ts +56 -4
  27. package/src/runs/shared/acceptance.ts +25 -9
  28. package/src/runs/shared/async-status-projection.ts +11 -43
  29. package/src/runs/shared/lane-metadata.ts +24 -3
  30. package/src/runs/shared/parallel-handoff.ts +4 -0
  31. package/src/runs/shared/pi-args.ts +9 -6
  32. package/src/runs/shared/subagent-control.ts +4 -2
  33. package/src/runs/shared/task-intent.ts +5 -2
  34. package/src/runs/shared/worktree.ts +467 -63
  35. package/src/shared/types.ts +38 -2
  36. package/src/shared/utils.ts +18 -7
  37. package/src/slash/subagents-admin.ts +24 -12
  38. package/src/tui/fleet-status.ts +61 -2
  39. package/src/tui/fleet.ts +12 -7
  40. package/src/tui/render.ts +227 -14
  41. package/src/watchdog/child-status.ts +54 -33
  42. package/src/watchdog/diff-tool.ts +77 -0
  43. package/src/watchdog/emission-guard.ts +5 -3
  44. package/src/watchdog/guidance.ts +20 -0
  45. package/src/watchdog/register-child.ts +16 -13
  46. package/src/watchdog/register-main.ts +10 -9
  47. package/src/watchdog/render.ts +4 -5
  48. package/src/watchdog/review.ts +15 -4
  49. package/src/watchdog/rules.ts +70 -0
  50. package/src/watchdog/runtime.ts +75 -92
  51. package/src/watchdog/scope.ts +0 -11
  52. package/src/watchdog/settings.ts +48 -104
  53. package/src/watchdog/types.ts +18 -32
  54. package/src/watchdog/warning-format.ts +0 -1
  55. package/src/workflows/chat-progress.ts +3 -2
  56. package/src/workflows/workflow-checklist.ts +439 -0
  57. package/src/workflows/workflow-preflight.ts +28 -1
@@ -2,6 +2,7 @@ import { sanitizeDisplayText, truncateDisplayText } from "../../shared/display-t
2
2
  import { formatModelThinking } from "../../shared/formatters.ts";
3
3
  import type { AsyncJobState, AsyncJobStep, HostStepFreshnessV1, HostStepMonitorKind, HostStepNodeV1, HostStepState, HostStepVerdict, NestedRunSummary, NestedStepSummary, SubagentRunMode, WorkflowGraphSnapshot, WorkflowPreflightLaneV1, WorkflowPreflightV1 } from "../../shared/types.ts";
4
4
  import { HOST_STEP_MAX_COUNT, HOST_STEP_MAX_DETAIL_CHARS, HOST_STEP_MAX_LABEL_CHARS, HOST_STEP_MAX_PROVIDER_CHARS, HOST_STEP_MAX_REASON_CHARS, HOST_STEP_MAX_REF_CHARS, HOST_STEP_MAX_ROLE_CHARS, HOST_STEP_MAX_TARGET_CHARS, hostStepReportName, parseHostStepNode, validHostStepNodes } from "./host-step-status.ts";
5
+ import { workflowPreflightLaneForRuntimeKey } from "../../workflows/workflow-preflight.ts";
5
6
  import { workflowGraphStageNodes } from "./workflow-graph.ts";
6
7
 
7
8
  export const ASYNC_STATUS_SNAPSHOT_KIND = "pi-subagents.async-status-snapshot";
@@ -472,7 +473,7 @@ function projectWorkflowGraphRow(node: WorkflowGraphSnapshot["nodes"][number], p
472
473
  };
473
474
  }
474
475
 
475
- /** Project loaded workflow child facts plus stored preflight hints into compact rows. */
476
+ /** Project authoritative workflow facts into compact rows, annotated by preflight hints. */
476
477
  export function projectAsyncWorkflowRows(
477
478
  steps: readonly AsyncJobStep[] | undefined,
478
479
  hostStepsOrPreflight?: readonly HostStepNodeV1[] | WorkflowGraphSnapshot | WorkflowPreflightV1,
@@ -482,7 +483,8 @@ export function projectAsyncWorkflowRows(
482
483
  const graph = isWorkflowGraph(hostStepsOrPreflight) ? hostStepsOrPreflight : undefined;
483
484
  const hostSteps = isWorkflowPreflight(hostStepsOrPreflight) || graph ? undefined : hostStepsOrPreflight;
484
485
  const loaded = steps ?? [];
485
- const declared = new Map<string, WorkflowPreflightLaneV1>();
486
+ const preflightForKey = (key: string, groupKeys: readonly (string | undefined)[] = []): WorkflowPreflightLaneV1 | undefined =>
487
+ workflowPreflightLaneForRuntimeKey(preflight, key, groupKeys);
486
488
  if (graph) {
487
489
  const loadedIndexesByKey = new Map<string, number[]>();
488
490
  for (const [index, step] of loaded.entries()) {
@@ -493,7 +495,6 @@ export function projectAsyncWorkflowRows(
493
495
  }
494
496
  const consumed = new Set<number>();
495
497
  const childRows: AsyncStatusWorkflowRow[] = [];
496
- for (const lane of preflight?.lanes ?? []) declared.set(lane.key, lane);
497
498
  const graphStages = workflowGraphStageNodes(graph);
498
499
  const graphKeys = new Set(graphStages.map((node) => node.id));
499
500
  const graphPhaseByNodeId = new Map<string, string>();
@@ -502,60 +503,27 @@ export function projectAsyncWorkflowRows(
502
503
  if (graphKeys.has(nodeId) && !graphPhaseByNodeId.has(nodeId)) graphPhaseByNodeId.set(nodeId, phase.title);
503
504
  }
504
505
  }
505
- const graphLaneKeys = new Set(graphPhaseByNodeId.values());
506
- const preflightForNode = (node: WorkflowGraphSnapshot["nodes"][number]): WorkflowPreflightLaneV1 | undefined => {
507
- const phaseTitle = graphPhaseByNodeId.get(node.id);
508
- return declared.get(node.id) ?? (node.phase ? declared.get(node.phase) : undefined) ?? (phaseTitle ? declared.get(phaseTitle) : undefined);
509
- };
510
506
  for (const node of graphStages) {
507
+ const lane = preflightForKey(node.id, [node.phase, graphPhaseByNodeId.get(node.id)]);
511
508
  const indexes = (loadedIndexesByKey.get(node.id) ?? []).filter((index) => !consumed.has(index));
512
- if (indexes.length > 0) {
513
- for (const index of indexes) {
514
- consumed.add(index);
515
- childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, preflightForNode(node)));
516
- }
517
- } else {
518
- childRows.push(projectWorkflowGraphRow(node, preflightForNode(node)));
519
- }
520
- }
521
- for (const lane of preflight?.lanes ?? []) {
522
- if (graphKeys.has(lane.key) || graphLaneKeys.has(lane.key)) continue;
523
- const indexes = (loadedIndexesByKey.get(lane.key) ?? []).filter((index) => !consumed.has(index));
524
509
  if (indexes.length > 0) {
525
510
  for (const index of indexes) {
526
511
  consumed.add(index);
527
512
  childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, lane));
528
513
  }
529
514
  } else {
530
- childRows.push({ name: lane.key, state: "planned", preflight: lane });
515
+ childRows.push(projectWorkflowGraphRow(node, lane));
531
516
  }
532
517
  }
533
518
  for (const [index, step] of loaded.entries()) {
534
- if (!consumed.has(index)) childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? declared.get(step.workflowKey) : undefined));
519
+ if (!consumed.has(index)) childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? preflightForKey(step.workflowKey, [step.phase]) : undefined));
535
520
  }
536
521
  return [...childRows, ...validHostStepList(graph).map(hostStepRow)];
537
522
  }
538
- const loadedIndexByKey = new Map<string, number>();
539
- for (const [index, step] of loaded.entries()) {
540
- if (step.workflowKey !== undefined && !loadedIndexByKey.has(step.workflowKey)) loadedIndexByKey.set(step.workflowKey, index);
541
- }
542
- const consumed = new Set<number>();
543
- const childRows: AsyncStatusWorkflowRow[] = [];
544
- for (const lane of preflight?.lanes ?? []) {
545
- declared.set(lane.key, lane);
546
- const index = loadedIndexByKey.get(lane.key);
547
- if (index === undefined) {
548
- childRows.push({ name: lane.key, state: "planned", preflight: lane });
549
- continue;
550
- }
551
- consumed.add(index);
552
- childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, lane));
553
- }
554
- for (const [index, step] of loaded.entries()) {
555
- if (consumed.has(index)) continue;
556
- childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? declared.get(step.workflowKey) : undefined));
557
- }
558
- return [...childRows, ...validHostStepList(hostSteps).map(hostStepRow)];
523
+ return [
524
+ ...loaded.map((step, index) => projectLoadedWorkflowRow(step, index, step.workflowKey ? preflightForKey(step.workflowKey, [step.phase]) : undefined)),
525
+ ...validHostStepList(hostSteps).map(hostStepRow),
526
+ ];
559
527
  }
560
528
 
561
529
  function projectLoadedWorkflowRow(step: AsyncJobStep, index: number, preflight?: WorkflowPreflightLaneV1): AsyncStatusWorkflowRow {
@@ -1,4 +1,4 @@
1
- import type { AsyncStatus, WorkflowLaneMetadata, WorkflowLaneMode } from "../../shared/types.ts";
1
+ import type { AsyncStatus, ManagedWorktreeProvider, WorktreeNaming, WorkflowLaneMetadata, WorkflowLaneMode } from "../../shared/types.ts";
2
2
 
3
3
  export const WORKFLOW_LANE_KEY_MAX_BYTES = 128;
4
4
  export const WORKFLOW_LANE_SOURCE_REF_MAX_BYTES = 128;
@@ -8,6 +8,7 @@ export const WORKFLOW_LANE_OUTPUT_PATH_MAX_BYTES = 256;
8
8
  export const WORKFLOW_LANE_OUTPUT_PATHS_MAX = 10;
9
9
  export const WORKTREE_STATUS_PATH_MAX_BYTES = 4096;
10
10
  export const WORKTREE_STATUS_BRANCH_MAX_BYTES = 256;
11
+ export const WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES = 256;
11
12
 
12
13
  const WORKFLOW_LANE_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
13
14
  const WORKFLOW_LANE_MODES = new Set<WorkflowLaneMode>(["mutation", "review", "scout", "gate"]);
@@ -75,16 +76,36 @@ export function assertWorkflowLaneKey(lane: WorkflowLaneMetadata | undefined, wo
75
76
  export interface WorktreeStatusReference {
76
77
  worktreePath: string;
77
78
  branch: string;
79
+ provider?: ManagedWorktreeProvider;
80
+ naming?: WorktreeNaming;
81
+ }
82
+
83
+ function normalizeWorktreeNaming(value: unknown, label: string): WorktreeNaming {
84
+ assertPlainObject(value, label);
85
+ assertKnownFields(value, ["requestedBranch", "branchPrefix", "label", "sanitizedPathComponent", "collision", "collisionSuffix"], label);
86
+ const collision = value.collision;
87
+ if (collision !== undefined && collision !== "branch" && collision !== "path" && collision !== "both") throw new Error(`${label}.collision is invalid.`);
88
+ return {
89
+ requestedBranch: boundedNonEmptyString(value.requestedBranch, `${label}.requestedBranch`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
90
+ branchPrefix: boundedNonEmptyString(value.branchPrefix, `${label}.branchPrefix`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
91
+ label: boundedNonEmptyString(value.label, `${label}.label`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES),
92
+ sanitizedPathComponent: boundedNonEmptyString(value.sanitizedPathComponent, `${label}.sanitizedPathComponent`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES),
93
+ ...(collision !== undefined ? { collision } : {}),
94
+ ...(value.collisionSuffix !== undefined ? { collisionSuffix: boundedNonEmptyString(value.collisionSuffix, `${label}.collisionSuffix`, WORKTREE_STATUS_NAMING_LABEL_MAX_BYTES) } : {}),
95
+ };
78
96
  }
79
97
 
80
98
  /** Validate the display-only worktree fields copied into status.json. */
81
99
  export function normalizeWorktreeStatusReference(value: unknown, label = "worktree status reference"): WorktreeStatusReference | undefined {
82
100
  if (value === undefined) return undefined;
83
101
  assertPlainObject(value, label);
84
- assertKnownFields(value, ["worktreePath", "branch"], label);
102
+ assertKnownFields(value, ["worktreePath", "branch", "provider", "naming"], label);
103
+ if (value.provider !== undefined && value.provider !== "native" && value.provider !== "worktrunk") throw new Error(`${label}.provider is invalid.`);
85
104
  return {
86
105
  worktreePath: boundedNonEmptyString(value.worktreePath, `${label}.worktreePath`, WORKTREE_STATUS_PATH_MAX_BYTES),
87
106
  branch: boundedNonEmptyString(value.branch, `${label}.branch`, WORKTREE_STATUS_BRANCH_MAX_BYTES),
107
+ ...(value.provider !== undefined ? { provider: value.provider as ManagedWorktreeProvider } : {}),
108
+ ...(value.naming !== undefined ? { naming: normalizeWorktreeNaming(value.naming, `${label}.naming`) } : {}),
88
109
  };
89
110
  }
90
111
 
@@ -100,6 +121,6 @@ export function validateAsyncStatusLaneMetadata(status: Pick<AsyncStatus, "runId
100
121
  const hasPath = step.worktreePath !== undefined;
101
122
  const hasBranch = step.branch !== undefined;
102
123
  if (hasPath !== hasBranch) throw new Error(`${label}.steps[${index}] must include both worktreePath and branch.`);
103
- if (hasPath) normalizeWorktreeStatusReference({ worktreePath: step.worktreePath, branch: step.branch }, `${label}.steps[${index}]`);
124
+ if (hasPath) normalizeWorktreeStatusReference({ worktreePath: step.worktreePath, branch: step.branch, ...(step.provider ? { provider: step.provider } : {}), ...(step.naming ? { naming: step.naming } : {}) }, `${label}.steps[${index}]`);
104
125
  }
105
126
  }
@@ -574,6 +574,8 @@ export function writeParallelHandoffGroup(input: {
574
574
  index: worktree.index,
575
575
  path: worktree.path,
576
576
  branch: worktree.branch,
577
+ ...(worktree.provider ? { provider: worktree.provider } : {}),
578
+ ...(worktree.naming ? { naming: worktree.naming } : {}),
577
579
  worktreeRemoved: false,
578
580
  branchRemoved: false,
579
581
  preserved: true,
@@ -653,6 +655,8 @@ export function discardPreservedWorktrees(
653
655
  path: task.path,
654
656
  agentCwd: task.path,
655
657
  branch: task.branch,
658
+ ...(task.provider ? { provider: task.provider } : {}),
659
+ ...(task.naming ? { naming: task.naming } : {}),
656
660
  index: task.index,
657
661
  nodeModulesLinked: false,
658
662
  syntheticPaths: [],
@@ -47,7 +47,6 @@ import {
47
47
  } from "./tool-availability.ts";
48
48
  import {
49
49
  CHILD_WATCHDOG_CONFIG_ENV,
50
- encodeChildWatchdogConfig,
51
50
  type ChildWatchdogConfig,
52
51
  } from "../../watchdog/child-status.ts";
53
52
  import { WAIT_TOOL_DEFAULT_TIMEOUT_MS_ENV, WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
@@ -93,11 +92,12 @@ export function resolveSubagentTaskDelivery(
93
92
  : "auto";
94
93
  }
95
94
 
96
- function shouldDeliverTaskViaFile(
95
+ export function shouldDeliverTaskViaFile(
97
96
  task: string,
98
97
  delivery: SubagentTaskDelivery,
98
+ platform: NodeJS.Platform = process.platform,
99
99
  ): boolean {
100
- return delivery === "file" || task.length > TASK_ARG_LIMIT;
100
+ return delivery === "file" || platform === "darwin" || task.length > TASK_ARG_LIMIT;
101
101
  }
102
102
  const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
103
103
  const PROMPT_RUNTIME_EXTENSION_PATH = path.join(
@@ -845,6 +845,11 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
845
845
  );
846
846
  env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
847
847
  let toolDiagnosticPath: string | undefined;
848
+ // Child launch environments are merged over process.env. Explicitly clear
849
+ // parent-scoped diagnostics so a nested zero-tool child cannot validate
850
+ // against, or overwrite, its parent's required-tool report.
851
+ env[REQUIRED_CHILD_TOOLS_ENV] = undefined;
852
+ env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = undefined;
848
853
  if (toolPlan.requiredChildTools.length > 0) {
849
854
  if (!tempDir)
850
855
  tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
@@ -1025,9 +1030,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
1025
1030
  const encodedToolBudget = encodeToolBudgetEnv(input.toolBudget);
1026
1031
  if (encodedToolBudget) env[TOOL_BUDGET_ENV] = encodedToolBudget;
1027
1032
  env[TOOL_BUDGET_ZERO_AUTH_ENV] = input.allowZeroToolBudget ? "1" : undefined;
1028
- const encodedChildWatchdog = encodeChildWatchdogConfig(input.childWatchdog);
1029
- if (encodedChildWatchdog)
1030
- env[CHILD_WATCHDOG_CONFIG_ENV] = encodedChildWatchdog;
1033
+ if (input.childWatchdog) env[CHILD_WATCHDOG_CONFIG_ENV] = JSON.stringify(input.childWatchdog);
1031
1034
 
1032
1035
  env[SUBAGENT_PARENT_SESSION_ENV] =
1033
1036
  input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
@@ -45,11 +45,12 @@ export function resolveControlConfig(
45
45
  const enabled = override?.enabled ?? globalConfig?.enabled ?? DEFAULT_CONTROL_CONFIG.enabled;
46
46
  const overrideNeedsAttentionAfterMs = parsePositiveInt(override?.needsAttentionAfterMs);
47
47
  const globalNeedsAttentionAfterMs = parsePositiveInt(globalConfig?.needsAttentionAfterMs);
48
+ const globalNeedsAttentionAfterMsIsExplicit = (globalConfig as Partial<ResolvedControlConfig> | undefined)?.needsAttentionAfterMsIsExplicit ?? globalNeedsAttentionAfterMs !== undefined;
48
49
  const needsAttentionAfterMs = overrideNeedsAttentionAfterMs
49
50
  ?? globalNeedsAttentionAfterMs
50
51
  ?? DEFAULT_CONTROL_CONFIG.needsAttentionAfterMs;
51
52
  const needsAttentionAfterMsIsExplicit = overrideNeedsAttentionAfterMs !== undefined
52
- || globalNeedsAttentionAfterMs !== undefined;
53
+ || globalNeedsAttentionAfterMsIsExplicit;
53
54
  const activeNoticeAfterMs = parsePositiveInt(override?.activeNoticeAfterMs)
54
55
  ?? parsePositiveInt(globalConfig?.activeNoticeAfterMs)
55
56
  ?? DEFAULT_CONTROL_CONFIG.activeNoticeAfterMs;
@@ -98,11 +99,12 @@ export function deriveActivityState(input: {
98
99
  config: ResolvedControlConfig;
99
100
  startedAt: number;
100
101
  lastActivityAt?: number;
102
+ turnCount?: number;
101
103
  currentTool?: string;
102
104
  thinking?: string | false;
103
105
  now?: number;
104
106
  }): ActivityState | undefined {
105
- if (!input.config.enabled || input.currentTool) return undefined;
107
+ if (!input.config.enabled || input.currentTool || (input.turnCount ?? 0) === 0) return undefined;
106
108
  const now = input.now ?? Date.now();
107
109
  const lastActivity = input.lastActivityAt ?? input.startedAt;
108
110
  const ageMs = Math.max(0, now - lastActivity);
@@ -41,6 +41,7 @@ const REVIEWER_REQUIRED_EDIT_PATTERNS = [
41
41
  // Accept serialized line separators too: workflow prompts can carry literal
42
42
  // `\\n`/`\\r\\n` between clauses instead of decoded newlines.
43
43
  const NO_EDIT_PROHIBITION_PATTERN = /(?:\b|\\(?:r\\n|n))(?:do not|don't|must not)\s+(?:edit|modify|write(?:\s+to)?|touch|change)\b((?:(?!\b(?:but|and|then)\b|\\(?:r\\n|n))[^.;,:!?\n–—-])*)/gi;
44
+ const COORDINATED_NO_EDIT_PROHIBITION_PATTERN = /(?:\b|\\(?:r\\n|n))(?:do not|don't|must not)\s+((?=(?:(?!\\(?:r\\n|n))[^.;:!?\n–—-])*\b(?:and|or)\s+(?:edit|modify|write(?:\s+to)?|touch|change)\b)(?:(?!\\(?:r\\n|n))[^.;:!?\n–—-])*?\b(?:and|or)\s+(?:edit|modify|write(?:\s+to)?|touch|change)\b(?:(?!\b(?:but|and|then)\b|\\(?:r\\n|n))[^.;,:!?\n–—-])*)/gi;
44
45
 
45
46
  /** Objects of a no-edit prohibition that mean "the codebase in general" rather than a named scope. */
46
47
  const GENERIC_PROHIBITION_OBJECT = /^\s*(?:(?:any|all|the|these|those|your|our|existing|project|product|source|sources|config|configs|repo|repository)[\s/,-]*)*(?:files?|code|codebase|sources?|anything|repo(?:sitory)?)?\s*$/i;
@@ -144,11 +145,13 @@ function analyzeNoEditProhibitions(taskText: string): NoEditProhibitionAnalysis
144
145
  || NO_TOOL_INTENT_PATTERNS.some((pattern) => pattern.test(taskText));
145
146
  let blanket = present;
146
147
  let strippedText = stripPatterns(taskText, [...REVIEW_ONLY_PATTERNS, ...NO_TOOL_INTENT_PATTERNS]);
147
- strippedText = strippedText.replace(new RegExp(NO_EDIT_PROHIBITION_PATTERN.source, NO_EDIT_PROHIBITION_PATTERN.flags), (match, object: string, offset: number, source: string) => {
148
+ const stripNoEditProhibition = (match: string, object: string, offset: number, source: string): string => {
148
149
  present = true;
149
150
  if (GENERIC_PROHIBITION_OBJECT.test(object) && !hasScopedProhibitionContinuation(source.slice(offset + match.length))) blanket = true;
150
151
  return " ";
151
- });
152
+ };
153
+ strippedText = strippedText.replace(new RegExp(COORDINATED_NO_EDIT_PROHIBITION_PATTERN.source, COORDINATED_NO_EDIT_PROHIBITION_PATTERN.flags), stripNoEditProhibition);
154
+ strippedText = strippedText.replace(new RegExp(NO_EDIT_PROHIBITION_PATTERN.source, NO_EDIT_PROHIBITION_PATTERN.flags), stripNoEditProhibition);
152
155
  // Restore boundaries after stripping a prohibition from serialized prompts.
153
156
  strippedText = strippedText.replace(/\\(?:r\\n|n)/g, "\n");
154
157
  return { present, blanket, strippedText };