pi-crew 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +249 -0
  2. package/dist/index.mjs +98 -307
  3. package/package.json +2 -1
  4. package/schema.json +11 -0
  5. package/skills/real-test-pi-crew/REPORT-TEMPLATE.md +6 -2
  6. package/skills/real-test-pi-crew/SKILL.md +278 -79
  7. package/src/config/config-merge.ts +11 -1
  8. package/src/config/config-validation.ts +40 -1
  9. package/src/config/config.ts +28 -6
  10. package/src/config/defaults.ts +35 -10
  11. package/src/config/env-vars.ts +27 -2
  12. package/src/config/types.ts +36 -0
  13. package/src/extension/registration/lifecycle-handlers.ts +40 -9
  14. package/src/extension/registration/team-tool.ts +53 -5
  15. package/src/extension/team-tool/doctor.ts +364 -7
  16. package/src/extension/team-tool/handle-settings.ts +19 -0
  17. package/src/extension/team-tool/inspect.ts +10 -2
  18. package/src/extension/team-tool/status.ts +7 -0
  19. package/src/extension/team-tool.ts +35 -2
  20. package/src/hooks/registry.ts +59 -56
  21. package/src/prompt/inbox-poll.ts +90 -0
  22. package/src/prompt/message-tool.ts +166 -0
  23. package/src/prompt/prompt-runtime.ts +201 -18
  24. package/src/prompt/surface-worker.ts +720 -0
  25. package/src/prompt/worker-events-channel.ts +49 -3
  26. package/src/runtime/async-runner.ts +29 -1
  27. package/src/runtime/background-runner.ts +13 -7
  28. package/src/runtime/broker/broker-issuer.ts +27 -2
  29. package/src/runtime/broker/crew-broker-tokens.ts +56 -4
  30. package/src/runtime/broker/crew-broker.ts +261 -41
  31. package/src/runtime/child-pi/child-pi-spawn.ts +23 -9
  32. package/src/runtime/child-pi/child-pi-streams.ts +9 -1
  33. package/src/runtime/child-pi/child-pi.ts +353 -5
  34. package/src/runtime/crew-agent-records.ts +13 -1
  35. package/src/runtime/dispatch-batch.ts +12 -1
  36. package/src/runtime/event-log-tail-source.ts +374 -0
  37. package/src/runtime/finalize-run.ts +4 -0
  38. package/src/runtime/live-session/live-agent-manager.ts +34 -1
  39. package/src/runtime/live-session/live-control-realtime.ts +10 -0
  40. package/src/runtime/live-session/live-session-runtime.ts +47 -27
  41. package/src/runtime/manifest-cache.ts +128 -17
  42. package/src/runtime/model/pi-args.ts +54 -65
  43. package/src/runtime/output/sidechain-output.ts +61 -6
  44. package/src/runtime/process/proc-stat.ts +46 -0
  45. package/src/runtime/process/zombie-scanner.ts +32 -19
  46. package/src/runtime/spawn-policy.ts +27 -41
  47. package/src/runtime/surface/degrade.ts +776 -0
  48. package/src/runtime/surface/herdr-provider.ts +546 -0
  49. package/src/runtime/surface/launch-script.ts +172 -0
  50. package/src/runtime/surface/resolve-surface.ts +274 -0
  51. package/src/runtime/surface/surface-provider.ts +129 -0
  52. package/src/runtime/surface/surface-spawn.ts +475 -0
  53. package/src/runtime/surface/tmux-provider.ts +400 -0
  54. package/src/runtime/task-runner/child-executor.ts +47 -0
  55. package/src/runtime/task-runner/post-execution.ts +57 -2
  56. package/src/runtime/task-runner/prompt-builder.ts +1 -0
  57. package/src/runtime/task-runner/retrieval-orchestrator.ts +191 -56
  58. package/src/runtime/task-runner/state-helpers.ts +54 -30
  59. package/src/runtime/task-runner.ts +4 -2
  60. package/src/runtime/team-runner.ts +101 -0
  61. package/src/schema/config-schema.ts +24 -0
  62. package/src/state/atomic-write.ts +219 -40
  63. package/src/state/coordination/locks.ts +7 -5
  64. package/src/state/coordination/mailbox.ts +56 -10
  65. package/src/state/event-log/cursor.ts +413 -23
  66. package/src/state/event-log/event-log.ts +120 -113
  67. package/src/state/event-log/sequence-cache.ts +21 -3
  68. package/src/state/stores/state-store.ts +98 -6
  69. package/src/state/types.ts +51 -0
  70. package/src/ui/inline-panel/agent-pane.ts +3 -0
  71. package/src/ui/render-diff.ts +16 -8
  72. package/src/ui/run-dashboard.ts +87 -42
  73. package/src/ui/run-event-bus.ts +10 -1
  74. package/src/ui/run-snapshot-cache.ts +83 -35
  75. package/src/ui/transcript-cache.ts +101 -13
  76. package/src/ui/transcript-viewer.ts +92 -24
  77. package/src/ui/widget/index.ts +32 -8
  78. package/src/utils/visual.ts +43 -0
  79. package/src/worktree/worktree-manager.ts +65 -4
package/dist/index.mjs CHANGED
@@ -13475,7 +13475,6 @@ function parseWorkflowFile(filePath, source) {
13475
13475
  topology: parseTopology(frontmatter.topology),
13476
13476
  coalesceMicroTasks: parseOptionalBoolean(frontmatter.coalesceMicroTasks),
13477
13477
  specStrict: parseOptionalBoolean(frontmatter.specStrict),
13478
- adaptive: parseOptionalBoolean(frontmatter.adaptive),
13479
13478
  steps
13480
13479
  };
13481
13480
  } catch {
@@ -21004,52 +21003,20 @@ function createRunPaths(cwd, runId = createRunId()) {
21004
21003
  eventsPath: path25.join(stateRoot, DEFAULT_PATHS.state.eventsFile)
21005
21004
  };
21006
21005
  }
