open-coleslaw 0.5.2 → 0.5.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.
package/dist/index.js CHANGED
@@ -964,6 +964,38 @@ ${previousMinutes.content}
964
964
  }
965
965
  };
966
966
 
967
+ // src/orchestrator/event-bus.ts
968
+ import { EventEmitter } from "events";
969
+ var EventBus = class {
970
+ emitter = new EventEmitter();
971
+ constructor() {
972
+ this.emitter.setMaxListeners(50);
973
+ }
974
+ emitAgentEvent(event) {
975
+ this.emitter.emit("agent_event", event);
976
+ }
977
+ on(event, listener) {
978
+ this.emitter.on(event, listener);
979
+ }
980
+ off(event, listener) {
981
+ this.emitter.off(event, listener);
982
+ }
983
+ once(event, listener) {
984
+ this.emitter.once(event, listener);
985
+ }
986
+ removeAllListeners(event) {
987
+ if (event) {
988
+ this.emitter.removeAllListeners(event);
989
+ } else {
990
+ this.emitter.removeAllListeners();
991
+ }
992
+ }
993
+ listenerCount(event) {
994
+ return this.emitter.listenerCount(event);
995
+ }
996
+ };
997
+ var eventBus = new EventBus();
998
+
967
999
  // src/tools/start-meeting.ts
