codingbuddy 4.4.0 → 5.0.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 (201) hide show
  1. package/dist/src/agent/agent.module.js +3 -1
  2. package/dist/src/agent/agent.module.js.map +1 -1
  3. package/dist/src/agent/agent.service.d.ts +13 -4
  4. package/dist/src/agent/agent.service.js +125 -12
  5. package/dist/src/agent/agent.service.js.map +1 -1
  6. package/dist/src/agent/agent.types.d.ts +53 -0
  7. package/dist/src/agent/agent.types.js.map +1 -1
  8. package/dist/src/collaboration/discussion-engine.d.ts +14 -0
  9. package/dist/src/collaboration/discussion-engine.js +49 -0
  10. package/dist/src/collaboration/discussion-engine.js.map +1 -0
  11. package/dist/src/collaboration/index.d.ts +5 -0
  12. package/dist/src/collaboration/index.js +22 -0
  13. package/dist/src/collaboration/index.js.map +1 -0
  14. package/dist/src/collaboration/opinion-adapter.d.ts +14 -0
  15. package/dist/src/collaboration/opinion-adapter.js +56 -0
  16. package/dist/src/collaboration/opinion-adapter.js.map +1 -0
  17. package/dist/src/collaboration/terminal-formatter.d.ts +5 -0
  18. package/dist/src/collaboration/terminal-formatter.js +57 -0
  19. package/dist/src/collaboration/terminal-formatter.js.map +1 -0
  20. package/dist/src/collaboration/types.d.ts +50 -0
  21. package/dist/src/collaboration/types.js +53 -0
  22. package/dist/src/collaboration/types.js.map +1 -0
  23. package/dist/src/config/config.schema.d.ts +8 -0
  24. package/dist/src/config/config.schema.js +4 -0
  25. package/dist/src/config/config.schema.js.map +1 -1
  26. package/dist/src/context/context-document.service.d.ts +1 -1
  27. package/dist/src/context/context-document.service.js +6 -2
  28. package/dist/src/context/context-document.service.js.map +1 -1
  29. package/dist/src/context/context-document.types.d.ts +18 -0
  30. package/dist/src/context/context-document.types.js +13 -1
  31. package/dist/src/context/context-document.types.js.map +1 -1
  32. package/dist/src/context/context-parser.utils.js +32 -0
  33. package/dist/src/context/context-parser.utils.js.map +1 -1
  34. package/dist/src/context/context-serializer.utils.d.ts +3 -2
  35. package/dist/src/context/context-serializer.utils.js +25 -1
  36. package/dist/src/context/context-serializer.utils.js.map +1 -1
  37. package/dist/src/keyword/explicit-pattern-matcher.d.ts +3 -0
  38. package/dist/src/keyword/explicit-pattern-matcher.js +34 -0
  39. package/dist/src/keyword/explicit-pattern-matcher.js.map +1 -0
  40. package/dist/src/keyword/keyword.module.js +17 -1
  41. package/dist/src/keyword/keyword.module.js.map +1 -1
  42. package/dist/src/keyword/keyword.service.d.ts +2 -0
  43. package/dist/src/keyword/keyword.service.js +15 -0
  44. package/dist/src/keyword/keyword.service.js.map +1 -1
  45. package/dist/src/keyword/keyword.types.d.ts +6 -1
  46. package/dist/src/keyword/keyword.types.js +7 -1
  47. package/dist/src/keyword/keyword.types.js.map +1 -1
  48. package/dist/src/keyword/primary-agent-resolver.d.ts +5 -2
  49. package/dist/src/keyword/primary-agent-resolver.js +22 -1
  50. package/dist/src/keyword/primary-agent-resolver.js.map +1 -1
  51. package/dist/src/keyword/strategies/act-agent.strategy.js +8 -0
  52. package/dist/src/keyword/strategies/act-agent.strategy.js.map +1 -1
  53. package/dist/src/keyword/strategies/index.d.ts +1 -1
  54. package/dist/src/keyword/strategies/index.js.map +1 -1
  55. package/dist/src/keyword/strategies/resolution-strategy.interface.d.ts +3 -0
  56. package/dist/src/keyword/taskmaestro-detector.d.ts +1 -0
  57. package/dist/src/keyword/taskmaestro-detector.js +16 -0
  58. package/dist/src/keyword/taskmaestro-detector.js.map +1 -0
  59. package/dist/src/mcp/handlers/agent.handler.d.ts +1 -0
  60. package/dist/src/mcp/handlers/agent.handler.js +51 -2
  61. package/dist/src/mcp/handlers/agent.handler.js.map +1 -1
  62. package/dist/src/mcp/handlers/context-document.handler.d.ts +3 -0
  63. package/dist/src/mcp/handlers/context-document.handler.js +91 -10
  64. package/dist/src/mcp/handlers/context-document.handler.js.map +1 -1
  65. package/dist/src/mcp/handlers/discussion.handler.d.ts +14 -0
  66. package/dist/src/mcp/handlers/discussion.handler.js +168 -0
  67. package/dist/src/mcp/handlers/discussion.handler.js.map +1 -0
  68. package/dist/src/mcp/handlers/discussion.types.d.ts +18 -0
  69. package/dist/src/mcp/handlers/discussion.types.js +11 -0
  70. package/dist/src/mcp/handlers/discussion.types.js.map +1 -0
  71. package/dist/src/mcp/handlers/index.d.ts +3 -0
  72. package/dist/src/mcp/handlers/index.js +7 -1
  73. package/dist/src/mcp/handlers/index.js.map +1 -1
  74. package/dist/src/mcp/handlers/mode.handler.d.ts +3 -0
  75. package/dist/src/mcp/handlers/mode.handler.js +58 -0
  76. package/dist/src/mcp/handlers/mode.handler.js.map +1 -1
  77. package/dist/src/mcp/handlers/pipeline.handler.d.ts +14 -0
  78. package/dist/src/mcp/handlers/pipeline.handler.js +137 -0
  79. package/dist/src/mcp/handlers/pipeline.handler.js.map +1 -0
  80. package/dist/src/mcp/mcp.module.js +5 -0
  81. package/dist/src/mcp/mcp.module.js.map +1 -1
  82. package/dist/src/mcp/sse-auth.guard.js +4 -6
  83. package/dist/src/mcp/sse-auth.guard.js.map +1 -1
  84. package/dist/src/parallel-validation/extract-file-paths.d.ts +1 -0
  85. package/dist/src/parallel-validation/extract-file-paths.js +19 -0
  86. package/dist/src/parallel-validation/extract-file-paths.js.map +1 -0
  87. package/dist/src/parallel-validation/index.d.ts +5 -0
  88. package/dist/src/parallel-validation/index.js +12 -0
  89. package/dist/src/parallel-validation/index.js.map +1 -0
  90. package/dist/src/parallel-validation/overlap-matrix.d.ts +10 -0
  91. package/dist/src/parallel-validation/overlap-matrix.js +23 -0
  92. package/dist/src/parallel-validation/overlap-matrix.js.map +1 -0
  93. package/dist/src/parallel-validation/parallel-validation.handler.d.ts +8 -0
  94. package/dist/src/parallel-validation/parallel-validation.handler.js +84 -0
  95. package/dist/src/parallel-validation/parallel-validation.handler.js.map +1 -0
  96. package/dist/src/parallel-validation/parallel-validation.types.d.ts +12 -0
  97. package/dist/src/parallel-validation/parallel-validation.types.js +3 -0
  98. package/dist/src/parallel-validation/parallel-validation.types.js.map +1 -0
  99. package/dist/src/parallel-validation/wave-splitter.d.ts +2 -0
  100. package/dist/src/parallel-validation/wave-splitter.js +39 -0
  101. package/dist/src/parallel-validation/wave-splitter.js.map +1 -0
  102. package/dist/src/pipeline/index.d.ts +5 -0
  103. package/dist/src/pipeline/index.js +14 -0
  104. package/dist/src/pipeline/index.js.map +1 -0
  105. package/dist/src/pipeline/pipeline.executors.d.ts +2 -0
  106. package/dist/src/pipeline/pipeline.executors.js +79 -0
  107. package/dist/src/pipeline/pipeline.executors.js.map +1 -0
  108. package/dist/src/pipeline/pipeline.module.d.ts +2 -0
  109. package/dist/src/pipeline/pipeline.module.js +21 -0
  110. package/dist/src/pipeline/pipeline.module.js.map +1 -0
  111. package/dist/src/pipeline/pipeline.service.d.ts +8 -0
  112. package/dist/src/pipeline/pipeline.service.js +89 -0
  113. package/dist/src/pipeline/pipeline.service.js.map +1 -0
  114. package/dist/src/pipeline/pipeline.types.d.ts +47 -0
  115. package/dist/src/pipeline/pipeline.types.js +33 -0
  116. package/dist/src/pipeline/pipeline.types.js.map +1 -0
  117. package/dist/src/rules/rule-tracker.d.ts +24 -0
  118. package/dist/src/rules/rule-tracker.js +74 -0
  119. package/dist/src/rules/rule-tracker.js.map +1 -0
  120. package/dist/src/shared/event-bridge-reader.d.ts +17 -0
  121. package/dist/src/shared/event-bridge-reader.js +56 -0
  122. package/dist/src/shared/event-bridge-reader.js.map +1 -0
  123. package/dist/src/shared/version.d.ts +1 -1
  124. package/dist/src/shared/version.js +1 -1
  125. package/dist/src/tui/components/AgentDiscussionPanel.d.ts +8 -0
  126. package/dist/src/tui/components/AgentDiscussionPanel.js +21 -0
  127. package/dist/src/tui/components/AgentDiscussionPanel.js.map +1 -0
  128. package/dist/src/tui/components/AgentDiscussionPanel.spec.js +85 -0
  129. package/dist/src/tui/components/AgentDiscussionPanel.spec.js.map +1 -0
  130. package/dist/src/tui/components/FlowMap.d.ts +3 -1
  131. package/dist/src/tui/components/FlowMap.js +30 -3
  132. package/dist/src/tui/components/FlowMap.js.map +1 -1
  133. package/dist/src/tui/components/FlowMap.spec.js +88 -0
  134. package/dist/src/tui/components/FlowMap.spec.js.map +1 -1
  135. package/dist/src/tui/components/FocusedAgentPanel.d.ts +3 -1
  136. package/dist/src/tui/components/FocusedAgentPanel.js +8 -3
  137. package/dist/src/tui/components/FocusedAgentPanel.js.map +1 -1
  138. package/dist/src/tui/components/FocusedAgentPanel.spec.js +76 -0
  139. package/dist/src/tui/components/FocusedAgentPanel.spec.js.map +1 -1
  140. package/dist/src/tui/components/HeaderBar.d.ts +3 -1
  141. package/dist/src/tui/components/HeaderBar.js +12 -6
  142. package/dist/src/tui/components/HeaderBar.js.map +1 -1
  143. package/dist/src/tui/components/HeaderBar.spec.js +31 -13
  144. package/dist/src/tui/components/HeaderBar.spec.js.map +1 -1
  145. package/dist/src/tui/components/StageHealthBar.d.ts +5 -1
  146. package/dist/src/tui/components/StageHealthBar.js +6 -1
  147. package/dist/src/tui/components/StageHealthBar.js.map +1 -1
  148. package/dist/src/tui/components/StageHealthBar.spec.js +22 -0
  149. package/dist/src/tui/components/StageHealthBar.spec.js.map +1 -1
  150. package/dist/src/tui/components/agent-discussion-panel.pure.d.ts +10 -0
  151. package/dist/src/tui/components/agent-discussion-panel.pure.js +94 -0
  152. package/dist/src/tui/components/agent-discussion-panel.pure.js.map +1 -0
  153. package/dist/src/tui/components/focused-agent.pure.d.ts +1 -0
  154. package/dist/src/tui/components/focused-agent.pure.js +11 -0
  155. package/dist/src/tui/components/focused-agent.pure.js.map +1 -1
  156. package/dist/src/tui/components/index.d.ts +5 -1
  157. package/dist/src/tui/components/index.js +18 -1
  158. package/dist/src/tui/components/index.js.map +1 -1
  159. package/dist/src/tui/components/live.pure.d.ts +9 -0
  160. package/dist/src/tui/components/live.pure.js +70 -0
  161. package/dist/src/tui/components/live.pure.js.map +1 -0
  162. package/dist/src/tui/dashboard-app.js +10 -6
  163. package/dist/src/tui/dashboard-app.js.map +1 -1
  164. package/dist/src/tui/dashboard-app.spec.js +14 -0
  165. package/dist/src/tui/dashboard-app.spec.js.map +1 -1
  166. package/dist/src/tui/dashboard-types.d.ts +8 -0
  167. package/dist/src/tui/dashboard-types.js.map +1 -1
  168. package/dist/src/tui/eventbus-ui.integration.spec.js +3 -0
  169. package/dist/src/tui/eventbus-ui.integration.spec.js.map +1 -1
  170. package/dist/src/tui/events/index.d.ts +1 -1
  171. package/dist/src/tui/events/index.js.map +1 -1
  172. package/dist/src/tui/events/types.d.ts +6 -0
  173. package/dist/src/tui/events/types.js +1 -0
  174. package/dist/src/tui/events/types.js.map +1 -1
  175. package/dist/src/tui/hooks/index.d.ts +1 -1
  176. package/dist/src/tui/hooks/index.js +3 -3
  177. package/dist/src/tui/hooks/index.js.map +1 -1
  178. package/dist/src/tui/hooks/use-dashboard-state.d.ts +4 -1
  179. package/dist/src/tui/hooks/use-dashboard-state.js +26 -0
  180. package/dist/src/tui/hooks/use-dashboard-state.js.map +1 -1
  181. package/dist/src/tui/hooks/use-tick.d.ts +1 -0
  182. package/dist/src/tui/hooks/use-tick.js +15 -0
  183. package/dist/src/tui/hooks/use-tick.js.map +1 -0
  184. package/dist/src/tui/hooks/use-tick.spec.d.ts +1 -0
  185. package/dist/src/tui/hooks/use-tick.spec.js +70 -0
  186. package/dist/src/tui/hooks/use-tick.spec.js.map +1 -0
  187. package/dist/src/tui/index.d.ts +1 -1
  188. package/dist/src/tui/index.js +2 -2
  189. package/dist/src/tui/index.js.map +1 -1
  190. package/dist/src/tui-bundle.mjs +468 -133
  191. package/dist/tsconfig.build.tsbuildinfo +1 -1
  192. package/package.json +15 -14
  193. package/dist/src/tui/components/header-bar.pure.d.ts +0 -1
  194. package/dist/src/tui/components/header-bar.pure.js +0 -9
  195. package/dist/src/tui/components/header-bar.pure.js.map +0 -1
  196. package/dist/src/tui/hooks/use-clock.d.ts +0 -1
  197. package/dist/src/tui/hooks/use-clock.js +0 -16
  198. package/dist/src/tui/hooks/use-clock.js.map +0 -1
  199. package/dist/src/tui/hooks/use-clock.spec.js +0 -43
  200. package/dist/src/tui/hooks/use-clock.spec.js.map +0 -1
  201. /package/dist/src/tui/{hooks/use-clock.spec.d.ts → components/AgentDiscussionPanel.spec.d.ts} +0 -0
