@selesai/code 0.13.12 → 0.13.14

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 (203) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
  3. package/dist/extensions/pi-subagents/VISION.md +12 -1
  4. package/dist/extensions/pi-subagents/docs/agents.md +13 -6
  5. package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
  6. package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
  7. package/dist/extensions/pi-subagents/docs/missions.md +1 -1
  8. package/dist/extensions/pi-subagents/docs/models.md +6 -6
  9. package/dist/extensions/pi-subagents/docs/observability.md +6 -3
  10. package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
  11. package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
  12. package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
  13. package/dist/extensions/pi-subagents/install.mjs +1 -2
  14. package/dist/extensions/pi-subagents/package.json +1 -1
  15. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  16. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  18. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  19. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
  20. package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
  21. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
  22. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
  23. package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
  24. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
  25. package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
  26. package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
  27. package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
  28. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
  29. package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
  30. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
  31. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
  32. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
  33. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
  34. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
  35. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  36. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
  37. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
  38. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
  39. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
  40. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
  41. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
  42. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
  43. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
  44. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
  45. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
  46. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
  47. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
  48. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
  49. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  50. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
  51. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
  52. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
  53. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
  54. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
  55. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
  56. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
  57. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
  58. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
  59. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
  60. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
  61. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
  62. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
  63. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
  64. package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
  65. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
  66. package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
  67. package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
  68. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
  69. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
  70. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
  71. package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
  72. package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
  73. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
  74. package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
  75. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
  76. package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
  77. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
  78. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
  79. package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
  80. package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
  81. package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
  82. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
  83. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
  84. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
  85. package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
  86. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
  87. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
  88. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
  89. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
  90. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
  91. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
  92. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
  93. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
  94. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
  95. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
  96. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
  97. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
  98. package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
  99. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
  100. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
  101. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
  102. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
  103. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
  104. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
  105. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
  106. package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
  107. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
  108. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
  109. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  110. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
  111. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
  112. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
  113. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
  114. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
  115. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
  116. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
  117. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
  118. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
  119. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
  120. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
  121. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
  122. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
  123. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
  124. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
  125. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
  126. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
  127. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
  128. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
  129. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
  130. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
  131. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
  132. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
  133. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
  134. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
  135. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
  136. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
  137. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
  138. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
  139. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
  140. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
  141. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
  142. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
  143. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
  144. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
  145. package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
  146. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
  147. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
  148. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
  149. package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
  150. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
  151. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
  152. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
  153. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
  154. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
  155. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
  156. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
  157. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
  158. package/dist/extensions/pi-zentui/README.md +19 -5
  159. package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
  160. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
  161. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
  162. package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
  163. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
  164. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
  165. package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
  166. package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
  167. package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
  168. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
  169. package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
  170. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
  171. package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
  172. package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
  173. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
  174. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
  175. package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
  176. package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
  177. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
  178. package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
  179. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
  180. package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
  181. package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
  182. package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
  183. package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
  184. package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
  185. package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
  186. package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
  187. package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
  188. package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
  189. package/dist/skills/brandkit/SKILL.md +0 -2
  190. package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
  191. package/dist/skills/gpt-taste/SKILL.md +0 -2
  192. package/dist/skills/image-to-code/SKILL.md +0 -2
  193. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
  194. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
  195. package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
  196. package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
  197. package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
  198. package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
  199. package/dist/skills/stitch/DESIGN.md +121 -0
  200. package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
  201. package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
  202. package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
  203. package/package.json +1 -1
@@ -14,7 +14,7 @@ import {
14
14
  createCompletionBatcher,
15
15
  resolveCompletionBatchConfig,
16
16
  } from "./completion-batcher.ts";
17
- import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
17
+ import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ChildWatchdogProgress, type ChildWatchdogWarningSummary, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
18
18
  import { safeTerminalText } from "../../shared/display-text.ts";
19
19
  import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
20
20
  import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
@@ -31,6 +31,8 @@ export interface SubagentNotifyChildOutput {
31
31
  previewUnavailableReason?: string;
32
32
  }
33
33
 