968
1000
  var startMeetingSchema = {
969
1001
  topic: z.string().describe("Meeting topic"),
@@ -987,6 +1019,14 @@ async function startMeetingHandler({
987
1019
  departments,
988
1020
  meetingType
989
1021
  });
1022
+ eventBus.emitAgentEvent({
1023
+ kind: "meeting_started",
1024
+ meetingId: result.meetingId,
1025
+ meetingType: result.meetingType,
1026
+ topic: result.topic,
1027
+ agenda: [...result.agenda],
1028
+ participants: [...result.departments]
1029
+ });
990
1030
  return {
991
1031
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
992
1032
  };
@@ -1244,40 +1284,6 @@ async function getAgentTreeHandler() {
1244
1284
 
1245
1285
  // src/tools/respond-to-mention.ts
1246
1286
  import { z as z5 } from "zod";
1247
-
1248
- // src/orchestrator/event-bus.ts
1249
- import { EventEmitter } from "events";
1250
- var EventBus = class {
1251
- emitter = new EventEmitter();
1252
- constructor() {
1253
- this.emitter.setMaxListeners(50);
1254
- }
1255
- emitAgentEvent(event) {
1256
- this.emitter.emit("agent_event", event);
1257
- }
1258
- on(event, listener) {
1259
- this.emitter.on(event, listener);
1260
- }
1261
- off(event, listener) {
1262
- this.emitter.off(event, listener);
1263
- }
1264
- once(event, listener) {
1265
- this.emitter.once(event, listener);
1266
- }
1267
- removeAllListeners(event) {
1268
- if (event) {
1269
- this.emitter.removeAllListeners(event);
1270
- } else {
1271
- this.emitter.removeAllListeners();
1272
- }
1273
- }
1274
- listenerCount(event) {
1275
- return this.emitter.listenerCount(event);
1276
- }
1277
- };
1278
- var eventBus = new EventBus();
1279
-
1280
- // src/tools/respond-to-mention.ts
1281
1287
  var respondToMentionSchema = {
1282
1288
  mentionId: z5.string().describe("ID of the mention to respond to"),
1283
1289
  decision: z5.string().describe("The decision made by the user"),
@@ -2765,21 +2771,36 @@ var MeetingRunner = class {
2765
2771
  // src/tools/add-transcript.ts
2766
2772
  var addTranscriptSchema = {
2767
2773
  meetingId: z13.string().describe("Meeting ID"),
2768
- speakerRole: z13.string().describe('Role of the speaker (e.g. "Architecture Lead", "Engineering Lead")'),
2769
- agendaItemIndex: z13.number().describe("Agenda item index (0-based). Use -1 for opening statements, -2 for synthesis"),
2774
+ speakerRole: z13.string().describe('Role of the speaker (e.g. "planner", "architect", "engineer", "user")'),
2775
+ agendaItemIndex: z13.number().describe("Agenda item index (0-based). Use -1 for opening statements, -2 for synthesis, -3 for user interjections."),
2770
2776
  roundNumber: z13.number().describe("Discussion round number (0 for opening/synthesis)"),
2771
- content: z13.string().describe("The transcript content from the speaker")
2777
+ content: z13.string().describe("The transcript content from the speaker"),
2778
+ stance: z13.enum(["agree", "disagree", "speaking"]).optional().describe("Optional stance \u2014 used when the speaker is answering a consensus check.")
2772
2779
  };
2773
2780
  async function addTranscriptHandler({
2774
2781
  meetingId,
2775
2782
  speakerRole,
2776
2783
  agendaItemIndex,
2777
2784
  roundNumber,
2778
- content
2785
+ content,
2786
+ stance
2779
2787
  }) {
2780
2788
  try {
2781
2789
  const runner = new MeetingRunner(meetingId);
2782
2790
  const entry = runner.addTranscript(speakerRole, agendaItemIndex, roundNumber, content);
2791
+ eventBus.emitAgentEvent({
2792
+ kind: "transcript_added",
2793
+ meetingId,
2794
+ comment: {
2795
+ id: entry.id,
2796
+ speakerRole,
2797
+ agendaItemIndex,
2798
+ roundNumber,
2799
+ content,
2800
+ stance: stance ?? "speaking",
2801
+ createdAt: entry.createdAt
2802
+ }
2803
+ });
2783
2804
  const result = {
2784
2805
  success: true,
2785
2806
  entryId: entry.id,
@@ -2814,6 +2835,16 @@ async function generateMinutesHandler({
2814
2835
  const runner = new MeetingRunner(meetingId);
2815
2836
  const minutesId = await runner.generateMinutes();
2816
2837
  const minutes = getMinutesByMeeting(meetingId);
2838
+ if (minutes) {
2839
+ const decisions = extractSection(minutes.content, "Decisions");
2840
+ const actionItems = (minutes.actionItems ?? []).map((a) => a.title || a.description);
2841
+ eventBus.emitAgentEvent({
2842
+ kind: "minutes_finalized",
2843
+ meetingId,
2844
+ decisions,
2845
+ actionItems
2846
+ });
2847
+ }
2817
2848
  const result = {
2818
2849
  minutesId,
2819
2850
  meetingId,
@@ -2832,6 +2863,22 @@ async function generateMinutesHandler({
2832
2863
  };
2833
2864
  }
2834
2865
  }
2866
+ function extractSection(md, heading) {
2867
+ const lines = md.split("\n");
2868
+ const out = [];
2869
+ let inSection = false;
2870
+ for (const line of lines) {
2871
+ const h = line.match(/^##\s+(.+)$/);
2872
+ if (h) {
2873
+ inSection = h[1].trim().toLowerCase().startsWith(heading.toLowerCase());
2874
+ continue;
2875
+ }
2876
+ if (!inSection) continue;
2877
+ const m = line.match(/^\s*[-*]\s+(.*\S)\s*$/);
2878
+ if (m) out.push(m[1]);
2879
+ }
2880
+ return out;
2881
+ }
2835
2882
 
2836
2883
  // src/server.ts
2837
2884
  function createServer() {
@@ -3104,11 +3151,61 @@ html, body {
3104
3151
  letter-spacing: 0.5px;
3105
3152
  }
3106
3153
  .past-meeting {
3107
- padding: 6px 10px;
3154
+ padding: 8px 10px;
3108
3155
  font-size: 11px;
3109
3156
  color: var(--text2);
3110
3157
  border-left: 2px solid var(--border);
3111
3158
  margin-bottom: 4px;
3159
+ cursor: pointer;
3160
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
3161
+ border-radius: 0 4px 4px 0;
3162
+ }
3163
+ .past-meeting:hover {
3164
+ background: rgba(0,240,255,0.04);
3165
+ border-left-color: var(--cyan);
3166
+ color: var(--text);
3167
+ }
3168
+ .past-meeting.viewing {
3169
+ background: rgba(0,240,255,0.08);
3170
+ border-left-color: var(--cyan);
3171
+ color: var(--cyan);
3172
+ }
3173
+ .past-meeting .pm-title {
3174
+ font-weight: 500;
3175
+ display: block;
3176
+ margin-bottom: 2px;
3177
+ }
3178
+ .past-meeting .pm-meta {
3179
+ font-size: 10px;
3180
+ opacity: 0.7;
3181
+ }
3182
+
3183
+ /* viewing-past banner in the main area */
3184
+ #viewing-banner {
3185
+ display: none;
3186
+ padding: 8px 24px;
3187
+ background: rgba(168,85,247,0.1);
3188
+ border-bottom: 1px solid rgba(168,85,247,0.3);
3189
+ font-size: 12px;
3190
+ color: var(--purple);
3191
+ display: none;
3192
+ align-items: center;
3193
+ justify-content: space-between;
3194
+ }
3195
+ #viewing-banner.show { display: flex; }
3196
+ #viewing-banner button {
3197
+ background: transparent;
3198
+ border: 1px solid var(--purple);
3199
+ color: var(--purple);
3200
+ font-family: var(--font);
3201
+ font-size: 11px;
3202
+ padding: 4px 12px;
3203
+ border-radius: 4px;
3204
+ cursor: pointer;
3205
+ }
3206
+ #viewing-banner button:hover {
3207
+ background: var(--purple);
3208
+ color: var(--bg);
3112
3209
  }
3113
3210
 
3114
3211
  /* Main thread area */
@@ -3319,6 +3416,10 @@ html, body {
3319
3416
  </aside>
3320
3417
 
3321
3418
  <main id="main">
3419
+ <div id="viewing-banner">
3420
+ <span id="viewing-banner-text">Viewing a past meeting</span>
3421
+ <button type="button" onclick="backToLive()">\u2190 Back to live meeting</button>
3422
+ </div>
3322
3423
  <div id="meeting-header">
3323
3424
  <div id="meeting-title">No meeting in progress</div>
3324
3425
  <div id="meeting-meta">Waiting for orchestrator\u2026</div>
@@ -3343,11 +3444,18 @@ html, body {
3343
3444
  // State
3344
3445
  // -----------------------------------------------------------------
3345
3446
  const state = {
3346
- sessions: new Map(), // sessionId \u2192 SessionSnapshot
3447
+ sessions: new Map(), // sessionId (== projectPath) \u2192 SessionSnapshot
3347
3448
  activeSessionId: null,
3449
+ viewedMeetingId: null, // null \u2192 show live meeting; else show that past meeting
3348
3450
  ws: null,
3349
3451
  };
3350
3452
 
3453
+ function backToLive() {
3454
+ state.viewedMeetingId = null;
3455
+ renderAll();
3456
+ }
3457
+ window.backToLive = backToLive;
3458
+
3351
3459
  const AVATARS = {
3352
3460
  planner: '\u{1F4CC}',
3353
3461
  architect: '\u{1F3DB}',
@@ -3508,7 +3616,7 @@ function renderTabs() {
3508
3616
  if (sid === state.activeSessionId) btn.classList.add('active');
3509
3617
  if (!s.isActive) btn.classList.add('inactive');
3510
3618
  btn.textContent = s.displayName + (s.currentMeeting ? ' \u2022' : '');
3511
- btn.onclick = () => { state.activeSessionId = sid; renderAll(); };
3619
+ btn.onclick = () => { state.activeSessionId = sid; state.viewedMeetingId = null; renderAll(); };
3512
3620
  bar.appendChild(btn);
3513
3621
  }
3514
3622
  }
@@ -3541,7 +3649,17 @@ function renderSidebar() {
3541
3649
  for (const m of s.pastMeetings) {
3542
3650
  const row = document.createElement('div');
3543
3651
  row.className = 'past-meeting';
3544
- row.textContent = m.topic + ' \u2014 ' + (m.decisions?.length || 0) + ' decisions';
3652
+ if (state.viewedMeetingId === m.meetingId) row.classList.add('viewing');
3653
+ const title = document.createElement('span');
3654
+ title.className = 'pm-title';
3655
+ title.textContent = m.topic;
3656
+ const meta = document.createElement('span');
3657
+ meta.className = 'pm-meta';
3658
+ meta.textContent =
3659
+ m.meetingType.toUpperCase() + ' \xB7 ' + (m.decisions?.length || 0) + ' decisions';
3660
+ row.appendChild(title);
3661
+ row.appendChild(meta);
3662
+ row.onclick = () => { state.viewedMeetingId = m.meetingId; renderAll(); };
3545
3663
  pastEl.appendChild(row);
3546
3664
  }
3547
3665
  }
@@ -3555,8 +3673,24 @@ function renderMain() {
3555
3673
  const thread = document.getElementById('thread');
3556
3674
  const input = document.getElementById('comment-input');
3557
3675
  const send = document.getElementById('comment-send');
3676
+ const banner = document.getElementById('viewing-banner');
3677
+ const bannerText = document.getElementById('viewing-banner-text');
3558
3678
 
3559
- if (!s || !s.currentMeeting) {
3679
+ // Resolve which meeting to render: viewedMeetingId (past) or current
3680
+ let m = null;
3681
+ let isPast = false;
3682
+ if (s) {
3683
+ if (state.viewedMeetingId) {
3684
+ m = (s.pastMeetings || []).find((x) => x.meetingId === state.viewedMeetingId) || null;
3685
+ isPast = !!m;
3686
+ // If the past meeting disappeared (retention eviction), fall back to live.
3687
+ if (!m) state.viewedMeetingId = null;
3688
+ }
3689
+ if (!m) m = s.currentMeeting;
3690
+ }
3691
+
3692
+ if (!m) {
3693
+ banner.classList.remove('show');
3560
3694
  title.textContent = 'No meeting in progress';
3561
3695
  meta.textContent = s ? 'Session ready' : 'No active session';
3562
3696
  agenda.innerHTML = '';
@@ -3566,9 +3700,15 @@ function renderMain() {
3566
3700
  return;
3567
3701
  }
3568
3702
 
3569
- const m = s.currentMeeting;
3703
+ if (isPast) {
3704
+ banner.classList.add('show');
3705
+ bannerText.textContent = '\u{1F4D6} Viewing past meeting \xB7 ' + m.topic;
3706
+ } else {
3707
+ banner.classList.remove('show');
3708
+ }
3709
+
3570
3710
  title.textContent = m.topic;
3571
- meta.textContent = [m.meetingType.toUpperCase(), m.status, m.participants.join(', ')].join(' \xB7 ');
3711
+ meta.textContent = [m.meetingType.toUpperCase(), m.status, (m.participants || []).join(', ')].join(' \xB7 ');
3572
3712
  agenda.innerHTML = '';
3573
3713
  (m.agenda || []).forEach((item, i) => {
3574
3714
  const chip = document.createElement('span');
@@ -3586,8 +3726,13 @@ function renderMain() {
3586
3726
  }
3587
3727
  thread.scrollTop = thread.scrollHeight;
3588
3728
 
3589
- input.disabled = (m.status === 'completed');
3590
- send.disabled = (m.status === 'completed');
3729
+ // Comment box: disabled when viewing a past meeting or when current meeting is completed
3730
+ const disabled = isPast || m.status === 'completed';
3731
+ input.disabled = disabled;
3732
+ send.disabled = disabled;
3733
+ input.placeholder = isPast
3734
+ ? 'Comments disabled \u2014 this is a past meeting'
3735
+ : 'Add a comment to this meeting\u2026 (Enter to send, Shift+Enter for newline)';
3591
3736
  }
3592
3737
 
3593
3738
  function commentEl(c) {
@@ -3665,31 +3810,54 @@ connect();
3665
3810
 
3666
3811
  // src/dashboard/state-bridge.ts
3667
3812
  import { EventEmitter as EventEmitter2 } from "events";
3668
- var MAX_PAST_MEETINGS = 5;
3813
+ var MAX_PAST_MEETINGS = 20;
3669
3814
  var EVENT_DEBOUNCE_MS = 100;
3670
3815
  var StateBridge = class extends EventEmitter2 {
3671
- sessions = /* @__PURE__ */ new Map();
3816
+ // projectPath ProjectState (single row per project, merged across terminals)
3817
+ projects = /* @__PURE__ */ new Map();
3818
+ // terminal sessionId → projectPath (for event routing)
3819
+ sessionToProject = /* @__PURE__ */ new Map();
3672
3820
  debounceTimers = /* @__PURE__ */ new Map();
3673
3821
  pendingEvents = /* @__PURE__ */ new Map();
3674
3822
  // ---- Session lifecycle --------------------------------------------------
3675
3823
  registerSession(info) {
3676
- const displayName = this.getUniqueDisplayName(info.projectName);
3677
- this.sessions.set(info.sessionId, {
3824
+ const existing = this.projects.get(info.projectPath);
3825
+ if (existing) {
3826
+ existing.activeSessionIds.add(info.sessionId);
3827
+ this.sessionToProject.set(info.sessionId, info.projectPath);
3828
+ logger.info(`Session reattached to existing project: ${existing.displayName} (${info.sessionId})`);
3829
+ this.emit(
3830
+ "broadcast",
3831
+ JSON.stringify({
3832
+ type: "session-registered",
3833
+ sessionId: info.projectPath,
3834
+ // wire sessionId = projectPath so UI tab is stable
3835
+ displayName: existing.displayName,
3836
+ projectPath: info.projectPath
3837
+ })
3838
+ );
3839
+ return existing.displayName;
3840
+ }
3841
+ const displayName = info.projectName;
3842
+ const state = {
3843
+ projectPath: info.projectPath,
3678
3844
  projectName: info.projectName,
3679
3845
  displayName,
3680
- projectPath: info.projectPath,
3681
- isActive: true,
3846
+ activeSessionIds: /* @__PURE__ */ new Set([info.sessionId]),
3682
3847
  currentMeeting: null,
3683
3848
  pastMeetings: [],
3684
3849
  mvps: [],
3685
3850
  totalCost: 0
3686
- });
3687
- logger.info(`Session registered: ${displayName} (${info.sessionId})`);
3851
+ };
3852
+ this.projects.set(info.projectPath, state);
3853
+ this.sessionToProject.set(info.sessionId, info.projectPath);
3854
+ logger.info(`Project registered: ${displayName} (${info.projectPath})`);
3688
3855
  this.emit(
3689
3856
  "broadcast",
3690
3857
  JSON.stringify({
3691
3858
  type: "session-registered",
3692
- sessionId: info.sessionId,
3859
+ sessionId: info.projectPath,
3860
+ // projectPath is the stable wire-side sessionId
3693
3861
  displayName,
3694
3862
  projectPath: info.projectPath
3695
3863
  })
@@ -3697,34 +3865,39 @@ var StateBridge = class extends EventEmitter2 {
3697
3865
  return displayName;
3698
3866
  }
3699
3867
  unregisterSession(sessionId) {
3700
- const session = this.sessions.get(sessionId);
3701
- if (session) {
3702
- session.isActive = false;
3703
- logger.info(`Session deactivated: ${session.displayName}`);
3868
+ const projectPath = this.sessionToProject.get(sessionId);
3869
+ if (!projectPath) return;
3870
+ this.sessionToProject.delete(sessionId);
3871
+ const project = this.projects.get(projectPath);
3872
+ if (!project) return;
3873
+ project.activeSessionIds.delete(sessionId);
3874
+ if (project.activeSessionIds.size === 0) {
3875
+ logger.info(`Project deactivated: ${project.displayName}`);
3704
3876
  this.emit(
3705
3877
  "broadcast",
3706
3878
  JSON.stringify({
3707
3879
  type: "session-unregistered",
3708
- sessionId
3880
+ sessionId: projectPath
3709
3881
  })
3710
3882
  );
3711
3883
  }
3712
3884
  }
3713
3885
  // ---- Event handling -----------------------------------------------------
3714
3886
  handleSessionEvent(sessionId, event) {
3715
- const session = this.sessions.get(sessionId);
3716
- if (!session) return;
3717
- this.applyEvent(session, event);
3718
- if (!this.pendingEvents.has(sessionId)) {
3719
- this.pendingEvents.set(sessionId, []);
3720
- }
3721
- this.pendingEvents.get(sessionId).push(event);
3722
- if (!this.debounceTimers.has(sessionId)) {
3887
+ const projectPath = this.sessionToProject.get(sessionId) ?? sessionId;
3888
+ const project = this.projects.get(projectPath);
3889
+ if (!project) return;
3890
+ this.applyEvent(project, event);
3891
+ if (!this.pendingEvents.has(projectPath)) {
3892
+ this.pendingEvents.set(projectPath, []);
3893
+ }
3894
+ this.pendingEvents.get(projectPath).push(event);
3895
+ if (!this.debounceTimers.has(projectPath)) {
3723
3896
  this.debounceTimers.set(
3724
- sessionId,
3897
+ projectPath,
3725
3898
  setTimeout(() => {
3726
- this.flushEvents(sessionId);
3727
- this.debounceTimers.delete(sessionId);
3899
+ this.flushEvents(projectPath);
3900
+ this.debounceTimers.delete(projectPath);
3728
3901
  }, EVENT_DEBOUNCE_MS)
3729
3902
  );
3730
3903
  }
@@ -3733,12 +3906,13 @@ var StateBridge = class extends EventEmitter2 {
3733
3906
  getSnapshot() {
3734
3907
  return {
3735
3908
  type: "multi-snapshot",
3736
- sessions: Array.from(this.sessions.entries()).map(
3737
- ([sessionId, s]) => ({
3738
- sessionId,
3909
+ sessions: Array.from(this.projects.values()).map(
3910
+ (s) => ({
3911
+ sessionId: s.projectPath,
3912
+ // stable wire-side id
3739
3913
  displayName: s.displayName,
3740
3914
  projectPath: s.projectPath,
3741
- isActive: s.isActive,
3915
+ isActive: s.activeSessionIds.size > 0,
3742
3916
  currentMeeting: s.currentMeeting,
3743
3917
  pastMeetings: [...s.pastMeetings],
3744
3918
  mvps: [...s.mvps],
@@ -3748,25 +3922,16 @@ var StateBridge = class extends EventEmitter2 {
3748
3922
  };
3749
3923
  }
3750
3924
  // ---- Private helpers ----------------------------------------------------
3751
- getUniqueDisplayName(projectName) {
3752
- const existing = Array.from(this.sessions.values()).map(
3753
- (s) => s.displayName
3754
- );
3755
- if (!existing.includes(projectName)) return projectName;
3756
- let i = 1;
3757
- while (existing.includes(`${projectName} (${i})`)) i++;
3758
- return `${projectName} (${i})`;
3759
- }
3760
- applyEvent(session, event) {
3925
+ applyEvent(project, event) {
3761
3926
  switch (event.kind) {
3762
3927
  case "meeting_started": {
3763
- if (session.currentMeeting) {
3764
- session.pastMeetings.unshift(session.currentMeeting);
3765
- if (session.pastMeetings.length > MAX_PAST_MEETINGS) {
3766
- session.pastMeetings.pop();
3928
+ if (project.currentMeeting) {
3929
+ project.pastMeetings.unshift(project.currentMeeting);
3930
+ if (project.pastMeetings.length > MAX_PAST_MEETINGS) {
3931
+ project.pastMeetings.pop();
3767
3932
  }
3768
3933
  }
3769
- session.currentMeeting = {
3934
+ project.currentMeeting = {
3770
3935
  meetingId: event.meetingId,
3771
3936
  meetingType: event.meetingType,
3772
3937
  topic: event.topic,
@@ -3775,7 +3940,7 @@ var StateBridge = class extends EventEmitter2 {
3775
3940
  status: "in-progress",
3776
3941
  phase: "opening",
3777
3942
  comments: [],
3778
- mvps: [],
3943
+ mvps: [...project.mvps],
3779
3944
  decisions: [],
3780
3945
  actionItems: [],
3781
3946
  startedAt: Date.now(),
@@ -3784,36 +3949,40 @@ var StateBridge = class extends EventEmitter2 {
3784
3949
  break;
3785
3950
  }
3786
3951
  case "transcript_added": {
3787
- if (session.currentMeeting?.meetingId === event.meetingId) {
3788
- session.currentMeeting.comments.push(event.comment);
3952
+ if (project.currentMeeting?.meetingId === event.meetingId) {
3953
+ project.currentMeeting.comments.push(event.comment);
3954
+ } else {
3955
+ const past = project.pastMeetings.find((m) => m.meetingId === event.meetingId);
3956
+ if (past) past.comments.push(event.comment);
3789
3957
  }
3790
3958
  break;
3791
3959
  }
3792
3960
  case "round_advanced": {
3793
- if (session.currentMeeting?.meetingId === event.meetingId) {
3794
- session.currentMeeting.phase = "discussion";
3961
+ if (project.currentMeeting?.meetingId === event.meetingId) {
3962
+ project.currentMeeting.phase = "discussion";
3795
3963
  }
3796
3964
  break;
3797
3965
  }
3798
3966
  case "consensus_checked": {
3799
- if (session.currentMeeting?.meetingId === event.meetingId) {
3800
- session.currentMeeting.status = event.allAgreed ? "in-progress" : "awaiting-consensus";
3967
+ if (project.currentMeeting?.meetingId === event.meetingId) {
3968
+ project.currentMeeting.status = event.allAgreed ? "in-progress" : "awaiting-consensus";
3801
3969
  }
3802
3970
  break;
3803
3971
  }
3804
3972
  case "minutes_finalized": {
3805
- if (session.currentMeeting?.meetingId === event.meetingId) {
3806
- session.currentMeeting.decisions = [...event.decisions];
3807
- session.currentMeeting.actionItems = [...event.actionItems];
3808
- session.currentMeeting.status = "completed";
3809
- session.currentMeeting.phase = "minutes-generation";
3810
- session.currentMeeting.completedAt = Date.now();
3973
+ const target = project.currentMeeting?.meetingId === event.meetingId ? project.currentMeeting : project.pastMeetings.find((m) => m.meetingId === event.meetingId);
3974
+ if (target) {
3975
+ target.decisions = [...event.decisions];
3976
+ target.actionItems = [...event.actionItems];
3977
+ target.status = "completed";
3978
+ target.phase = "minutes-generation";
3979
+ target.completedAt = Date.now();
3811
3980
  }
3812
3981
  break;
3813
3982
  }
3814
3983
  case "user_comment_added": {
3815
- if (session.currentMeeting?.meetingId === event.meetingId) {
3816
- const nextId = session.currentMeeting.comments.length + 1;
3984
+ if (project.currentMeeting?.meetingId === event.meetingId) {
3985
+ const nextId = project.currentMeeting.comments.length + 1;
3817
3986
  const userComment = {
3818
3987
  id: nextId,
3819
3988
  speakerRole: "user",
@@ -3823,19 +3992,19 @@ var StateBridge = class extends EventEmitter2 {
3823
3992
  stance: "speaking",
3824
3993
  createdAt: Date.now()
3825
3994
  };
3826
- session.currentMeeting.comments.push(userComment);
3995
+ project.currentMeeting.comments.push(userComment);
3827
3996
  }
3828
3997
  break;
3829
3998
  }
3830
3999
  case "mvp_progress": {
3831
- session.mvps = [...event.mvps];
3832
- if (session.currentMeeting) {
3833
- session.currentMeeting.mvps = [...event.mvps];
4000
+ project.mvps = [...event.mvps];
4001
+ if (project.currentMeeting) {
4002
+ project.currentMeeting.mvps = [...event.mvps];
3834
4003
  }
3835
4004
  break;
3836
4005
  }
3837
4006
  case "cost_update": {
3838
- session.totalCost = event.totalCost;
4007
+ project.totalCost = event.totalCost;
3839
4008
  break;
3840
4009
  }
3841
4010
  case "mention_created":
@@ -3843,19 +4012,19 @@ var StateBridge = class extends EventEmitter2 {
3843
4012
  break;
3844
4013
  }
3845
4014
  }
3846
- flushEvents(sessionId) {
3847
- const events = this.pendingEvents.get(sessionId);
3848
- const session = this.sessions.get(sessionId);
3849
- if (!events || !session || events.length === 0) return;
4015
+ flushEvents(projectPath) {
4016
+ const events = this.pendingEvents.get(projectPath);
4017
+ const project = this.projects.get(projectPath);
4018
+ if (!events || !project || events.length === 0) return;
3850
4019
  const delta = {
3851
4020
  type: "session-delta",
3852
- sessionId,
3853
- displayName: session.displayName,
4021
+ sessionId: projectPath,
4022
+ displayName: project.displayName,
3854
4023
  timestamp: Date.now(),
3855
4024
  events: [...events]
3856
4025
  };
3857
4026
  this.emit("broadcast", JSON.stringify(delta));
3858
- this.pendingEvents.set(sessionId, []);
4027
+ this.pendingEvents.set(projectPath, []);
3859
4028
  }
3860
4029
  };
3861
4030