pi-subagents 0.50.0 → 0.52.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 (109) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/agents/oracle.md +3 -1
  3. package/agents/reviewer.md +1 -0
  4. package/agents/scout.md +2 -2
  5. package/agents/worker.md +2 -1
  6. package/async-retention-discovery-worker.mjs +180 -0
  7. package/docs/agents.md +40 -2
  8. package/docs/configuration.md +30 -12
  9. package/docs/extension-api.md +42 -1
  10. package/docs/models.md +2 -0
  11. package/docs/observability.md +41 -5
  12. package/docs/tool-reference.md +38 -39
  13. package/docs/workflows.md +171 -3
  14. package/package.json +4 -2
  15. package/skills/pi-subagents/SKILL.md +6 -4
  16. package/skills/pi-subagents/references/constraints-and-recipes.md +12 -6
  17. package/skills/pi-subagents/references/execution-controls.md +29 -15
  18. package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
  19. package/skills/pi-subagents/references/prompting-and-roles.md +4 -2
  20. package/src/agents/agent-management.ts +124 -351
  21. package/src/agents/agents.ts +157 -31
  22. package/src/agents/skills.ts +1 -1
  23. package/src/api/external-job-provider.ts +185 -0
  24. package/src/api/preflight.ts +36 -8
  25. package/src/api/shared-types.ts +2 -0
  26. package/src/extension/config.ts +3 -3
  27. package/src/extension/doctor.ts +3 -6
  28. package/src/extension/fanout-child.ts +2 -2
  29. package/src/extension/index.ts +166 -88
  30. package/src/extension/public-execution.ts +31 -2
  31. package/src/extension/schemas.ts +12 -35
  32. package/src/extension/tool-description.ts +35 -24
  33. package/src/inspectors/herdr/actions.ts +2 -2
  34. package/src/inspectors/herdr/inspector-runner.ts +2 -1
  35. package/src/inspectors/herdr/project-panes.ts +2 -2
  36. package/src/intercom/native-supervisor-channel.ts +32 -10
  37. package/src/missions/lifecycle.ts +6 -1
  38. package/src/missions/store.ts +4 -9
  39. package/src/missions/workflow-state.ts +2 -2
  40. package/src/profiles/profiles.ts +3 -1
  41. package/src/runs/background/active-run-index.ts +31 -8
  42. package/src/runs/background/async-execution.ts +68 -40
  43. package/src/runs/background/async-job-tracker.ts +20 -4
  44. package/src/runs/background/async-resume.ts +47 -15
  45. package/src/runs/background/async-retention.ts +886 -0
  46. package/src/runs/background/async-status.ts +39 -53
  47. package/src/runs/background/chain-append.ts +3 -33
  48. package/src/runs/background/completion-dedupe.ts +5 -1
  49. package/src/runs/background/completion-replay.ts +22 -12
  50. package/src/runs/background/control-channel.ts +14 -68
  51. package/src/runs/background/fleet-view.ts +68 -19
  52. package/src/runs/background/index-segment.ts +59 -0
  53. package/src/runs/background/inspect-rpc.ts +443 -0
  54. package/src/runs/background/notify.ts +31 -5
  55. package/src/runs/background/result-files.ts +158 -90
  56. package/src/runs/background/result-watcher.ts +116 -20
  57. package/src/runs/background/resume-guidance.ts +8 -5
  58. package/src/runs/background/retained-children.ts +13 -3
  59. package/src/runs/background/run-id-query.ts +7 -0
  60. package/src/runs/background/run-id-resolver.ts +11 -9
  61. package/src/runs/background/run-status.ts +27 -18
  62. package/src/runs/background/scheduled-runs.ts +24 -4
  63. package/src/runs/background/stale-run-reconciler.ts +6 -3
  64. package/src/runs/background/steering.ts +11 -1
  65. package/src/runs/background/subagent-runner.ts +253 -140
  66. package/src/runs/background/subagent-wait.ts +8 -8
  67. package/src/runs/background/terminal-run-index.ts +129 -0
  68. package/src/runs/background/wait-completions.ts +21 -4
  69. package/src/runs/background/wait-subscriptions.ts +81 -2
  70. package/src/runs/foreground/async-steering-action.ts +21 -14
  71. package/src/runs/foreground/execution.ts +3 -1
  72. package/src/runs/foreground/subagent-executor.ts +666 -1579
  73. package/src/runs/foreground/workflow-detach-reconcile.ts +194 -0
  74. package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
  75. package/src/runs/shared/acceptance.ts +4 -4
  76. package/src/runs/shared/chain-outputs.ts +1 -3
  77. package/src/runs/shared/external-job-bridge.ts +444 -0
  78. package/src/runs/shared/external-job-runner.ts +286 -0
  79. package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
  80. package/src/runs/shared/model-fallback.ts +98 -38
  81. package/src/runs/shared/orca-progress-tabs.ts +84 -22
  82. package/src/runs/shared/parallel-handoff.ts +46 -4
  83. package/src/runs/shared/parallel-utils.ts +4 -15
  84. package/src/runs/shared/permissions.ts +5 -1
  85. package/src/runs/shared/pi-args.ts +8 -1
  86. package/src/runs/shared/session-lease.ts +0 -6
  87. package/src/runs/shared/subagent-control.ts +26 -4
  88. package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
  89. package/src/runs/shared/workflow-graph.ts +1 -23
  90. package/src/runs/shared/worktree.ts +14 -2
  91. package/src/shared/atomic-json.ts +22 -2
  92. package/src/shared/capacity-resilient-json.ts +102 -0
  93. package/src/shared/completion-owner.ts +14 -0
  94. package/src/shared/file-system-retry.ts +49 -1
  95. package/src/shared/fork-context.ts +42 -0
  96. package/src/shared/prompt-resources.ts +0 -40
  97. package/src/shared/settings.ts +3 -27
  98. package/src/shared/types.ts +71 -26
  99. package/src/shared/utils.ts +8 -0
  100. package/src/shared/watch-strategy.ts +10 -0
  101. package/src/slash/slash-bridge.ts +2 -1
  102. package/src/slash/slash-commands.ts +29 -3
  103. package/src/tui/fleet.ts +63 -15
  104. package/src/watchdog/change-signature.ts +1 -1
  105. package/src/watchdog/lsp-diagnostics.ts +1 -0
  106. package/src/workflows/chat-progress.ts +2 -2
  107. package/src/workflows/scripted-workflow.ts +220 -67
  108. package/src/runs/foreground/chain-clarify.ts +0 -1354
  109. package/src/runs/foreground/chain-execution.ts +0 -1581