34
+ export type SubagentNotifyWatchdogBlocker = Pick<ChildWatchdogWarningSummary, "summary" | "addressed" | "stalemate"> & { agent: string };
35
+
34
36
  export interface SubagentNotifyDetails {
35
37
  agent: string;
36
38
  status: "completed" | "failed" | "paused" | "stopped";
@@ -47,6 +49,7 @@ export interface SubagentNotifyDetails {
47
49
  handoffPath?: string;
48
50
  /** Present when a durable schedule launched the run. */
49
51
  scheduleOrigin?: ScheduleOrigin;
52
+ watchdogBlockers?: SubagentNotifyWatchdogBlocker[];
50
53
  }
51
54
 
52
55
  export interface CompletionNotification {
@@ -85,7 +88,9 @@ export interface CompletionNotification {
85
88
  timedOut?: boolean;
86
89
  stopped?: boolean;
87
90
  turnBudgetExceeded?: boolean;
91
+ watchdog?: ChildWatchdogProgress;
88
92
  }>;
93
+ watchdog?: ChildWatchdogProgress;
89
94
  timestamp?: number;
90
95
  durationMs?: number;
91
96
  cwd?: string;
@@ -249,9 +254,28 @@ function formatCorrelationLines(details: SubagentNotifyDetails): string[] {
249
254
  ].filter((line): line is string => line !== undefined);
250
255
  }
251
256
 
257
+ const WATCHDOG_BLOCKERS_HEADING = "Watchdog blockers:";
258
+
259
+ function formatWatchdogBlockerLines(details: SubagentNotifyDetails): string[] {
260
+ if (!details.watchdogBlockers?.length) return [];
261
+ return [WATCHDOG_BLOCKERS_HEADING, ...details.watchdogBlockers.map((blocker) => `- ${blocker.agent}: ${blocker.summary} (${blocker.stalemate ? "stalemate" : blocker.addressed ? "addressed" : "unaddressed"})`)];
262
+ }
263
+
264
+ // A stalemate blocker parses back as unaddressed; acceptance treats both as unresolved.
265
+ function parseWatchdogBlockerLines(lines: string[]): SubagentNotifyWatchdogBlocker[] {
266
+ const blockers: SubagentNotifyWatchdogBlocker[] = [];
267
+ for (const line of lines) {
268
+ const match = line.match(/^- (.+?): (.+) \((addressed|unaddressed|stalemate)\)$/);
269
+ if (!match) break;
270
+ blockers.push({ agent: match[1]!, summary: match[2]!, addressed: match[3] === "addressed", stalemate: match[3] === "stalemate" });
271
+ }
272
+ return blockers;
273
+ }
274
+
252
275
  export function formatSingleCompletion(details: SubagentNotifyDetails): string {
253
276
  const sessionLine = formatSessionLine(details);
254
277
  const correlationLines = formatCorrelationLines(details);
278
+ const watchdogLines = formatWatchdogBlockerLines(details);
255
279
  const taskKind = details.source === "foreground" ? "Detached foreground task" : "Background task";
256
280
  const scheduleLine = details.scheduleOrigin
257
281
  ? `Scheduled run from **${details.scheduleOrigin.name ?? details.scheduleOrigin.id}** (schedule ${details.scheduleOrigin.id}).`
@@ -262,6 +286,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
262
286
  scheduleLine,
263
287
  scheduleLine ? "" : undefined,
264
288
  formatResultPreview(details),
289
+ ...(watchdogLines.length ? ["", ...watchdogLines] : []),
265
290
  details.handoffPath ? "" : undefined,
266
291
  details.handoffPath ? `Parallel handoff: ${details.handoffPath}` : undefined,
267
292
  correlationLines.length && !details.handoffPath ? "" : undefined,
@@ -300,7 +325,9 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
300
325
  const workflowRunIndex = body.findIndex((line) => line.startsWith("Workflow run: "));
301
326
  const childRunsIndex = body.findIndex((line) => line.startsWith("Child runs: "));
302
327
  const reconciledIndex = body.findIndex((line) => line.startsWith("Reconciled detached child: "));
303
- const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex].filter((index) => index >= 0);
328
+ const watchdogIndex = body.findIndex((line) => line === WATCHDOG_BLOCKERS_HEADING);
329
+ const watchdogBlockers = watchdogIndex >= 0 ? parseWatchdogBlockerLines(body.slice(watchdogIndex + 1)) : [];
330
+ const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex, watchdogIndex].filter((index) => index >= 0);
304
331
  const firstMetadataIndex = metadataIndexes.length ? Math.min(...metadataIndexes) : body.length;
