issue-flow 0.15.0 → 0.17.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 (169) hide show
  1. package/dist/agent-TB6W7PT7.js +30 -0
  2. package/dist/{analyze-O2PYDIFK.js → analyze-6M3KEOX7.js} +22 -22
  3. package/dist/{apply-SSU6QCD4.js → apply-ADB3ZK7W.js} +14 -14
  4. package/dist/{bench-ODFPAGW6.js → bench-FM7I7EIV.js} +23 -23
  5. package/dist/{chunk-BMHGRYL6.js → chunk-2SUFAE6X.js} +67 -24
  6. package/dist/chunk-2SUFAE6X.js.map +1 -0
  7. package/dist/{chunk-QRCGW2ZO.js → chunk-32JSYXEK.js} +8 -8
  8. package/dist/chunk-3R27MJ5R.js +25 -0
  9. package/dist/chunk-3R27MJ5R.js.map +1 -0
  10. package/dist/{chunk-MGCOQU6N.js → chunk-6YQT3SSM.js} +4 -4
  11. package/dist/chunk-6YQT3SSM.js.map +1 -0
  12. package/dist/{chunk-A35HE4V7.js → chunk-7F7LGMVO.js} +3 -3
  13. package/dist/{chunk-RPXDBX6R.js → chunk-A4H7P7T7.js} +102 -102
  14. package/dist/chunk-A4H7P7T7.js.map +1 -0
  15. package/dist/{chunk-2ZAM45PZ.js → chunk-ANI2NTR4.js} +9 -9
  16. package/dist/{chunk-ZPIVCM7Q.js → chunk-B3TSS7BT.js} +3 -3
  17. package/dist/chunk-C5TL7T4V.js +27 -0
  18. package/dist/chunk-C5TL7T4V.js.map +1 -0
  19. package/dist/chunk-D6ZBGJ3L.js +321 -0
  20. package/dist/chunk-D6ZBGJ3L.js.map +1 -0
  21. package/dist/{chunk-ZXPASFHG.js → chunk-DGRTJD5R.js} +3 -3
  22. package/dist/{chunk-6QYMSNVA.js → chunk-DUS5ZNKL.js} +476 -362
  23. package/dist/chunk-DUS5ZNKL.js.map +1 -0
  24. package/dist/{chunk-VR34TXHF.js → chunk-DV7HUC4Z.js} +7 -7
  25. package/dist/{chunk-6SRE4KD7.js → chunk-E6KXCV3T.js} +4 -4
  26. package/dist/{chunk-HGSYD57B.js → chunk-HJBF4WD2.js} +1656 -1070
  27. package/dist/chunk-HJBF4WD2.js.map +1 -0
  28. package/dist/{chunk-PCN7ZQND.js → chunk-HLAPOUEM.js} +8 -8
  29. package/dist/{chunk-WM3Y7Q4A.js → chunk-HY5HLFDU.js} +630 -571
  30. package/dist/chunk-HY5HLFDU.js.map +1 -0
  31. package/dist/{chunk-XDKGA6UX.js → chunk-ICM463EO.js} +64 -14
  32. package/dist/chunk-ICM463EO.js.map +1 -0
  33. package/dist/{chunk-MEFY4B7T.js → chunk-ISZ54SVE.js} +9 -9
  34. package/dist/{chunk-ARWNYXFL.js → chunk-KZ5EY5XX.js} +12 -12
  35. package/dist/{chunk-ZFIXEYUJ.js → chunk-KZXQWC7U.js} +32 -20
  36. package/dist/chunk-KZXQWC7U.js.map +1 -0
  37. package/dist/{chunk-KAHQ6F5C.js → chunk-LPWPFIIB.js} +11 -11
  38. package/dist/{chunk-FK4IDY6P.js → chunk-MI5TREN3.js} +9 -9
  39. package/dist/{chunk-UNQYMR3H.js → chunk-PPZ3K4YM.js} +2 -2
  40. package/dist/{chunk-F4OK7KEP.js → chunk-QMIQR7EE.js} +10 -10
  41. package/dist/{chunk-AYYMW33T.js → chunk-QPGEFLMY.js} +2 -2
  42. package/dist/chunk-RCZJQUTC.js +196 -0
  43. package/dist/chunk-RCZJQUTC.js.map +1 -0
  44. package/dist/{chunk-MROS3SKL.js → chunk-RNFUVKC3.js} +3 -3
  45. package/dist/{chunk-RWEXK55D.js → chunk-RYOUZ6EK.js} +2 -2
  46. package/dist/{chunk-6E2PEBFT.js → chunk-SGTTR4XW.js} +10 -10
  47. package/dist/{chunk-XQHFC62U.js → chunk-SOM4QA6C.js} +3 -3
  48. package/dist/{chunk-5ZKBIXET.js → chunk-UK4E2FUA.js} +132 -34
  49. package/dist/chunk-UK4E2FUA.js.map +1 -0
  50. package/dist/{chunk-6FXOU2KR.js → chunk-UWDCIFTP.js} +7 -7
  51. package/dist/{chunk-XG2NYDRA.js → chunk-VE3IYNDN.js} +3 -3
  52. package/dist/{chunk-3BWZO4YI.js → chunk-WCCZJL7W.js} +2 -2
  53. package/dist/{chunk-NVZMBXTE.js → chunk-XZUEFTUP.js} +6 -6
  54. package/dist/{chunk-TBDP6QPJ.js → chunk-YII4U7ZD.js} +10 -10
  55. package/dist/{chunk-TBDP6QPJ.js.map → chunk-YII4U7ZD.js.map} +1 -1
  56. package/dist/{chunk-4RXYFWP7.js → chunk-YUNBOUQH.js} +6 -6
  57. package/dist/{chunk-GSZCQUCY.js → chunk-YVPZUN6X.js} +2 -2
  58. package/dist/{chunk-XRLRKTQA.js → chunk-Z5O2XBWA.js} +14 -14
  59. package/dist/cli.js +61 -52
  60. package/dist/cli.js.map +1 -1
  61. package/dist/{config-UMP6DBJB.js → config-KGZUPAUP.js} +13 -13
  62. package/dist/{conventions-DKH7AE4T.js → conventions-RYN7OXJN.js} +15 -15
  63. package/dist/{diagnostics-GOGCLTRS.js → diagnostics-HTCWF7F4.js} +7 -7
  64. package/dist/execute-QL3ZQIAP.js +37 -0
  65. package/dist/{generate-AGBT7EHX.js → generate-CX6KNHZS.js} +22 -22
  66. package/dist/{git-AC542GU2.js → git-A2IP32PQ.js} +2 -2
  67. package/dist/init-PDVQDRS6.js +24 -0
  68. package/dist/{operations-JQYK2EMT.js → operations-CAKIT2BO.js} +15 -15
  69. package/dist/{permissions-6HL52K54.js → permissions-G4XCAESF.js} +4 -4
  70. package/dist/plan-PRORGS3L.js +34 -0
  71. package/dist/{policy-IMRXGG7X.js → policy-WX6O2JID.js} +14 -14
  72. package/dist/pr-27RMQI5F.js +39 -0
  73. package/dist/pr-review-VNESMAMT.js +32 -0
  74. package/dist/prd-ZNC4T5BS.js +33 -0
  75. package/dist/{ps-KUUZ6TUL.js → ps-76L6ZAZL.js} +12 -12
  76. package/dist/{recorder-BJ2KIAWT.js → recorder-N7U66HCO.js} +7 -7
  77. package/dist/{registry-SB7XTMGE.js → registry-XLHHYLTR.js} +5 -5
  78. package/dist/{resume-QNLQSCR5.js → resume-VLWIE264.js} +40 -38
  79. package/dist/{resume-QNLQSCR5.js.map → resume-VLWIE264.js.map} +1 -1
  80. package/dist/review-7CREJDKB.js +35 -0
  81. package/dist/routing-VCDYHSXA.js +33 -0
  82. package/dist/run-3MPORLF5.js +56 -0
  83. package/dist/{session-publisher-T7EI3CIP.js → session-publisher-KAJQRHNZ.js} +3 -2
  84. package/dist/{state-manager-ZEPWTCA6.js → state-manager-UPHO2EA2.js} +4 -4
  85. package/dist/state-manager-UPHO2EA2.js.map +1 -0
  86. package/dist/{usage-QL563I7F.js → usage-U7QNGCPK.js} +12 -12
  87. package/dist/{web-PPSMNTQ6.js → web-HUNDHCJ4.js} +21 -18
  88. package/dist/{web-PPSMNTQ6.js.map → web-HUNDHCJ4.js.map} +1 -1
  89. package/package.json +6 -5
  90. package/web/AGENTS.md +15 -11
  91. package/web/public/app.css +51 -0
  92. package/web/public/app.js +325 -56
  93. package/web/public/index.html +6 -2
  94. package/dist/agent-AT64RXTA.js +0 -30
  95. package/dist/chunk-5ZKBIXET.js.map +0 -1
  96. package/dist/chunk-6QYMSNVA.js.map +0 -1
  97. package/dist/chunk-7Z4Y3VKM.js +0 -26
  98. package/dist/chunk-7Z4Y3VKM.js.map +0 -1
  99. package/dist/chunk-BMHGRYL6.js.map +0 -1
  100. package/dist/chunk-HGSYD57B.js.map +0 -1
  101. package/dist/chunk-MGCOQU6N.js.map +0 -1
  102. package/dist/chunk-RPXDBX6R.js.map +0 -1
  103. package/dist/chunk-WM3Y7Q4A.js.map +0 -1
  104. package/dist/chunk-XDKGA6UX.js.map +0 -1
  105. package/dist/chunk-XLVGPXNL.js +0 -180
  106. package/dist/chunk-XLVGPXNL.js.map +0 -1
  107. package/dist/chunk-ZFIXEYUJ.js.map +0 -1
  108. package/dist/execute-E3BRMDAQ.js +0 -35
  109. package/dist/init-LLBVJNPE.js +0 -24
  110. package/dist/plan-HSLWDJEZ.js +0 -34
  111. package/dist/pr-6TCZXG6K.js +0 -39
  112. package/dist/pr-review-6MWJO2NP.js +0 -32
  113. package/dist/prd-D6YDYAFX.js +0 -33
  114. package/dist/review-3KJ4BH2V.js +0 -35
  115. package/dist/routing-TN7GLMGU.js +0 -94
  116. package/dist/routing-TN7GLMGU.js.map +0 -1
  117. package/dist/run-5EHIFF2V.js +0 -54
  118. /package/dist/{agent-AT64RXTA.js.map → agent-TB6W7PT7.js.map} +0 -0
  119. /package/dist/{analyze-O2PYDIFK.js.map → analyze-6M3KEOX7.js.map} +0 -0
  120. /package/dist/{apply-SSU6QCD4.js.map → apply-ADB3ZK7W.js.map} +0 -0
  121. /package/dist/{bench-ODFPAGW6.js.map → bench-FM7I7EIV.js.map} +0 -0
  122. /package/dist/{chunk-QRCGW2ZO.js.map → chunk-32JSYXEK.js.map} +0 -0
  123. /package/dist/{chunk-A35HE4V7.js.map → chunk-7F7LGMVO.js.map} +0 -0
  124. /package/dist/{chunk-2ZAM45PZ.js.map → chunk-ANI2NTR4.js.map} +0 -0
  125. /package/dist/{chunk-ZPIVCM7Q.js.map → chunk-B3TSS7BT.js.map} +0 -0
  126. /package/dist/{chunk-ZXPASFHG.js.map → chunk-DGRTJD5R.js.map} +0 -0
  127. /package/dist/{chunk-VR34TXHF.js.map → chunk-DV7HUC4Z.js.map} +0 -0
  128. /package/dist/{chunk-6SRE4KD7.js.map → chunk-E6KXCV3T.js.map} +0 -0
  129. /package/dist/{chunk-PCN7ZQND.js.map → chunk-HLAPOUEM.js.map} +0 -0
  130. /package/dist/{chunk-MEFY4B7T.js.map → chunk-ISZ54SVE.js.map} +0 -0
  131. /package/dist/{chunk-ARWNYXFL.js.map → chunk-KZ5EY5XX.js.map} +0 -0
  132. /package/dist/{chunk-KAHQ6F5C.js.map → chunk-LPWPFIIB.js.map} +0 -0
  133. /package/dist/{chunk-FK4IDY6P.js.map → chunk-MI5TREN3.js.map} +0 -0
  134. /package/dist/{chunk-UNQYMR3H.js.map → chunk-PPZ3K4YM.js.map} +0 -0
  135. /package/dist/{chunk-F4OK7KEP.js.map → chunk-QMIQR7EE.js.map} +0 -0
  136. /package/dist/{chunk-AYYMW33T.js.map → chunk-QPGEFLMY.js.map} +0 -0
  137. /package/dist/{chunk-MROS3SKL.js.map → chunk-RNFUVKC3.js.map} +0 -0
  138. /package/dist/{chunk-RWEXK55D.js.map → chunk-RYOUZ6EK.js.map} +0 -0
  139. /package/dist/{chunk-6E2PEBFT.js.map → chunk-SGTTR4XW.js.map} +0 -0
  140. /package/dist/{chunk-XQHFC62U.js.map → chunk-SOM4QA6C.js.map} +0 -0
  141. /package/dist/{chunk-6FXOU2KR.js.map → chunk-UWDCIFTP.js.map} +0 -0
  142. /package/dist/{chunk-XG2NYDRA.js.map → chunk-VE3IYNDN.js.map} +0 -0
  143. /package/dist/{chunk-3BWZO4YI.js.map → chunk-WCCZJL7W.js.map} +0 -0
  144. /package/dist/{chunk-NVZMBXTE.js.map → chunk-XZUEFTUP.js.map} +0 -0
  145. /package/dist/{chunk-4RXYFWP7.js.map → chunk-YUNBOUQH.js.map} +0 -0
  146. /package/dist/{chunk-GSZCQUCY.js.map → chunk-YVPZUN6X.js.map} +0 -0
  147. /package/dist/{chunk-XRLRKTQA.js.map → chunk-Z5O2XBWA.js.map} +0 -0
  148. /package/dist/{config-UMP6DBJB.js.map → config-KGZUPAUP.js.map} +0 -0
  149. /package/dist/{conventions-DKH7AE4T.js.map → conventions-RYN7OXJN.js.map} +0 -0
  150. /package/dist/{diagnostics-GOGCLTRS.js.map → diagnostics-HTCWF7F4.js.map} +0 -0
  151. /package/dist/{execute-E3BRMDAQ.js.map → execute-QL3ZQIAP.js.map} +0 -0
  152. /package/dist/{generate-AGBT7EHX.js.map → generate-CX6KNHZS.js.map} +0 -0
  153. /package/dist/{git-AC542GU2.js.map → git-A2IP32PQ.js.map} +0 -0
  154. /package/dist/{init-LLBVJNPE.js.map → init-PDVQDRS6.js.map} +0 -0
  155. /package/dist/{operations-JQYK2EMT.js.map → operations-CAKIT2BO.js.map} +0 -0
  156. /package/dist/{permissions-6HL52K54.js.map → permissions-G4XCAESF.js.map} +0 -0
  157. /package/dist/{plan-HSLWDJEZ.js.map → plan-PRORGS3L.js.map} +0 -0
  158. /package/dist/{policy-IMRXGG7X.js.map → policy-WX6O2JID.js.map} +0 -0
  159. /package/dist/{pr-6TCZXG6K.js.map → pr-27RMQI5F.js.map} +0 -0
  160. /package/dist/{pr-review-6MWJO2NP.js.map → pr-review-VNESMAMT.js.map} +0 -0
  161. /package/dist/{prd-D6YDYAFX.js.map → prd-ZNC4T5BS.js.map} +0 -0
  162. /package/dist/{ps-KUUZ6TUL.js.map → ps-76L6ZAZL.js.map} +0 -0
  163. /package/dist/{recorder-BJ2KIAWT.js.map → recorder-N7U66HCO.js.map} +0 -0
  164. /package/dist/{registry-SB7XTMGE.js.map → registry-XLHHYLTR.js.map} +0 -0
  165. /package/dist/{review-3KJ4BH2V.js.map → review-7CREJDKB.js.map} +0 -0
  166. /package/dist/{run-5EHIFF2V.js.map → routing-VCDYHSXA.js.map} +0 -0
  167. /package/dist/{session-publisher-T7EI3CIP.js.map → run-3MPORLF5.js.map} +0 -0
  168. /package/dist/{state-manager-ZEPWTCA6.js.map → session-publisher-KAJQRHNZ.js.map} +0 -0
  169. /package/dist/{usage-QL563I7F.js.map → usage-U7QNGCPK.js.map} +0 -0