@@ -1371,12 +1371,12 @@ var require_eventemitter2 = __commonJS({
1371
1371
  });
1372
1372
 
1373
1373
  // src/tui/index.tsx
1374
- import React11 from "react";
1374
+ import React12 from "react";
1375
1375
  import { render } from "ink";
1376
1376
 
1377
1377
  // src/tui/dashboard-app.tsx
1378
- import React8, { useMemo as useMemo3 } from "react";
1379
- import { Box as Box8 } from "ink";
1378
+ import React9, { useMemo as useMemo4 } from "react";
1379
+ import { Box as Box9 } from "ink";
1380
1380
 
1381
1381
  // src/tui/hooks/use-terminal-size.ts
1382
1382
  import { useState, useEffect } from "react";
@@ -1431,8 +1431,21 @@ function useTerminalSize() {
1431
1431
  return { ...size, layoutMode: getLayoutMode(size.columns) };
1432
1432
  }
1433
1433
 
1434
+ // src/tui/hooks/use-tick.ts
1435
+ import { useState as useState2, useEffect as useEffect2 } from "react";
1436
+ function useTick(intervalMs = 1e3) {
1437
+ const [tick, setTick] = useState2(0);
1438
+ useEffect2(() => {
1439
+ const id = setInterval(() => {
1440
+ setTick((prev) => prev + 1);
1441
+ }, intervalMs);
1442
+ return () => clearInterval(id);
1443
+ }, [intervalMs]);
1444
+ return tick;
1445
+ }
1446
+
1434
1447
  // src/tui/hooks/use-dashboard-state.ts
1435
- import { useReducer, useEffect as useEffect2 } from "react";
1448
+ import { useReducer, useEffect as useEffect3 } from "react";
1436
1449
 
1437
1450
  // src/tui/events/event-bus.ts
1438
1451
  var import_eventemitter2 = __toESM(require_eventemitter2());
@@ -1483,7 +1496,8 @@ var TUI_EVENTS = Object.freeze({
1483
1496
  TOOL_INVOKED: "tool:invoked",
1484
1497
  OBJECTIVE_SET: "objective:set",
1485
1498
  SESSION_RESET: "session:reset",
1486
- CONTEXT_UPDATED: "context:updated"
1499
+ CONTEXT_UPDATED: "context:updated",
1500
+ DISCUSSION_ROUND_ADDED: "discussion:round-added"
1487
1501
  });
1488
1502
 
1489
1503
  // src/tui/events/parse-agent.ts