305
332
  const resultEnd = firstMetadataIndex > 0 && body[firstMetadataIndex - 1]?.trim() === "" ? firstMetadataIndex - 1 : firstMetadataIndex;
306
333
  const resultPreview = body.slice(0, resultEnd).join("\n").trim() || "(no output)";
@@ -336,6 +363,7 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
336
363
  ...(workflowRunId ? { workflowRunId } : {}),
337
364
  ...(childRuns?.length ? { childRuns } : {}),
338
365
  ...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
366
+ ...(watchdogBlockers.length ? { watchdogBlockers } : {}),
339
367
  ...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
340
368
  };
341
369
  }
@@ -349,6 +377,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
349
377
  const sessionLine = formatSessionLine(detail);
350
378
  blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
351
379
  blocks.push(formatResultPreview(detail));
380
+ blocks.push(...formatWatchdogBlockerLines(detail));
352
381
  if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
353
382
  blocks.push(...formatCorrelationLines(detail));
354
383
  if (sessionLine) blocks.push(sessionLine);
@@ -420,6 +449,18 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
420
449
  const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
421
450
  const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
422
451
  const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
452
+ const directChild = !workflowRunId && result.results?.length === 1 ? result.results[0]! : undefined;
453
+ const directStructuredPreview = directChild
454
+ ? childInlinePreview(directChild).preview
455
+ : undefined;
456
+ const directSummary = summary.trim();
457
+ const directAgent = typeof directChild?.agent === "string" ? directChild.agent : agent;
458
+ const directNoOutputSummary = directChild && (!directSummary
459
+ || directSummary === "(no output)"
460
+ || (directAgent && directSummary === `${directAgent}:\n(no output)`));
461
+ const resultPreview = directStructuredPreview && directNoOutputSummary
462
+ ? `Structured output:\n${directStructuredPreview}`
463
+ : summary;
423
464
  const childRuns = result.results?.flatMap((child) => {
424
465
  const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
425
466
  const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
@@ -450,6 +491,15 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
450
491
  })
451
492
  : undefined;
452
493
  const reconciledFromDetachedChild = typeof result.reconciledFromDetachedChild === "string" ? result.reconciledFromDetachedChild : undefined;
494
+ const watchdogBlockers: SubagentNotifyWatchdogBlocker[] = [];
495
+ const collectWatchdogBlockers = (owner: string, progress: ChildWatchdogProgress | undefined) => {
496
+ for (const warning of progress?.warnings ?? []) {
497
+ if (warning.severity !== "blocker") continue;
498
+ watchdogBlockers.push({ agent: owner, summary: warning.summary, addressed: warning.addressed, stalemate: warning.stalemate });
499
+ }
500
+ };
501
+ collectWatchdogBlockers(agent, result.watchdog);
502
+ for (const child of result.results ?? []) collectWatchdogBlockers(typeof child.agent === "string" ? child.agent : agent, child.watchdog);
453
503
  const session =
454
504
  result.shareUrl
455
505
  ? { label: "Session", value: result.shareUrl }
@@ -468,13 +518,14 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
468
518
  ...(scheduleOrigin ? { scheduleOrigin } : {}),
469
519
  ...(result.source ? { source: result.source } : {}),
470
520
  ...(taskInfo ? { taskInfo } : {}),
471
- resultPreview: summary,
521
+ resultPreview,
472
522
  ...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
473
523
  ...(handoffPath ? { handoffPath } : {}),
474
524
  ...(workflowRunId ? { workflowRunId } : {}),
475
525
  ...(childRuns.length ? { childRuns } : {}),
476
526
  ...(childOutputs?.length ? { childOutputs } : {}),
477
527
  ...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
528
+ ...(watchdogBlockers.length ? { watchdogBlockers } : {}),
478
529
  ...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