package/web/public/app.js CHANGED
@@ -4,8 +4,6 @@
4
4
  // todo texto dinâmico entra via textContent (nunca innerHTML com dados
5
5
  // do snapshot).
6
6
  (() => {
7
- 'use strict';
8
-
9
7
  const REFRESH_OPTIONS = [3, 5, 10, 30];
10
8
  const PAUSED = 0;
11
9
  const STORAGE_KEY = 'issue-flow:refresh-seconds';
@@ -89,6 +87,8 @@
89
87
  now: document.getElementById('now'),
90
88
  resilience: document.getElementById('resilience'),
91
89
  configuration: document.getElementById('configuration'),
90
+ appVersion: document.getElementById('app-version'),
91
+ appVersionDashboard: document.getElementById('app-version-dashboard'),
92
92
  phases: document.getElementById('phases'),
93
93
  nextSteps: document.getElementById('next-steps'),
94
94
  stories: document.getElementById('stories'),
@@ -127,9 +127,12 @@
127
127
  // guardar o nó levaria a uma referência morta.
128
128
  selectedDetail: null,
129
129
  configWritable: false,
130
+ configData: null,
130
131
  // Identifica o processo que serviu os assets atuais. Uma troca significa
131
132
  // que --restart-web colocou código novo no mesmo origin e exige reload.
132
133
  serverInstanceId: null,
134
+ // Versão do monitor, vinda de /api/health. Null enquanto não respondeu.
135
+ monitorVersion: null,
133
136
  // Multi-sessão (#35): lista de /api/sessions e seleção explícita do usuário.
134
137
  // selectedSessionId null = modo automático (1 sessão → detalhe; 2+ → dashboard).
135
138
  sessions: [],
@@ -287,7 +290,8 @@
287
290
  ...story,
288
291
  status: STORY_STATUS_LABELS[status] !== undefined ? status : 'backlog',
289
292
  stage: STORY_STAGE_LABELS[stage] !== undefined ? stage : 'pending',
290
- stageSince: story.stageSince !== null && story.stageSince !== undefined ? story.stageSince : null,
293
+ stageSince:
294
+ story.stageSince !== null && story.stageSince !== undefined ? story.stageSince : null,
291
295
  stageDetail:
292
296
  story.stageDetail !== null && story.stageDetail !== undefined ? story.stageDetail : null,
293
297
  dependencies: list(story.dependencies),
@@ -531,6 +535,21 @@
531
535
  state.eventsUrl = null;
532
536
  }
533
537
 
538
+ // A versão exibida no header é a do processo que serviu esta página, nunca a
539
+ // da CLI que iniciou a execução: os assets vivem na memória desse processo,
540
+ // então é ela que explica o que está na tela. As duas aparecem juntas no card
541
+ // de configuração quando divergem.
542
+ function renderMonitorVersion(version) {
543
+ const label = typeof version === 'string' && version !== '' ? 'v' + version : null;
544
+ state.monitorVersion = label === null ? null : version;
545
+ for (const node of [els.appVersion, els.appVersionDashboard]) {
546
+ if (!node) continue;
547
+ node.textContent = label || '';
548
+ node.title = label === null ? '' : 'Versão do monitor que serve este painel';
549
+ node.hidden = label === null;
550
+ }
551
+ }
552
+
534
553
  function serverInstanceChanged(response) {
535
554
  const instanceId = response.headers.get('X-Issue-Flow-Instance');
536
555
  if (!instanceId) return false; // servidor anterior à identidade de instância
@@ -594,9 +613,7 @@
594
613
  }
595
614
 
596
615
  const nextId =
597
- resolved.session && resolved.session.sessionId != null
598
- ? resolved.session.sessionId
599
- : null;
616
+ resolved.session && resolved.session.sessionId != null ? resolved.session.sessionId : null;
600
617
  const sessionChanged = state.detailSessionId !== nextId;
601
618
  if (sessionChanged) {
602
619
  clearDetailState();
@@ -643,6 +660,12 @@
643
660
  state.snapshot = await res.json();
644
661
  render();
645
662
  }
663
+ const configUrl = 'api/config?session=' + encodeURIComponent(state.detailSessionId || '');
664
+ const configRes = await fetch(configUrl, { cache: 'no-store' });
665
+ if (configRes.ok) {
666
+ state.configData = await configRes.json();
667
+ if (state.snapshot) renderConfiguration(state.snapshot);
668
+ }
646
669
  if (state.eventsUrl) {
647
670
  const eventsRes = await fetch(state.eventsUrl, { cache: 'no-store' });
648
671
  if (!eventsRes.ok) throw new Error('HTTP ' + eventsRes.status);
@@ -695,10 +718,7 @@
695
718
  function renderDashboard(sessions) {
696
719
  const active = document.activeElement;
697
720
  const focusedId =
698
- active &&
699
- active.dataset &&
700
- els.dashboard.contains(active) &&
701
- active.dataset.sessionId
721
+ active && active.dataset && els.dashboard.contains(active) && active.dataset.sessionId
702
722
  ? active.dataset.sessionId
703
723
  : null;
704
724
 
@@ -734,20 +754,15 @@
734
754
  if (session.issueNumber !== null && session.issueNumber !== undefined) {
735
755
  titleRow.appendChild(el('span', 'dashboard-issue', '#' + session.issueNumber));
736
756
  }
737
- titleRow.appendChild(
738
- el('span', 'dashboard-title', session.issueTitle || 'Sem título'),
739
- );
757
+ titleRow.appendChild(el('span', 'dashboard-title', session.issueTitle || 'Sem título'));
740
758
  card.appendChild(titleRow);
741
759
 
742
760
  const summary = truncateText(session.issueDescription, DESCRIPTION_PREVIEW);
743
761
  card.appendChild(el('span', 'dashboard-summary muted', summary || 'Sem descrição'));
744
762
 
745
763
  const meta = el('span', 'dashboard-meta-row');
746
- meta.appendChild(
747
- el('span', null, 'Fase: ' + (session.currentPhase || '—')),
748
- );
749
- const percent =
750
- typeof session.progressPercent === 'number' ? session.progressPercent : 0;
764
+ meta.appendChild(el('span', null, 'Fase: ' + (session.currentPhase || '—')));
765
+ const percent = typeof session.progressPercent === 'number' ? session.progressPercent : 0;
751
766
  meta.appendChild(el('span', null, percent + '%'));
752
767
  const elapsed =
753
768
  typeof session.elapsedSeconds === 'number'
@@ -912,14 +927,19 @@
912
927
 
913
928
  const heading = el('p', 'issue-summary-title');
914
929
  heading.appendChild(
915
- el('span', 'mono', issue.number !== null && issue.number !== undefined ? '#' + issue.number : '—'),
930
+ el(
931
+ 'span',
932
+ 'mono',
933
+ issue.number !== null && issue.number !== undefined ? '#' + issue.number : '—',
934
+ ),
916
935
  );
917
936
  heading.appendChild(document.createTextNode(' ' + (issue.title || 'Sem título')));
918
937
  els.issueSummary.appendChild(heading);
919
938
 
920
939
  const meta = el('div', 'issue-summary-meta');
921
940
  const state = issue.state || null;
922
- const stateClass = state === 'open' ? 'state-open' : state === 'closed' ? 'state-closed' : 'state-unknown';
941
+ const stateClass =
942
+ state === 'open' ? 'state-open' : state === 'closed' ? 'state-closed' : 'state-unknown';
923
943
  meta.appendChild(el('span', 'badge ' + stateClass, state || 'estado desconhecido'));
924
944
  meta.appendChild(el('span', 'muted', 'Prioridade: Não definida'));
925
945
  els.issueSummary.appendChild(meta);
@@ -933,7 +953,9 @@
933
953
  els.issueSummary.appendChild(labelRow);
934
954
  }
935
955
 
936
- els.issueSummary.appendChild(el('p', 'issue-description', issue.description || 'Sem descrição.'));
956
+ els.issueSummary.appendChild(
957
+ el('p', 'issue-description', issue.description || 'Sem descrição.'),
958
+ );
937
959
  }
938
960
 
939
961
  function metaRow(grid, label, value, className) {
@@ -1044,12 +1066,41 @@
1044
1066
  env: 'variável de ambiente',
1045
1067
  cli: 'override da execução',
1046
1068
  fallback: 'fallback',
1069
+ recommended: 'política recomendada',
1047
1070
  };
1048
1071
  return labels[source] || source || 'não informado';
1049
1072
  }