21007
- function stripGoalTail(prefix) {
21008
- let name = prefix.replace(/[\s:—-]+$/, "").trim();
21009
- let changed = true;
21010
- while (changed) {
21011
- changed = false;
21012
- const lowered = name.toLowerCase();
21013
- for (const tail of GOAL_TAILS) {
21014
- if (lowered.endsWith(tail) && lowered.length > tail.length) {
21015
- name = name.slice(0, name.length - tail.length).replace(/[\s:—-]+$/, "").trim();
21016
- changed = true;
21017
- break;
21018
- }
21019
- }
21020
- }
21021
- return name;
21022
- }
21023
- function createTasksFromWorkflow(runId, workflow, team, cwd, goal) {
21006
+ function createTasksFromWorkflow(runId, workflow, team, cwd) {
21024
21007
  const stepToTaskId = new Map(workflow.steps.map((step, index) => [step.id, createTaskId(step.id, index)]));
21025
21008
  return workflow.steps.map((step, index) => {
21026
21009
  const role = team.roles.find((candidate) => candidate.name === step.role);
21027
21010
  const id = stepToTaskId.get(step.id) ?? createTaskId(step.id, index);
21028
21011
  const dependencies = step.dependsOn ?? [];
21029
21012
  const children = workflow.steps.filter((candidate) => candidate.dependsOn?.includes(step.id)).map((candidate) => stepToTaskId.get(candidate.id)).filter((childId) => childId !== void 0);
21030
- const bodyLines = typeof step.task === "string" && step.task.trim() ? step.task.split("\n").map((line4) => line4.trim()).filter(Boolean) : [];
21031
- const heading = bodyLines.find((line4) => line4.startsWith("#"));
21032
- const titleLine = heading ?? bodyLines[0];
21033
- const substitute = (text) => goal ? text.replaceAll("{goal}", goal) : text;
21034
- let title;
21035
- let restLines;
21036
- const placeholderAt = titleLine?.indexOf("{goal}") ?? -1;
21037
- if (titleLine && placeholderAt >= 0) {
21038
- title = stripGoalTail(titleLine.slice(0, placeholderAt)) || goal || step.id;
21039
- restLines = bodyLines.filter((line4) => line4 !== titleLine);
21040
- } else {
21041
- title = (titleLine ?? step.id).replace(/^#+\s*/, "");
21042
- restLines = heading ? bodyLines.filter((line4) => line4 !== titleLine) : bodyLines.slice(1);
21043
- }
21044
- const description = restLines.map(substitute).join("\n");
21045
21013
  return {
21046
21014
  id,
21047
21015
  runId,
21048
21016
  stepId: step.id,
21049
21017
  role: step.role,
21050
21018
  agent: role?.agent ?? step.role,
21051
- title: title.slice(0, 120),
21052
- description: description && description !== title ? description : void 0,
21019
+ title: step.id,
21053
21020
  status: "queued",
21054
21021
  dependsOn: dependencies,
21055
21022
  cwd,
@@ -21067,7 +21034,7 @@ function createTasksFromWorkflow(runId, workflow, team, cwd, goal) {
21067
21034
  function createRunManifest(params) {
21068
21035
  const paths = createRunPaths(params.cwd);
21069
21036
  const now = (/* @__PURE__ */ new Date()).toISOString();
21070
- const tasks = params.workflow ? createTasksFromWorkflow(paths.runId, params.workflow, params.team, params.cwd, params.goal) : [];
21037
+ const tasks = params.workflow ? createTasksFromWorkflow(paths.runId, params.workflow, params.team, params.cwd) : [];
21071
21038
  const manifest = {
21072
21039
  schemaVersion: CURRENT_SCHEMA_VERSION,
21073
21040
  runId: paths.runId,
@@ -21518,7 +21485,7 @@ async function loadRunManifestByIdAsync(cwd, runId) {
21518
21485
  });
21519
21486
  return { manifest, tasks: tasks ?? [] };
21520
21487
  }
21521
- var manifestCacheGeneration, MANIFEST_CACHE_GENERATION_MAX, MANIFEST_CACHE_TTL_MS, LOAD_MANIFEST_RETRY_LIMIT, manifestCache, MANIFEST_CACHE_TTL_MS_VALUE, runStateRootCache, RUN_STATE_ROOT_TTL_MS, RUN_STATE_ROOT_CACHE_MAX, GOAL_TAILS;
21488
+ var manifestCacheGeneration, MANIFEST_CACHE_GENERATION_MAX, MANIFEST_CACHE_TTL_MS, LOAD_MANIFEST_RETRY_LIMIT, manifestCache, MANIFEST_CACHE_TTL_MS_VALUE, runStateRootCache, RUN_STATE_ROOT_TTL_MS, RUN_STATE_ROOT_CACHE_MAX;
21522
21489
  var init_state_store = __esm({
21523
21490
  "src/state/stores/state-store.ts"() {
21524
21491
  "use strict";
@@ -21546,7 +21513,6 @@ var init_state_store = __esm({
21546
21513
  runStateRootCache = /* @__PURE__ */ new Map();
21547
21514
  RUN_STATE_ROOT_TTL_MS = 1e4;
21548
21515
  RUN_STATE_ROOT_CACHE_MAX = 256;
21549
- GOAL_TAILS = ["for the goal", "for goal", "the goal", "goal", "cho m\u1EE5c ti\xEAu", "c\u1EE7a m\u1EE5c ti\xEAu", "m\u1EE5c ti\xEAu", "for", "of", "cho", "c\u1EE7a"];
21550
21516
  }
21551
21517
  });
21552
21518
 
@@ -21819,14 +21785,9 @@ function isDisplayActiveRun(run, agents = [], now = Date.now()) {
21819
21785
  }
21820
21786
  if (!isActiveRunStatus(run.status)) return false;
21821
21787
  if (agents.length === 0) return false;
21822
- if (agents.some(hasDurableActiveAgentEvidence)) return true;
21823
- if (agents.some((agent) => agent.status === "queued" || agent.status === "waiting")) {
21824
- const updatedAt = new Date(run.updatedAt).getTime();
21825
- if (Number.isFinite(updatedAt) && now - updatedAt < SPAWN_GAP_VISIBILITY_MS) return true;
21826
- }
21827
- return false;
21788
+ return agents.some(hasDurableActiveAgentEvidence);
21828
21789
  }
21829
- var ORPHANED_ACTIVE_RUN_MS, COMPLETED_VISIBILITY_GRACE_MS, ERROR_VISIBILITY_GRACE_MS, STALE_ACTIVE_RUN_MS, SPAWN_GAP_VISIBILITY_MS, ORPHANED_NO_PROGRESS_MS;
21790
+ var ORPHANED_ACTIVE_RUN_MS, COMPLETED_VISIBILITY_GRACE_MS, ERROR_VISIBILITY_GRACE_MS, STALE_ACTIVE_RUN_MS, ORPHANED_NO_PROGRESS_MS;
21830
21791
  var init_process_status = __esm({
21831
21792
  "src/runtime/process-status.ts"() {
21832
21793
  "use strict";
@@ -21835,7 +21796,6 @@ var init_process_status = __esm({
21835
21796
  COMPLETED_VISIBILITY_GRACE_MS = 8e3;
21836
21797
  ERROR_VISIBILITY_GRACE_MS = 10 * 60 * 1e3;
21837
21798
  STALE_ACTIVE_RUN_MS = 30 * 60 * 1e3;
21838
- SPAWN_GAP_VISIBILITY_MS = 60 * 1e3;
21839
21799
  ORPHANED_NO_PROGRESS_MS = 5 * 60 * 1e3;
21840
21800
  }
21841
21801
  });
@@ -57904,12 +57864,10 @@ function isActiveStatus(status) {
57904
57864
  return status === "running" || status === "queued" || status === "waiting";
57905
57865
  }
57906
57866
  function orderWidgetAgents(entry, now = Date.now()) {
57907
- const runDone = isFinishedRunStatus(entry.run.status);
57908
57867
  const active = entry.agents.filter((agent) => isActiveStatus(agent.status));
57909
57868
  const finished = entry.agents.filter((agent) => {
57910
57869
  if (isActiveStatus(agent.status)) return false;
57911
57870
  if (!agent.completedAt) return false;
57912
- if (!runDone) return true;
57913
57871
  const maxAgeMs = (ERROR_STATUSES.has(agent.status) ? ERROR_LINGER_MAX_AGE : FINISHED_LINGER_MAX_AGE) * 6e4;
57914
57872
  const age = now - new Date(agent.completedAt).getTime();
57915
57873
  return Number.isFinite(age) && age < maxAgeMs;
@@ -57919,79 +57877,40 @@ function orderWidgetAgents(entry, now = Date.now()) {
57919
57877
  finished
57920
57878
  };
57921
57879
  }
57922
- function shortModelLabel(agent, run) {
57923
- const model = agent.model ?? run.modelContext?.parentModel ?? run.modelContext?.override;
57924
- if (typeof model !== "string" || !model) return void 0;
57925
- return model.split("/").at(-1) ?? model;
57926
- }
57927
- function compactAgentRow(run, agent, finished, runs, options, width, liveHandle) {
57928
- const marker = options.selectedTaskId === agent.taskId ? "\u276F" : " ";
57929
- const dockGlyph = options.viewedTaskId === agent.taskId ? "\u23FA" : dockStatusIcon(agent.status);
57930
- const name = liveHandle?.agent ?? agent.agent;
57931
- const label = liveHandle?.description ?? agent.role ?? "";
57932
- const runTag = runs.length > 1 ? `${shortRunLabel(run)} \xB7 ` : "";
57933
- const taskTag = agent.taskId ? ` \xB7 ${agent.taskId}` : "";
57934
- const roleTag = label && label !== agent.taskId && label !== name ? ` \xB7 ${label}` : "";
57935
- const nameText = runTag + name + taskTag + roleTag;
57936
- const liveLine = liveHandle?.activity?.responseText?.split("\n").find((line4) => line4.trim())?.trim();
57937
- const activity = !finished && liveHandle?.status === "running" && liveLine ? liveLine.length > 60 ? `${liveLine.slice(0, 60)}\u2026` : liveLine : finished ? dockStatusLabel(agent.status) : agentActivity(agent, liveHandle);
57938
- const usage = dockUsageText(agent, liveHandle, { viewed: options.viewedTaskId === agent.taskId });
57939
- const ageText = dockElapsed(agent.completedAt ?? agent.startedAt);
57940
- const model = shortModelLabel(agent, run);
57941
- const suffix = `${model ? ` \xB7 ${model}` : ""}${usage ? ` \xB7 ${usage}` : ""}${ageText ? ` \xB7 ${ageText}` : ""}`;
57942
- return budgetedRow({ lead: `${marker} ${dockGlyph} `, name: nameText, activity, suffix }, width);
57943
- }
57944
- function compactDockLines(runs, options, width, maxLines, notificationCount, runningGlyph) {
57945
- const now = Date.now();
57946
- const flat = [];
57947
- for (const entry of runs) {
57948
- const { active, finished } = orderWidgetAgents(entry, now);
57949
- const liveForRun = listLiveAgents().filter((a) => a.runId === entry.run.runId);
57950
- for (const agent of active) {
57951
- flat.push({ run: entry.run, agent, finished: false, liveHandle: liveForRun.find((h) => h.taskId === agent.taskId) });
57952
- }
57953
- for (const agent of finished) {
57954
- flat.push({ run: entry.run, agent, finished: true, liveHandle: liveForRun.find((h) => h.taskId === agent.taskId) });
57955
- }
57956
- }
57957
- if (flat.length === 0) return [widgetHeader(runs, runningGlyph, maxLines, notificationCount)];
57958
- const lines = [];
57959
- let hint;
57960
- if (options.viewedTaskId) {
57961
- const viewedName = flat.find((row) => row.agent.taskId === options.viewedTaskId)?.agent.agent ?? "agent";
57962
- hint = `viewing @${viewedName} \u2014 typing goes to the agent \xB7 \u2193 switch \xB7 esc back`;
57963
- } else if (options.focused) {
57964
- hint = "enter to view \xB7 x to stop/cancel \xB7 esc back";
57965
- } else {
57966
- hint = `agents (${flat.length}) \u2014 \u2193 to select`;
57967
- }
57968
- lines.push(truncate(hint, width));
57969
- const mainMarker = options.focused && !options.selectedTaskId ? "\u276F" : " ";
57970
- const mainIcon = options.viewedTaskId ? "\u25EF" : "\u25CF";
57971
- lines.push(truncate(`${mainMarker} ${mainIcon} main`, width));
57972
- const selectedIndex = options.selectedTaskId ? flat.findIndex((row) => row.agent.taskId === options.selectedTaskId) : -1;
57973
- const windowStart = selectedIndex >= 0 ? Math.max(0, selectedIndex - MAX_AGENTS_DISPLAY + 1) : 0;
57974
- const windowEnd = Math.min(flat.length, windowStart + MAX_AGENTS_DISPLAY);
57975
- if (windowStart > 0) lines.push(truncate(` \u2026 \u2191${windowStart} earlier (\u2191 to scroll)`, width));
57976
- for (const row of flat.slice(windowStart, windowEnd)) {
57977
- lines.push(compactAgentRow(row.run, row.agent, row.finished, runs, options, width, row.liveHandle));
57978
- }
57979
- if (windowEnd < flat.length) lines.push(truncate(` \u2026 +${flat.length - windowEnd} more (\u2193 to scroll)`, width));
57980
- return lines;
57981
- }
57982
57880
  function buildWidgetLines(cwd, frame = 0, maxLines = 8, providedRuns, notificationCount = 0, width = DEFAULT_WIDGET_WIDTH, options = {}) {
57983
57881
  const rowStyle = options.rowStyle ?? "detailed";
57984
57882
  const focused = options.focused === true;
57985
57883
  const runs = providedRuns ?? activeWidgetRuns(cwd);
57986
57884
  if (!runs.length) return [];
57987
57885
  const runningGlyph = spinnerFrame("widget-header");
57886
+ const lines = [];
57988
57887
  if (rowStyle === "compact") {
57989
- const lines2 = compactDockLines(runs, options, width, maxLines, notificationCount, runningGlyph);
57990
- return focused ? lines2 : lines2.slice(0, maxLines);
57888
+ const agentCount = runs.reduce((n, entry) => {
57889
+ const { active, finished } = orderWidgetAgents(entry);
57890
+ return n + active.length + finished.length;
57891
+ }, 0);
57892
+ if (agentCount > 0) {
57893
+ let hint;
57894
+ if (options.viewedTaskId) {
57895
+ const viewedName = runs.flatMap((entry) => entry.agents).find((a) => a.taskId === options.viewedTaskId)?.agent ?? "agent";
57896
+ hint = `viewing @${viewedName} \u2014 typing goes to the agent \xB7 \u2193 switch \xB7 esc back`;
57897
+ } else if (options.focused) {
57898
+ hint = "enter to view \xB7 x to stop/cancel \xB7 esc back";
57899
+ } else {
57900
+ hint = `agents (${agentCount}) \u2014 \u2193 to select`;
57901
+ }
57902
+ lines.push(truncate(hint, width));
57903
+ const mainMarker = options.focused && !options.selectedTaskId ? "\u276F" : " ";
57904
+ const mainIcon = options.viewedTaskId ? "\u25EF" : "\u25CF";
57905
+ lines.push(truncate(`${mainMarker} ${mainIcon} main`, width));
57906
+ } else {
57907
+ lines.push(widgetHeader(runs, runningGlyph, maxLines, notificationCount));
57908
+ }
57909
+ } else {
57910
+ lines.push(widgetHeader(runs, runningGlyph, maxLines, notificationCount));
57991
57911
  }
57992
- const lines = [widgetHeader(runs, runningGlyph, maxLines, notificationCount)];
57993
57912
  for (const entry of runs) {
57994
- const { run, agents } = entry;
57913
+ const { run, agents, snapshot } = entry;
57995
57914
  const now = Date.now();
57996
57915
  const { active: activeAgents, finished: finishedAgents } = orderWidgetAgents(entry, now);
57997
57916
  const completed = agents.filter((a) => a.status === "completed").length;
@@ -58004,7 +57923,9 @@ function buildWidgetLines(cwd, frame = 0, maxLines = 8, providedRuns, notificati
58004
57923
  const runElapsedText = `${Math.floor(runElapsedMs / 1e3)}s`;
58005
57924
  const statusLabel = isTerminal ? ` \xB7 ${run.status}` : "";
58006
57925
  const progressPart = `${agentCountText} \xB7 ${runElapsedText}${statusLabel}`;
58007
- lines.push(truncate(`\u251C\u2500 ${runGlyph} ${shortRunLabel(run)} \xB7 ${progressPart} \xB7 ${run.runId.slice(-8)}`, width));
57926
+ if (rowStyle !== "compact") {
57927
+ lines.push(truncate(`\u251C\u2500 ${runGlyph} ${shortRunLabel(run)} \xB7 ${progressPart} \xB7 ${run.runId.slice(-8)}`, width));
57928
+ }
58008
57929
  const liveForRun = listLiveAgents().filter((a) => a.runId === run.runId);
58009
57930
  const activeCap = focused ? activeAgents.length : MAX_AGENTS_DISPLAY;
58010
57931
  const finishedSlots = focused ? finishedAgents.length : Math.max(0, Math.min(2, MAX_AGENTS_DISPLAY - activeAgents.length));
@@ -58014,10 +57935,33 @@ function buildWidgetLines(cwd, frame = 0, maxLines = 8, providedRuns, notificati
58014
57935
  const last = index === visibleAgents.length - 1 && activeAgents.length <= activeCap && finishedSlots === 0;
58015
57936
  const branch = last ? "\u2514\u2500" : "\u251C\u2500";
58016
57937
  const liveHandle = liveForRun.find((h) => h.taskId === agent.taskId);
57938
+ const dockGlyph = options.viewedTaskId === agent.taskId ? "\u23FA" : dockStatusIcon(agent.status);
58017
57939
  const legacyGlyph = options.viewedTaskId === agent.taskId ? "\u25C9" : iconForStatus(agent.status, { runningGlyph });
58018
57940
  const stats = agentStats(agent, liveHandle);
58019
57941
  const name = liveHandle?.agent ?? agent.agent;
58020
57942
  const activity = agentActivity(agent, liveHandle);
57943
+ if (rowStyle === "compact") {
57944
+ const label = liveHandle?.description ?? agent.role ?? "";
57945
+ const runTag = runs.length > 1 ? `${shortRunLabel(run)} \xB7 ` : "";
57946
+ const nameText = runTag + (label ? `${name} \xB7 ${label}` : name);
57947
+ const liveLine = liveHandle?.activity?.responseText?.split("\n").find((l) => l.trim())?.trim();
57948
+ const dockActivity = liveHandle?.status === "running" && liveLine ? liveLine.length > 60 ? `${liveLine.slice(0, 60)}\u2026` : liveLine : activity;
57949
+ const usage = dockUsageText(agent, liveHandle, { viewed: options.viewedTaskId === agent.taskId });
57950
+ const ageText = dockElapsed(agent.completedAt ?? agent.startedAt);
57951
+ const suffix = `${usage ? ` \xB7 ${usage}` : ""}${ageText ? ` \xB7 ${ageText}` : ""}`;
57952
+ lines.push(
57953
+ budgetedRow(
57954
+ {
57955
+ lead: `${markerFor2(agent.taskId)} ${dockGlyph} `,
57956
+ name: nameText,
57957
+ activity: dockActivity,
57958
+ suffix
57959
+ },
57960
+ width
57961
+ )
57962
+ );
57963
+ continue;
57964
+ }
58021
57965
  const desc = truncate(liveHandle?.description ?? agent.role ?? "", TASK_DESC_MAX);
58022
57966
  const _activeMain = truncate(`\u2502 ${branch} ${legacyGlyph} ${name}${desc ? ` \xB7 ${desc}` : ` \xB7 ${agent.role}`}`, width);
58023
57967
  lines.push(_activeMain);
@@ -58025,13 +57969,39 @@ function buildWidgetLines(cwd, frame = 0, maxLines = 8, providedRuns, notificati
58025
57969
  lines.push(_activity);
58026
57970
  }
58027
57971
  if (activeAgents.length > activeCap) {
58028
- lines.push(truncate(`\u2502 \u2514\u2500 \u2026 +${activeAgents.length - activeCap} more agents`, width));
57972
+ lines.push(
57973
+ truncate(
57974
+ rowStyle === "compact" ? `\u2026 +${activeAgents.length - activeCap} more agents` : `\u2502 \u2514\u2500 \u2026 +${activeAgents.length - activeCap} more agents`,
57975
+ width
57976
+ )
57977
+ );
58029
57978
  }
58030
57979
  for (const [index, agent] of finishedAgents.slice(0, finishedSlots).entries()) {
58031
57980
  const liveHandle = liveForRun.find((h) => h.taskId === agent.taskId);
58032
57981
  const name = liveHandle?.agent ?? agent.agent;
57982
+ const dockIcon = dockStatusIcon(agent.status);
58033
57983
  const legacyIcon = agent.status === "completed" ? "\u2713" : agent.status === "failed" ? "\u2717" : agent.status === "needs_attention" ? "\u26A0" : "\u25AA";
58034
57984
  const stats = agentStats(agent, liveHandle);
57985
+ if (rowStyle === "compact") {
57986
+ const label = liveHandle?.description ?? agent.role ?? "";
57987
+ const runTag = runs.length > 1 ? `${shortRunLabel(run)} \xB7 ` : "";
57988
+ const nameText = runTag + (label ? `${name} \xB7 ${label}` : name);
57989
+ const usage = dockUsageText(agent, liveHandle, { viewed: options.viewedTaskId === agent.taskId });
57990
+ const ageText = dockElapsed(agent.completedAt ?? agent.startedAt);
57991
+ const suffix = `${usage ? ` \xB7 ${usage}` : ""}${ageText ? ` \xB7 ${ageText}` : ""}`;
57992
+ lines.push(
57993
+ budgetedRow(
57994
+ {
57995
+ lead: `${markerFor2(agent.taskId)} ${dockIcon} `,
57996
+ name: nameText,
57997
+ activity: dockStatusLabel(agent.status),
57998
+ suffix
57999
+ },
58000
+ width
58001
+ )
58002
+ );
58003
+ continue;
58004
+ }
58035
58005
  const desc = truncate(liveHandle?.description ?? agent.role ?? "", TASK_DESC_MAX);
58036
58006
  const isLastFinished = index === Math.min(finishedAgents.length, finishedSlots) - 1;
58037
58007
  const branch = isLastFinished ? "\u2514\u2500" : "\u251C\u2500";
@@ -58508,138 +58478,6 @@ var init_shared_overlay_scheduler = __esm({
58508
58478
  }
58509
58479
  });
58510
58480
 
58511
- // src/ui/widget/task-list.ts
58512
- function isDoneStatus(status) {
58513
- return status === "completed" || status === "failed" || status === "cancelled" || status === "skipped";
58514
- }
58515
- function taskTitle(task) {
58516
- return (task.displayName ?? task.title ?? "").replace(/\s+/g, " ").trim();
58517
- }
58518
- function taskStatusIcon(task) {
58519
- switch (task.status) {
58520
- case "running":
58521
- return spinnerFrame("crew-task-list");
58522
- case "queued":
58523
- return "\u25FB";
58524
- case "waiting":
58525
- case "needs_attention":
58526
- return "\u25FC";
58527
- case "completed":
58528
- return "\u2714";
58529
- case "failed":
58530
- case "cancelled":
58531
- case "skipped":
58532
- return "\u2717";
58533
- default:
58534
- return "?";
58535
- }
58536
- }
58537
- function formatDuration2(ms) {
58538
- if (!Number.isFinite(ms) || ms < 0) return "";
58539
- const totalSeconds = Math.floor(ms / 1e3);
58540
- if (totalSeconds < 60) return `${totalSeconds}s`;
58541
- const minutes = Math.floor(totalSeconds / 60);
58542
- if (minutes < 60) return `${minutes}m ${String(totalSeconds % 60).padStart(2, "0")}s`;
58543
- const hours = Math.floor(minutes / 60);
58544
- return `${hours}h ${String(minutes % 60).padStart(2, "0")}m`;
58545
- }
58546
- function compactTokens2(count2) {
58547
- if (typeof count2 !== "number" || !Number.isFinite(count2) || count2 <= 0) return "";
58548
- if (count2 >= 1e6) return `${(count2 / 1e6).toFixed(1)}M`;
58549
- if (count2 >= 1e3) return `${(count2 / 1e3).toFixed(1)}k`;
58550
- return `${count2}`;
58551
- }
58552
- function runningSuffix(task) {
58553
- const parts = [];
58554
- const start = Date.parse(task.startedAt ?? "");
58555
- if (Number.isFinite(start) && start > 0) {
58556
- const end = task.finishedAt ? Date.parse(task.finishedAt) : Date.now();
58557
- const elapsed2 = formatDuration2(end - start);
58558
- if (elapsed2) parts.push(elapsed2);
58559
- }
58560
- const input = task.usage?.input ?? 0;
58561
- const output = task.usage?.output ?? 0;
58562
- if (input > 0 || output > 0) {
58563
- parts.push(`\u2191 ${compactTokens2(input) || "0"} \u2193 ${compactTokens2(output) || "0"}`);
58564
- } else {
58565
- const live = compactTokens2(task.agentProgress?.tokens ?? 0);
58566
- if (live) parts.push(live);
58567
- }
58568
- return parts.length ? ` (${parts.join(" \xB7 ")})` : "";
58569
- }
58570
- function blockedSuffix(task, tasks, numberById) {
58571
- if (task.status !== "queued") return "";
58572
- const openDeps = (task.dependsOn ?? []).map((depId) => tasks.find((entry) => entry.id === depId)).filter((dep) => dep !== void 0 && dep.status !== "completed");
58573
- if (openDeps.length === 0) return "";
58574
- const labels = openDeps.map((dep) => `#${numberById.get(dep.id) ?? "?"}`);
58575
- return ` \u203A blocked by ${labels.join(", ")}`;
58576
- }
58577
- function taskRow(task, taskNumber, tasks, numberById, width) {
58578
- const title = taskTitle(task);
58579
- const suffix = task.status === "running" ? runningSuffix(task) : blockedSuffix(task, tasks, numberById);
58580
- if (task.status === "completed") {
58581
- const struck = `${STRIKE_ON}${DIM_ON}#${taskNumber} ${title}${DIM_OFF}${STRIKE_OFF}`;
58582
- return truncate(` \u2714 ${struck}`.trimEnd(), width);
58583
- }
58584
- return truncate(` ${taskStatusIcon(task)} #${taskNumber} ${title}${suffix}`.trimEnd(), width);
58585
- }
58586
- function primaryRun(runs) {
58587
- return runs.find((entry) => (entry.snapshot?.tasks ?? []).some((task) => ACTIVE_TASK_STATUSES.has(task.status))) ?? runs[0];
58588
- }
58589
- function visibleTasks(tasks) {
58590
- if (tasks.length <= MAX_TASK_ROWS) return [...tasks];
58591
- const active = tasks.filter((task) => ACTIVE_TASK_STATUSES.has(task.status));
58592
- if (active.length >= MAX_TASK_ROWS) return active.slice(0, MAX_TASK_ROWS);
58593
- const keep = new Set(active.map((task) => task.id));
58594
- for (const task of tasks) {
58595
- if (keep.size >= MAX_TASK_ROWS) break;
58596
- keep.add(task.id);
58597
- }
58598
- return tasks.filter((task) => keep.has(task.id));
58599
- }
58600
- function buildTaskListLines(runs, width) {
58601
- const entry = primaryRun(runs);
58602
- if (!entry) return [];
58603
- const tasks = entry.snapshot?.tasks ?? [];
58604
- if (tasks.length === 0) return [];
58605
- const numberById = new Map(tasks.map((task, index) => [task.id, index + 1]));
58606
- const done = tasks.filter((task) => task.status === "completed").length;
58607
- const dead = tasks.filter((task) => isDoneStatus(task.status) && task.status !== "completed").length;
58608
- const inProgress = tasks.filter(
58609
- (task) => task.status === "running" || task.status === "waiting" || task.status === "needs_attention"
58610
- ).length;
58611
- const open = tasks.filter((task) => task.status === "queued").length;
58612
- const counts = [`${done} done`];
58613
- if (dead) counts.push(`${dead} failed`);
58614
- counts.push(`${inProgress} in progress`, `${open} open`);
58615
- const headerParts = [`\u25CF ${tasks.length} tasks (${counts.join(", ")})`];
58616
- if (runs.length > 1) headerParts.push(shortRunLabel(entry.run));
58617
- const lines = [truncate(headerParts.join(" \xB7 "), width)];
58618
- const visible = visibleTasks(tasks);
58619
- for (const task of visible) {
58620
- lines.push(taskRow(task, numberById.get(task.id) ?? 0, tasks, numberById, width));
58621
- }
58622
- if (tasks.length > visible.length) {
58623
- lines.push(truncate(` \u2026 and ${tasks.length - visible.length} more`, width));
58624
- }
58625
- return lines;
58626
- }
58627
- var MAX_TASK_ROWS, ACTIVE_TASK_STATUSES, DIM_ON, DIM_OFF, STRIKE_ON, STRIKE_OFF;
58628
- var init_task_list = __esm({
58629
- "src/ui/widget/task-list.ts"() {
58630
- "use strict";
58631
- init_visual();
58632
- init_spinner();
58633
- init_widget_model();
58634
- MAX_TASK_ROWS = 10;
58635
- ACTIVE_TASK_STATUSES = /* @__PURE__ */ new Set(["running", "queued", "waiting", "needs_attention"]);
58636
- DIM_ON = "\x1B[2m";
58637
- DIM_OFF = "\x1B[22m";
58638
- STRIKE_ON = "\x1B[9m";
58639
- STRIKE_OFF = "\x1B[29m";
58640
- }
58641
- });
58642
-
58643
58481
  // src/ui/widget/index.ts
58644
58482
  var widget_exports = {};
58645
58483
  __export(widget_exports, {
@@ -58678,7 +58516,7 @@ function uninstallResizeListener() {
58678
58516
  clearTimeout(resizeTimer);
58679
58517
  resizeTimer = void 0;
58680
58518
  }
58681
- activeResizeTargets.clear();
58519
+ activeResizeTarget = void 0;
58682
58520
  process.off("SIGWINCH", onResize);
58683
58521
  if (typeof process.stdout?.off === "function") {
58684
58522
  process.stdout.off("resize", onResize);
@@ -58716,8 +58554,6 @@ function updateCrewWidget(ctx, state2, config, manifestCache2, snapshotCache, pr
58716
58554
  if (!lines.length) {
58717
58555
  if (state2.lastVisibility !== "hidden" || state2.lastPlacement !== placement) {
58718
58556
  setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement: piPlacement });
58719
- setExtensionWidget(ctx, TASKS_WIDGET_KEY, void 0, { placement: "aboveEditor" });
58720
- state2.lastTasksVisibility = "hidden";
58721
58557
  state2.footerDock?.dispose();
58722
58558
  state2.footerDock = void 0;
58723
58559
  setFooterDockProvider(void 0);
@@ -58786,20 +58622,6 @@ function updateCrewWidget(ctx, state2, config, manifestCache2, snapshotCache, pr
58786
58622
  state2.lastMaxLines = maxLines;
58787
58623
  state2.lastCwd = ctx.cwd;
58788
58624
  }
58789
- const tasksVisible = runs.length > 0 && Boolean(state2.model);
58790
- if (tasksVisible && state2.lastTasksVisibility !== "visible") {
58791
- const model = state2.model;
58792
- setExtensionWidget(
58793
- ctx,
58794
- TASKS_WIDGET_KEY,
58795
- ((_tui, theme) => new CrewWidgetComponent(model, theme, _tui, "tasks")),
58796
- { placement: "aboveEditor" }
58797
- );
58798
- state2.lastTasksVisibility = "visible";
58799
- } else if (!tasksVisible && state2.lastTasksVisibility === "visible") {
58800
- setExtensionWidget(ctx, TASKS_WIDGET_KEY, void 0, { placement: "aboveEditor" });
58801
- state2.lastTasksVisibility = "hidden";
58802
- }
58803
58625
  requestRender(ctx);
58804
58626
  }
58805
58627
  function stopCrewWidget(ctx, state2, config) {
@@ -58810,8 +58632,6 @@ function stopCrewWidget(ctx, state2, config) {
58810
58632
  ctx.ui.setStatus(STATUS_KEY, void 0);
58811
58633
  setExtensionWidget(ctx, LEGACY_WIDGET_KEY, void 0, { placement: piPlacement });
58812
58634
  setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement: piPlacement });
58813
- setExtensionWidget(ctx, TASKS_WIDGET_KEY, void 0, { placement: "aboveEditor" });
58814
- state2.lastTasksVisibility = "hidden";
58815
58635
  state2.footerDock?.dispose();
58816
58636
  state2.footerDock = void 0;
58817
58637
  setFooterDockProvider(void 0);
@@ -58823,7 +58643,7 @@ function stopCrewWidget(ctx, state2, config) {
58823
58643
  requestRender(ctx);
58824
58644
  }
58825
58645
  }
58826
- var MAX_LINES_DEFAULT, LEGACY_WIDGET_KEY, WIDGET_KEY, TASKS_WIDGET_KEY, STATUS_KEY, SIGNATURE_CACHE_TTL_MS, resizeListenerInstalled, activeResizeTargets, resizeTimer, onResize, CrewWidgetComponent, FooterDockHost;
58646
+ var MAX_LINES_DEFAULT, LEGACY_WIDGET_KEY, WIDGET_KEY, STATUS_KEY, SIGNATURE_CACHE_TTL_MS, resizeListenerInstalled, activeResizeTarget, resizeTimer, onResize, CrewWidgetComponent, FooterDockHost;
58827
58647
  var init_widget = __esm({
58828
58648
  "src/ui/widget/index.ts"() {
58829
58649
  "use strict";
@@ -58836,7 +58656,6 @@ var init_widget = __esm({
58836
58656
  init_shared_overlay_scheduler();
58837
58657
  init_spinner();
58838
58658
  init_theme_adapter();
58839
- init_task_list();
58840
58659
  init_widget_model();
58841
58660
  init_widget_renderer();
58842
58661
  init_widget_model();
@@ -58846,26 +58665,20 @@ var init_widget = __esm({
58846
58665
  MAX_LINES_DEFAULT = DEFAULT_UI.widgetMaxLines;
58847
58666
  LEGACY_WIDGET_KEY = "pi-crew";
58848
58667
  WIDGET_KEY = "pi-crew-active";
58849
- TASKS_WIDGET_KEY = "pi-crew-tasks";
58850
58668
  STATUS_KEY = "pi-crew";
58851
58669
  SIGNATURE_CACHE_TTL_MS = 100;
58852
58670
  resizeListenerInstalled = false;
58853
- activeResizeTargets = /* @__PURE__ */ new Set();
58854
58671
  onResize = () => {
58855
58672
  if (resizeTimer) clearTimeout(resizeTimer);
58856
58673
  resizeTimer = setTimeout(() => {
58857
58674
  resizeTimer = void 0;
58858
- for (const target of activeResizeTargets) {
58859
- target.invalidate();
58860
- target.requestRepaint();
58861
- }
58675
+ activeResizeTarget?.invalidate();
58676
+ activeResizeTarget?.requestRepaint();
58862
58677
  }, 120);
58863
58678
  };
58864
58679
  CrewWidgetComponent = class {
58865
58680
  model;
58866
58681
  theme;
58867
- /** Which surface this instance paints: the agent dock, or the task list. */
58868
- variant;
58869
58682
  cacheSignature = "";
58870
58683
  /** C4 — invalidate-on-write cache for the buildSignature() result. */
58871
58684
  cachedBuildSignature = "";
@@ -58878,13 +58691,12 @@ var init_widget = __esm({
58878
58691
  unsubscribeTheme;
58879
58692
  unsubscribePanel;
58880
58693
  schedulerHandle;
58881
- constructor(model, themeLike, tui, variant = "dock") {
58694
+ constructor(model, themeLike, tui) {
58882
58695
  this.model = model;
58883
- this.variant = variant;
58884
58696
  this.theme = asCrewTheme(themeLike);
58885
58697
  this.cachedTheme = this.theme;
58886
58698
  this.tui = tui;
58887
- activeResizeTargets.add(this);
58699
+ activeResizeTarget = this;
58888
58700
  installResizeListener();
58889
58701
  this.unsubscribeTheme = subscribeThemeChange(themeLike, () => this.invalidate());
58890
58702
  this.unsubscribePanel = subscribePanelChange(() => {
@@ -58942,7 +58754,7 @@ var init_widget = __esm({
58942
58754
  this.unsubscribeTheme();
58943
58755
  this.unsubscribePanel();
58944
58756
  this.schedulerHandle.dispose();
58945
- activeResizeTargets.delete(this);
58757
+ if (activeResizeTarget === this) activeResizeTarget = void 0;
58946
58758
  }
58947
58759
  render(width) {
58948
58760
  const runs = activeWidgetRuns(
@@ -58963,20 +58775,6 @@ var init_widget = __esm({
58963
58775
  }
58964
58776
  const signature = `${sigBase}:${this.model.notificationCount ?? 0}`;
58965
58777
  const runningGlyph = spinnerFrame("widget-header");
58966
- if (this.variant === "tasks") {
58967
- if (this.cacheSignature !== signature || width !== this.cachedWidth || this.cachedTheme !== this.theme) {
58968
- this.cachedBaseLines = buildTaskListLines(runs, width);
58969
- this.cachedLines = this.colorize(this.cachedBaseLines, width);
58970
- this.cachedWidth = width;
58971
- this.cachedTheme = this.theme;
58972
- this.cacheSignature = signature;
58973
- }
58974
- if (runs.length === 0) {
58975
- this.invalidate();
58976
- return [];
58977
- }
58978
- return this.cachedLines.map((line4) => truncate(line4, width));
58979
- }
58980
58778
  const panel = panelDisplayState();
58981
58779
  const signatureWithPanel = `${signature}|panel:${panel.selectedTaskId ?? ""}/${panel.viewedTaskId ?? ""}/${panel.focused ? 1 : 0}`;
58982
58780
  const compactDock = this.model.rowStyle === "compact";
@@ -60266,7 +60064,7 @@ function loadRunSummaries(cwd, options = {}) {
60266
60064
  }
60267
60065
  return summaries;
60268
60066
  }
60269
- function formatDuration3(createdAt, completedAt) {
60067
+ function formatDuration2(createdAt, completedAt) {
60270
60068
  const start = new Date(createdAt).getTime();
60271
60069
  const end = completedAt ? new Date(completedAt).getTime() : Date.now();
60272
60070
  if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return "?";
@@ -60310,7 +60108,7 @@ function buildTeamOnboarding(team, cwd, options = {}) {
60310
60108
  lines.push("| Run | Goal | Duration | Status |");
60311
60109
  lines.push("|-----|------|----------|--------|");
60312
60110
  for (const run of runs) {
60313
- const duration = formatDuration3(run.createdAt, run.completedAt);
60111
+ const duration = formatDuration2(run.createdAt, run.completedAt);
60314
60112
  const goalPreview = run.goal ? run.goal.slice(0, 40) : "N/A";
60315
60113
  const goalSuffix = run.goal && run.goal.length > 40 ? "..." : "";
60316
60114
  const status = run.status ?? "unknown";
@@ -63456,11 +63254,8 @@ function reconstructAdaptiveWorkflow(workflow, tasks) {
63456
63254
  }
63457
63255
  return steps.length ? { ...workflow, steps: [...workflow.steps, ...steps] } : workflow;
63458
63256
  }
63459
- function isAdaptiveWorkflow(workflow) {
63460
- return workflow.adaptive === true || workflow.name === "implementation";
63461
- }
63462
63257
  async function injectAdaptivePlanIfReady(input) {
63463
- if (!isAdaptiveWorkflow(input.workflow) || input.executeWorkers === false)
63258
+ if (input.workflow.name !== "implementation")
63464
63259
  return {
63465
63260
  tasks: input.tasks,
63466
63261
  workflow: input.workflow,
@@ -63580,13 +63375,11 @@ async function injectAdaptivePlanIfReady(input) {
63580
63375
  const itemId = `adaptive-p${phaseIndex + 1}-t${taskIndex + 1}`;
63581
63376
  const stepId = `adaptive-${phaseIndex + 1}-${taskIndex + 1}-${slug(planned.role)}`;
63582
63377
  const taskId = `adaptive-${String(counter).padStart(2, "0")}-${slug(planned.role)}`;
63583
- const firstLine = planned.task.split("\n").map((line4) => line4.trim()).filter(Boolean).find((line4) => !line4.startsWith("#")) ?? "";
63584
- const title = (planned.title ?? firstLine ?? stepId).slice(0, 120);
63585
63378
  phaseItemIds.push(itemId);
63586
63379
  planItems.push({
63587
63380
  id: itemId,
63588
63381
  ref: stepId,
63589
- title,
63382
+ title: planned.title ?? planned.task.slice(0, 80),
63590
63383
  taskIds: [],
63591
63384
  specIds: [],
63592
63385
  acceptance: [],
@@ -63605,8 +63398,7 @@ async function injectAdaptivePlanIfReady(input) {
63605
63398
  stepId,
63606
63399
  role: planned.role,
63607
63400
  agent: input.team.roles.find((role) => role.name === planned.role)?.agent ?? planned.role,
63608
- title,
63609
- description: planned.task !== title ? planned.task : void 0,
63401
+ title: planned.title ?? stepId,
63610
63402
  status: "queued",
63611
63403
  dependsOn: previousStepIds,
63612
63404
  cwd: input.manifest.cwd,
@@ -64328,7 +64120,7 @@ async function executeTeamRunCore(input, manifest, workflow) {
64328
64120
  }
64329
64121
  let tasks = refreshTaskGraphQueues(input.tasks);
64330
64122
  let queueIndex = buildTaskGraphIndex(tasks);
64331
- const canInjectAdaptivePlan = isAdaptiveWorkflow(workflow);
64123
+ const canInjectAdaptivePlan = workflow.name === "implementation";
64332
64124
  let adaptivePlanInjected = false;
64333
64125
  let adaptivePlanMissing = false;
64334
64126
  const attemptAdaptivePlan = async () => {
@@ -64337,8 +64129,7 @@ async function executeTeamRunCore(input, manifest, workflow) {
64337
64129
  manifest,
64338
64130
  tasks,
64339
64131
  workflow,
64340
- team: input.team,
64341
- executeWorkers: input.executeWorkers
64132
+ team: input.team
64342
64133
  });
64343
64134
  adaptivePlanInjected = adaptivePlanInjected || adaptivePlan.injected;
64344
64135
  adaptivePlanMissing = adaptivePlan.missingPlan;