479
530
  };
480
531
  }
@@ -115,6 +115,22 @@ export function writeProcessTerminalCandidate(asyncDir: string, candidate: Proce
115
115
  writePrivateAtomicJson(processTerminalCandidatePath(asyncDir), candidate);
116
116
  }
117
117
 
118
+ /** Establish ownership before authorizing a runner to launch any child process. */
119
+ export function initializeProcessTerminal(asyncDir: string, runId: string, runnerProcessInstanceId: string): void {
120
+ writeProcessTerminalCandidate(asyncDir, {
121
+ version: 1,
122
+ runId,
123
+ runnerProcessInstanceId,
124
+ writers: {},
125
+ });
126
+ writeAtomicJson(processTerminalPath(asyncDir), {
127
+ version: 1,
128
+ state: "pending",
129
+ runId,
130
+ runnerProcessInstanceId,
131
+ });
132
+ }
133
+
118
134
  export function markProcessTerminalCandidateLeaseRelease(asyncDir: string, token: string, acknowledged: boolean): void {
119
135
  const candidate = readProcessTerminalCandidate(asyncDir);
120
136
  if (!candidate || candidate.revivalLeaseToken !== token) return;
@@ -28,6 +28,8 @@ import { formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetD
28
28
  import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
29
29
  import { getExternalJobProvider } from "../../api/external-job-provider.ts";
30
30
  import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.ts";
31
+ import { formatWorkflowChecklistText, projectWorkflowChecklist } from "../../workflows/workflow-checklist.ts";
32
+ import { validHostStepNodes } from "../shared/host-step-status.ts";
31
33
 
32
34
  interface RunStatusParams {
33
35
  action?: string;
@@ -74,7 +76,7 @@ function formatWorkflowDebug(status: AsyncStatus): string[] {
74
76
  status.lane ? `Lane: ${status.lane.key}${status.lane.mode ? ` (${status.lane.mode})` : ""}` : undefined,
75
77
  ].filter((line): line is string => line !== undefined);
76
78
  for (const [index, step] of (status.steps ?? []).entries()) {
77
- lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}` : ""}`);
79
+ lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}${step.provider ? ` · provider ${step.provider}` : ""}` : ""}`);
78
80
  }
79
81
  return lines;
80
82
  }
@@ -522,6 +524,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
522
524
  status.mode === "workflow" && workflowReturnPreview !== undefined ? `Return: ${workflowReturnPreview}` : undefined,
523
525
  status.mode === "workflow" && workflowEmitPreview !== undefined ? `Latest emit: ${workflowEmitPreview}` : undefined,
524
526
  `Progress: ${progressLabel}`,
527
+ ...(status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
528
+ graph: status.workflowGraph,
529
+ steps: status.steps,
530
+ hostSteps: validHostStepNodes(status.workflowGraph),
531
+ preflight: status.preflight,
532
+ trace: status.workflow?.trace,
533
+ now: status.lastUpdate ?? status.endedAt ?? Date.now(),
534
+ }), "", { includeItems: false }) : []),
525
535
  status.pendingAppends ? `Pending appends: ${status.pendingAppends}` : undefined,
526
536
  `Started: ${started}`,
527
537
  `Updated: ${updated}`,
@@ -551,6 +561,9 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
551
561
  const display = runStatusStepDisplayName(step);
552
562
  const phase = step.phase ? `[${step.phase}] ` : "";