1050
1073
 
1051
1074
  function renderConfiguration(snapshot) {
1052
1075
  clear(els.configuration);
1076
+
1077
+ // Quem executou e quem está mostrando. São processos diferentes e podem
1078
+ // estar em versões diferentes; este é o único lugar onde as duas aparecem
1079
+ // lado a lado, então a divergência é dita aqui.
1080
+ const env = snapshot.environment;
1081
+ const runVersion = env && env.cliVersion ? env.cliVersion : null;
1082
+ const runtime = el('dl', 'now-grid');
1083
+ const runtimeBits = [runVersion ? 'v' + runVersion : 'versão não registrada'];
1084
+ if (env && env.node) runtimeBits.push(env.node);
1085
+ if (env && env.platform) runtimeBits.push(env.platform);
1086
+ nowRow(runtime, 'Issue Flow (execução)', runtimeBits.join(' · '));
1087
+ nowRow(
1088
+ runtime,
1089
+ 'Monitor (este painel)',
1090
+ state.monitorVersion ? 'v' + state.monitorVersion : '—',
1091
+ );
1092
+ els.configuration.appendChild(runtime);
1093
+ if (runVersion !== null && state.monitorVersion && runVersion !== state.monitorVersion) {
1094
+ els.configuration.appendChild(
1095
+ el(
1096
+ 'p',
1097
+ 'alert-entry level-warn',
1098
+ 'Este painel é servido por uma versão diferente da que executa o pipeline. ' +
1099
+ 'Reinicie o monitor com --restart-web para ver a interface desta versão.',
1100
+ ),
1101
+ );
1102
+ }
1103
+
1053
1104
  const config = snapshot.configuration;
1054
1105
  if (!config) {
1055
1106
  els.configuration.appendChild(el('p', 'empty', 'Configuração não capturada nesta execução.'));
@@ -1060,7 +1111,9 @@
1060
1111
  nowRow(
1061
1112
  summary,
1062
1113
  'Harness padrão',
1063
- (config.defaultProvider.value || '—') + ' · ' + configSourceLabel(config.defaultProvider.source),
1114
+ (config.defaultProvider.value || '—') +
1115
+ ' · ' +
1116
+ configSourceLabel(config.defaultProvider.source),
1064
1117
  );
1065
1118
  nowRow(
1066
1119
  summary,
@@ -1073,21 +1126,126 @@
1073
1126
  nowRow(summary, 'Precedência', list(config.precedence).join(' → '));
1074
1127
  els.configuration.appendChild(summary);
1075
1128
 
1129
+ const liveRouting = state.configData && state.configData.routing;
1130
+ if (liveRouting) {
1131
+ const routingSummary = el('dl', 'now-grid config-routing-summary');
1132
+ nowRow(routingSummary, 'Routing', liveRouting.mode + ' · perfil ' + liveRouting.profile);
1133
+ nowRow(
1134
+ routingSummary,
1135
+ 'Política',
1136
+ liveRouting.policy === 'recommended' ? 'recomendada (opt-in)' : 'score adaptativo',
1137
+ );
1138
+ els.configuration.appendChild(routingSummary);
1139
+
1140
+ if (state.configWritable) {
1141
+ const routingForm = el('form', 'config-form config-routing-form');
1142
+ const mode = el('select');
1143
+ mode.setAttribute('aria-label', 'Modo de routing');
1144
+ for (const value of ['off', 'shadow', 'recommend', 'active']) {
1145
+ const option = el('option', null, value);
1146
+ option.value = value;
1147
+ mode.appendChild(option);
1148
+ }
1149
+ mode.value = liveRouting.mode;
1150
+ const profile = el('select');
1151
+ profile.setAttribute('aria-label', 'Perfil de routing');
1152
+ for (const value of ['economy', 'balanced', 'quality', 'speed']) {
1153
+ const option = el('option', null, value);
1154
+ option.value = value;
1155
+ profile.appendChild(option);
1156
+ }
1157
+ profile.value = liveRouting.profile;
1158
+ const saveRouting = el('button', 'config-save', 'Salvar routing');
1159
+ saveRouting.type = 'submit';
1160
+ const applyPolicy = el(
1161
+ 'button',
1162
+ 'config-save config-policy',
1163
+ 'Aplicar política recomendada',
1164
+ );
1165
+ applyPolicy.type = 'button';
1166
+ const routingFeedback = el('span', 'muted');
1167
+ routingForm.appendChild(mode);
1168
+ routingForm.appendChild(profile);
1169
+ routingForm.appendChild(saveRouting);
1170
+ routingForm.appendChild(applyPolicy);
1171
+ routingForm.appendChild(routingFeedback);
1172
+ routingForm.addEventListener('submit', async (event) => {
1173
+ event.preventDefault();
1174
+ await saveRoutingConfig(
1175
+ { mode: mode.value, profile: profile.value },
1176
+ saveRouting,
1177
+ routingFeedback,
1178
+ );
1179
+ });
1180
+ applyPolicy.addEventListener('click', async () => {
1181
+ await saveRoutingConfig({ policy: 'recommended' }, applyPolicy, routingFeedback);
1182
+ });
1183
+ els.configuration.appendChild(routingForm);
1184
+ }
1185
+ }
1186
+
1076
1187
  const phases = el('div', 'config-phase-grid');
1077
1188
  for (const phase of list(config.phases)) {
1078
- const row = el('button', 'config-phase-row');
1079
- row.type = 'button';
1080
- row.appendChild(el('span', 'mono', phase.phase));
1081
- row.appendChild(el('span', null, phase.provider.value || '—'));
1082
- row.appendChild(el('span', null, phase.model.value || 'default do provider'));
1083
- row.appendChild(
1084
- el(
1085
- 'span',
1086
- 'muted',
1087
- configSourceLabel(phase.provider.source) + ' / ' + configSourceLabel(phase.model.source),
1088
- ),
1089
- );
1090
- row.addEventListener('click', () => openDrawer('phase', phase.phase));
1189
+ const row = el(state.configWritable ? 'form' : 'button', 'config-phase-row');
1190
+ if (!state.configWritable) row.type = 'button';
1191
+ if (state.configWritable) {
1192
+ row.classList.add('is-editable');
1193
+ const phaseButton = el('button', 'config-phase-open mono', phase.phase);
1194
+ phaseButton.type = 'button';
1195
+ phaseButton.addEventListener('click', () => openDrawer('phase', phase.phase));
1196
+ row.appendChild(phaseButton);
1197
+ const provider = el('select');
1198
+ provider.setAttribute('aria-label', 'Harness para ' + phase.phase);
1199
+ fillProviderSelect(provider, phase.provider.value || 'claude');
1200
+ const model = el('select');
1201
+ model.setAttribute('aria-label', 'Tier e modelo para ' + phase.phase);
1202
+ fillModelSelect(model, provider.value, phase.model.value || '');
1203
+ provider.addEventListener('change', () => fillModelSelect(model, provider.value, ''));
1204
+ const save = el('button', 'config-save', 'Salvar');
1205
+ save.type = 'submit';
1206
+ const feedback = el('span', 'muted config-phase-feedback');
1207
+ row.appendChild(provider);
1208
+ row.appendChild(model);
1209
+ row.appendChild(save);
1210
+ row.appendChild(feedback);
1211
+ row.addEventListener('submit', async (event) => {
1212
+ event.preventDefault();
1213
+ save.disabled = true;
1214
+ feedback.textContent = 'salvando…';
1215
+ try {
1216
+ const response = await fetch('api/config/agent', {
1217
+ method: 'POST',
1218
+ headers: { 'Content-Type': 'application/json' },
1219
+ body: JSON.stringify({
1220
+ provider: provider.value,
1221
+ model: model.value,
1222
+ phase: phase.phase,
1223
+ }),
1224
+ });
1225
+ const result = await response.json();
1226
+ if (!response.ok) throw new Error(result.error || 'falha ao salvar');
1227
+ feedback.textContent = 'salvo para a próxima execução';
1228
+ } catch (error) {
1229
+ feedback.textContent = error.message || 'falha ao salvar';
1230
+ } finally {
1231
+ save.disabled = false;
1232
+ }
1233
+ });
1234
+ } else {
1235
+ row.appendChild(el('span', 'mono', phase.phase));
1236
+ row.appendChild(el('span', null, phase.provider.value || '—'));
1237
+ row.appendChild(el('span', null, phase.model.value || 'default do provider'));
1238
+ row.appendChild(
1239
+ el(
1240
+ 'span',
1241
+ 'muted',
1242
+ configSourceLabel(phase.provider.source) +
1243
+ ' / ' +
1244
+ configSourceLabel(phase.model.source),
1245
+ ),
1246
+ );
1247
+ row.addEventListener('click', () => openDrawer('phase', phase.phase));
1248
+ }
1091
1249
  phases.appendChild(row);
1092
1250
  }
1093
1251
  els.configuration.appendChild(phases);
@@ -1135,6 +1293,68 @@
1135
1293
  els.configuration.appendChild(form);
1136
1294
  }
1137
1295
 
1296
+ function catalog() {
1297
+ return list(state.configData && state.configData.catalog);
1298
+ }
1299
+
1300
+ function fillProviderSelect(select, selected) {
1301
+ clear(select);
1302
+ const entries = catalog().filter((entry) => entry.installed && entry.authenticated);
1303
+ const providers = entries.length
1304
+ ? entries.map((entry) => entry.provider)
1305
+ : ['claude', 'codex', 'cursor', 'antigravity'];
1306
+ if (!providers.includes(selected)) providers.push(selected);
1307
+ for (const provider of providers) {
1308
+ const option = el('option', null, provider);
1309
+ option.value = provider;
1310
+ select.appendChild(option);
1311
+ }
1312
+ select.value = selected;
1313
+ }
1314
+
1315
+ function fillModelSelect(select, provider, selected) {
1316
+ clear(select);
1317
+ const defaultOption = el('option', null, 'default do provider');
1318
+ defaultOption.value = '';
1319
+ select.appendChild(defaultOption);
1320
+ const entry = catalog().find((candidate) => candidate.provider === provider);
1321
+ for (const model of list(entry && entry.models)) {
1322
+ if (!model.id) continue;
1323
+ const option = el('option', null, model.tier + ' · ' + model.id);
1324
+ option.value = model.id;
1325
+ select.appendChild(option);
1326
+ }
1327
+ if (
1328
+ selected &&
1329
+ !Array.prototype.some.call(select.options, (option) => option.value === selected)
1330
+ ) {
1331
+ const current = el('option', null, 'configurado · ' + selected);
1332
+ current.value = selected;
1333
+ select.appendChild(current);
1334
+ }
1335
+ select.value = selected || '';
1336
+ }
1337
+
1338
+ async function saveRoutingConfig(values, button, feedback) {
1339
+ button.disabled = true;
1340
+ feedback.textContent = 'salvando…';
1341
+ try {
1342
+ const response = await fetch('api/config/routing', {
1343
+ method: 'POST',
1344
+ headers: { 'Content-Type': 'application/json' },
1345
+ body: JSON.stringify(values),
1346
+ });
1347
+ const result = await response.json();
1348
+ if (!response.ok) throw new Error(result.error || 'falha ao salvar');
1349
+ state.configData.routing = Object.assign({}, state.configData.routing, values);
1350
+ feedback.textContent = 'salvo para execuções futuras';
1351
+ } catch (error) {
1352
+ feedback.textContent = error.message || 'falha ao salvar';
1353
+ } finally {
1354
+ button.disabled = false;
1355
+ }
1356
+ }
1357
+
1138
1358
  function renderPhases(snapshot) {
1139
1359
  clear(els.phases);
1140
1360
  if (snapshot.phases.length === 0) {
@@ -1144,7 +1364,9 @@
1144
1364
  for (const phase of snapshot.phases) {
1145
1365
  const item = el('li');
1146
1366
  item.className = 'detail-row';
1147
- item.appendChild(el('span', 'item-icon icon-' + phase.status, PHASE_ICONS[phase.status] || '○'));
1367
+ item.appendChild(
1368
+ el('span', 'item-icon icon-' + phase.status, PHASE_ICONS[phase.status] || '○'),
1369
+ );
1148
1370
  const main = el('div', 'item-main');
1149
1371
  main.appendChild(el('div', 'item-title', phase.name));
1150
1372
  if (phase.error) main.appendChild(el('div', 'item-error', phase.error));
@@ -1189,7 +1411,8 @@
1189
1411
  for (const story of snapshot.stories) {
1190
1412
  // stage vem com default no schema ('pending'), mas session.json gravado
1191
1413
  // antes da issue 38 pode chegar sem ele.
1192
- const storyStage = story.stage !== null && story.stage !== undefined ? story.stage : 'pending';
1414
+ const storyStage =
1415
+ story.stage !== null && story.stage !== undefined ? story.stage : 'pending';
1193
1416
  const item = el('li', storyStage === 'executing' ? 'story-executing' : null);
1194
1417
  item.classList.add('detail-row');
1195
1418
  const status = story.passes ? 'completed' : 'pending';
@@ -1202,10 +1425,15 @@
1202
1425
 
1203
1426
  // status/dependencies vêm com default no schema (backlog/[]), mas
1204
1427
  // session.json gravado antes de #29 pode chegar sem eles.
1205
- const storyStatus = story.status !== null && story.status !== undefined ? story.status : 'backlog';
1428
+ const storyStatus =
1429
+ story.status !== null && story.status !== undefined ? story.status : 'backlog';
1206
1430
  const meta = el('div', 'story-meta');
1207
1431
  meta.appendChild(
1208
- el('span', 'badge story-status-' + storyStatus, STORY_STATUS_LABELS[storyStatus] || storyStatus),
1432
+ el(
1433
+ 'span',
1434
+ 'badge story-status-' + storyStatus,
1435
+ STORY_STATUS_LABELS[storyStatus] || storyStatus,
1436
+ ),
1209
1437
  );
1210
1438
  const dependencies = story.dependencies || [];
1211
1439
  if (dependencies.length > 0) {
@@ -1248,7 +1476,11 @@
1248
1476
 
1249
1477
  const head = el('span', 'kanban-card-head');
1250
1478
  head.appendChild(
1251
- el('span', 'item-icon icon-' + (story.passes ? 'completed' : 'pending'), story.passes ? '✓' : '○'),
1479
+ el(
1480
+ 'span',
1481
+ 'item-icon icon-' + (story.passes ? 'completed' : 'pending'),
1482
+ story.passes ? '✓' : '○',
1483
+ ),
1252
1484
  );
1253
1485
  head.appendChild(el('span', 'story-id', story.id));
1254
1486
  card.appendChild(head);
@@ -1331,16 +1563,30 @@
1331
1563
  item.appendChild(title);
1332
1564
  const grid = el('dl', 'now-grid execution-grid');
1333
1565
  nowRow(grid, 'Harness', execution.agent?.harness || '—');
1334
- nowRow(grid, 'Modelo', execution.agent?.model?.resolved || execution.agent?.model?.requested || '—');
1566
+ nowRow(
1567
+ grid,
1568
+ 'Modelo',
1569
+ execution.agent?.model?.resolved || execution.agent?.model?.requested || '—',
1570
+ );
1335
1571
  nowRow(grid, 'Início', formatClock(execution.startedAt));
1336
- nowRow(grid, 'Fim', execution.finishedAt ? formatClock(execution.finishedAt) : 'em andamento');
1337
- nowRow(grid, 'Duração', metric(execution.durationMs) !== null ? formatDuration(execution.durationMs / 1000) : '—');
1572
+ nowRow(
1573
+ grid,
1574
+ 'Fim',
1575
+ execution.finishedAt ? formatClock(execution.finishedAt) : 'em andamento',
1576
+ );
1577
+ nowRow(
1578
+ grid,
1579
+ 'Duração',
1580
+ metric(execution.durationMs) !== null ? formatDuration(execution.durationMs / 1000) : '—',
1581
+ );
1338
1582
  nowRow(grid, 'Tokens', formatUsage(execution.usage || {}) || '—');
1339
1583
  nowRow(grid, 'Custo', executionCost(execution));
1340
- if (execution.correctionCycle) nowRow(grid, 'Correção', 'ciclo ' + execution.correctionCycle);
1584
+ if (execution.correctionCycle)
1585
+ nowRow(grid, 'Correção', 'ciclo ' + execution.correctionCycle);
1341
1586
  if (execution.verdict?.status) nowRow(grid, 'Veredito', execution.verdict.status);
1342
1587
  item.appendChild(grid);
1343
- if (execution.failure?.message) item.appendChild(el('p', 'item-error', execution.failure.message));
1588
+ if (execution.failure?.message)
1589
+ item.appendChild(el('p', 'item-error', execution.failure.message));
1344
1590
  timeline.appendChild(item);
1345
1591
  }
1346
1592
  body.appendChild(timeline);
@@ -1380,7 +1626,9 @@
1380
1626
  if (entries.length === 0) return;
1381
1627
  drawerSection('Diagnóstico global persistente', (body) => {
1382
1628
  const details = el('details', 'process-output');
1383
- details.appendChild(el('summary', null, entries.length + ' registro(s) em ~/.issue-flow/logs'));
1629
+ details.appendChild(
1630
+ el('summary', null, entries.length + ' registro(s) em ~/.issue-flow/logs'),
1631
+ );
1384
1632
  const output = el('pre', 'process-output-body');
1385
1633
  output.textContent = entries
1386
1634
  .slice(0, 200)
@@ -1397,7 +1645,8 @@
1397
1645
  const kind = state.selectedDetail.kind;
1398
1646
  const id = state.selectedDetail.id;
1399
1647
  const story = kind === 'story' ? getStoryById(snapshot, id) : null;
1400
- const phase = kind === 'phase' ? list(snapshot.phases).find((entry) => entry.name === id) : null;
1648
+ const phase =
1649
+ kind === 'phase' ? list(snapshot.phases).find((entry) => entry.name === id) : null;
1401
1650
  if ((kind === 'story' && story === null) || (kind === 'phase' && !phase)) {
1402
1651
  closeDrawer();
1403
1652
  return;
@@ -1410,9 +1659,15 @@
1410
1659
  const grid = el('dl', 'now-grid drawer-summary-grid');
1411
1660
  nowRow(grid, 'Início', phase.startedAt ? formatClock(phase.startedAt) : '—');
1412
1661
  nowRow(grid, 'Fim', phase.endedAt ? formatClock(phase.endedAt) : '—');
1413
- nowRow(grid, 'Duração', metric(phase.durationSeconds) !== null ? formatDuration(phase.durationSeconds) : '—');
1662
+ nowRow(
1663
+ grid,
1664
+ 'Duração',
1665
+ metric(phase.durationSeconds) !== null ? formatDuration(phase.durationSeconds) : '—',
1666
+ );
1414
1667
  nowRow(grid, 'Uso total', formatUsage(phase) || '—');
1415
- const configured = list(snapshot.configuration?.phases).find((entry) => entry.phase === phase.name);
1668
+ const configured = list(snapshot.configuration?.phases).find(
1669
+ (entry) => entry.phase === phase.name,
1670
+ );
1416
1671
  if (configured) {
1417
1672
  nowRow(grid, 'Harness efetivo', configured.provider.value || '—');
1418
1673
  nowRow(grid, 'Origem', configSourceLabel(configured.provider.source));
@@ -1452,7 +1707,8 @@
1452
1707
  return;
1453
1708
  }
1454
1709
  const items = el('ul', 'drawer-list');
1455
- for (const criterion of story.acceptanceCriteria) items.appendChild(el('li', null, criterion));
1710
+ for (const criterion of story.acceptanceCriteria)
1711
+ items.appendChild(el('li', null, criterion));
1456
1712
  body.appendChild(items);
1457
1713
  });
1458
1714
 
@@ -1566,9 +1822,7 @@
1566
1822
  function renderLogs(snapshot) {
1567
1823
  clear(els.logs);
1568
1824
  const filter = state.logFilter;
1569
- const entries = snapshot.logs.filter(
1570
- (entry) => filter === 'all' || entry.level === filter,
1571
- );
1825
+ const entries = snapshot.logs.filter((entry) => filter === 'all' || entry.level === filter);
1572
1826
  if (entries.length === 0) {
1573
1827
  els.logs.appendChild(el('p', 'empty', 'Nenhum log para exibir.'));
1574
1828
  return;
@@ -1612,9 +1866,20 @@
1612
1866
  case 'agent:activity':
1613
1867
  return 'Atividade recebida de ' + event.provider;
1614
1868
  case 'agent:result':
1615
- return event.provider + (event.success ? ' concluiu a tentativa' : ' falhou: ' + (event.failureKind || 'desconhecida'));
1869
+ return (
1870
+ event.provider +
1871
+ (event.success
1872
+ ? ' concluiu a tentativa'
1873
+ : ' falhou: ' + (event.failureKind || 'desconhecida'))
1874
+ );
1616
1875
  case 'failover':
1617
- return 'Failover de ' + event.from + ' para ' + event.to + (event.reason ? ': ' + event.reason : '');
1876
+ return (
1877
+ 'Failover de ' +
1878
+ event.from +
1879
+ ' para ' +
1880
+ event.to +
1881
+ (event.reason ? ': ' + event.reason : '')
1882
+ );
1618
1883
  case 'correction:cycle':
1619
1884
  return 'Ciclo de correção ' + event.cycle + '/' + event.maxCycles;
1620
1885
  case 'log':
@@ -1743,7 +2008,11 @@
1743
2008
  const healthRes = await fetch('api/health', { cache: 'no-store' });
1744
2009
  serverInstanceChanged(healthRes);
1745
2010
  const health = await healthRes.json();
1746
- state.configWritable = list(health.capabilities).includes('config:agent:write');
2011
+ renderMonitorVersion(health.version);
2012
+ const capabilities = list(health.capabilities);
2013
+ state.configWritable =
2014
+ capabilities.includes('config:agent:write') &&
2015
+ capabilities.includes('config:routing:write');
1747
2016
  const suggested = Number(health.refreshSeconds);
1748
2017
  if (stored === null && Number.isFinite(suggested) && suggested > 0) {
1749
2018
  state.refreshSeconds = suggested;
@@ -37,7 +37,10 @@
37
37
  <div id="view-dashboard" class="view" hidden>
38
38
  <header class="card header">
39
39
  <div class="header-main">
40
- <h1><span class="brand">issue-flow</span></h1>
40
+ <h1>
41
+ <span class="brand">issue-flow</span>
42
+ <span id="app-version-dashboard" class="app-version" hidden></span>
43
+ </h1>
41
44
  <p class="muted">Execuções ativas</p>
42
45
  </div>
43
46
  <div class="header-side">
@@ -69,6 +72,7 @@
69
72
  </button>
70
73
  <h1>
71
74
  <span class="brand">issue-flow</span>
75
+ <span id="app-version" class="app-version" hidden></span>
72
76
  <a id="issue-link" class="issue-link" target="_blank" rel="noopener" hidden>#—</a>
73
77
  </h1>
74
78
  <p id="branch-line" class="muted mono"></p>
@@ -142,7 +146,7 @@
142
146
  <section class="card">
143
147
  <div class="section-head">
144
148
  <h2>Harnesses e configuração efetiva</h2>
145
- <span class="muted">preferências futuras e overrides desta execução</span>
149
+ <span class="muted">versões, preferências futuras e overrides desta execução</span>
146
150
  </div>
147
151
  <div id="configuration"></div>
148
152
  </section>
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- AGENT_COMMAND_SCHEMA_VERSION,
4
- persistFirstAgentChoice,
5
- runAgent,
6
- runAgentUse,
7
- writeAgentPreference
8
- } from "./chunk-6FXOU2KR.js";
9
- import "./chunk-WM3Y7Q4A.js";
10
- import "./chunk-VR34TXHF.js";
11
- import "./chunk-NVZMBXTE.js";
12
- import "./chunk-6QYMSNVA.js";
13
- import "./chunk-AYYMW33T.js";
14
- import "./chunk-HAKDLR6N.js";
15
- import "./chunk-GSZCQUCY.js";
16
- import "./chunk-TBDP6QPJ.js";
17
- import "./chunk-P3LRUNC6.js";
18
- import "./chunk-UNQYMR3H.js";
19
- import "./chunk-XQHFC62U.js";
20
- import "./chunk-XDKGA6UX.js";
21
- import "./chunk-7Z4Y3VKM.js";
22
- import "./chunk-XEYH6ZZI.js";
23
- export {
24
- AGENT_COMMAND_SCHEMA_VERSION,
25
- persistFirstAgentChoice,
26
- runAgent,
27
- runAgentUse,
28
- writeAgentPreference
29
- };
30
- //# sourceMappingURL=agent-AT64RXTA.js.map