cli-jaw 1.7.6 → 1.7.7

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 (154) hide show
  1. package/dist/src/agent/events.js +55 -12
  2. package/dist/src/agent/events.js.map +1 -1
  3. package/dist/src/agent/lifecycle-handler.js +24 -10
  4. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  5. package/dist/src/agent/live-run-state.js +42 -0
  6. package/dist/src/agent/live-run-state.js.map +1 -0
  7. package/dist/src/agent/spawn.js +81 -25
  8. package/dist/src/agent/spawn.js.map +1 -1
  9. package/dist/src/cli/claude-models.js +2 -0
  10. package/dist/src/cli/claude-models.js.map +1 -1
  11. package/dist/src/cli/registry.js +3 -2
  12. package/dist/src/cli/registry.js.map +1 -1
  13. package/dist/src/core/bus.js +2 -2
  14. package/dist/src/core/bus.js.map +1 -1
  15. package/dist/src/memory/bootstrap.js +76 -1
  16. package/dist/src/memory/bootstrap.js.map +1 -1
  17. package/dist/src/memory/identity.js +5 -1
  18. package/dist/src/memory/identity.js.map +1 -1
  19. package/dist/src/memory/runtime.js +29 -4
  20. package/dist/src/memory/runtime.js.map +1 -1
  21. package/dist/src/memory/shared.js.map +1 -1
  22. package/dist/src/orchestrator/distribute.js +5 -4
  23. package/dist/src/orchestrator/distribute.js.map +1 -1
  24. package/dist/src/orchestrator/gateway.js +1 -1
  25. package/dist/src/orchestrator/gateway.js.map +1 -1
  26. package/dist/src/orchestrator/research.js +2 -0
  27. package/dist/src/orchestrator/research.js.map +1 -1
  28. package/dist/src/prompt/soul-bootstrap-prompt.js +37 -0
  29. package/dist/src/prompt/soul-bootstrap-prompt.js.map +1 -0
  30. package/dist/src/routes/jaw-memory.js +124 -8
  31. package/dist/src/routes/jaw-memory.js.map +1 -1
  32. package/dist/src/routes/memory.js +10 -8
  33. package/dist/src/routes/memory.js.map +1 -1
  34. package/dist/src/routes/orchestrate.js +4 -1
  35. package/dist/src/routes/orchestrate.js.map +1 -1
  36. package/package.json +1 -1
  37. package/public/css/chat.css +1 -0
  38. package/public/dist/assets/architecture-YZFGNWBL-B2Nc8YC_.js +1 -0
  39. package/public/dist/assets/architectureDiagram-Q4EWVU46-EML5rtmP.js +1 -0
  40. package/public/dist/assets/blockDiagram-DXYQGD6D-Ds9bBl-N.js +1 -0
  41. package/public/dist/assets/c4Diagram-AHTNJAMY-BVpUT3Om.js +1 -0
  42. package/public/dist/assets/classDiagram-6PBFFD2Q-DG8IYzhr.js +1 -0
  43. package/public/dist/assets/classDiagram-v2-HSJHXN6E-B5j0d5gs.js +1 -0
  44. package/public/dist/assets/constants-Bzu8ZQYX.js +1 -0
  45. package/public/dist/assets/cose-bilkent-S5V4N54A-kNrexiHE.js +1 -0
  46. package/public/dist/assets/dagre-KV5264BT-Ylb1W0yQ.js +1 -0
  47. package/public/dist/assets/diagram-5BDNPKRD-CPxqMVrp.js +1 -0
  48. package/public/dist/assets/diagram-G4DWMVQ6-Dm1xfj8I.js +1 -0
  49. package/public/dist/assets/diagram-MMDJMWI5-VsjfxFEK.js +1 -0
  50. package/public/dist/assets/diagram-TYMM5635-CrMZri_r.js +1 -0
  51. package/public/dist/assets/{employees-CBTZXCUO.js → employees-MOWdS3OJ.js} +1 -1
  52. package/public/dist/assets/erDiagram-SMLLAGMA-Felxus25.js +1 -0
  53. package/public/dist/assets/flowDiagram-DWJPFMVM-kQrkSkw2.js +1 -0
  54. package/public/dist/assets/ganttDiagram-T4ZO3ILL-CulD-LkD.js +1 -0
  55. package/public/dist/assets/gitGraph-7Q5UKJZL-DbbbZYM5.js +1 -0
  56. package/public/dist/assets/gitGraphDiagram-UUTBAWPF-oTUYJsKL.js +1 -0
  57. package/public/dist/assets/index-CLvmDyQF.css +1 -0
  58. package/public/dist/assets/index-DKwE5mA1.js +32 -0
  59. package/public/dist/assets/info-OMHHGYJF-auV0JwD8.js +1 -0
  60. package/public/dist/assets/infoDiagram-42DDH7IO-B2XOJXrL.js +1 -0
  61. package/public/dist/assets/ishikawaDiagram-UXIWVN3A-CsmszxH_.js +1 -0
  62. package/public/dist/assets/journeyDiagram-VCZTEJTY-MmzEBROj.js +1 -0
  63. package/public/dist/assets/kanban-definition-6JOO6SKY-DcbxzAKu.js +1 -0
  64. package/public/dist/assets/memory-BQsMwpMr.js +20 -0
  65. package/public/dist/assets/memory-BsSHMs1m.js +1 -0
  66. package/public/dist/assets/mermaid.core-CoRN09Dx.js +1 -0
  67. package/public/dist/assets/mindmap-definition-QFDTVHPH-CMqZWcUf.js +1 -0
  68. package/public/dist/assets/packet-4T2RLAQJ-C_PFKo5G.js +1 -0
  69. package/public/dist/assets/pie-ZZUOXDRM-CcqLxwbG.js +1 -0
  70. package/public/dist/assets/pieDiagram-DEJITSTG-C9r3BQ9f.js +1 -0
  71. package/public/dist/assets/quadrantDiagram-34T5L4WZ-Du4pEL4T.js +1 -0
  72. package/public/dist/assets/radar-PYXPWWZC-BWOF5omS.js +1 -0
  73. package/public/dist/assets/{render-CNEhXgGl.js → render-6la5reIT.js} +1 -1
  74. package/public/dist/assets/requirementDiagram-MS252O5E-Dyz9eEFP.js +1 -0
  75. package/public/dist/assets/sankeyDiagram-XADWPNL6-DLgUCKs2.js +1 -0
  76. package/public/dist/assets/sequenceDiagram-FGHM5R23-c0nA9K_Q.js +1 -0
  77. package/public/dist/assets/settings-CDPMT-Ed.js +1 -0
  78. package/public/dist/assets/{settings-DPerKbn4.js → settings-CItUiBUH.js} +1 -1
  79. package/public/dist/assets/{skills-BibeUJO1.js → skills-B5Ne7q-a.js} +1 -1
  80. package/public/dist/assets/skills-wYJkTYAo.js +1 -0
  81. package/public/dist/assets/{slash-commands-CgWxFIcD.js → slash-commands-D8TM1_bi.js} +1 -1
  82. package/public/dist/assets/slash-commands-YfX2FoQO.js +1 -0
  83. package/public/dist/assets/stateDiagram-FHFEXIEX-D2aVOdby.js +1 -0
  84. package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-DuwdzBLR.js +1 -0
  85. package/public/dist/assets/timeline-definition-GMOUNBTQ-PuaZYI31.js +1 -0
  86. package/public/dist/assets/treeView-SZITEDCU-D8mODguI.js +1 -0
  87. package/public/dist/assets/treemap-W4RFUUIX-DqqBuiM1.js +1 -0
  88. package/public/dist/assets/ui-CetOMHcw.js +130 -0
  89. package/public/dist/assets/ui-Dsg2wSAw.js +1 -0
  90. package/public/dist/assets/{vendor-mermaid-C2RBgdM6.js → vendor-mermaid-UktBx7L0.js} +4 -4
  91. package/public/dist/assets/vennDiagram-DHZGUBPP-4blQ8E1z.js +1 -0
  92. package/public/dist/assets/wardley-RL74JXVD-Dn1IRCw2.js +1 -0
  93. package/public/dist/assets/wardleyDiagram-NUSXRM2D-OUBAKNJu.js +1 -0
  94. package/public/dist/assets/ws-ZgyiUpJG.js +2 -0
  95. package/public/dist/assets/xychartDiagram-5P7HB3ND-CyZD7ovz.js +1 -0
  96. package/public/dist/index.html +2 -2
  97. package/public/js/constants.ts +4 -3
  98. package/public/js/features/memory.ts +48 -0
  99. package/public/js/features/process-block.ts +19 -34
  100. package/public/js/main.ts +2 -1
  101. package/public/js/streaming-render.ts +14 -0
  102. package/public/js/ui.ts +35 -2
  103. package/public/js/virtual-scroll.ts +60 -20
  104. package/public/js/ws.ts +24 -18
  105. package/public/dist/assets/architecture-YZFGNWBL-ztGcIsMX.js +0 -1
  106. package/public/dist/assets/architectureDiagram-Q4EWVU46-BeLDNmwN.js +0 -1
  107. package/public/dist/assets/blockDiagram-DXYQGD6D-ChvfvChp.js +0 -1
  108. package/public/dist/assets/c4Diagram-AHTNJAMY-BzjAXXeE.js +0 -1
  109. package/public/dist/assets/classDiagram-6PBFFD2Q-Ce2Zjeeb.js +0 -1
  110. package/public/dist/assets/classDiagram-v2-HSJHXN6E-DGGliiix.js +0 -1
  111. package/public/dist/assets/constants-DwGsi0Gn.js +0 -1
  112. package/public/dist/assets/cose-bilkent-S5V4N54A-T-OwcThC.js +0 -1
  113. package/public/dist/assets/dagre-KV5264BT-DmdK-8pb.js +0 -1
  114. package/public/dist/assets/diagram-5BDNPKRD-Bf8S6ACH.js +0 -1
  115. package/public/dist/assets/diagram-G4DWMVQ6-C37hs32X.js +0 -1
  116. package/public/dist/assets/diagram-MMDJMWI5-9NNeBmGR.js +0 -1
  117. package/public/dist/assets/diagram-TYMM5635-l-8r-KVr.js +0 -1
  118. package/public/dist/assets/erDiagram-SMLLAGMA-BfSqi5hi.js +0 -1
  119. package/public/dist/assets/flowDiagram-DWJPFMVM-B6am8zYz.js +0 -1
  120. package/public/dist/assets/ganttDiagram-T4ZO3ILL-weu0QfVv.js +0 -1
  121. package/public/dist/assets/gitGraph-7Q5UKJZL-BucTBJ0C.js +0 -1
  122. package/public/dist/assets/gitGraphDiagram-UUTBAWPF-D4BnXgVC.js +0 -1
  123. package/public/dist/assets/index-BCag5Xso.js +0 -50
  124. package/public/dist/assets/index-DzQ_i0rm.css +0 -1
  125. package/public/dist/assets/info-OMHHGYJF-OpOBLEsS.js +0 -1
  126. package/public/dist/assets/infoDiagram-42DDH7IO-9W0kVtoy.js +0 -1
  127. package/public/dist/assets/ishikawaDiagram-UXIWVN3A-BEgeMMA5.js +0 -1
  128. package/public/dist/assets/journeyDiagram-VCZTEJTY-gIlNwmx5.js +0 -1
  129. package/public/dist/assets/kanban-definition-6JOO6SKY-DV9gfO6_.js +0 -1
  130. package/public/dist/assets/mermaid.core-CYqc8Qyq.js +0 -1
  131. package/public/dist/assets/mindmap-definition-QFDTVHPH-DFHJRlCi.js +0 -1
  132. package/public/dist/assets/packet-4T2RLAQJ-DxyOEAi5.js +0 -1
  133. package/public/dist/assets/pie-ZZUOXDRM-CU7m5wDm.js +0 -1
  134. package/public/dist/assets/pieDiagram-DEJITSTG-BEGiEzHN.js +0 -1
  135. package/public/dist/assets/quadrantDiagram-34T5L4WZ-E5jEMjzC.js +0 -1
  136. package/public/dist/assets/radar-PYXPWWZC-CNpXegnm.js +0 -1
  137. package/public/dist/assets/requirementDiagram-MS252O5E-B3fjwTBx.js +0 -1
  138. package/public/dist/assets/sankeyDiagram-XADWPNL6-BIyouHFw.js +0 -1
  139. package/public/dist/assets/sequenceDiagram-FGHM5R23-CROknRPY.js +0 -1
  140. package/public/dist/assets/settings-CnBXCAy-.js +0 -1
  141. package/public/dist/assets/skills-CwL2wGFw.js +0 -1
  142. package/public/dist/assets/slash-commands-DyMNhbjH.js +0 -1
  143. package/public/dist/assets/stateDiagram-FHFEXIEX-DqgrX77_.js +0 -1
  144. package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-CMxISNLC.js +0 -1
  145. package/public/dist/assets/timeline-definition-GMOUNBTQ-CheLYWOf.js +0 -1
  146. package/public/dist/assets/treeView-SZITEDCU-CWgmTXw-.js +0 -1
  147. package/public/dist/assets/treemap-W4RFUUIX-CMkxaF1N.js +0 -1
  148. package/public/dist/assets/ui-D5O0jrTm.js +0 -1
  149. package/public/dist/assets/ui-hnmEjpWt.js +0 -134
  150. package/public/dist/assets/vennDiagram-DHZGUBPP-Cm4YZzbv.js +0 -1
  151. package/public/dist/assets/wardley-RL74JXVD-HPEa5s3y.js +0 -1
  152. package/public/dist/assets/wardleyDiagram-NUSXRM2D-vZvODvIY.js +0 -1
  153. package/public/dist/assets/ws-B1CW7OER.js +0 -2
  154. package/public/dist/assets/xychartDiagram-5P7HB3ND-DdPllF4_.js +0 -1