553
563
  lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${steeringSuffix}${acceptanceText}${budgetText}${errorText}`);
564
+ const structuredOutputPreview = step.structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(step.structuredOutput, 4_000);
565
+ if (structuredOutputPreview !== undefined) lines.push(` Structured output: ${structuredOutputPreview}`);
566
+ if (step.structuredOutputPath) lines.push(` Structured output path: ${step.structuredOutputPath}`);
554
567
  lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
555
568
  if (step.runner?.type === "external-cli") {
556
569
  const runner = normalizeExternalCliRunnerStatus(step.runner);
@@ -670,7 +683,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
670
683
  if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);
671
684
  const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
672
685
  lines.push(...formatTimeoutRecoveryLines(data.timeoutRecovery, " "));
673
- for (const child of children) lines.push(...formatTimeoutRecoveryLines(child.timeoutRecovery, " "));
686
+ for (const [index, child] of children.entries()) {
687
+ const structuredOutput = (child as { structuredOutput?: unknown }).structuredOutput;
688
+ const structuredOutputPreview = structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(structuredOutput, 4_000);
689
+ if (structuredOutputPreview !== undefined) lines.push(` Structured output${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPreview}`);
690
+ const structuredOutputPath = (child as { structuredOutputPath?: unknown }).structuredOutputPath;
691
+ if (typeof structuredOutputPath === "string" && structuredOutputPath.trim()) lines.push(` Structured output path${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPath}`);
692
+ lines.push(...formatTimeoutRecoveryLines(child.timeoutRecovery, " "));
693
+ }
674
694
  lines.push(formatResumeGuidance(runId, children, data.sessionFile, { stopped: status === "stopped" }));
675
695
  if (data.summary) lines.push("", data.summary);
676
696
  const workflowChildren = parseWorkflowChildSummary((data as unknown as Record<string, unknown>).workflowChildren);
@@ -52,6 +52,8 @@ export interface ScheduleRecord {
52
52
  catchUp: "none" | "latest";
53
53
  timeoutMs?: number;
54
54
  paused: boolean;
55
+ sessionOnly?: boolean;
56
+ ownerSessionFile?: string;
55
57
  createdAt: string;
56
58
  updatedAt: string;
57
59
  activeRunId?: string;
@@ -72,6 +74,8 @@ export interface ScheduleRunRecord {
72
74
  error?: string;
73
75
  }
74
76
 
77
+ type PublicScheduleRecord = Omit<ScheduleRecord, "ownerSessionFile">;
78
+
75
79
  type ScheduledRunManagerDeps = {
76
80
  config: ExtensionConfig;
77
81
  launch(params: SubagentParamsLike, ctx: ExtensionContext, signal: AbortSignal): Promise<AgentToolResult<Details>>;
@@ -295,6 +299,8 @@ function parseSchedule(value: unknown, file: string): ScheduleRecord {
295
299
  } else if (record.trigger.kind === "interval") {
296
300
  if (typeof record.trigger.every !== "string" || typeof record.trigger.everyMs !== "number" || typeof record.trigger.anchorAt !== "string" || typeof record.trigger.nextRunAt !== "string") throw new Error(`Schedule record '${file}' has an invalid interval trigger.`);
297
301
  } else throw new Error(`Schedule record '${file}' has an unsupported trigger.`);
302
+ if (record.sessionOnly !== undefined && typeof record.sessionOnly !== "boolean") throw new Error(`Schedule record '${file}' has invalid sessionOnly.`);
303
+ if (record.sessionOnly === true && (typeof record.ownerSessionFile !== "string" || !record.ownerSessionFile.trim())) throw new Error(`Schedule record '${file}' is session-only but has no owner session file.`);
298
304
  return { ...record, target: parseScheduleTarget(record.target, file) } as ScheduleRecord;
299
305
  }
300
306
 
@@ -399,13 +405,19 @@ function duePlannedAt(schedule: ScheduleRecord, now: number): number | undefined
399
405
  }
400
406
 
401
407
  function textResult(text: string, schedules?: ScheduleRecord[], runs?: ScheduleRunRecord[], isError = false): AgentToolResult<Details> {
408
+ const publicSchedules = schedules?.map(publicScheduleRecord);
402
409
  return {
403
410
  content: [{ type: "text", text }],
404
411
  ...(isError ? { isError: true } : {}),
405
- details: { mode: "management", results: [], schedules: { ...(schedules ? { records: schedules } : {}), ...(runs ? { runs } : {}) } },
412
+ details: { mode: "management", results: [], schedules: { ...(publicSchedules ? { records: publicSchedules } : {}), ...(runs ? { runs } : {}) } },
406
413
  };
407
414
  }
408
415
 
416
+ function publicScheduleRecord(schedule: ScheduleRecord): PublicScheduleRecord {
417
+ const { ownerSessionFile: _ownerSessionFile, ...rest } = schedule;
418
+ return rest;
419
+ }
420
+
409
421
  function targetLabel(target: ScheduleTarget): string {
410
422
  const preview = previewSimpleWorkflowRun(target.workflowScript);
411
423
  return preview?.agent ? `workflowScript -> agent ${preview.agent}` : "workflowScript (dynamic)";
@@ -450,6 +462,32 @@ function snapshotContext(ctx: ExtensionContext, cwd: string): ExtensionContext {
450
462
  return { ...ctx, cwd, sessionManager };
451
463
  }
452
464
 
465
+ /**
466
+ * 规范化会话文件路径, 兼容 Windows 路径大小写差异.
467
+ *
468
+ * @param value 会话文件路径
469
+ * @returns 规范化后的路径, 空值时返回 undefined
470
+ */
471
+ function normalizedSessionFile(value: string | undefined): string | undefined {
472
+ if (!value || !value.trim()) return undefined;
473
+ const normalized = path.normalize(path.resolve(value));
474
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
475
+ }
476
+
477
+ /**
478
+ * 判断 Schedule 是否属于当前 Pi 会话.
479
+ *
480
+ * @param schedule Schedule 记录
481
+ * @param ctx 当前 Pi 会话上下文
482
+ * @returns 是否允许当前会话执行该 Schedule
483
+ */
484
+ function scheduleBelongsToSession(schedule: ScheduleRecord, ctx: ExtensionContext): boolean {
485
+ if (schedule.sessionOnly !== true) return true;
486
+ const ownerSessionFile = normalizedSessionFile(schedule.ownerSessionFile);
487
+ const currentSessionFile = normalizedSessionFile(ctx.sessionManager.getSessionFile());
488
+ return ownerSessionFile !== undefined && ownerSessionFile === currentSessionFile;
489
+ }
490
+
453
491
  export function listScheduledRunSummaries(cwd: string, root?: string): ScheduleRecord[] {
454
492
  return new ScheduleStore(scheduledRunStorePath(cwd, undefined, root), root === undefined ? path.resolve(cwd) : undefined).list();
455
493
  }
@@ -560,6 +598,10 @@ export class ScheduledRunManager {
560
598
  if (params.catchUp !== undefined && params.catchUp !== "none" && params.catchUp !== "latest") return textResult("catchUp must be 'none' or 'latest'.", undefined, undefined, true);
561
599
  if (params.missionId !== undefined || params.mission !== undefined || params.missionUpdate !== undefined || params.missionStatus !== undefined || params.missionScope !== undefined) return textResult("Mission attachment is deferred from this first schedule slice.", undefined, undefined, true);
562
600
  if (params.on !== undefined || params.timezone !== undefined || every === "day" || every === "week" || every === "month" || every === "year") return textResult("Calendar schedules are deferred from this first safe slice. Use a fixed interval such as every:'24h' or every:'7d'.", undefined, undefined, true);
601
+ const sessionOnly = params.sessionOnly === true;
602
+ if (sessionOnly && params.cwd !== undefined && !samePath(params.cwd, ctx.cwd)) return textResult("sessionOnly schedules cannot use an explicit cross-project cwd.", undefined, undefined, true);
603
+ const ownerSessionFile = sessionOnly ? ctx.sessionManager.getSessionFile() : undefined;
604
+ if (sessionOnly && !ownerSessionFile) return textResult("sessionOnly schedules require a persisted current session.", undefined, undefined, true);
563
605
  const sessionId = ctx.sessionManager.getSessionId() ?? "unknown";
564
606
  if (this.deps.resolveCapabilityCeiling?.(sessionId)) return textResult("Cannot persist a schedule while a capability ceiling is active.", undefined, undefined, true);
565
607
  const pendingCount = store.list().filter(hasPendingScheduleWork).length;
@@ -587,24 +629,25 @@ export class ScheduledRunManager {
587
629
  catchUp: params.catchUp ?? "latest",
588
630
  ...(params.timeoutMs === undefined ? {} : { timeoutMs: params.timeoutMs }),
589
631
  paused: false,
632
+ ...(sessionOnly ? { sessionOnly: true, ownerSessionFile: path.resolve(ownerSessionFile!) } : {}),
590
633
  createdAt: timestamp(now),
591
634
  updatedAt: timestamp(now),
592
635
  };
593
636
  store.write(schedule);
594
637
  store.appendEvent(schedule, "schedule.created");
595
638
  this.arm(schedule, store);
596
- return textResult(`Created schedule ${id}.\nName: ${schedule.name}\nTrigger: ${at ? `at ${at}` : `every ${every}`}\nNext: ${schedule.trigger.nextRunAt}\nTarget: ${targetLabel(schedule.target)}`, [schedule]);
639
+ return textResult(`Created schedule ${id}.\nName: ${schedule.name}\nTrigger: ${at ? `at ${at}` : `every ${every}`}\nSession only: ${schedule.sessionOnly === true ? "yes" : "no"}\nNext: ${schedule.trigger.nextRunAt}\nTarget: ${targetLabel(schedule.target)}`, [schedule]);
597
640
  }
598
641
 
599
642
  private list(): AgentToolResult<Details> {
600
643
  const schedules = this.requireStore().list().sort((a, b) => (a.trigger.nextRunAt ?? "").localeCompare(b.trigger.nextRunAt ?? ""));
601
644
  if (!schedules.length) return textResult("No project schedules.", []);
602
- return textResult([`Project schedules: ${schedules.length}`, ...schedules.map((item) => `- ${item.id} | ${item.paused ? "paused" : item.activeRunId ? "running" : "scheduled"} | ${item.trigger.nextRunAt ?? "no next run"} | ${item.name}`)].join("\n"), schedules);
645
+ return textResult([`Project schedules: ${schedules.length}`, ...schedules.map((item) => `- ${item.id} | ${item.paused ? "paused" : item.activeRunId ? "running" : "scheduled"} | ${item.trigger.nextRunAt ?? "no next run"} | ${item.sessionOnly === true ? "session-only" : "project"} | ${item.name}`)].join("\n"), schedules);
603
646
  }
604
647
 
605
648
  private show(params: SubagentParamsLike): AgentToolResult<Details> {
606
649
  const schedule = this.resolve(params);
607
- return textResult([`Schedule: ${schedule.id}`, `Name: ${schedule.name}`, `State: ${schedule.paused ? "paused" : schedule.activeRunId ? "running" : "scheduled"}`, `Target: ${targetLabel(schedule.target)}`, `CWD: ${shortenPath(schedule.cwd)}`, `Next: ${schedule.trigger.nextRunAt ?? "none"}`, `Catch up: ${schedule.catchUp}`, schedule.activeRunId ? `Active run: ${schedule.activeRunId}` : undefined].filter(Boolean).join("\n"), [schedule]);
650
+ return textResult([`Schedule: ${schedule.id}`, `Name: ${schedule.name}`, `State: ${schedule.paused ? "paused" : schedule.activeRunId ? "running" : "scheduled"}`, `Session only: ${schedule.sessionOnly === true ? "yes" : "no"}`, `Target: ${targetLabel(schedule.target)}`, `CWD: ${shortenPath(schedule.cwd)}`, `Next: ${schedule.trigger.nextRunAt ?? "none"}`, `Catch up: ${schedule.catchUp}`, schedule.activeRunId ? `Active run: ${schedule.activeRunId}` : undefined].filter(Boolean).join("\n"), [schedule]);
608
651
  }
609
652
 
610
653
  private history(params: SubagentParamsLike): AgentToolResult<Details> {
@@ -628,13 +671,18 @@ export class ScheduledRunManager {
628
671
  private async runManual(params: SubagentParamsLike): Promise<AgentToolResult<Details>> {
629
672
  const store = this.requireStore();
630
673
  const schedule = this.resolve(params);
674
+ const context = this.requireContext(store);
675
+ if (!scheduleBelongsToSession(schedule, context)) {
676
+ return textResult(`Skipped schedule ${schedule.id}: current session is not its owner.`, [schedule]);
677
+ }
631
678
  const run = await this.launch(store, schedule, this.now(), "manual", false);
632
679
  return textResult(`Manual schedule run ${run.id}: ${run.state}${run.asyncId ? ` (async ${run.asyncId})` : ""}.`, [store.get(schedule.id)], [run], run.state === "failed_launch");
633
680
  }
634
681
 
635
682
  private async runDue(): Promise<AgentToolResult<Details>> {
636
683
  const store = this.requireStore();
637
- const due = store.list().filter((schedule) => !schedule.paused && nextRunAt(schedule) !== undefined && nextRunAt(schedule)! <= this.now());
684
+ const context = this.requireContext(store);
685
+ const due = store.list().filter((schedule) => scheduleBelongsToSession(schedule, context) && !schedule.paused && nextRunAt(schedule) !== undefined && nextRunAt(schedule)! <= this.now());
638
686
  const runs: ScheduleRunRecord[] = [];
639
687
  for (const schedule of due) {
640
688
  const planned = duePlannedAt(schedule, this.now())!;
@@ -659,6 +707,7 @@ export class ScheduledRunManager {
659
707
  }
660
708
 
661
709
  private restoreOne(store: ScheduleStore, schedule: ScheduleRecord, notBefore?: number, rearm = true): void {
710
+ if (!scheduleBelongsToSession(schedule, this.requireContext(store))) return;
662
711
  if (schedule.activeRunId) {
663
712
  const run = store.history(schedule.id).find((item) => item.id === schedule.activeRunId);
664
713
  if (run?.state === "running" && run.asyncId) this.observedAsyncIds.add(run.asyncId);
@@ -737,6 +786,7 @@ export class ScheduledRunManager {
737
786
  // is then nothing to run and nothing to re-arm.
738
787
  const schedule = store.find(id);
739
788
  if (!schedule) return;
789
+ if (!scheduleBelongsToSession(schedule, this.requireContext(store))) return;
740
790
  const planned = duePlannedAt(schedule, this.now());
741
791
  if (planned === undefined || schedule.paused) return;
742
792
  if (planned > this.now()) return this.arm(schedule, store);
@@ -857,13 +907,20 @@ export class ScheduledRunManager {
857
907
  private selectProject(cwd: string, ctx: ExtensionContext): void {
858
908
  const projectCwd = path.resolve(cwd);
859
909
  const root = scheduledRunStorePath(projectCwd, undefined, this.deps.storeRoot);
860
- if (path.resolve(ctx.cwd) === projectCwd) this.contexts.set(root, snapshotContext(ctx, projectCwd));
910
+ const isBoundContext = path.resolve(ctx.cwd) === projectCwd;
911
+ const previousContext = this.contexts.get(root);
912
+ const contextChanged = isBoundContext
913
+ && previousContext !== undefined
914
+ && normalizedSessionFile(previousContext.sessionManager.getSessionFile()) !== normalizedSessionFile(ctx.sessionManager.getSessionFile());
915
+ if (isBoundContext) this.contexts.set(root, snapshotContext(ctx, projectCwd));
861
916
  else if (!this.contexts.has(root)) throw new Error(`Cannot use project '${projectCwd}' until that project has been opened in this runtime.`);
862
917
  let store = this.stores.get(root);
863
918
  if (!store) {
864
919
  store = new ScheduleStore(root, this.deps.storeRoot === undefined ? projectCwd : undefined);
865
920
  this.stores.set(root, store);
866
921
  this.restore(store);
922
+ } else if (contextChanged) {
923
+ this.restore(store);
867
924
  }
868
925
  this.store = store;
869
926
  }
@@ -23,7 +23,10 @@ export function steeringMessagePreview(message: string): string {
23
23
  }
24
24
 
25
25
  export function steeringReceipt(message: string, receipt: string): string {
26
- return `${receipt} Message: ${JSON.stringify(steeringMessagePreview(message))}`;
26
+ const preview = steeringMessagePreview(message);
27
+ const longestFence = Math.max(2, ...[...preview.matchAll(/`{3,}/g)].map((match) => match[0]!.length));
28
+ const fence = "`".repeat(longestFence + 1);
29
+ return `${receipt}\n\nMessage sent:\n${fence}text\n${preview}\n${fence}`;
27
30
  }
28
31
 
29
32
  export function createSteeringStatus(): SteeringStatus {