@@ -2824,6 +2838,22 @@ var TestStrategyConfigSchema = z3.object({
2824
2838
  var AIConfigSchema = z3.object({
2825
2839
  defaultModel: z3.string().optional(),
2826
2840
  primaryAgent: z3.string().optional(),
2841
+ /**
2842
+ * Override default dispatch strength for parallel specialist agents.
2843
+ * - "auto": Always dispatch specialists automatically
2844
+ * - "recommend": Suggest dispatch (default for PLAN/ACT)
2845
+ * - "skip": Do not dispatch specialists
2846
+ *
2847
+ * Default varies by mode: EVAL="auto", PLAN/ACT="recommend"
2848
+ *
2849
+ * @example
2850
+ * ```javascript
2851
+ * ai: {
2852
+ * dispatchStrength: 'auto',
2853
+ * }
2854
+ * ```
2855
+ */
2856
+ dispatchStrength: z3.enum(["auto", "recommend", "skip"]).optional(),
2827
2857
  /**
2828
2858
  * List of agent names to exclude from automatic resolution.
2829
2859
  * Useful for project-specific exclusions (e.g., exclude mobile-developer for backend-only projects).
@@ -2849,7 +2879,27 @@ var AIConfigSchema = z3.object({
2849
2879
  * }
2850
2880
  * ```
2851
2881
  */
2852
- maxIncludedSkills: z3.number().int().min(0).max(10).optional()
2882
+ maxIncludedSkills: z3.number().int().min(0).max(10).optional(),
2883
+ /**
2884
+ * Enable/disable automatic plan-reviewer gate after PLAN completion.
2885
+ * When enabled, parse_mode PLAN response includes a planReviewGate recommendation.
2886
+ * Default: true (enabled)
2887
+ *
2888
+ * @example
2889
+ * ```javascript
2890
+ * ai: {
2891
+ * planReviewGate: false, // disable plan review gate
2892
+ * }
2893
+ * ```
2894
+ */
2895
+ planReviewGate: z3.boolean().optional(),
2896
+ /**
2897
+ * Enable/disable agent discussion integration in EVAL mode.
2898
+ * When enabled, parse_mode EVAL response includes agentDiscussion config
2899
+ * for structuring specialist findings as AgentOpinion protocol.
2900
+ * Default: true (enabled)
2901
+ */
2902
+ agentDiscussion: z3.boolean().optional()
2853
2903
  });
2854
2904
  var AutoConfigSchema = z3.object({
2855
2905
  maxIterations: z3.number().int().min(1).max(10).default(3)
@@ -2877,6 +2927,8 @@ var CodingBuddyConfigSchema = z3.object({
2877
2927
  auto: AutoConfigSchema.optional(),
2878
2928
  // Context document limits (DoS prevention)
2879
2929
  context: ContextConfigSchema.optional(),
2930
+ // Upstream Repository Mapping (for cross-repo issue creation)
2931
+ upstreamRepos: z3.record(z3.string(), z3.string()).optional(),
2880
2932
  // Additional Context
2881
2933
  keyFiles: z3.array(z3.string()).optional(),
2882
2934
  avoid: z3.array(z3.string()).optional(),
@@ -3974,6 +4026,7 @@ function createInitialDashboardState() {
3974
4026
  tasks: [],
3975
4027
  eventLog: [],
3976
4028
  toolCalls: [],
4029
+ activityHistory: [],
3977
4030
  objectives: [],
3978
4031
  activeSkills: [],
3979
4032
  toolInvokeCount: 0,
@@ -3983,7 +4036,8 @@ function createInitialDashboardState() {
3983
4036
  contextDecisions: [],
3984
4037
  contextNotes: [],
3985
4038
  contextMode: null,
3986
- contextStatus: null
4039
+ contextStatus: null,
4040
+ discussionRounds: []
3987
4041
  };
3988
4042
  }
3989
4043
  function cloneAgents(agents) {
@@ -4075,7 +4129,8 @@ function dashboardReducer(state, action) {
4075
4129
  const entry = {
4076
4130
  timestamp: new Date(action.payload.timestamp).toTimeString().slice(0, 8),
4077
4131
  message: `${action.payload.toolName}${action.payload.agentId ? ` [${action.payload.agentId}]` : ""}`,
4078
- level: "info"
4132
+ level: "info",
4133
+ rawTimestamp: action.payload.timestamp
4079
4134
  };
4080
4135
  const base = state.eventLog.length >= EVENT_LOG_MAX ? state.eventLog.slice(1) : state.eventLog;
4081
4136
  const invokedAgentId = action.payload.agentId;
@@ -4103,11 +4158,24 @@ function dashboardReducer(state, action) {
4103
4158
  status: "completed"
4104
4159
  };
4105
4160
  const toolCallsBase = state.toolCalls.length >= TOOL_CALLS_MAX ? state.toolCalls.slice(1) : state.toolCalls;
4161
+ const sec = Math.floor(action.payload.timestamp / 1e3);
4162
+ const history = state.activityHistory;
4163
+ const last = history.length > 0 ? history[history.length - 1] : null;
4164
+ let activityHistory;
4165
+ if (last && last.timestamp === sec) {
4166
+ activityHistory = [
4167
+ ...history.slice(0, -1),
4168
+ { timestamp: sec, toolCalls: last.toolCalls + 1 }
4169
+ ];
4170
+ } else {
4171
+ activityHistory = [...history.slice(-59), { timestamp: sec, toolCalls: 1 }];
4172
+ }
4106
4173
  return {
4107
4174
  ...state,
4108
4175
  agents,
4109
4176
  eventLog: [...base, entry],
4110
4177
  toolCalls: [...toolCallsBase, toolCall],
4178
+ activityHistory,
4111
4179
  toolInvokeCount: state.toolInvokeCount + 1
4112
4180
  };
4113
4181
  }
@@ -4152,6 +4220,12 @@ function dashboardReducer(state, action) {
4152
4220
  contextStatus: status
4153
4221
  };
4154
4222
  }
4223
+ case "ADD_DISCUSSION_ROUND": {
4224
+ return {
4225
+ ...state,
4226
+ discussionRounds: [...state.discussionRounds, action.payload.round]
4227
+ };
4228
+ }
4155
4229
  case "SESSION_RESET":
4156
4230
  return createInitialDashboardState();
4157
4231
  case "CLEANUP_STALE_AGENTS": {
@@ -4181,7 +4255,7 @@ function dashboardReducer(state, action) {
4181
4255
  }
4182
4256
  function useDashboardState(eventBus) {
4183
4257
  const [state, dispatch] = useReducer(dashboardReducer, void 0, createInitialDashboardState);
4184
- useEffect2(() => {
4258
+ useEffect3(() => {
4185
4259
  if (!eventBus) return;
4186
4260
  const onActivated = (p) => dispatch({ type: "AGENT_ACTIVATED", payload: p });
4187
4261
  const onDeactivated = (p) => dispatch({ type: "AGENT_DEACTIVATED", payload: p });
@@ -4196,6 +4270,7 @@ function useDashboardState(eventBus) {
4196
4270
  const onObjectiveSet = (p) => dispatch({ type: "OBJECTIVE_SET", payload: p });
4197
4271
  const onSessionReset = (p) => dispatch({ type: "SESSION_RESET", payload: p });
4198
4272
  const onContextUpdated = (p) => dispatch({ type: "CONTEXT_UPDATED", payload: p });
4273
+ const onDiscussionRoundAdded = (p) => dispatch({ type: "ADD_DISCUSSION_ROUND", payload: p });
4199
4274
  eventBus.on(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4200
4275
  eventBus.on(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
4201
4276
  eventBus.on(TUI_EVENTS.MODE_CHANGED, onModeChanged);
@@ -4209,6 +4284,7 @@ function useDashboardState(eventBus) {
4209
4284
  eventBus.on(TUI_EVENTS.OBJECTIVE_SET, onObjectiveSet);
4210
4285
  eventBus.on(TUI_EVENTS.SESSION_RESET, onSessionReset);
4211
4286
  eventBus.on(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4287
+ eventBus.on(TUI_EVENTS.DISCUSSION_ROUND_ADDED, onDiscussionRoundAdded);
4212
4288
  return () => {
4213
4289
  eventBus.off(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4214
4290
  eventBus.off(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
@@ -4223,9 +4299,10 @@ function useDashboardState(eventBus) {
4223
4299
  eventBus.off(TUI_EVENTS.OBJECTIVE_SET, onObjectiveSet);
4224
4300
  eventBus.off(TUI_EVENTS.SESSION_RESET, onSessionReset);
4225
4301
  eventBus.off(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4302
+ eventBus.off(TUI_EVENTS.DISCUSSION_ROUND_ADDED, onDiscussionRoundAdded);
4226
4303
  };
4227
4304
  }, [eventBus]);
4228
- useEffect2(() => {
4305
+ useEffect3(() => {
4229
4306
  const cleanupInterval = setInterval(() => {
4230
4307
  dispatch({ type: "CLEANUP_STALE_AGENTS", payload: { now: Date.now(), ttlMs: 3e4 } });
4231
4308
  }, 1e4);
@@ -4348,6 +4425,59 @@ function getAgentAvatar(agentName) {
4348
4425
  return "\u{1F916}";
4349
4426
  }
4350
4427
 
4428
+ // src/tui/components/live.pure.ts
4429
+ function formatElapsed(startedAt, now) {
4430
+ const totalSec = Math.max(0, Math.floor((now - startedAt) / 1e3));
4431
+ const min = Math.floor(totalSec / 60);
4432
+ const sec = totalSec % 60;
4433
+ return min > 0 ? `${min}m ${sec}s` : `${sec}s`;
4434
+ }
4435
+ function formatRelativeTime(timestamp, now) {
4436
+ const diffSec = Math.max(0, Math.floor((now - timestamp) / 1e3));
4437
+ if (diffSec <= 2) return "just now";
4438
+ if (diffSec < 60) return `${diffSec}s ago`;
4439
+ const diffMin = Math.floor(diffSec / 60);
4440
+ if (diffMin < 60) return `${diffMin}m ago`;
4441
+ const diffHr = Math.floor(diffMin / 60);
4442
+ return `${diffHr}h ago`;
4443
+ }
4444
+ var SPINNER_FRAMES = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827";
4445
+ function spinnerFrame(tick) {
4446
+ return SPINNER_FRAMES[tick % 8];
4447
+ }
4448
+ function pulseIcon(tick) {
4449
+ return tick % 2 === 0 ? "\u25CF" : "\u25C9";
4450
+ }
4451
+ var SPARK_CHARS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
4452
+ function renderSparkline(samples, width) {
4453
+ if (samples.length === 0) return "";
4454
+ const visible = samples.slice(-width);
4455
+ const min = Math.min(...visible);
4456
+ const max = Math.max(...visible);
4457
+ const range = max - min;
4458
+ return visible.map((v) => {
4459
+ if (range === 0) return SPARK_CHARS[0];
4460
+ const idx = Math.round((v - min) / range * 7);
4461
+ return SPARK_CHARS[idx];
4462
+ }).join("");
4463
+ }
4464
+ function computeThroughput(samples) {
4465
+ if (samples.length <= 1) return "0.0/min";
4466
+ const first = samples[0];
4467
+ const last = samples[samples.length - 1];
4468
+ const durationMin = (last.timestamp - first.timestamp) / 60;
4469
+ if (durationMin <= 0) return "0.0/min";
4470
+ const totalCalls = samples.reduce((sum, s) => sum + s.toolCalls, 0);
4471
+ return `${(totalCalls / durationMin).toFixed(1)}/min`;
4472
+ }
4473
+ function formatTimeWithSeconds(now) {
4474
+ const d = new Date(now);
4475
+ const h = String(d.getHours()).padStart(2, "0");
4476
+ const m = String(d.getMinutes()).padStart(2, "0");
4477
+ const s = String(d.getSeconds()).padStart(2, "0");
4478
+ return `${h}:${m}:${s}`;
4479
+ }
4480
+
4351
4481
  // src/tui/components/HeaderBar.tsx
4352
4482
  var PROCESS_MODES = ["PLAN", "ACT", "EVAL"];
4353
4483
  function ModeFlow({ currentMode }) {
@@ -4358,17 +4488,23 @@ function ModeFlow({ currentMode }) {
4358
4488
  return /* @__PURE__ */ React.createElement(React.Fragment, { key: mode }, i > 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2192 "), isActive ? /* @__PURE__ */ React.createElement(Text, { color, bold: true }, "[", mode, "]") : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, mode));
4359
4489
  }));
4360
4490
  }
4361
- function StateIndicator({ globalState }) {
4362
- const icon = GLOBAL_STATE_ICONS[globalState];
4491
+ function StateIndicator({
4492
+ globalState,
4493
+ tick
4494
+ }) {
4495
+ const isRunning = globalState === "RUNNING";
4496
+ const icon = isRunning && tick !== void 0 ? spinnerFrame(tick) : GLOBAL_STATE_ICONS[globalState];
4363
4497
  const color = GLOBAL_STATE_COLORS[globalState];
4364
- return /* @__PURE__ */ React.createElement(Text, { color, bold: globalState === "RUNNING" || globalState === "ERROR" }, icon, " ", globalState);
4498
+ return /* @__PURE__ */ React.createElement(Text, { color, bold: isRunning || globalState === "ERROR" }, icon, " ", globalState);
4365
4499
  }
4366
4500
  function HeaderBar({
4367
4501
  workspace,
4368
4502
  currentMode,
4369
4503
  globalState,
4370
4504
  layoutMode,
4371
- width
4505
+ width,
4506
+ tick,
4507
+ now
4372
4508
  }) {
4373
4509
  if (layoutMode === "narrow") {
4374
4510
  return /* @__PURE__ */ React.createElement(
@@ -4384,7 +4520,8 @@ function HeaderBar({
4384
4520
  /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }),
4385
4521
  /* @__PURE__ */ React.createElement(ModeFlow, { currentMode }),
4386
4522
  /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }),
4387
- /* @__PURE__ */ React.createElement(StateIndicator, { globalState })
4523
+ /* @__PURE__ */ React.createElement(StateIndicator, { globalState, tick }),
4524
+ now !== void 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", formatTimeWithSeconds(now))
4388
4525
  );
4389
4526
  }
4390
4527
  return /* @__PURE__ */ React.createElement(
@@ -4396,7 +4533,7 @@ function HeaderBar({
4396
4533
  overflowX: "hidden",
4397
4534
  flexDirection: "row"
4398
4535
  },
4399
- /* @__PURE__ */ React.createElement(Box, { gap: 2, flexShrink: 1, minWidth: 0 }, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "\u27E8\u27E9 CODINGBUDDY AGENT DASHBOARD"), /* @__PURE__ */ React.createElement(ModeFlow, { currentMode }), /* @__PURE__ */ React.createElement(StateIndicator, { globalState })),
4536
+ /* @__PURE__ */ React.createElement(Box, { gap: 2, flexShrink: 1, minWidth: 0 }, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "\u27E8\u27E9 CODINGBUDDY AGENT DASHBOARD"), /* @__PURE__ */ React.createElement(ModeFlow, { currentMode }), /* @__PURE__ */ React.createElement(StateIndicator, { globalState, tick }), now !== void 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, formatTimeWithSeconds(now))),
4400
4537
  /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }),
4401
4538
  /* @__PURE__ */ React.createElement(Box, { flexShrink: 1, overflowX: "hidden" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true, wrap: "truncate" }, workspace))
4402
4539
  );
@@ -4915,10 +5052,18 @@ function FlowMap({
4915
5052
  layoutMode,
4916
5053
  width,
4917
5054
  height,
4918
- activeStage = null
5055
+ activeStage = null,
5056
+ tick,
5057
+ now
4919
5058
  }) {
4920
5059
  const contentWidth = Math.max(1, width - 2);
4921
5060
  const contentHeight = Math.max(1, height - 3);
5061
+ const hasRunningAgents = useMemo(
5062
+ () => [...agents.values()].some((a) => a.status === "running"),
5063
+ [agents]
5064
+ );
5065
+ const liveTick = hasRunningAgents ? tick ?? 0 : 0;
5066
+ const liveNow = hasRunningAgents ? now : void 0;
4922
5067
  const compactContent = useMemo(() => {
4923
5068
  if (layoutMode !== "narrow") return null;
4924
5069
  return renderFlowMapCompact(agents);
@@ -4928,6 +5073,20 @@ function FlowMap({
4928
5073
  const buf = layoutMode === "wide" ? renderFlowMap(agents, edges, contentWidth, contentHeight, activeStage) : renderFlowMapSimplified(agents, contentWidth, contentHeight);
4929
5074
  return buf.toLinesDirect();
4930
5075
  }, [agents, edges, contentWidth, contentHeight, layoutMode, activeStage]);
5076
+ const liveOverlays = useMemo(() => {
5077
+ if (liveNow === void 0) return null;
5078
+ const overlays = [];
5079
+ for (const [id, agent] of agents) {
5080
+ if (agent.status === "running" && agent.startedAt != null) {
5081
+ overlays.push({
5082
+ id,
5083
+ icon: pulseIcon(liveTick),
5084
+ elapsed: formatElapsed(agent.startedAt, liveNow)
5085
+ });
5086
+ }
5087
+ }
5088
+ return overlays.length > 0 ? overlays : null;
5089
+ }, [agents, liveTick, liveNow]);
4931
5090
  if (layoutMode === "narrow") {
4932
5091
  return /* @__PURE__ */ React2.createElement(
4933
5092
  Box2,
@@ -4939,7 +5098,8 @@ function FlowMap({
4939
5098
  height
4940
5099
  },
4941
5100
  /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
4942
- /* @__PURE__ */ React2.createElement(Text2, null, compactContent)
5101
+ /* @__PURE__ */ React2.createElement(Text2, null, compactContent),
5102
+ liveOverlays?.map((o) => /* @__PURE__ */ React2.createElement(Text2, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
4943
5103
  );
4944
5104
  }
4945
5105
  if (!lines) {
@@ -4965,13 +5125,162 @@ function FlowMap({
4965
5125
  height
4966
5126
  },
4967
5127
  /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
4968
- lines.map((row, y) => /* @__PURE__ */ React2.createElement(ColorRow, { key: y, cells: row }))
5128
+ lines.map((row, y) => /* @__PURE__ */ React2.createElement(ColorRow, { key: y, cells: row })),
5129
+ liveOverlays?.map((o) => /* @__PURE__ */ React2.createElement(Text2, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
5130
+ );
5131
+ }
5132
+
5133
+ // src/tui/components/AgentDiscussionPanel.tsx
5134
+ import React3, { useMemo as useMemo2 } from "react";
5135
+ import { Box as Box3, Text as Text3 } from "ink";
5136
+
5137
+ // src/collaboration/types.ts
5138
+ var STANCES = Object.freeze(["approve", "concern", "reject"]);
5139
+ var STANCE_ICONS = Object.freeze({
5140
+ approve: "\u2705",
5141
+ concern: "\u26A0\uFE0F",
5142
+ reject: "\u274C"
5143
+ });
5144
+ function calculateConsensus(opinions) {
5145
+ const approveCount = opinions.filter((o) => o.stance === "approve").length;
5146
+ const concernCount = opinions.filter((o) => o.stance === "concern").length;
5147
+ const rejectCount = opinions.filter((o) => o.stance === "reject").length;
5148
+ return {
5149
+ totalAgents: opinions.length,
5150
+ approveCount,
5151
+ concernCount,
5152
+ rejectCount,
5153
+ reached: opinions.length > 0 && rejectCount === 0,
5154
+ criticalCount: rejectCount
5155
+ };
5156
+ }
5157
+
5158
+ // src/tui/components/agent-discussion-panel.pure.ts
5159
+ var CROSS_REVIEW_VERBS = {
5160
+ approve: "agrees",
5161
+ concern: "notes",
5162
+ reject: "disagrees"
5163
+ };
5164
+ function renderOpinionLine(opinion, stanceHistory) {
5165
+ const avatar = getAgentAvatar(opinion.agentName);
5166
+ const icon = STANCE_ICONS[opinion.stance];
5167
+ let suffix = "";
5168
+ if (stanceHistory && stanceHistory.length > 1) {
5169
+ const lastStance = stanceHistory[stanceHistory.length - 1];
5170
+ const prevStance = stanceHistory[stanceHistory.length - 2];
5171
+ if (lastStance !== prevStance) {
5172
+ suffix = ` (${prevStance === "reject" || prevStance === "concern" ? "revised" : "alt"})`;
5173
+ }
5174
+ }
5175
+ return {
5176
+ text: `${avatar} ${opinion.agentName} ${icon} ${opinion.stance}${suffix}: "${opinion.reasoning}"`,
5177
+ type: "opinion"
5178
+ };
5179
+ }
5180
+ function renderCrossReviewLine(review, agentNames) {
5181
+ const fromAvatar = getAgentAvatar(agentNames[review.fromAgentId] ?? "unknown");
5182
+ const fromName = agentNames[review.fromAgentId] ?? review.fromAgentId;
5183
+ const toAvatar = getAgentAvatar(agentNames[review.toAgentId] ?? "unknown");
5184
+ const toName = agentNames[review.toAgentId] ?? review.toAgentId;
5185
+ const verb = CROSS_REVIEW_VERBS[review.stance];
5186
+ return {
5187
+ text: `${fromAvatar} ${fromName} \u2192 ${toAvatar} ${toName} ${verb}: "${review.comment}"`,
5188
+ type: "cross-review"
5189
+ };
5190
+ }
5191
+ function renderConsensusLine(consensus) {
5192
+ const icon = consensus.reached ? "\u2705" : "\u274C";
5193
+ return {
5194
+ text: `${icon} Consensus: ${consensus.approveCount}/${consensus.totalAgents} | Critical: ${consensus.criticalCount}`,
5195
+ type: "consensus"
5196
+ };
5197
+ }
5198
+ function buildAgentNameMap(rounds) {
5199
+ const map = {};
5200
+ for (const round of rounds) {
5201
+ for (const opinion of round.opinions) {
5202
+ map[opinion.agentId] = opinion.agentName;
5203
+ }
5204
+ }
5205
+ return map;
5206
+ }
5207
+ function buildStanceHistories(rounds) {
5208
+ const histories = {};
5209
+ for (const round of rounds) {
5210
+ for (const opinion of round.opinions) {
5211
+ if (!histories[opinion.agentId]) {
5212
+ histories[opinion.agentId] = [];
5213
+ }
5214
+ histories[opinion.agentId].push(opinion.stance);
5215
+ }
5216
+ }
5217
+ return histories;
5218
+ }
5219
+ function renderDiscussionPanel(rounds, _width) {
5220
+ if (rounds.length === 0) {
5221
+ return [{ text: "No agent discussion yet", type: "empty" }];
5222
+ }
5223
+ const lines = [];
5224
+ const agentNames = buildAgentNameMap(rounds);
5225
+ const stanceHistories = buildStanceHistories(rounds);
5226
+ const latestRound = rounds[rounds.length - 1];
5227
+ lines.push({ text: `\u2500\u2500 Agent Discussion \u2500\u2500`, type: "header" });
5228
+ for (const opinion of latestRound.opinions) {
5229
+ lines.push(renderOpinionLine(opinion, stanceHistories[opinion.agentId]));
5230
+ }
5231
+ for (const review of latestRound.crossReviews) {
5232
+ lines.push(renderCrossReviewLine(review, agentNames));
5233
+ }
5234
+ const consensus = calculateConsensus(latestRound.opinions);
5235
+ lines.push(renderConsensusLine(consensus));
5236
+ return lines;
5237
+ }
5238
+
5239
+ // src/tui/components/AgentDiscussionPanel.tsx
5240
+ var LINE_COLORS = {
5241
+ opinion: "white",
5242
+ "cross-review": "cyan",
5243
+ consensus: "green",
5244
+ header: "magenta",
5245
+ empty: "gray"
5246
+ };
5247
+ function AgentDiscussionPanel({
5248
+ rounds,
5249
+ width,
5250
+ height
5251
+ }) {
5252
+ const lines = useMemo2(
5253
+ () => renderDiscussionPanel(rounds, width),
5254
+ [rounds, width]
5255
+ );
5256
+ const maxLines = Math.max(0, height - 2);
5257
+ const visibleLines = lines.slice(0, maxLines);
5258
+ return /* @__PURE__ */ React3.createElement(
5259
+ Box3,
5260
+ {
5261
+ flexDirection: "column",
5262
+ width,
5263
+ height,
5264
+ borderStyle: "round",
5265
+ borderColor: BORDER_COLORS.panel
5266
+ },
5267
+ visibleLines.map((line, i) => /* @__PURE__ */ React3.createElement(
5268
+ Text3,
5269
+ {
5270
+ key: i,
5271
+ color: LINE_COLORS[line.type],
5272
+ bold: line.type === "header" || line.type === "consensus",
5273
+ dimColor: line.type === "empty",
5274
+ wrap: "truncate"
5275
+ },
5276
+ line.text
5277
+ ))
4969
5278
  );
4970
5279
  }
4971
5280
 
4972
5281
  // src/tui/components/FocusedAgentPanel.tsx
4973
- import React4 from "react";
4974
- import { Box as Box4, Text as Text4 } from "ink";
5282
+ import React5 from "react";
5283
+ import { Box as Box5, Text as Text5 } from "ink";
4975
5284
 
4976
5285
  // src/tui/components/focused-agent.pure.ts
4977
5286
  function formatObjective(objectives, maxLines = 3) {
@@ -4985,6 +5294,13 @@ function formatLogTail(events, maxLines = 10) {
4985
5294
  const tail = events.slice(-maxLines);
4986
5295
  return tail.map((e) => `${e.timestamp} ${e.message}`).join("\n");
4987
5296
  }
5297
+ function formatLogTailRelative(events, now, maxLines = 10) {
5298
+ const tail = events.slice(-maxLines);
5299
+ return tail.map((e) => {
5300
+ const ts = e.rawTimestamp != null ? formatRelativeTime(e.rawTimestamp, now) : e.timestamp;
5301
+ return `${ts} ${e.message}`;
5302
+ }).join("\n");
5303
+ }
4988
5304
  function formatSectionDivider(title, width = 50) {
4989
5305
  const prefix = "\u2500\u2500\u2500 ";
4990
5306
  const suffix = " ";
@@ -5010,8 +5326,8 @@ function formatEnhancedChecklist(tasks, maxItems = 6) {
5010
5326
  }
5011
5327
 
5012
5328
  // src/tui/components/ContextSection.tsx
5013
- import React3 from "react";
5014
- import { Box as Box3, Text as Text3 } from "ink";
5329
+ import React4 from "react";
5330
+ import { Box as Box4, Text as Text4 } from "ink";
5015
5331
 
5016
5332
  // src/tui/components/context-section.pure.ts
5017
5333
  function formatContextDecisions(decisions, maxItems = 5) {
@@ -5041,27 +5357,27 @@ function ContextSection({
5041
5357
  const hasDecisions = decisions.length > 0;
5042
5358
  const hasNotes = notes.length > 0;
5043
5359
  if (!hasDecisions && !hasNotes) return null;
5044
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, hasDecisions && /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true, bold: true }, "Decisions"), formatContextDecisions(decisions)?.split("\n").map((line, i) => /* @__PURE__ */ React3.createElement(Text3, { key: i, color: "cyan" }, line))), hasNotes && /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true, bold: true }, "Notes"), formatContextNotes(notes)?.split("\n").map((line, i) => /* @__PURE__ */ React3.createElement(Text3, { key: i, dimColor: true }, line))));
5360
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, hasDecisions && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true, bold: true }, "Decisions"), formatContextDecisions(decisions)?.split("\n").map((line, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i, color: "cyan" }, line))), hasNotes && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true, bold: true }, "Notes"), formatContextNotes(notes)?.split("\n").map((line, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i, dimColor: true }, line))));
5045
5361
  }
5046
5362
 
5047
5363
  // src/tui/components/FocusedAgentPanel.tsx
5048
5364
  function SectionDivider({ title }) {
5049
- return /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, formatSectionDivider(title));
5365
+ return /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, formatSectionDivider(title));
5050
5366
  }
5051
5367
  function LogSection({
5052
5368
  logs,
5053
5369
  eventLog
5054
5370
  }) {
5055
- if (!logs) return /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No events");
5371
+ if (!logs) return /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No events");
5056
5372
  const logLines = logs.split("\n");
5057
5373
  const offset = eventLog.length - logLines.length;
5058
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, logLines.map((line, i) => {
5374
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, logLines.map((line, i) => {
5059
5375
  const ev = eventLog[offset + i];
5060
5376
  const isError = ev?.level === "error";
5061
5377
  const spaceIdx = line.indexOf(" ");
5062
5378
  const timestamp = spaceIdx > 0 ? line.slice(0, spaceIdx) : line;
5063
5379
  const message = spaceIdx > 0 ? line.slice(spaceIdx + 1) : "";
5064
- return /* @__PURE__ */ React4.createElement(Box4, { key: i, gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, timestamp), /* @__PURE__ */ React4.createElement(Text4, { color: isError ? "red" : void 0 }, message));
5380
+ return /* @__PURE__ */ React5.createElement(Box5, { key: i, gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, timestamp), /* @__PURE__ */ React5.createElement(Text5, { color: isError ? "red" : void 0 }, message));
5065
5381
  }));
5066
5382
  }
5067
5383
  function FocusedAgentPanel({
@@ -5071,12 +5387,14 @@ function FocusedAgentPanel({
5071
5387
  eventLog,
5072
5388
  contextDecisions = [],
5073
5389
  contextNotes = [],
5390
+ tick,
5391
+ now,
5074
5392
  width,
5075
5393
  height
5076
5394
  }) {
5077
5395
  if (!agent) {
5078
- return /* @__PURE__ */ React4.createElement(
5079
- Box4,
5396
+ return /* @__PURE__ */ React5.createElement(
5397
+ Box5,
5080
5398
  {
5081
5399
  borderStyle: "single",
5082
5400
  borderColor: BORDER_COLORS.panel,
@@ -5084,7 +5402,7 @@ function FocusedAgentPanel({
5084
5402
  width,
5085
5403
  height
5086
5404
  },
5087
- /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No agent focused")
5405
+ /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No agent focused")
5088
5406
  );
5089
5407
  }
5090
5408
  const avatar = getAgentAvatar(agent.name);
@@ -5093,9 +5411,12 @@ function FocusedAgentPanel({
5093
5411
  const statusLabel = agent.status.toUpperCase();
5094
5412
  const progressBar = formatEnhancedProgressBar(agent.progress);
5095
5413
  const objective = formatObjective(objectives);
5096
- const logs = formatLogTail(eventLog);
5097
- return /* @__PURE__ */ React4.createElement(
5098
- Box4,
5414
+ const logs = now != null ? formatLogTailRelative(eventLog, now) : formatLogTail(eventLog);
5415
+ const isRunning = agent.status === "running";
5416
+ const showSpinner = isRunning && tick != null;
5417
+ const showElapsed = isRunning && now != null && agent.startedAt != null;
5418
+ return /* @__PURE__ */ React5.createElement(
5419
+ Box5,
5099
5420
  {
5100
5421
  borderStyle: "single",
5101
5422
  borderColor: BORDER_COLORS.panel,
@@ -5103,22 +5424,22 @@ function FocusedAgentPanel({
5103
5424
  width,
5104
5425
  height
5105
5426
  },
5106
- /* @__PURE__ */ React4.createElement(Box4, { gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, null, avatar), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, agent.name), /* @__PURE__ */ React4.createElement(Text4, { color: statusColor, bold: true }, icon), /* @__PURE__ */ React4.createElement(Text4, { color: statusColor }, statusLabel), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, agent.stage)),
5107
- /* @__PURE__ */ React4.createElement(Box4, { gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, progressBar.bar), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, progressBar.label)),
5108
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Objective" }),
5109
- objective ? /* @__PURE__ */ React4.createElement(Text4, null, objective) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No objectives"),
5110
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Skills" }),
5111
- activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i }, " ", s)) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No skills"),
5112
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Context" }),
5113
- contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React4.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No context"),
5114
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Event Log" }),
5115
- /* @__PURE__ */ React4.createElement(LogSection, { logs, eventLog })
5427
+ /* @__PURE__ */ React5.createElement(Box5, { gap: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, avatar), /* @__PURE__ */ React5.createElement(Text5, { bold: true }, agent.name), showSpinner ? /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, spinnerFrame(tick)) : /* @__PURE__ */ React5.createElement(Text5, { color: statusColor, bold: true }, icon), /* @__PURE__ */ React5.createElement(Text5, { color: statusColor }, statusLabel), showElapsed && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, formatElapsed(agent.startedAt, now)), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, agent.stage)),
5428
+ /* @__PURE__ */ React5.createElement(Box5, { gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, progressBar.bar), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, progressBar.label)),
5429
+ /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Objective" }),
5430
+ objective ? /* @__PURE__ */ React5.createElement(Text5, null, objective) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No objectives"),
5431
+ /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Skills" }),
5432
+ activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React5.createElement(Text5, { key: i }, " ", s)) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No skills"),
5433
+ /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Context" }),
5434
+ contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React5.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No context"),
5435
+ /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Event Log" }),
5436
+ /* @__PURE__ */ React5.createElement(LogSection, { logs, eventLog })
5116
5437
  );
5117
5438
  }
5118
5439
 
5119
5440
  // src/tui/components/ChecklistPanel.tsx
5120
- import React5 from "react";
5121
- import { Box as Box5, Text as Text5 } from "ink";
5441
+ import React6 from "react";
5442
+ import { Box as Box6, Text as Text6 } from "ink";
5122
5443
 
5123
5444
  // src/tui/components/checklist-panel.pure.ts
5124
5445
  function resolveChecklistTasks(tasks, contextDecisions, contextNotes) {
@@ -5146,8 +5467,8 @@ function ChecklistPanel({
5146
5467
  }) {
5147
5468
  const resolvedTasks = resolveChecklistTasks(tasks, contextDecisions, contextNotes);
5148
5469
  const checklist = formatEnhancedChecklist(resolvedTasks);
5149
- return /* @__PURE__ */ React5.createElement(
5150
- Box5,
5470
+ return /* @__PURE__ */ React6.createElement(
5471
+ Box6,
5151
5472
  {
5152
5473
  borderStyle: "single",
5153
5474
  borderColor: BORDER_COLORS.panel,
@@ -5155,17 +5476,17 @@ function ChecklistPanel({
5155
5476
  width,
5156
5477
  height
5157
5478
  },
5158
- /* @__PURE__ */ React5.createElement(Text5, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
5479
+ /* @__PURE__ */ React6.createElement(Text6, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
5159
5480
  checklist.split("\n").map((line, i) => {
5160
5481
  const isCompleted = line.includes("\u2714");
5161
- return /* @__PURE__ */ React5.createElement(Text5, { key: i, color: isCompleted ? "green" : void 0 }, line);
5482
+ return /* @__PURE__ */ React6.createElement(Text6, { key: i, color: isCompleted ? "green" : void 0 }, line);
5162
5483
  })
5163
5484
  );
5164
5485
  }
5165
5486
 
5166
5487
  // src/tui/components/StageHealthBar.tsx
5167
- import React6 from "react";
5168
- import { Box as Box6, Text as Text6 } from "ink";
5488
+ import React7 from "react";
5489
+ import { Box as Box7, Text as Text7 } from "ink";
5169
5490
 
5170
5491
  // src/tui/components/stage-health.pure.ts
5171
5492
  function formatCount(n) {
@@ -5233,29 +5554,29 @@ function StageStatDisplay({
5233
5554
  const parts = [];
5234
5555
  if (stats.running > 0)
5235
5556
  parts.push(
5236
- /* @__PURE__ */ React6.createElement(Text6, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5557
+ /* @__PURE__ */ React7.createElement(Text7, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5237
5558
  );
5238
5559
  if (stats.blocked > 0)
5239
5560
  parts.push(
5240
- /* @__PURE__ */ React6.createElement(Text6, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5561
+ /* @__PURE__ */ React7.createElement(Text7, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5241
5562
  );
5242
5563
  if (stats.waiting > 0)
5243
5564
  parts.push(
5244
- /* @__PURE__ */ React6.createElement(Text6, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5565
+ /* @__PURE__ */ React7.createElement(Text7, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5245
5566
  );
5246
5567
  if (stats.done > 0)
5247
5568
  parts.push(
5248
- /* @__PURE__ */ React6.createElement(Text6, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5569
+ /* @__PURE__ */ React7.createElement(Text7, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5249
5570
  );
5250
5571
  if (stats.error > 0)
5251
5572
  parts.push(
5252
- /* @__PURE__ */ React6.createElement(Text6, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5573
+ /* @__PURE__ */ React7.createElement(Text7, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5253
5574
  );
5254
5575
  if (parts.length === 0)
5255
5576
  parts.push(
5256
- /* @__PURE__ */ React6.createElement(Text6, { key: "idle", dimColor: true }, " ", "idle")
5577
+ /* @__PURE__ */ React7.createElement(Text7, { key: "idle", dimColor: true }, " ", "idle")
5257
5578
  );
5258
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color, bold: true }, mode, ":"), parts);
5579
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color, bold: true }, mode, ":"), parts);
5259
5580
  }
5260
5581
  function StageHealthBar({
5261
5582
  stageHealth,
@@ -5263,23 +5584,30 @@ function StageHealthBar({
5263
5584
  toolCount,
5264
5585
  agentCount,
5265
5586
  skillCount,
5266
- width
5587
+ width,
5588
+ activityHistory,
5589
+ tick: _tick,
5590
+ now: _now
5267
5591
  }) {
5268
- return /* @__PURE__ */ React6.createElement(
5269
- Box6,
5592
+ const hasActivity = activityHistory && activityHistory.length > 0;
5593
+ return /* @__PURE__ */ React7.createElement(
5594
+ Box7,
5270
5595
  {
5271
5596
  borderStyle: "double",
5272
5597
  borderColor: BORDER_COLORS.panel,
5273
5598
  width,
5274
5599
  flexDirection: "column"
5275
5600
  },
5276
- /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Box6, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React6.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React6.createElement(Box6, { flexGrow: 1 }), /* @__PURE__ */ React6.createElement(Box6, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React6.createElement(Text6, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u{1F916} ", formatCount(agentCount)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2699 ", formatCount(skillCount)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u{1F527} ", formatCount(toolCount))))
5601
+ /* @__PURE__ */ React7.createElement(Box7, null, hasActivity && /* @__PURE__ */ React7.createElement(Box7, { gap: 1, marginRight: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, renderSparkline(
5602
+ activityHistory.map((s) => s.toolCalls),
5603
+ 10
5604
+ )), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, computeThroughput(activityHistory))), /* @__PURE__ */ React7.createElement(Box7, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React7.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React7.createElement(Box7, { flexGrow: 1 }), /* @__PURE__ */ React7.createElement(Box7, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u{1F916} ", formatCount(agentCount)), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u2699 ", formatCount(skillCount)), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u{1F527} ", formatCount(toolCount))))
5277
5605
  );
5278
5606
  }
5279
5607
 
5280
5608
  // src/tui/components/ActivityVisualizer.tsx
5281
- import React7, { useMemo as useMemo2 } from "react";
5282
- import { Box as Box7, Text as Text7 } from "ink";
5609
+ import React8, { useMemo as useMemo3 } from "react";
5610
+ import { Box as Box8, Text as Text8 } from "ink";
5283
5611
 
5284
5612
  // src/tui/utils/display-width.ts
5285
5613
  function isWide(code) {
@@ -5448,11 +5776,11 @@ function ActivityVisualizer({
5448
5776
  const treeWidth = Math.floor(width * 0.6);
5449
5777
  const cardWidth = width - treeWidth;
5450
5778
  const contentHeight = Math.max(1, height - 2);
5451
- const treeLines = useMemo2(
5779
+ const treeLines = useMemo3(
5452
5780
  () => renderAgentTree(agents, edges, activeSkills, Math.max(1, treeWidth - 2), contentHeight),
5453
5781
  [agents, edges, activeSkills, treeWidth, contentHeight]
5454
5782
  );
5455
- const cardLines = useMemo2(
5783
+ const cardLines = useMemo3(
5456
5784
  () => renderAgentStatusCard(
5457
5785
  focusedAgent,
5458
5786
  currentMode,
@@ -5464,10 +5792,10 @@ function ActivityVisualizer({
5464
5792
  [focusedAgent, currentMode, objectives, activeSkills, cardWidth, contentHeight]
5465
5793
  );
5466
5794
  if (width <= 0 || height <= 0) {
5467
- return /* @__PURE__ */ React7.createElement(Box7, null);
5795
+ return /* @__PURE__ */ React8.createElement(Box8, null);
5468
5796
  }
5469
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row", width, height }, /* @__PURE__ */ React7.createElement(
5470
- Box7,
5797
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width, height }, /* @__PURE__ */ React8.createElement(
5798
+ Box8,
5471
5799
  {
5472
5800
  borderStyle: "single",
5473
5801
  borderColor: BORDER_COLORS.panel,
@@ -5475,9 +5803,9 @@ function ActivityVisualizer({
5475
5803
  width: treeWidth,
5476
5804
  height
5477
5805
  },
5478
- treeLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
5479
- ), /* @__PURE__ */ React7.createElement(
5480
- Box7,
5806
+ treeLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i }, line))
5807
+ ), /* @__PURE__ */ React8.createElement(
5808
+ Box8,
5481
5809
  {
5482
5810
  borderStyle: "single",
5483
5811
  borderColor: BORDER_COLORS.panel,
@@ -5485,7 +5813,7 @@ function ActivityVisualizer({
5485
5813
  width: cardWidth,
5486
5814
  height
5487
5815
  },
5488
- cardLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
5816
+ cardLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i }, line))
5489
5817
  ));
5490
5818
  }
5491
5819
 
@@ -5571,25 +5899,29 @@ function DashboardApp({
5571
5899
  workspace: workspaceProp
5572
5900
  }) {
5573
5901
  const { columns, rows, layoutMode } = useTerminalSize();
5902
+ const tick = useTick(1e3);
5903
+ const now = useMemo4(() => Date.now(), [tick]);
5574
5904
  const internalState = useDashboardState(externalState ? void 0 : eventBus);
5575
5905
  const state = externalState ?? internalState;
5576
5906
  const focusedAgent = state.focusedAgentId ? state.agents.get(state.focusedAgentId) ?? null : null;
5577
- const stageHealth = useMemo3(() => computeStageHealth(state.agents), [state.agents]);
5578
- const bottlenecks = useMemo3(() => detectBottlenecks(state.eventLog), [state.eventLog]);
5579
- const grid = useMemo3(
5907
+ const stageHealth = useMemo4(() => computeStageHealth(state.agents), [state.agents]);
5908
+ const bottlenecks = useMemo4(() => detectBottlenecks(state.eventLog), [state.eventLog]);
5909
+ const grid = useMemo4(
5580
5910
  () => computeGridLayout(columns, rows, layoutMode),
5581
5911
  [columns, rows, layoutMode]
5582
5912
  );
5583
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React8.createElement(
5913
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React9.createElement(
5584
5914
  HeaderBar,
5585
5915
  {
5586
5916
  workspace: workspaceProp ?? state.workspace,
5587
5917
  currentMode: state.currentMode,
5588
5918
  globalState: state.globalState,
5589
5919
  layoutMode,
5590
- width: grid.header.width
5920
+ width: grid.header.width,
5921
+ tick,
5922
+ now
5591
5923
  }
5592
- ), layoutMode === "narrow" ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React8.createElement(
5924
+ ), layoutMode === "narrow" ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React9.createElement(
5593
5925
  ChecklistPanel,
5594
5926
  {
5595
5927
  tasks: state.tasks,
@@ -5598,7 +5930,7 @@ function DashboardApp({
5598
5930
  width: grid.checklistPanel.width,
5599
5931
  height: grid.checklistPanel.height
5600
5932
  }
5601
- ), /* @__PURE__ */ React8.createElement(
5933
+ ), /* @__PURE__ */ React9.createElement(
5602
5934
  FocusedAgentPanel,
5603
5935
  {
5604
5936
  agent: focusedAgent,
@@ -5608,9 +5940,18 @@ function DashboardApp({
5608
5940
  contextDecisions: state.contextDecisions,
5609
5941
  contextNotes: state.contextNotes,
5610
5942
  width: grid.focusedAgent.width,
5611
- height: grid.focusedAgent.height
5943
+ height: grid.focusedAgent.height,
5944
+ tick,
5945
+ now
5612
5946
  }
5613
- ), /* @__PURE__ */ React8.createElement(
5947
+ ), state.discussionRounds.length > 0 ? /* @__PURE__ */ React9.createElement(
5948
+ AgentDiscussionPanel,
5949
+ {
5950
+ rounds: state.discussionRounds,
5951
+ width: grid.flowMap.width,
5952
+ height: grid.flowMap.height
5953
+ }
5954
+ ) : /* @__PURE__ */ React9.createElement(
5614
5955
  FlowMap,
5615
5956
  {
5616
5957
  agents: state.agents,
@@ -5618,16 +5959,25 @@ function DashboardApp({
5618
5959
  layoutMode,
5619
5960
  width: grid.flowMap.width,
5620
5961
  height: grid.flowMap.height,
5621
- activeStage: state.currentMode
5962
+ activeStage: state.currentMode,
5963
+ tick,
5964
+ now
5622
5965
  }
5623
- )) : /* @__PURE__ */ React8.createElement(
5624
- Box8,
5966
+ )) : /* @__PURE__ */ React9.createElement(
5967
+ Box9,
5625
5968
  {
5626
5969
  flexDirection: "row",
5627
5970
  width: grid.total.width,
5628
5971
  height: grid.checklistPanel.height + grid.focusedAgent.height
5629
5972
  },
5630
- /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React8.createElement(
5973
+ /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.flowMap.width }, state.discussionRounds.length > 0 ? /* @__PURE__ */ React9.createElement(
5974
+ AgentDiscussionPanel,
5975
+ {
5976
+ rounds: state.discussionRounds,
5977
+ width: grid.flowMap.width,
5978
+ height: grid.flowMap.height
5979
+ }
5980
+ ) : /* @__PURE__ */ React9.createElement(
5631
5981
  FlowMap,
5632
5982
  {
5633
5983
  agents: state.agents,
@@ -5635,9 +5985,11 @@ function DashboardApp({
5635
5985
  layoutMode,
5636
5986
  width: grid.flowMap.width,
5637
5987
  height: grid.flowMap.height,
5638
- activeStage: state.currentMode
5988
+ activeStage: state.currentMode,
5989
+ tick,
5990
+ now
5639
5991
  }
5640
- ), /* @__PURE__ */ React8.createElement(
5992
+ ), /* @__PURE__ */ React9.createElement(
5641
5993
  ActivityVisualizer,
5642
5994
  {
5643
5995
  currentMode: state.currentMode,
@@ -5650,7 +6002,7 @@ function DashboardApp({
5650
6002
  height: grid.monitorPanel.height
5651
6003
  }
5652
6004
  )),
5653
- /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React8.createElement(
6005
+ /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React9.createElement(
5654
6006
  ChecklistPanel,
5655
6007
  {
5656
6008
  tasks: state.tasks,
@@ -5659,7 +6011,7 @@ function DashboardApp({
5659
6011
  width: grid.checklistPanel.width,
5660
6012
  height: grid.checklistPanel.height
5661
6013
  }
5662
- ), /* @__PURE__ */ React8.createElement(
6014
+ ), /* @__PURE__ */ React9.createElement(
5663
6015
  FocusedAgentPanel,
5664
6016
  {
5665
6017
  agent: focusedAgent,
@@ -5669,10 +6021,12 @@ function DashboardApp({
5669
6021
  contextDecisions: state.contextDecisions,
5670
6022
  contextNotes: state.contextNotes,
5671
6023
  width: grid.focusedAgent.width,
5672
- height: grid.focusedAgent.height
6024
+ height: grid.focusedAgent.height,
6025
+ tick,
6026
+ now
5673
6027
  }
5674
6028
  ))
5675
- ), /* @__PURE__ */ React8.createElement(
6029
+ ), /* @__PURE__ */ React9.createElement(
5676
6030
  StageHealthBar,
5677
6031
  {
5678
6032
  stageHealth,
@@ -5680,18 +6034,21 @@ function DashboardApp({
5680
6034
  toolCount: state.toolInvokeCount,
5681
6035
  agentCount: state.agentActivateCount,
5682
6036
  skillCount: state.skillInvokeCount,
5683
- width: grid.stageHealth.width
6037
+ width: grid.stageHealth.width,
6038
+ activityHistory: state.activityHistory,
6039
+ tick,
6040
+ now
5684
6041
  }
5685
6042
  ));
5686
6043
  }
5687
6044
 
5688
6045
  // src/tui/multi-session-app.tsx
5689
- import React10, { useMemo as useMemo4, useCallback as useCallback2 } from "react";
5690
- import { Box as Box10, useInput } from "ink";
6046
+ import React11, { useMemo as useMemo5, useCallback as useCallback2 } from "react";
6047
+ import { Box as Box11, useInput } from "ink";
5691
6048
  import * as path8 from "path";
5692
6049
 
5693
6050
  // src/tui/hooks/use-multi-session-state.ts
5694
- import { useState as useState2, useCallback, useEffect as useEffect3, useRef } from "react";
6051
+ import { useState as useState3, useCallback, useEffect as useEffect4, useRef } from "react";
5695
6052
  function toSessionState(managed) {
5696
6053
  return {
5697
6054
  pid: managed.instance.pid,
@@ -5711,10 +6068,10 @@ function getPidArray(sessions) {
5711
6068
  return [...sessions.keys()];
5712
6069
  }
5713
6070
  function useMultiSessionState(manager) {
5714
- const [sessions, setSessions] = useState2(
6071
+ const [sessions, setSessions] = useState3(
5715
6072
  () => buildInitialSessions(manager)
5716
6073
  );
5717
- const [activeSessionPid, setActiveSessionPid] = useState2(() => {
6074
+ const [activeSessionPid, setActiveSessionPid] = useState3(() => {
5718
6075
  const initial = manager.getSessions();
5719
6076
  return initial.length > 0 ? initial[0].instance.pid : null;
5720
6077
  });
@@ -5789,7 +6146,7 @@ function useMultiSessionState(manager) {
5789
6146
  eventCleanupRef.current.delete(pid);
5790
6147
  }
5791
6148
  }, []);
5792
- useEffect3(() => {
6149
+ useEffect4(() => {
5793
6150
  for (const managed of manager.getSessions()) {
5794
6151
  subscribeSession(managed.instance.pid, managed.eventBus);
5795
6152
  }
@@ -5843,8 +6200,8 @@ function useMultiSessionState(manager) {
5843
6200
  }
5844
6201
 
5845
6202
  // src/tui/components/SessionTabBar.tsx
5846
- import React9 from "react";
5847
- import { Box as Box9, Text as Text8 } from "ink";
6203
+ import React10 from "react";
6204
+ import { Box as Box10, Text as Text9 } from "ink";
5848
6205
 
5849
6206
  // src/tui/components/session-tab-bar.pure.ts
5850
6207
  var STATUS_ICONS2 = {
@@ -5898,14 +6255,14 @@ function SessionTabBar({
5898
6255
  if (formatted === "") {
5899
6256
  return null;
5900
6257
  }
5901
- return /* @__PURE__ */ React9.createElement(Box9, { width, height: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, formatted));
6258
+ return /* @__PURE__ */ React10.createElement(Box10, { width, height: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, formatted));
5902
6259
  }
5903
6260
 
5904
6261
  // src/tui/multi-session-app.tsx
5905
6262
  function MultiSessionApp({ manager }) {
5906
6263
  const { columns, layoutMode } = useTerminalSize();
5907
6264
  const { sessions, activeSessionPid, switchNext, switchPrev, switchByIndex } = useMultiSessionState(manager);
5908
- const tabs = useMemo4(() => {
6265
+ const tabs = useMemo5(() => {
5909
6266
  let index = 0;
5910
6267
  const result = [];
5911
6268
  for (const [pid, sessionState] of sessions) {
@@ -5920,13 +6277,13 @@ function MultiSessionApp({ manager }) {
5920
6277
  }
5921
6278
  return result;
5922
6279
  }, [sessions, activeSessionPid]);
5923
- const activeEventBus = useMemo4(() => {
6280
+ const activeEventBus = useMemo5(() => {
5924
6281
  if (activeSessionPid === null) return void 0;
5925
6282
  const managedSessions = manager.getSessions();
5926
6283
  const found = managedSessions.find((s) => s.instance.pid === activeSessionPid);
5927
6284
  return found?.eventBus;
5928
6285
  }, [manager, activeSessionPid]);
5929
- const activeProjectRoot = useMemo4(() => {
6286
+ const activeProjectRoot = useMemo5(() => {
5930
6287
  if (activeSessionPid === null) return void 0;
5931
6288
  return sessions.get(activeSessionPid)?.projectRoot;
5932
6289
  }, [sessions, activeSessionPid]);
@@ -5948,7 +6305,7 @@ function MultiSessionApp({ manager }) {
5948
6305
  [switchNext, switchPrev, switchByIndex]
5949
6306
  );
5950
6307
  useInput(handleInput);
5951
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React10.createElement(
6308
+ return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React11.createElement(
5952
6309
  DashboardApp,
5953
6310
  {
5954
6311
  key: activeSessionPid ?? "none",
@@ -5993,35 +6350,13 @@ function createDefaultAgentState(params) {
5993
6350
  return state;
5994
6351
  }
5995
6352
 
5996
- // src/tui/hooks/use-clock.ts
5997
- import { useState as useState3, useEffect as useEffect4 } from "react";
5998
-
5999
- // src/tui/components/header-bar.pure.ts
6000
- function formatTime(date) {
6001
- const hours = String(date.getHours()).padStart(2, "0");
6002
- const minutes = String(date.getMinutes()).padStart(2, "0");
6003
- return `${hours}:${minutes}`;
6004
- }
6005
-
6006
- // src/tui/hooks/use-clock.ts
6007
- function useClock() {
6008
- const [time, setTime] = useState3(() => formatTime(/* @__PURE__ */ new Date()));
6009
- useEffect4(() => {
6010
- const interval = setInterval(() => {
6011
- setTime(formatTime(/* @__PURE__ */ new Date()));
6012
- }, 1e3);
6013
- return () => clearInterval(interval);
6014
- }, []);
6015
- return time;
6016
- }
6017
-
6018
6353
  // src/tui/index.tsx
6019
6354
  function startTui(options) {
6020
6355
  const renderOptions = options.stdout ? { stdout: options.stdout } : {};
6021
- return render(/* @__PURE__ */ React11.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
6356
+ return render(/* @__PURE__ */ React12.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
6022
6357
  }
6023
6358
  function renderMultiSession(options) {
6024
- return render(/* @__PURE__ */ React11.createElement(MultiSessionApp, { manager: options.manager }));
6359
+ return render(/* @__PURE__ */ React12.createElement(MultiSessionApp, { manager: options.manager }));
6025
6360
  }
6026
6361
  export {
6027
6362
  AGENT_STATUSES,
@@ -6035,9 +6370,9 @@ export {
6035
6370
  getLayoutMode,
6036
6371
  renderMultiSession,
6037
6372
  startTui,
6038
- useClock,
6039
6373
  useDashboardState,
6040
- useTerminalSize
6374
+ useTerminalSize,
6375
+ useTick
6041
6376
  };
6042
6377
  /*! Bundled license information:
6043
6378