@@ -39,6 +39,7 @@ interface EventBus {
39
39
 
40
40
  interface SlashBridgeOptions {
41
41
  events: EventBus;
42
+ asyncByDefault: boolean;
42
43
  getContext: () => ExtensionContext | null;
43
44
  execute: (
44
45
  id: string,
@@ -79,7 +80,7 @@ export function registerSlashSubagentBridge(options: SlashBridgeOptions): {
79
80
  const request = data as Partial<SlashSubagentRequest>;
80
81
  if (typeof request.requestId !== "string" || !request.params) return;
81
82
  const { requestId } = request as SlashSubagentRequest;
82
- const normalized = normalizePublicSubagentExecution((request as SlashSubagentRequest).params);
83
+ const normalized = normalizePublicSubagentExecution((request as SlashSubagentRequest).params, { asyncByDefault: options.asyncByDefault });
83
84
  if (!normalized.ok) {
84
85
  options.events.emit(SLASH_SUBAGENT_RESPONSE_EVENT, {
85
86
  requestId,
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { keyText, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
5
5
  import { Key, matchesKey, truncateToWidth, type Component, type KeyId, type TUI } from "@earendil-works/pi-tui";
6
- import { BUILTIN_AGENT_NAMES, discoverAgents } from "../agents/agents.ts";
6
+ import { BUILTIN_AGENT_NAMES, discoverAgents, findBlockingAgentDiagnostic, resolveAgentName } from "../agents/agents.ts";
7
7
  import { resolveExistingReadPaths } from "../shared/settings.ts";
8
8
  import {
9
9
  DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,
@@ -18,6 +18,7 @@ import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts
18
18
  import { findModelInfo, toModelInfo } from "../shared/model-info.ts";
19
19
  import { formatTokens, shortenPath } from "../shared/formatters.ts";
20
20
  import { listAsyncRuns, formatAsyncRunProgressLabel, type AsyncRunSummary } from "../runs/background/async-status.ts";
21
+ import { encodeInspectReply, handleInspectRpcArgs, INSPECT_WIDGET_KEY } from "../runs/background/inspect-rpc.ts";
21
22
  import { listScheduledRunSummaries } from "../runs/background/scheduled-runs.ts";
22
23
  import { SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi-args.ts";
23
24
  import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
@@ -671,8 +672,16 @@ export function registerSlashCommands(
671
672
  const task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
672
673
 
673
674
  if (!state.baseCwd) { ctx.ui.notify("Subagent session cwd is not initialized yet", "error"); return; }
674
- const agents = discoverAgents(state.baseCwd, "both").agents;
675
- if (!agents.find((a) => a.name === agentName)) { ctx.ui.notify(`Unknown agent: ${agentName}`, "error"); return; }
675
+ const discovered = discoverAgents(state.baseCwd, "both");
676
+ const resolvedAgent = resolveAgentName(agentName, discovered.agents);
677
+ const candidates = resolvedAgent.error
678
+ ? discovered.agents.filter((agent) => resolveAgentName(agentName, [agent]).agent)
679
+ : resolvedAgent.agent;
680
+ const diagnostic = findBlockingAgentDiagnostic(agentName, candidates, discovered.agentDiagnostics);
681
+ if (diagnostic || resolvedAgent.error || !resolvedAgent.agent) {
682
+ ctx.ui.notify(diagnostic ? `Agent '${agentName}' has invalid configuration: ${diagnostic.error}` : resolvedAgent.error ?? `Unknown agent: ${agentName}`, "error");
683
+ return;
684
+ }
676
685
 
677
686
  let finalTask = task;
678
687
  if (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {
@@ -703,6 +712,23 @@ export function registerSlashCommands(
703
712
  },
704
713
  });
705
714
 
715
+ pi.registerCommand("subagents-inspect-rpc", {
716
+ description: "Host integration bridge: answer an async child inspection request with a correlated widget payload (no model turn)",
717
+ handler: async (args, ctx) => {
718
+ if (ctx.mode === "tui") {
719
+ ctx.ui.notify("Inspection replies are emitted only on RPC surfaces. Use /subagents or subagent({ action: \"status\", view: \"transcript\" }) interactively.", "info");
720
+ return;
721
+ }
722
+ if (!ctx.hasUI) return;
723
+ const reply = handleInspectRpcArgs(args, { state });
724
+ // Emit-then-retract in one handler: stdio delivers the two widget
725
+ // updates in order, the host buffers the payload by requestId, and
726
+ // the dedicated key never accumulates visible state.
727
+ ctx.ui.setWidget(INSPECT_WIDGET_KEY, encodeInspectReply(reply));
728
+ ctx.ui.setWidget(INSPECT_WIDGET_KEY, undefined);
729
+ },
730
+ });
731
+
706
732
  pi.registerCommand("subagents-guide", {
707
733
  description: "Show a packaged subagents guide topic",
708
734
  getArgumentCompletions: (prefix) => prefix.includes(" ") ? null : SUBAGENT_GUIDE_TOPICS
package/src/tui/fleet.ts CHANGED
@@ -14,10 +14,12 @@ import { listAsyncRuns, type AsyncRunSummary } from "../runs/background/async-st
14
14
  import { steerAsyncRun } from "../runs/foreground/async-steering-action.ts";
15
15
  import type { SteerDeliveryMode } from "../runs/background/control-channel.ts";
16
16
  import { stopAsyncRun } from "../runs/foreground/async-stop-action.ts";
17
+ import { resolveWorkflowForegroundSteeringTarget, steerWorkflowForegroundTarget } from "../runs/foreground/workflow-foreground-steering.ts";
17
18
  import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.ts";
18
19
  import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
19
20
  import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
20
21
  import { handleHerdrInspectorAction } from "../inspectors/herdr/actions.ts";
22
+ import type { HerdrClient } from "../inspectors/herdr/client.ts";
21
23
  import { getLivePromptAudit, type LivePromptAudit, type PromptAuditView } from "../runs/foreground/prompt-audit.ts";
22
24
 
23
25
  const REFRESH_MS = 750;
@@ -108,6 +110,7 @@ export interface FleetViewOptions {
108
110
  fleetKeybindings?: FleetKeybindingsConfig;
109
111
  actions?: FleetActionHandlers;
110
112
  copyText?: (text: string) => Promise<void> | void;
113
+ herdrClient?: HerdrClient;
111
114
  }
112
115
 
113
116
  function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
@@ -193,9 +196,22 @@ export function collectFleetSnapshot(
193
196
  const workflowParentIds = new Set([...trackedJobs.values()]
194
197
  .filter((job) => job.mode === "workflow" && belongsToCurrentSession(job.sessionId, state.currentSessionId))
195
198
  .map((job) => job.asyncId));
199
+ const workflowForegroundChildCounts = new Map<string, number>();
200
+ const liveWorkflowForegroundControls = new Set<ForegroundRunControl>();
201
+ for (const control of state.foregroundControls.values()) {
202
+ const activeChildCount = control.activeChildren?.size ?? 0;
203
+ if (!control.parentWorkflowRunId
204
+ || !workflowParentIds.has(control.parentWorkflowRunId)
205
+ || !belongsToCurrentSession(control.sessionId, state.currentSessionId)
206
+ || !control.workflowSteeringDir
207
+ || activeChildCount === 0) continue;
208
+ liveWorkflowForegroundControls.add(control);
209
+ workflowForegroundChildCounts.set(control.parentWorkflowRunId, (workflowForegroundChildCounts.get(control.parentWorkflowRunId) ?? 0) + activeChildCount);
210
+ }
196
211
  for (const control of [...state.foregroundControls.values()].sort((left, right) => right.updatedAt - left.updatedAt)) {
197
212
  activeForegroundIds.add(control.runId);
198
- if (control.parentWorkflowRunId && workflowParentIds.has(control.parentWorkflowRunId)) continue;
213
+ if (control.parentWorkflowRunId && workflowParentIds.has(control.parentWorkflowRunId)
214
+ && ((workflowForegroundChildCounts.get(control.parentWorkflowRunId) ?? 0) <= 1 || !liveWorkflowForegroundControls.has(control))) continue;
199
215
  if (control.activeChildren) {
200
216
  for (const child of [...control.activeChildren.values()].sort((left, right) => left.index - right.index)) {
201
217
  items.push({
@@ -478,10 +494,15 @@ function externalElapsedEnd(run: ExternalRun): number {
478
494
  return terminal ? (run.endedAt ?? run.updatedAt ?? Date.now()) : Date.now();
479
495
  }
480
496
 
481
- function asyncDetail(item: Extract<FleetItem, { kind: "async" }>): string[] {
497
+ function asyncDetail(item: Extract<FleetItem, { kind: "async" }>, state: SubagentState): string[] {
482
498
  const status = readStatus(item.run.asyncDir);
483
499
  if (status) {
484
- return formatAsyncRunTranscript(status, item.run.asyncDir, { index: item.index, lines: TRANSCRIPT_LINES }).split("\n");
500
+ const trackedJob = state.fleetJobs?.get(item.runId) ?? state.asyncJobs.get(item.runId);
501
+ return formatAsyncRunTranscript(status, item.run.asyncDir, {
502
+ index: item.index,
503
+ lines: TRANSCRIPT_LINES,
504
+ sessionRoots: uniquePaths([...(state.trustedSessionRoots ?? []), trackedJob?.sessionRoot]),
505
+ }).split("\n");
485
506
  }
486
507
  const outputPath = item.index !== undefined ? path.join(item.run.asyncDir, `output-${item.index}.log`) : undefined;
487
508
  return [
@@ -527,7 +548,7 @@ function detailLines(item: FleetItem | undefined, error: string | undefined, sta
527
548
  ? foregroundRecentDetail(item, state)
528
549
  : item.kind === "external"
529
550
  ? externalDetail(item)
530
- : asyncDetail(item);
551
+ : asyncDetail(item, state);
531
552
  if (error) lines.unshift(`Fleet scan warning: ${error}`, "");
532
553
  return lines;
533
554
  }
@@ -835,6 +856,18 @@ export class SubagentFleetComponent implements Component {
835
856
  return { item };
836
857
  }
837
858
 
859
+ private selectedSteerAction(): { runId: string; asyncDir: string; index?: number } | { reason: string } {
860
+ const item = this.snapshot.items[this.selected];
861
+ if (item?.kind === "foreground-active" && item.control.parentWorkflowRunId) {
862
+ const parent = this.state.asyncJobs.get(item.control.parentWorkflowRunId) ?? this.state.fleetJobs?.get(item.control.parentWorkflowRunId);
863
+ if (!parent || !isActionableAsyncState(parent.status)) return { reason: "The parent workflow is no longer available for steering." };
864
+ return { runId: item.runId, asyncDir: parent.asyncDir, ...(item.index !== undefined ? { index: item.index } : {}) };
865
+ }
866
+ const target = this.selectedAsyncAction();
867
+ if ("reason" in target) return target;
868
+ return { runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}) };
869
+ }
870
+
838
871
  private selectedHerdrInspectAction(): { runId: string; asyncDir: string; index?: number } | { reason: string } {
839
872
  const item = this.snapshot.items[this.selected];
840
873
  if (!item) return { reason: "No child is selected." };
@@ -992,12 +1025,12 @@ export class SubagentFleetComponent implements Component {
992
1025
  this.setActionNotice({ text: "Steer message cannot be empty.", isError: true });
993
1026
  return;
994
1027
  }
995
- const target = this.selectedAsyncAction();
1028
+ const target = this.selectedSteerAction();
996
1029
  if ("reason" in target || !this.options.actions) {
997
1030
  this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
998
1031
  return;
999
1032
  }
1000
- this.runAction(() => this.options.actions!.steer({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}), message, mode: this.steerMode }));
1033
+ this.runAction(() => this.options.actions!.steer({ ...target, message, mode: this.steerMode }));
1001
1034
  return;
1002
1035
  }
1003
1036
  if (matchesKey(data, "tab") || data === "\t") {
@@ -1065,7 +1098,7 @@ export class SubagentFleetComponent implements Component {
1065
1098
  return;
1066
1099
  }
1067
1100
  if (matchesFleetAction(data, this.keybindings, "steer")) {
1068
- const target = this.selectedAsyncAction();
1101
+ const target = this.selectedSteerAction();
1069
1102
  if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
1070
1103
  else {
1071
1104
  this.actionNotice = undefined;
@@ -1274,23 +1307,38 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
1274
1307
  await copyToClipboard(text);
1275
1308
  });
1276
1309
  const actions = options.actions ?? {
1277
- steer: async (input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }) => firstToolResultText(await steerAsyncRun({
1278
- state,
1279
- runId: input.runId,
1280
- ...(input.index !== undefined ? { index: input.index } : {}),
1281
- message: input.message,
1282
- mode: input.mode,
1283
- location: { asyncDir: input.asyncDir, resolvedId: input.runId } as Parameters<typeof steerAsyncRun>[0]["location"],
1284
- }), `Failed to steer async run ${input.runId}.`),
1310
+ steer: async (input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }) => {
1311
+ const status = readStatus(input.asyncDir);
1312
+ const liveWorkflowRunId = status?.mode === "workflow" && state.workflowControllers?.has(status.runId || input.runId)
1313
+ ? status.runId || input.runId
1314
+ : undefined;
1315
+ if (liveWorkflowRunId) {
1316
+ const route = state.foregroundControls.get(input.runId)?.parentWorkflowRunId === liveWorkflowRunId
1317
+ ? resolveWorkflowForegroundSteeringTarget({ state, childRunId: input.runId, asyncDirRoot: options.asyncDirRoot ?? DIRS.async })
1318
+ : resolveWorkflowForegroundSteeringTarget({ state, workflowRunId: liveWorkflowRunId, asyncDirRoot: options.asyncDirRoot ?? DIRS.async });
1319
+ if (!route.ok) return { text: route.message, isError: true };
1320
+ return firstToolResultText(await steerWorkflowForegroundTarget({ target: route.target, message: input.message, mode: input.mode, ...(input.index !== undefined ? { index: input.index } : {}) }), `Failed to steer foreground run ${input.runId}.`);
1321
+ }
1322
+ return firstToolResultText(await steerAsyncRun({
1323
+ state,
1324
+ runId: input.runId,
1325
+ ...(input.index !== undefined ? { index: input.index } : {}),
1326
+ message: input.message,
1327
+ mode: input.mode,
1328
+ location: { asyncDir: input.asyncDir, resolvedId: input.runId } as Parameters<typeof steerAsyncRun>[0]["location"],
1329
+ }), `Failed to steer async run ${input.runId}.`);
1330
+ },
1285
1331
  stop: (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(stopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }), `Failed to stop async run ${input.runId}.`),
1286
1332
  inspect: async (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(await handleHerdrInspectorAction("inspector.open", {
1287
1333
  id: input.runId,
1288
1334
  dir: input.asyncDir,
1335
+ focus: true,
1289
1336
  ...(input.index !== undefined ? { index: input.index } : {}),
1290
1337
  }, {
1291
1338
  state,
1292
1339
  sessionRoots: state.trustedSessionRoots,
1293
1340
  cwd: state.baseCwd,
1341
+ ...(options.herdrClient ? { client: options.herdrClient } : {}),
1294
1342
  ...(state.authorityPolicy ? { authorityPolicy: state.authorityPolicy } : {}),
1295
1343
  ...(state.missionStoreConfig ? { missions: state.missionStoreConfig } : {}),
1296
1344
  }), `Failed to open Herdr inspector for async run ${input.runId}.`),
@@ -38,7 +38,7 @@ export interface WatchdogRepoChangeSignature {
38
38
  }
39
39
 
40
40
  function git(cwd: string, args: string[]): string | undefined {
41
- const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 });
41
+ const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024, windowsHide: true });
42
42
  if (result.status !== 0) return undefined;
43
43
  return result.stdout;
44
44
  }
@@ -444,6 +444,7 @@ async function collectWithTypeScriptLanguageServer(input: {
444
444
  }): Promise<WatchdogLspResult> {
445
445
  const started = Date.now();
446
446
  const child = spawn(input.command.command, input.command.args, {
447
+ windowsHide: true,
447
448
  cwd: input.root,
448
449
  stdio: "pipe",
449
450
  env: { ...process.env, NO_COLOR: "1" },
@@ -26,7 +26,7 @@ interface ResolveWorkflowChatProgressInput {
26
26
  }
27
27
 
28
28
  function git(cwd: string, args: string[]): string | undefined {
29
- const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8" });
29
+ const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", windowsHide: true });
30
30
  if (result.status !== 0) return undefined;
31
31
  const output = result.stdout.trim();
32
32
  return output || undefined;
@@ -90,7 +90,7 @@ export function resolveWorkflowChatProgress(input: ResolveWorkflowChatProgressIn
90
90
  else mode = requestedMode;
91
91
 
92
92
  if (mode === "live-card" && !sameRepo) return { error: "chatProgress: 'live-card' is only available for workflowScript runs in the same Git repository." };
93
- if (mode === "live-card" && input.background) return { error: "chatProgress: 'live-card' requires a watched foreground workflow; pass async:false." };
93
+ if (mode === "live-card" && input.background) return { error: "chatProgress: 'live-card' is unavailable for async workflowScript. Async workflows have no inline live card; omit chatProgress or use auto/off. Use async:false only when the parent must block." };
94
94
  return { projection: { mode, repoRelation, ...(repoLabel ? { repoLabel } : {}) } };
95
95
  }
96
96