@@ -25,9 +25,9 @@
25
25
  href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap"
26
26
  rel="stylesheet">
27
27
  <!-- Vite handles module bundling in dev (HMR) and production (build) -->
28
- <script type="module" crossorigin src="/dist/assets/index-BCag5Xso.js"></script>
28
+ <script type="module" crossorigin src="/dist/assets/index-DKwE5mA1.js"></script>
29
29
  <link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-Bjnw0wQ6.css">
30
- <link rel="stylesheet" crossorigin href="/dist/assets/index-DzQ_i0rm.css">
30
+ <link rel="stylesheet" crossorigin href="/dist/assets/index-CLvmDyQF.css">
31
31
  </head>
32
32
 
33
33
  <body>
@@ -13,13 +13,13 @@ export type CliRegistry = Record<string, CliEntry>;
13
13
  const FALLBACK_CLI_REGISTRY: CliRegistry = {
14
14
  claude: {
15
15
  label: 'Claude',
16
- efforts: ['low', 'medium', 'high'],
17
- models: ['sonnet', 'opus', 'sonnet[1m]', 'opus[1m]', 'haiku'],
16
+ efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
17
+ models: ['claude-opus-4-6', 'claude-opus-4-6[1m]', 'sonnet', 'opus', 'sonnet[1m]', 'opus[1m]', 'haiku'],
18
18
  },
19
19
  codex: {
20
20
  label: 'Codex',
21
21
  efforts: ['low', 'medium', 'high', 'xhigh'],
22
- models: ['gpt-5.4', 'gpt-5.3-codex', 'gpt-5.3-codex-spark', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini'],
22
+ models: ['gpt-5.5', 'gpt-5.4', 'gpt-5.3-codex', 'gpt-5.3-codex-spark', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini'],
23
23
  },
24
24
  gemini: {
25
25
  label: 'Gemini',
@@ -57,6 +57,7 @@ const FALLBACK_CLI_REGISTRY: CliRegistry = {
57
57
  efforts: ['low', 'medium', 'high'],
58
58
  effortNote: '-> ~/.copilot/config.json',
59
59
  models: [
60
+ 'gpt-5.5',
60
61
  'claude-sonnet-4.6',
61
62
  'claude-opus-4.6',
62
63
  'claude-haiku-4.5',
@@ -50,6 +50,7 @@ interface AdvancedMemoryStatus {
50
50
  migrationLocked?: boolean;
51
51
  staleWarnings?: string[];
52
52
  hasSoul?: boolean;
53
+ soulSynthesized?: boolean;
53
54
  }
54
55
 
55
56
  interface AdvancedMemoryFiles {
@@ -83,6 +84,10 @@ function syncSidebarBadge(status: AdvancedMemoryStatus | null, basicCount: numbe
83
84
  sideBtn.innerHTML = `${ICONS.brain} Memory · <span style="color:var(--accent)">${t('updateNeeded')}</span>`;
84
85
  return;
85
86
  }
87
+ if (status?.enabled && status?.hasSoul && !status?.soulSynthesized) {
88
+ sideBtn.innerHTML = `${ICONS.brain} Memory · <span style="color:var(--accent)">Soul 최적화</span>`;
89
+ return;
90
+ }
86
91
  const state = status?.indexState === 'ready'
87
92
  ? 'Ready'
88
93
  : status?.state === 'not_initialized'
@@ -105,6 +110,11 @@ function renderStatusBanner(status: AdvancedMemoryStatus | null) {
105
110
  <button id="advUpgradeSoulBtn" class="btn-sm" style="margin-left:8px">${t('memoryUpdateBtn')}</button>`;
106
111
  return;
107
112
  }
113
+ if (status.hasSoul && !status.soulSynthesized) {
114
+ banner.innerHTML = `<span>Soul identity can be personalized with your active CLI.</span>
115
+ <button id="advSynthesizeSoulBtn" class="btn-sm" style="margin-left:8px">Soul 최적화</button>`;
116
+ return;
117
+ }
108
118
  if (status.state === 'not_initialized') {
109
119
  banner.textContent = 'Integrated memory is preparing its index. Temporary fallback memory context is active until initialization completes.';
110
120
  return;
@@ -344,6 +354,32 @@ export async function upgradeSoulMemory(): Promise<void> {
344
354
  renderStatusBanner(freshStatus);
345
355
  }
346
356
 
357
+ export async function synthesizeSoul(): Promise<void> {
358
+ setAdvBusy(true);
359
+ setAdvBanner('Soul 최적화 중... (활성 CLI가 작업합니다)', true);
360
+ const result = await apiJson<{
361
+ ok: boolean;
362
+ reason?: string;
363
+ action?: string;
364
+ }>('/api/soul/bootstrap', 'POST', {});
365
+ setAdvBusy(false);
366
+ if (!result?.ok) {
367
+ const msg = result?.reason === 'already_synthesized'
368
+ ? '✓ Soul already optimized.'
369
+ : result?.reason === 'no_active_agent'
370
+ ? 'No active CLI agent. Please start an agent first.'
371
+ : `Soul optimization failed: ${result?.reason || 'unknown'}`;
372
+ setAdvBanner(msg);
373
+ return;
374
+ }
375
+ setAdvBanner('✓ Soul 최적화 프롬프트 전송됨. 채팅창을 확인하세요.');
376
+ await openMemoryModal();
377
+ switchMemTab('status');
378
+ const freshStatus = await apiJson<any>('/api/memory/status');
379
+ syncSidebarBadge(freshStatus, 0);
380
+ renderStatusBanner(freshStatus);
381
+ }
382
+
347
383
  export async function reindexAdvancedMemory(): Promise<void> {
348
384
  setAdvBusy(true);
349
385
  setAdvBanner('메모리 인덱스를 재생성하는 중...', true);
@@ -387,6 +423,18 @@ export function bindAdvancedProviderUi(): void {
387
423
  return;
388
424
  }
389
425
 
426
+ /** Lightweight sidebar-only refresh triggered by WS memory_status events */
427
+ export async function refreshMemorySidebar(): Promise<void> {
428
+ try {
429
+ const [basic, status] = await Promise.all([
430
+ api<MemoryData>('/api/memory-files'),
431
+ api<AdvancedMemoryStatus>('/api/memory/status'),
432
+ ]);
433
+ syncSidebarBadge(status, basic?.files?.length || 0);
434
+ renderStatusBanner(status);
435
+ } catch { /* best effort */ }
436
+ }
437
+
390
438
  export async function triggerFlushNow(): Promise<void> {
391
439
  const btn = $('memFlushNowBtn') as HTMLButtonElement | null;
392
440
  if (btn) btn.disabled = true;
@@ -40,12 +40,6 @@ function previewText(text: string, max = 120): string {
40
40
  return singleLine.length > max ? `${singleLine.slice(0, max - 1)}…` : singleLine;
41
41
  }
42
42
 
43
- function hasExpandableDetail(step: ProcessStep): boolean {
44
- const detail = (step.detail || '').trim();
45
- if (!detail) return false;
46
- return detail !== (step.label || '').trim();
47
- }
48
-
49
43
  function renderStep(step: ProcessStep): string {
50
44
  const dotClass = `process-step-dot ${step.status}`;
51
45
  const badgeClass = `process-step-badge ${step.type}`;
@@ -54,34 +48,25 @@ function renderStep(step: ProcessStep): string {
54
48
  const detail = step.detail || '';
55
49
  const detailId = `process-detail-${step.id}`;
56
50
 
57
- // Only create expandable DOM when there's actual detail content
58
- // to prevent DOM bloat (50 msgs × 10 steps = 500 extra DOM trees)
59
- if (hasExpandableDetail(step)) {
60
- const snippetPreview = previewText(detail, step.type === 'thinking' ? 120 : 80);
61
- const snippetHtml = snippetPreview
62
- ? `<span class="process-step-snippet">${escapeHtml(snippetPreview)}</span>`
63
- : '';
64
- return `<div class="process-step process-step-expandable" data-step-id="${step.id}" data-type="${step.type}">
65
- <button class="process-step-toggle" aria-expanded="false" aria-controls="${detailId}">
66
- <span class="${dotClass}"></span>
67
- <span class="${badgeClass}">${badgeText}</span>
68
- <span class="process-step-main">
69
- <span class="process-step-label">${label}</span>
70
- ${snippetHtml}
71
- </span>
72
- <span class="process-step-chevron">${ICONS.chevronRight}</span>
73
- </button>
74
- <div class="process-step-details collapsed" id="${detailId}">
75
- <pre class="process-step-full">${escapeHtml(detail)}</pre>
76
- </div>
77
- </div>`;
78
- }
79
-
80
- // Lightweight flat render — no toggle, no hidden detail section
81
- return `<div class="process-step" data-step-id="${step.id}" data-type="${step.type}">
82
- <span class="${dotClass}"></span>
83
- <span class="${badgeClass}">${badgeText}</span>
84
- <span class="process-step-label">${label}</span>
51
+ // Always render expandable VS (THRESHOLD=1) keeps DOM count low
52
+ // so the extra elements per step are negligible
53
+ const snippetPreview = previewText(detail, step.type === 'thinking' ? 120 : 80);
54
+ const snippetHtml = snippetPreview
55
+ ? `<span class="process-step-snippet">${escapeHtml(snippetPreview)}</span>`
56
+ : '';
57
+ return `<div class="process-step process-step-expandable" data-step-id="${step.id}" data-type="${step.type}">
58
+ <button class="process-step-toggle" aria-expanded="false" aria-controls="${detailId}">
59
+ <span class="${dotClass}"></span>
60
+ <span class="${badgeClass}">${badgeText}</span>
61
+ <span class="process-step-main">
62
+ <span class="process-step-label">${label}</span>
63
+ ${snippetHtml}
64
+ </span>
65
+ <span class="process-step-chevron">${ICONS.chevronRight}</span>
66
+ </button>
67
+ <div class="process-step-details collapsed" id="${detailId}">
68
+ <pre class="process-step-full">${escapeHtml(detail)}</pre>
69
+ </div>
85
70
  </div>`;
86
71
  }
87
72
 
package/public/js/main.ts CHANGED
@@ -43,7 +43,7 @@ import {
43
43
  import {
44
44
  openMemoryModal, closeMemoryModal, switchMemTab, setMemEnabled,
45
45
  saveMemSettings, deleteMemFile, viewMemFile,
46
- rerunAdvancedBootstrap, reindexAdvancedMemory, upgradeSoulMemory, openCorruptedFolder,
46
+ rerunAdvancedBootstrap, reindexAdvancedMemory, upgradeSoulMemory, synthesizeSoul, openCorruptedFolder,
47
47
  bindAdvancedProviderUi, triggerFlushNow
48
48
  } from './features/memory.js';
49
49
  import { state } from './state.js';
@@ -398,6 +398,7 @@ document.getElementById('advReimportBtn')?.addEventListener('click', rerunAdvanc
398
398
  document.getElementById('advOpenCorruptedBtn')?.addEventListener('click', openCorruptedFolder);
399
399
  document.getElementById('advStatusBanner')?.addEventListener('click', (e) => {
400
400
  if ((e.target as HTMLElement)?.id === 'advUpgradeSoulBtn') upgradeSoulMemory();
401
+ if ((e.target as HTMLElement)?.id === 'advSynthesizeSoulBtn') synthesizeSoul();
401
402
  });
402
403
  bindAdvancedProviderUi();
403
404
 
@@ -25,6 +25,20 @@ export function createStreamRenderer(el: HTMLElement): StreamState {
25
25
  };
26
26
  }
27
27
 
28
+ export function hydrateStreamRenderer(el: HTMLElement, text = ''): StreamState {
29
+ const ss = createStreamRenderer(el);
30
+ if (text) {
31
+ ss.chunks = [text];
32
+ ss.fullText = text;
33
+ ss.textDirty = false;
34
+ ss.element.innerHTML = renderMarkdown(text, true) +
35
+ '<span class="stream-cursor" aria-hidden="true"></span>';
36
+ } else {
37
+ ss.element.innerHTML = '<span class="stream-cursor" aria-hidden="true"></span>';
38
+ }
39
+ return ss;
40
+ }
41
+
28
42
  function getFullText(ss: StreamState): string {
29
43
  if (ss.textDirty) {
30
44
  ss.fullText = ss.chunks.join('');
package/public/js/ui.ts CHANGED
@@ -9,7 +9,7 @@ import { api } from './api.js';
9
9
  import { cacheMessages, getCachedMessages, appendCachedMessage, upsertMessage, setMessageScope, getScopedMessages } from './features/idb-cache.js';
10
10
  import { getVirtualScroll, VS_THRESHOLD, type VirtualItem } from './virtual-scroll.js';
11
11
  import { bootstrapVirtualHistory, type VirtualHistoryBootstrapDeps } from './virtual-scroll-bootstrap.js';
12
- import { createStreamRenderer, appendChunk, finalizeStream, type StreamState } from './streaming-render.js';
12
+ import { createStreamRenderer, appendChunk, finalizeStream, hydrateStreamRenderer, type StreamState } from './streaming-render.js';
13
13
  import { activateWidgets } from './diagram/iframe-renderer.js';
14
14
  import { renderLiveToolActivity, cleanupToolElements, bindToolItemInteractions, type ToolLogEntry } from './features/tool-ui.js';
15
15
  import { ICONS, emojiToIcon, emojiToStatus, isCompletionEmoji } from './icons.js';
@@ -25,6 +25,8 @@ import {
25
25
  type ProcessStep,
26
26
  } from './features/process-block.js';
27
27
  interface MessageItem { role: string; content: string; tool_log?: string | null; cli?: string | null; }
28
+ interface QueuedOverlayItem { id: string; prompt: string; source?: string; ts?: number; }
29
+ interface ActiveRunSnapshot { running?: boolean; cli?: string; text?: string; toolLog?: ToolLogEntry[]; }
28
30
 
29
31
  function parseToolLog(toolLog?: string | null): ToolLogEntry[] {
30
32
  if (!toolLog) return [];
@@ -66,7 +68,6 @@ export function setStatus(s: string): void {
66
68
  if (badge) { badge.className = 'status-badge status-idle'; badge.textContent = 'idle'; }
67
69
  if (btn) { btn.innerHTML = ICONS.send; btn.title = 'Send'; btn.classList.remove('stop-mode'); }
68
70
  removeSkeleton();
69
- updateQueueBadge(0);
70
71
  }
71
72
  }
72
73
 
@@ -87,6 +88,7 @@ export function updateQueueBadge(count: number): void {
87
88
  function showSkeleton(): void {
88
89
  const container = document.getElementById('chatMessages');
89
90
  if (!container || container.querySelector('.skeleton-msg')) return;
91
+ if (state.currentAgentDiv && state.currentAgentDiv.isConnected) return;
90
92
  // No flushToDOM — skeleton goes directly into container as overlay
91
93
  hideEmptyState();
92
94
  const skel = document.createElement('div');
@@ -132,6 +134,7 @@ export function cleanupToolActivity(): void {
132
134
  cleanupToolElements();
133
135
  state.currentAgentDiv = null;
134
136
  state.currentProcessBlock = null;
137
+ currentStream = null;
135
138
  }
136
139
 
137
140
  export function showLiveToolActivity(label: string): void {
@@ -215,6 +218,36 @@ export function showProcessStep(step: ProcessStep): void {
215
218
 
216
219
  let currentStream: StreamState | null = null;
217
220
 
221
+ export function applyQueuedOverlay(items: QueuedOverlayItem[] = []): void {
222
+ document.querySelectorAll('[data-queued-overlay="true"]').forEach(el => el.remove());
223
+ for (const item of items) {
224
+ const queuedId = String(item.id || '');
225
+ if (!queuedId) continue;
226
+ if (document.querySelector(`[data-queued-id="${queuedId}"]`)) continue;
227
+ const div = addMessage('user', item.prompt || '');
228
+ div.setAttribute('data-queued-overlay', 'true');
229
+ div.setAttribute('data-queued-id', queuedId);
230
+ }
231
+ }
232
+
233
+ export function hydrateActiveRun(snapshot?: ActiveRunSnapshot | null): void {
234
+ if (!snapshot?.running) return;
235
+ cleanupToolElements();
236
+ removeSkeleton();
237
+ state.currentAgentDiv = addMessage('agent', '', snapshot.cli || null);
238
+ state.currentProcessBlock = null;
239
+ const body = state.currentAgentDiv.querySelector('.agent-body') as HTMLElement | null;
240
+ if (body && snapshot.toolLog?.length) {
241
+ const pb = createProcessBlock(body);
242
+ for (const tool of toProcessSteps(snapshot.toolLog)) addStep(pb, tool);
243
+ state.currentProcessBlock = pb;
244
+ }
245
+ const content = state.currentAgentDiv.querySelector('.msg-content') as HTMLElement | null;
246
+ if (content) {
247
+ currentStream = hydrateStreamRenderer(content, snapshot.text || '');
248
+ }
249
+ }
250
+
218
251
  export function appendAgentText(text: string): void {
219
252
  if (!text) return;
220
253
  removeSkeleton();
@@ -190,6 +190,55 @@ export class VirtualScroll {
190
190
  });
191
191
 
192
192
  this.cleanupFn = this.virtualizer._didMount();
193
+
194
+ // ── Resize invalidation ──
195
+ // When viewport width changes, message text reflows and heights change.
196
+ // TanStack's ResizeObserver watches individual items, but not the
197
+ // container width — a window resize doesn't trigger item RO callbacks
198
+ // because items are position:absolute (width from left:0 + right:0).
199
+ let resizeRaf = 0;
200
+ const onResize = () => {
201
+ cancelAnimationFrame(resizeRaf);
202
+ resizeRaf = requestAnimationFrame(() => {
203
+ if (!this.virtualizer) return;
204
+ this.itemGap = 0;
205
+ const newGap = this.measureGap();
206
+ this.virtualizer.setOptions({
207
+ ...this.virtualizer.options,
208
+ gap: newGap,
209
+ });
210
+ this.virtualizer.measure();
211
+ });
212
+ };
213
+ window.addEventListener('resize', onResize);
214
+ const prevCleanup = this.cleanupFn;
215
+ this.cleanupFn = () => {
216
+ window.removeEventListener('resize', onResize);
217
+ cancelAnimationFrame(resizeRaf);
218
+ prevCleanup?.();
219
+ };
220
+
221
+ // ── bfcache restoration ──
222
+ // pageshow fires when page is restored from bfcache (persisted=true).
223
+ // Regular reload goes through normal activate() flow, but bfcache
224
+ // restores the JS heap with stale cached measurements.
225
+ const onPageShow = (e: PageTransitionEvent) => {
226
+ if (!e.persisted || !this.virtualizer) return;
227
+ this.itemGap = 0;
228
+ const newGap = this.measureGap();
229
+ this.virtualizer.setOptions({
230
+ ...this.virtualizer.options,
231
+ gap: newGap,
232
+ });
233
+ this.virtualizer.measure();
234
+ };
235
+ window.addEventListener('pageshow', onPageShow);
236
+ const prevCleanup2 = this.cleanupFn;
237
+ this.cleanupFn = () => {
238
+ window.removeEventListener('pageshow', onPageShow);
239
+ prevCleanup2?.();
240
+ };
241
+
193
242
  this.virtualizer._willUpdate();
194
243
 
195
244
  if (toBottom && this.items.length > 0) {
@@ -271,13 +320,11 @@ export class VirtualScroll {
271
320
  el.style.transform = `translateY(${vItem.start}px)`;
272
321
  }
273
322
 
274
- // Let tanstack measure real heights via ResizeObserver
275
- // only for newly mounted elements (already-observed ones are tracked)
276
- for (const el of newlyMounted) {
277
- this.virtualizer!.measureElement(el);
278
- }
279
-
280
- // Lazy render: process any lazy-pending elements
323
+ // Lazy render BEFORE measuring onLazyRender processes markdown,
324
+ // code blocks, math which dramatically changes element heights.
325
+ // Measuring first would record pre-render heights (e.g. 50px),
326
+ // then lazy render expands to 300px+, causing overlap until the
327
+ // deferred ResizeObserver catches up (1+ frames later).
281
328
  if (this.onLazyRender) {
282
329
  const lazyTargets = this.innerEl.querySelectorAll<HTMLElement>('.lazy-pending');
283
330
  if (lazyTargets.length > 0) {
@@ -289,6 +336,12 @@ export class VirtualScroll {
289
336
  if (this.onPostRender) {
290
337
  this.onPostRender(this.innerEl);
291
338
  }
339
+
340
+ // Now measure real heights — elements have their final rendered content.
341
+ // Only for newly mounted elements (already-observed ones are tracked).
342
+ for (const el of newlyMounted) {
343
+ this.virtualizer!.measureElement(el);
344
+ }
292
345
  }
293
346
  }
294
347
 
@@ -303,17 +356,4 @@ export function getVirtualScroll(): VirtualScroll {
303
356
  return instance;
304
357
  }
305
358
 
306
- // ── Compat exports ──
307
-
308
- /** @deprecated Kept for test compat — tanstack handles anchoring internally */
309
- export function computeAnchoredScrollTop(
310
- anchorTop: number,
311
- offsetWithinItem: number,
312
- containerPadTop: number,
313
- maxScrollTop: number,
314
- ): number {
315
- const nextScrollTop = containerPadTop + anchorTop + offsetWithinItem;
316
- return Math.max(0, Math.min(nextScrollTop, maxScrollTop));
317
- }
318
-
319
359
  export { THRESHOLD as VS_THRESHOLD };
package/public/js/ws.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // ── WebSocket Connection ──
2
2
  import { state } from './state.js';
3
- import { setStatus, updateQueueBadge, addSystemMsg, appendAgentText, finalizeAgent, addMessage, showProcessStep, cleanupToolActivity } from './ui.js';
3
+ import { setStatus, updateQueueBadge, addSystemMsg, appendAgentText, finalizeAgent, addMessage, showProcessStep, cleanupToolActivity, applyQueuedOverlay, hydrateActiveRun } from './ui.js';
4
4
  import { t, getLang } from './features/i18n.js';
5
5
  import { getVirtualScroll } from './virtual-scroll.js';
6
6
  import { ICONS, emojiToIcon } from './icons.js';
@@ -66,6 +66,7 @@ interface WsMessage {
66
66
  delay?: number;
67
67
  state?: string;
68
68
  title?: string;
69
+ isEmployee?: boolean;
69
70
  }
70
71
 
71
72
  // Agent phase state (populated by agent_status events from orchestrator)
@@ -74,6 +75,21 @@ const agentPhaseState: Record<string, { phase: string; phaseLabel: string }> = {
74
75
  let currentOrcScope = '';
75
76
  let lastLoadTs = 0;
76
77
 
78
+ async function refreshRuntimeSnapshot(options: { hydrateRun?: boolean } = {}): Promise<void> {
79
+ const response = await fetch('/api/orchestrate/snapshot');
80
+ const snap = await response.json();
81
+ currentOrcScope = String(snap.orc.scope || '');
82
+ applyOrcState(snap.orc.state);
83
+ hydrateAgentPhases(snap.workers);
84
+ updateQueueBadge(snap.runtime.queuePending);
85
+ applyQueuedOverlay(snap.queued || []);
86
+ if (options.hydrateRun) hydrateActiveRun(snap.activeRun);
87
+ setStatus(snap.runtime.busy ? 'running' : 'idle');
88
+ import('./features/employees.js').then(m => {
89
+ if (typeof m.renderEmployees === 'function') m.renderEmployees();
90
+ });
91
+ }
92
+
77
93
  /** Hydrate agent phase cache from snapshot (used after reconnect) */
78
94
  export function hydrateAgentPhases(workers: Array<{
79
95
  agentId: string;
@@ -219,6 +235,7 @@ export function connect(): void {
219
235
  }
220
236
  } else if (msg.type === 'queue_update') {
221
237
  updateQueueBadge(msg.pending || 0);
238
+ refreshRuntimeSnapshot().catch(() => { /* snapshot not critical — UI recovers on next event */ });
222
239
  } else if (msg.type === 'worklog_created') {
223
240
  addSystemMsg(`${ICONS.clipboard} Worklog: ${escapeHtml(msg.path || '')}`);
224
241
  } else if (msg.type === 'round_start') {
@@ -234,13 +251,14 @@ export function connect(): void {
234
251
  addSystemMsg(t('ws.roundRetry', { round: msg.round || 0 }));
235
252
  }
236
253
  } else if (msg.type === 'agent_tool') {
254
+ const empPrefix = msg.isEmployee ? '(E) ' : '';
237
255
  const stepType = msg.toolType === 'thinking' ? 'thinking'
238
256
  : msg.toolType === 'search' ? 'search' : 'tool';
239
257
  showProcessStep({
240
258
  id: `step-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
241
259
  type: stepType,
242
260
  icon: msg.icon || ICONS.tool,
243
- label: msg.label || '',
261
+ label: empPrefix + (msg.label || ''),
244
262
  detail: msg.detail || '',
245
263
  stepRef: msg.stepRef || '',
246
264
  status: (msg.status as 'running' | 'done' | 'error') || 'running',
@@ -273,6 +291,8 @@ export function connect(): void {
273
291
  } else if (msg.type === 'orc_state') {
274
292
  if (msg.scope && currentOrcScope && msg.scope !== currentOrcScope) return;
275
293
  applyOrcState(typeof msg.state === 'string' ? msg.state : 'IDLE', msg.title);
294
+ } else if (msg.type === 'memory_status') {
295
+ import('./features/memory.js').then(m => m.refreshMemorySidebar());
276
296
  } else if (msg.type === 'new_message' && (msg.source === 'telegram' || msg.source === 'discord')) {
277
297
  addMessage(msg.role === 'assistant' ? 'agent' : (msg.role || 'user'), msg.content || '', msg.cli);
278
298
  }
@@ -291,23 +311,9 @@ export function connect(): void {
291
311
  console.error('[ws] loadMessages failed', error);
292
312
  }
293
313
  }
294
- m.setStatus('idle');
314
+ refreshRuntimeSnapshot({ hydrateRun: true })
315
+ .catch(() => { /* snapshot not critical — UI recovers on next WS event */ });
295
316
  });
296
-
297
- // Reconnect: restore orchestration state
298
- fetch('/api/orchestrate/snapshot')
299
- .then(r => r.json())
300
- .then((snap: any) => {
301
- currentOrcScope = String(snap.orc.scope || '');
302
- applyOrcState(snap.orc.state);
303
- hydrateAgentPhases(snap.workers);
304
- updateQueueBadge(snap.runtime.queuePending);
305
- setStatus(snap.runtime.busy ? 'running' : 'idle');
306
- import('./features/employees.js').then(m => {
307
- if (typeof m.renderEmployees === 'function') m.renderEmployees();
308
- });
309
- })
310
- .catch(() => { /* snapshot not critical — UI recovers on next WS event */ });
311
317
  };
312
318
  state.ws.onclose = () => {
313
319
  console.log('[ws] disconnected, reconnecting in 2s...');
@@ -1 +0,0 @@
1
- import{X as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as createArchitectureServices};
@@ -1 +0,0 @@
1
- import{M as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{j as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{A as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{k as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{O as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{t as e}from"./api-DygAf_G_.js";var t={claude:{label:`Claude`,efforts:[`low`,`medium`,`high`],models:[`sonnet`,`opus`,`sonnet[1m]`,`opus[1m]`,`haiku`]},codex:{label:`Codex`,efforts:[`low`,`medium`,`high`,`xhigh`],models:[`gpt-5.4`,`gpt-5.3-codex`,`gpt-5.3-codex-spark`,`gpt-5.2-codex`,`gpt-5.1-codex-max`,`gpt-5.1-codex-mini`]},gemini:{label:`Gemini`,efforts:[],models:[`gemini-3.0-pro-preview`,`gemini-3.1-pro-preview`,`gemini-2.5-pro`,`gemini-3-flash-preview`,`gemini-2.5-flash`]},opencode:{label:`OpenCode`,efforts:[`minimal`,`low`,`high`,`max`],models:[`anthropic/claude-opus-4-6-thinking`,`anthropic/claude-sonnet-4-6-thinking`,`anthropic/claude-sonnet-4-6`,`openai/gpt-5.4-xhigh`,`openai/gpt-5.4-high`,`openai/gpt-5.3-codex-xhigh`,`openai/gpt-5.3-codex-high`,`opencode/big-pickle`,`opencode-go/glm-5`,`opencode-go/glm-5.1`,`opencode-go/kimi-k2.5`,`opencode-go/mimo-v2-pro`,`opencode-go/mimo-v2-omni`,`opencode-go/minimax-m2.5`,`opencode-go/minimax-m2.7`,`opencode/GLM-5 Free`,`opencode/MiniMax M2.5 Free`,`opencode/Kimi K2.5 Free`,`opencode/GPT 5 Nano Free`,`opencode/Grok Code Fast 1 Free`]},copilot:{label:`Copilot`,efforts:[`low`,`medium`,`high`],effortNote:`-> ~/.copilot/config.json`,models:[`claude-sonnet-4.6`,`claude-opus-4.6`,`claude-haiku-4.5`,`gpt-5.4`,`gpt-5.3-codex`,`gpt-5.2-codex`,`gpt-5.1-codex`,`gpt-4.1`,`gpt-5-mini`,`gemini-3-pro-preview`]}};function n(e){let t={};for(let[n,r]of Object.entries(e))t[n]=Array.isArray(r?.models)?[...r.models]:[];return t}function r(e){let t={};for(let[n,r]of Object.entries(e||{})){if(!r||typeof r!=`object`)continue;let e=r,i={label:e.label||n,efforts:Array.isArray(e.efforts)?[...e.efforts]:[],models:Array.isArray(e.models)?[...e.models]:[]};typeof e.effortNote==`string`&&e.effortNote.trim()&&(i.effortNote=e.effortNote),t[n]=i}return t}var i=r(t),a=Object.keys(i),o=n(i);function s(e){let t=r(e);return Object.keys(t).length?(i=t,a=Object.keys(t),o=n(t),!0):!1}async function c(){try{let t=await e(`/api/cli-registry`);if(!t||!s(t))throw Error(`invalid registry`)}catch(e){console.warn(`[cli-registry] fallback:`,e.message),s(t)}return i}function l(){return a}function u(e){return i[e]||null}var d=[{value:`frontend`,labelKey:`role.label.frontend`,label:`Frontend`,prompt:`Frontend employee — UI/UX, CSS, components`,skill:`dev-frontend`},{value:`backend`,labelKey:`role.label.backend`,label:`Backend`,prompt:`Backend employee — API, DB, server logic`,skill:`dev-backend`},{value:`research`,labelKey:`role.label.research`,label:`Research`,prompt:`Research employee — search, codebase exploration, uncertainty reduction, read-only reports`,skill:`research-worker`},{value:`data`,labelKey:`role.label.data`,label:`Data`,prompt:`Data employee — data pipeline, analysis, ML`,skill:`dev-data`},{value:`docs`,labelKey:`role.label.docs`,label:`Docs`,prompt:`Docs employee — documentation, README, API docs`,skill:`documentation`},{value:`custom`,labelKey:`role.label.custom`,label:`Custom...`,prompt:``,skill:null}];export{c as a,u as i,d as n,l as r,o as t};
@@ -1 +0,0 @@
1
- import{D as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as render};
@@ -1 +0,0 @@
1
- import{E as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as render};
@@ -1 +0,0 @@
1
- import{T as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{w as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{C as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{S as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{x as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{b as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{y as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{J as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as createGitGraphServices};
@@ -1 +0,0 @@
1
- import{v as e}from"./vendor-mermaid-C2RBgdM6.js";export{e as diagram};