tryassay 0.22.0 → 0.22.2

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 (130) hide show
  1. package/demo/css/style.css +495 -836
  2. package/demo/index.html +40 -184
  3. package/demo/js/chat.js +385 -142
  4. package/demo/js/preview.js +456 -0
  5. package/demo/js/sse-client.js +262 -135
  6. package/demo/js/state.js +11 -1
  7. package/demo/js/timeline.js +57 -371
  8. package/dist/api/server.d.ts +2 -0
  9. package/dist/api/server.js +63 -19
  10. package/dist/api/server.js.map +1 -1
  11. package/dist/cli.js +2 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/assess.d.ts +2 -0
  14. package/dist/commands/assess.js +132 -164
  15. package/dist/commands/assess.js.map +1 -1
  16. package/dist/commands/demo.js +259 -9
  17. package/dist/commands/demo.js.map +1 -1
  18. package/dist/lib/__tests__/arithmetic-quick-test.d.ts +6 -0
  19. package/dist/lib/__tests__/arithmetic-quick-test.js +197 -0
  20. package/dist/lib/__tests__/arithmetic-quick-test.js.map +1 -0
  21. package/dist/lib/__tests__/arithmetic-real-llm-test.d.ts +13 -0
  22. package/dist/lib/__tests__/arithmetic-real-llm-test.js +284 -0
  23. package/dist/lib/__tests__/arithmetic-real-llm-test.js.map +1 -0
  24. package/dist/lib/__tests__/arithmetic-value-demo.d.ts +10 -0
  25. package/dist/lib/__tests__/arithmetic-value-demo.js +193 -0
  26. package/dist/lib/__tests__/arithmetic-value-demo.js.map +1 -0
  27. package/dist/lib/__tests__/flow-to-claims.test.d.ts +1 -0
  28. package/dist/lib/__tests__/flow-to-claims.test.js +91 -0
  29. package/dist/lib/__tests__/flow-to-claims.test.js.map +1 -0
  30. package/dist/lib/__tests__/formal-verifier-api-misuse.test.d.ts +9 -0
  31. package/dist/lib/__tests__/formal-verifier-api-misuse.test.js +391 -0
  32. package/dist/lib/__tests__/formal-verifier-api-misuse.test.js.map +1 -0
  33. package/dist/lib/__tests__/formal-verifier-arithmetic.test.d.ts +7 -0
  34. package/dist/lib/__tests__/formal-verifier-arithmetic.test.js +318 -0
  35. package/dist/lib/__tests__/formal-verifier-arithmetic.test.js.map +1 -0
  36. package/dist/lib/__tests__/intent-extractor.test.d.ts +1 -0
  37. package/dist/lib/__tests__/intent-extractor.test.js +97 -0
  38. package/dist/lib/__tests__/intent-extractor.test.js.map +1 -0
  39. package/dist/lib/__tests__/intent-reviewer.test.d.ts +1 -0
  40. package/dist/lib/__tests__/intent-reviewer.test.js +55 -0
  41. package/dist/lib/__tests__/intent-reviewer.test.js.map +1 -0
  42. package/dist/lib/__tests__/mr-gsm8k-benchmark.d.ts +11 -0
  43. package/dist/lib/__tests__/mr-gsm8k-benchmark.js +224 -0
  44. package/dist/lib/__tests__/mr-gsm8k-benchmark.js.map +1 -0
  45. package/dist/lib/anthropic.js +25 -33
  46. package/dist/lib/anthropic.js.map +1 -1
  47. package/dist/lib/assessment-reporter.js +9 -13
  48. package/dist/lib/assessment-reporter.js.map +1 -1
  49. package/dist/lib/claim-extractor.js +10 -19
  50. package/dist/lib/claim-extractor.js.map +1 -1
  51. package/dist/lib/code-verifier.js +16 -36
  52. package/dist/lib/code-verifier.js.map +1 -1
  53. package/dist/lib/constraint-engine.js +10 -19
  54. package/dist/lib/constraint-engine.js.map +1 -1
  55. package/dist/lib/formal-verifier.d.ts +1 -1
  56. package/dist/lib/formal-verifier.js +454 -0
  57. package/dist/lib/formal-verifier.js.map +1 -1
  58. package/dist/lib/guided-generator.js +19 -37
  59. package/dist/lib/guided-generator.js.map +1 -1
  60. package/dist/lib/intent-extractor.d.ts +47 -0
  61. package/dist/lib/intent-extractor.js +432 -0
  62. package/dist/lib/intent-extractor.js.map +1 -0
  63. package/dist/lib/intent-reviewer.d.ts +14 -0
  64. package/dist/lib/intent-reviewer.js +148 -0
  65. package/dist/lib/intent-reviewer.js.map +1 -0
  66. package/dist/lib/intent-types.d.ts +89 -0
  67. package/dist/lib/intent-types.js +5 -0
  68. package/dist/lib/intent-types.js.map +1 -0
  69. package/dist/lib/inventory-extractor.js +9 -22
  70. package/dist/lib/inventory-extractor.js.map +1 -1
  71. package/dist/lib/llm-provider.d.ts +23 -0
  72. package/dist/lib/llm-provider.js +130 -0
  73. package/dist/lib/llm-provider.js.map +1 -0
  74. package/dist/lib/remediator.js +20 -28
  75. package/dist/lib/remediator.js.map +1 -1
  76. package/dist/lib/requirements-generator.js +14 -19
  77. package/dist/lib/requirements-generator.js.map +1 -1
  78. package/dist/lib/spec-synthesizer.js +10 -19
  79. package/dist/lib/spec-synthesizer.js.map +1 -1
  80. package/dist/runtime/app-create-orchestrator.d.ts +5 -1
  81. package/dist/runtime/app-create-orchestrator.js +114 -39
  82. package/dist/runtime/app-create-orchestrator.js.map +1 -1
  83. package/dist/runtime/check-catalog.js +5 -3
  84. package/dist/runtime/check-catalog.js.map +1 -1
  85. package/dist/runtime/check-definitions.d.ts +10 -0
  86. package/dist/runtime/check-definitions.js +52 -2
  87. package/dist/runtime/check-definitions.js.map +1 -1
  88. package/dist/runtime/composition-verifier.js +8 -12
  89. package/dist/runtime/composition-verifier.js.map +1 -1
  90. package/dist/runtime/gap-detector.js +8 -10
  91. package/dist/runtime/gap-detector.js.map +1 -1
  92. package/dist/runtime/input-validator.d.ts +7 -0
  93. package/dist/runtime/input-validator.js +162 -0
  94. package/dist/runtime/input-validator.js.map +1 -0
  95. package/dist/runtime/model-router.d.ts +10 -0
  96. package/dist/runtime/model-router.js +42 -0
  97. package/dist/runtime/model-router.js.map +1 -0
  98. package/dist/runtime/pattern-extractor.js +8 -10
  99. package/dist/runtime/pattern-extractor.js.map +1 -1
  100. package/dist/runtime/planner.js +11 -16
  101. package/dist/runtime/planner.js.map +1 -1
  102. package/dist/runtime/prompt-guard.d.ts +2 -0
  103. package/dist/runtime/prompt-guard.js +180 -0
  104. package/dist/runtime/prompt-guard.js.map +1 -0
  105. package/dist/runtime/prompt-safety-analyzer.js +8 -13
  106. package/dist/runtime/prompt-safety-analyzer.js.map +1 -1
  107. package/dist/runtime/reasoner.js +19 -33
  108. package/dist/runtime/reasoner.js.map +1 -1
  109. package/dist/runtime/rule-meta-verifier.js +9 -11
  110. package/dist/runtime/rule-meta-verifier.js.map +1 -1
  111. package/dist/runtime/safe-executor.d.ts +23 -0
  112. package/dist/runtime/safe-executor.js +151 -0
  113. package/dist/runtime/safe-executor.js.map +1 -0
  114. package/dist/runtime/specialized-agent.js +10 -14
  115. package/dist/runtime/specialized-agent.js.map +1 -1
  116. package/dist/runtime/strategy-library.js +8 -10
  117. package/dist/runtime/strategy-library.js.map +1 -1
  118. package/dist/runtime/supabase-experience-store.js.map +1 -1
  119. package/dist/runtime/supabase-provisioner.d.ts +35 -0
  120. package/dist/runtime/supabase-provisioner.js +192 -0
  121. package/dist/runtime/supabase-provisioner.js.map +1 -0
  122. package/dist/runtime/types.d.ts +88 -0
  123. package/dist/sdk/forward-verify.js +16 -33
  124. package/dist/sdk/forward-verify.js.map +1 -1
  125. package/package.json +1 -1
  126. package/demo/data/demo-events.json +0 -103
  127. package/demo/js/demo-mode.js +0 -107
  128. package/demo/js/orb.js +0 -634
  129. package/demo/js/question-cards.js +0 -207
  130. package/demo/js/voice.js +0 -154
@@ -1,8 +1,8 @@
1
1
  // sse-client.js — EventSource to AppState bridge
2
2
  // Connects to the Assay API SSE stream and maps events to state updates
3
3
 
4
- import { showQuestions, hideQuestions, configureQuestionCards } from './question-cards.js';
5
- import { configureChat, addArchitectMessage, closeChat } from './chat.js';
4
+ import { configureChat, addArchitectMessage, lockChat, unlockChat } from './chat.js';
5
+ import { downloadProject, collectProjectFiles } from './preview.js';
6
6
 
7
7
  const AppState = window.AppState;
8
8
 
@@ -20,7 +20,6 @@ function autoApprovePlan(appId) {
20
20
  }).then(res => {
21
21
  if (res.ok) {
22
22
  AppState.addLog({ level: 'info', message: 'Plan approved — building...' });
23
- hidePlanOverlay();
24
23
  } else {
25
24
  console.warn('[SSE] Auto-approve failed:', res.status);
26
25
  }
@@ -29,64 +28,23 @@ function autoApprovePlan(appId) {
29
28
  });
30
29
  }
31
30
 
32
- function showPlanOverlay(summary) {
33
- let overlay = document.getElementById('plan-overlay');
34
- if (!overlay) return;
35
-
36
- const features = (summary.features || []).map(f =>
37
- `<div class="plan-feature">
38
- <span class="plan-feature-name">${f.name || f.id}</span>
39
- <span class="plan-feature-meta">${f.complexity} &middot; ${f.routeCount || 0} routes &middot; ${f.pageCount || 0} pages</span>
40
- </div>`
41
- ).join('');
42
-
43
- const warnings = (summary.warnings || []).map(w =>
44
- `<div class="plan-warning">${w}</div>`
45
- ).join('');
46
-
47
- overlay.innerHTML = `
48
- <div class="plan-card">
49
- <div class="plan-header">Architecture Plan</div>
50
- <div class="plan-meta">
51
- <span>${summary.techStack || ''}</span>
52
- <span>${summary.estimatedComplexity || ''}</span>
53
- <span>${summary.featureCount || 0} features</span>
54
- <span>${summary.schemaEntities || 0} entities</span>
55
- <span>${summary.apiRouteCount || 0} routes</span>
56
- <span>${summary.pageCount || 0} pages</span>
57
- </div>
58
- <div class="plan-features">${features}</div>
59
- ${warnings ? `<div class="plan-warnings">${warnings}</div>` : ''}
60
- <div class="plan-auto">Auto-approving...</div>
61
- </div>
62
- `;
63
- overlay.classList.add('visible');
64
- }
65
-
66
- function hidePlanOverlay() {
67
- const overlay = document.getElementById('plan-overlay');
68
- if (overlay) {
69
- overlay.classList.remove('visible');
70
- setTimeout(() => { overlay.innerHTML = ''; }, 500);
71
- }
72
- }
73
-
74
31
  function showCompletionOverlay(data) {
75
32
  const overlay = document.getElementById('completion-overlay');
76
33
  if (!overlay) return;
77
34
 
78
35
  const m = AppState.get().metrics;
79
36
  const appName = data.appName || 'Application';
80
- const status = data.status === 'completed' ? 'Build Complete' : 'Build Finished (with issues)';
81
- const statusClass = data.status === 'completed' ? 'success' : 'partial';
37
+ const isSuccess = data.status === 'completed';
38
+ const status = isSuccess ? 'Build Complete' : 'Build Finished with Issues';
39
+ const statusClass = isSuccess ? 'success' : 'partial';
82
40
 
83
- // Grab total elapsed time from the global clock
84
41
  const clockVal = document.getElementById('clock-value');
85
42
  const totalTime = clockVal ? clockVal.textContent : '';
86
43
 
87
44
  overlay.innerHTML = `
88
45
  <div class="completion-card">
89
- <div class="completion-icon ${statusClass}">${data.status === 'completed' ? '\u2713' : '\u26a0'}</div>
46
+ <button class="completion-close-btn" id="completion-close" title="Close">\u00d7</button>
47
+ <div class="completion-icon ${statusClass}">${isSuccess ? '\u2713' : '\u26a0'}</div>
90
48
  <div class="completion-header">${status}</div>
91
49
  <div class="completion-app-name">${appName}</div>
92
50
  <div class="completion-metrics">
@@ -95,85 +53,217 @@ function showCompletionOverlay(data) {
95
53
  <span>${m.claimsFailed} caught</span>
96
54
  ${totalTime ? `<span>${totalTime} total</span>` : ''}
97
55
  </div>
98
- <button class="completion-launch-btn" id="launch-btn">Launch Application</button>
99
- <div class="completion-status" id="launch-status"></div>
56
+ <div class="completion-actions">
57
+ <button class="completion-action-btn completion-download-btn" id="completion-download">
58
+ <span class="completion-action-icon">\u2913</span>
59
+ Download Project (.zip)
60
+ </button>
61
+ <button class="completion-action-btn completion-github-btn" id="completion-github">
62
+ <span class="completion-action-icon">\u2197</span>
63
+ Push to GitHub
64
+ </button>
65
+ </div>
66
+ <div class="completion-status" id="completion-status"></div>
100
67
  </div>
101
68
  `;
102
69
  overlay.classList.add('visible');
103
70
 
104
- document.getElementById('launch-btn')?.addEventListener('click', () => {
105
- launchApp(window.__appId);
71
+ // Close button — dismisses modal, reveals preview + chat
72
+ document.getElementById('completion-close')?.addEventListener('click', () => {
73
+ overlay.classList.remove('visible');
74
+ });
75
+
76
+ // Download button — calls existing zip download from preview.js
77
+ document.getElementById('completion-download')?.addEventListener('click', () => {
78
+ downloadProject();
79
+ });
80
+
81
+ // GitHub button — starts GitHub export flow
82
+ document.getElementById('completion-github')?.addEventListener('click', () => {
83
+ startGitHubExport(appName);
106
84
  });
107
85
  }
108
86
 
109
- function launchApp(appId) {
110
- const btn = document.getElementById('launch-btn');
111
- const statusEl = document.getElementById('launch-status');
112
- if (btn) {
113
- btn.disabled = true;
114
- btn.textContent = 'Launching...';
115
- }
116
- if (statusEl) statusEl.textContent = 'Installing dependencies...';
87
+ /**
88
+ * GitHub export flow:
89
+ * 1. Check if user is authenticated via GitHub OAuth
90
+ * 2. If not, open OAuth popup
91
+ * 3. Once authenticated, show repo form and push
92
+ */
93
+ async function startGitHubExport(appName) {
94
+ const statusEl = document.getElementById('completion-status');
95
+ const githubBtn = document.getElementById('completion-github');
117
96
 
118
- const headers = { 'Content-Type': 'application/json' };
119
- if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
97
+ // GitHub auth endpoints live on the UI server (same origin), not the API server
98
+ const uiOrigin = window.location.origin;
120
99
 
121
- fetch(`${apiBase}/api/v1/app/${appId}/launch`, {
122
- method: 'POST',
123
- headers,
124
- }).then(res => {
125
- if (!res.ok) {
126
- if (statusEl) statusEl.textContent = 'Launch failed. Check server logs.';
100
+ // Check auth status
101
+ if (statusEl) statusEl.textContent = 'Checking GitHub connection...';
102
+
103
+ try {
104
+ const res = await fetch(`${uiOrigin}/api/v1/auth/github/status`);
105
+ const authData = await res.json();
106
+
107
+ if (!authData.configured) {
108
+ if (statusEl) statusEl.textContent = 'GitHub not configured. Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET env vars, then restart the demo.';
127
109
  return;
128
110
  }
129
- // Start polling for launch status
130
- pollLaunchStatus(appId);
131
- }).catch(err => {
132
- console.error('[Launch] Error:', err);
133
- if (statusEl) statusEl.textContent = 'Launch error: ' + err.message;
111
+
112
+ if (authData.authenticated) {
113
+ showGitHubForm(appName, authData.username);
114
+ } else {
115
+ // Open OAuth popup
116
+ if (statusEl) statusEl.textContent = 'Opening GitHub authorization...';
117
+ const popup = window.open(
118
+ `${uiOrigin}/api/v1/auth/github`,
119
+ 'github-oauth',
120
+ 'width=600,height=700,menubar=no,toolbar=no'
121
+ );
122
+
123
+ // Poll for auth completion
124
+ const pollAuth = setInterval(async () => {
125
+ try {
126
+ if (popup && popup.closed) {
127
+ clearInterval(pollAuth);
128
+ const checkRes = await fetch(`${uiOrigin}/api/v1/auth/github/status`);
129
+ const checkData = await checkRes.json();
130
+ if (checkData.authenticated) {
131
+ showGitHubForm(appName, checkData.username);
132
+ } else {
133
+ if (statusEl) statusEl.textContent = 'GitHub authorization cancelled. You can still download the zip.';
134
+ }
135
+ return;
136
+ }
137
+ } catch { /* keep polling */ }
138
+ }, 1000);
139
+ }
140
+ } catch (err) {
141
+ console.error('[GitHub] Auth check failed:', err);
142
+ if (statusEl) statusEl.textContent = 'GitHub connection unavailable. Download the zip instead.';
143
+ }
144
+ }
145
+
146
+ function showGitHubForm(appName, username) {
147
+ const statusEl = document.getElementById('completion-status');
148
+ if (!statusEl) return;
149
+
150
+ const slugName = appName
151
+ .toLowerCase()
152
+ .replace(/[^a-z0-9]+/g, '-')
153
+ .replace(/^-|-$/g, '') || 'my-app';
154
+
155
+ statusEl.innerHTML = `
156
+ <div class="github-form">
157
+ <div class="github-form-row">
158
+ <label>Repository</label>
159
+ <input type="text" id="github-repo-name" value="${slugName}" placeholder="repo-name">
160
+ </div>
161
+ <div class="github-form-row">
162
+ <label>Visibility</label>
163
+ <label class="github-toggle">
164
+ <input type="checkbox" id="github-private" checked>
165
+ <span>Private</span>
166
+ </label>
167
+ </div>
168
+ <div class="github-form-row">
169
+ <span class="github-user">Pushing as ${username}</span>
170
+ <button class="completion-action-btn completion-github-push-btn" id="github-push">Create & Push</button>
171
+ </div>
172
+ </div>
173
+ `;
174
+
175
+ document.getElementById('github-push')?.addEventListener('click', () => {
176
+ executeGitHubPush();
134
177
  });
135
178
  }
136
179
 
137
- function pollLaunchStatus(appId) {
138
- const statusEl = document.getElementById('launch-status');
139
- const btn = document.getElementById('launch-btn');
180
+ async function executeGitHubPush() {
181
+ const repoNameEl = document.getElementById('github-repo-name');
182
+ const privateEl = document.getElementById('github-private');
183
+ const pushBtn = document.getElementById('github-push');
184
+ const statusEl = document.getElementById('completion-status');
140
185
 
141
- const headers = {};
142
- if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
186
+ if (!repoNameEl) return;
143
187
 
144
- const poll = setInterval(() => {
145
- fetch(`${apiBase}/api/v1/app/${appId}/launch`, { headers })
146
- .then(res => res.json())
147
- .then(data => {
148
- if (data.status === 'installing') {
149
- if (statusEl) statusEl.textContent = 'Installing dependencies...';
150
- } else if (data.status === 'starting') {
151
- if (statusEl) statusEl.textContent = 'Starting dev server...';
152
- } else if (data.status === 'ready') {
153
- clearInterval(poll);
154
- if (btn) {
155
- btn.textContent = 'Open Application';
156
- btn.disabled = false;
157
- btn.onclick = () => window.open(data.url, '_blank');
158
- btn.classList.add('ready');
159
- }
160
- if (statusEl) {
161
- statusEl.innerHTML = `<a href="${data.url}" target="_blank" class="launch-url">${data.url}</a>`;
162
- }
163
- AppState.addLog({ level: 'info', message: `Application ready at ${data.url}` });
164
- } else if (data.status === 'failed') {
165
- clearInterval(poll);
166
- if (btn) {
167
- btn.textContent = 'Launch Failed';
168
- btn.disabled = true;
169
- }
170
- if (statusEl) statusEl.textContent = data.error || 'Launch failed';
171
- }
172
- })
173
- .catch(() => {
174
- // Ignore polling errors, keep trying
175
- });
176
- }, 2000);
188
+ const repoName = repoNameEl.value.trim();
189
+ if (!repoName) return;
190
+
191
+ const isPrivate = privateEl?.checked ?? true;
192
+
193
+ if (pushBtn) {
194
+ pushBtn.disabled = true;
195
+ pushBtn.textContent = 'Creating repo...';
196
+ }
197
+
198
+ try {
199
+ // Collect files from WebContainer
200
+ const files = await collectProjectFiles();
201
+ if (files.length === 0) {
202
+ if (statusEl) statusEl.innerHTML = '<span class="github-error">No files found in project.</span>';
203
+ return;
204
+ }
205
+
206
+ if (pushBtn) pushBtn.textContent = `Pushing ${files.length} files...`;
207
+
208
+ // Export endpoint lives on the UI server (same origin)
209
+ const uiOrigin = window.location.origin;
210
+ const res = await fetch(`${uiOrigin}/api/v1/app/${window.__appId}/export/github`, {
211
+ method: 'POST',
212
+ headers: { 'Content-Type': 'application/json' },
213
+ body: JSON.stringify({ repoName, private: isPrivate, files }),
214
+ });
215
+
216
+ const result = await res.json();
217
+
218
+ if (result.success && result.repoUrl) {
219
+ if (statusEl) {
220
+ statusEl.innerHTML = `
221
+ <div class="github-success">
222
+ Pushed to <a href="${result.repoUrl}" target="_blank" class="github-link">${result.repoUrl}</a>
223
+ </div>
224
+ `;
225
+ }
226
+ } else {
227
+ if (statusEl) {
228
+ statusEl.innerHTML = `<span class="github-error">${result.error || 'Push failed. Try downloading the zip instead.'}</span>`;
229
+ }
230
+ }
231
+ } catch (err) {
232
+ console.error('[GitHub] Push failed:', err);
233
+ if (statusEl) {
234
+ statusEl.innerHTML = `<span class="github-error">Push failed: ${err.message}</span>`;
235
+ }
236
+ }
237
+
238
+ if (pushBtn) {
239
+ pushBtn.disabled = false;
240
+ pushBtn.textContent = 'Create & Push';
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Append a compact activity item to the chat messages panel.
246
+ * Used during build to show real-time progress.
247
+ */
248
+ function appendActivityItem(icon, text, className) {
249
+ const messagesEl = document.getElementById('chat-messages');
250
+ if (!messagesEl) return;
251
+
252
+ const item = document.createElement('div');
253
+ item.className = `activity-item ${className || ''}`;
254
+
255
+ const iconSpan = document.createElement('span');
256
+ iconSpan.className = 'activity-icon';
257
+ iconSpan.textContent = icon;
258
+ item.appendChild(iconSpan);
259
+
260
+ const textSpan = document.createElement('span');
261
+ textSpan.className = 'activity-text';
262
+ textSpan.textContent = text;
263
+ item.appendChild(textSpan);
264
+
265
+ messagesEl.appendChild(item);
266
+ messagesEl.scrollTop = messagesEl.scrollHeight;
177
267
  }
178
268
 
179
269
  // Track whether completion overlay has been shown
@@ -194,6 +284,19 @@ function handleSSEEvent(type, data) {
194
284
  if (phaseName) {
195
285
  AppState.update({ phase: phaseName });
196
286
 
287
+ // Activity feed: show phase changes
288
+ const featureName = phase.featureName || phase.featureId || data.currentFeature;
289
+ const phaseLabel = phaseName.replace(/_/g, ' ');
290
+ if (phaseName === 'provisioning_supabase') {
291
+ appendActivityItem('\u2601', 'Provisioning Supabase database...', 'activity-phase');
292
+ } else if (phaseName === 'supabase_migrations') {
293
+ appendActivityItem('\u2601', 'Pushing database migrations...', 'activity-phase');
294
+ } else if (featureName) {
295
+ appendActivityItem('\u25b6', `${phaseLabel} \u2014 ${featureName}`, 'activity-phase');
296
+ } else if (!['completed', 'failed'].includes(phaseName)) {
297
+ appendActivityItem('\u25b6', phaseLabel, 'activity-phase');
298
+ }
299
+
197
300
  // Extract feature info for voice narration
198
301
  if (phase.featureId || phase.featureName || data.currentFeature) {
199
302
  AppState.update({
@@ -220,10 +323,7 @@ function handleSSEEvent(type, data) {
220
323
  setTimeout(() => {
221
324
  if (!completionShown) {
222
325
  console.log('[SSE] Showing completion overlay from progress fallback');
223
- const input = document.getElementById('prompt-input');
224
- const btn = document.getElementById('submit-btn');
225
- if (input) input.disabled = false;
226
- if (btn) btn.disabled = false;
326
+ unlockChat();
227
327
  showCompletionOverlay({
228
328
  status: phaseName,
229
329
  appName: data.appName || data.currentFeature || 'Application',
@@ -243,6 +343,12 @@ function handleSSEEvent(type, data) {
243
343
  } else {
244
344
  AppState.addTask(data);
245
345
  }
346
+ // Activity feed: show task completion
347
+ if (data.status === 'completed' && data.claims) {
348
+ const passed = data.claims.passed || data.claims.totalClaims - (data.claims.failed || 0) || 0;
349
+ const failed = data.claims.failed || 0;
350
+ appendActivityItem('\u2713', `${data.name || data.id} (${passed} claims, ${failed} failed)`, failed > 0 ? 'activity-warn' : 'activity-success');
351
+ }
246
352
  // Extract per-feature claim counts into verification metrics
247
353
  if (data.claims) {
248
354
  const c = data.claims;
@@ -258,6 +364,12 @@ function handleSSEEvent(type, data) {
258
364
  break;
259
365
  }
260
366
  case 'verification': {
367
+ // Activity feed: show verification results
368
+ if (data.passed_count !== undefined || data.failed_count !== undefined) {
369
+ const p = data.passed_count || 0;
370
+ const f = data.failed_count || 0;
371
+ appendActivityItem('\u2714', `Verification: ${p} passed, ${f} failed`, f > 0 ? 'activity-warn' : 'activity-success');
372
+ }
261
373
  // Add the phase-level verification result
262
374
  AppState.addVerification({
263
375
  claim: data.claim,
@@ -281,6 +393,12 @@ function handleSSEEvent(type, data) {
281
393
  language: data.language || 'typescript',
282
394
  content: data.content
283
395
  });
396
+ // Activity feed: show generated file
397
+ appendActivityItem('+', data.path, 'activity-file');
398
+ const previewPanel = document.getElementById('preview-panel');
399
+ if (previewPanel && !previewPanel.classList.contains('live')) {
400
+ previewPanel.classList.add('live');
401
+ }
284
402
  break;
285
403
  }
286
404
  case 'agent_status': {
@@ -298,7 +416,7 @@ function handleSSEEvent(type, data) {
298
416
  case 'plan_questions': {
299
417
  AppState.update({ phase: 'plan_questioning' });
300
418
  AppState.addLog({ level: 'info', message: `Requirements round ${data.round}: ${data.questions.length} questions (${Math.round(data.confidence * 100)}% confidence)` });
301
- showQuestions(data);
419
+ // Questions now handled via chat_message events
302
420
  break;
303
421
  }
304
422
  case 'plan_readiness': {
@@ -323,12 +441,14 @@ function handleSSEEvent(type, data) {
323
441
  break;
324
442
  }
325
443
  case 'plan_summary': {
326
- // Architecture plan arrived — show it in the UI overlay (questions are done)
327
- hideQuestions();
328
- closeChat();
444
+ // Architecture plan arrived — show summary in chat panel
329
445
  AppState.update({ planSummary: data });
446
+ appendActivityItem('\u2692', `Architecture: ${data.featureCount} features, ${data.apiRouteCount} routes, ${data.pageCount} pages`, 'activity-phase');
330
447
  AppState.addLog({ level: 'info', message: `Architecture: ${data.featureCount} features, ${data.apiRouteCount} routes, ${data.pageCount} pages` });
331
- showPlanOverlay(data);
448
+ const featureList = (data.features || []).map(f => `${f.name || f.id} (${f.complexity})`).join(', ');
449
+ addArchitectMessage({
450
+ message: `Architecture plan ready: ${data.featureCount} features, ${data.apiRouteCount} routes, ${data.pageCount} pages. ${featureList ? 'Features: ' + featureList + '.' : ''} Auto-approving...`,
451
+ });
332
452
  break;
333
453
  }
334
454
  case 'awaiting_approval': {
@@ -346,9 +466,12 @@ function handleSSEEvent(type, data) {
346
466
  const ftPhase = data.phase || 'functional_testing';
347
467
  AppState.update({ phase: ftPhase });
348
468
  if (data.passedCount !== undefined) {
349
- AppState.addLog({ level: 'info', message: `Functional: ${data.passedCount} passed, ${data.failedCount} failed` });
469
+ const failed = data.failedCount || 0;
470
+ appendActivityItem('\u2731', `Testing: ${data.passedCount} passed, ${failed} failed`, failed > 0 ? 'activity-warn' : 'activity-success');
471
+ AppState.addLog({ level: 'info', message: `Functional: ${data.passedCount} passed, ${failed} failed` });
350
472
  }
351
473
  if (data.failureCount) {
474
+ appendActivityItem('\u21bb', `Repairing ${data.failureCount} failures (attempt ${data.attempt})`, 'activity-warn');
352
475
  AppState.addLog({ level: 'warn', message: `Repairing ${data.failureCount} test failures (attempt ${data.attempt})` });
353
476
  }
354
477
  break;
@@ -356,11 +479,9 @@ function handleSSEEvent(type, data) {
356
479
  case 'error': {
357
480
  AppState.update({ phase: 'failed' });
358
481
  AppState.addLog({ level: 'error', message: data.message || data.error || 'Unknown error' });
482
+ appendActivityItem('\u2718', data.message || data.error || 'Build failed', 'activity-error');
483
+ unlockChat();
359
484
  if (!completionShown) {
360
- const input = document.getElementById('prompt-input');
361
- const btn = document.getElementById('submit-btn');
362
- if (input) input.disabled = false;
363
- if (btn) btn.disabled = false;
364
485
  showCompletionOverlay({ status: 'failed', appName: 'Application' });
365
486
  completionShown = true;
366
487
  }
@@ -372,23 +493,26 @@ function handleSSEEvent(type, data) {
372
493
  if (data.metrics) {
373
494
  AppState.update({ metrics: data.metrics });
374
495
  }
375
- // Re-enable input
376
- const input = document.getElementById('prompt-input');
377
- const btn = document.getElementById('submit-btn');
378
- if (input) input.disabled = false;
379
- if (btn) btn.disabled = false;
380
- // Show completion overlay with launch button
496
+ appendActivityItem('\u2713', 'Build complete', 'activity-success');
497
+ unlockChat();
381
498
  if (!completionShown) {
382
499
  showCompletionOverlay(data);
383
500
  completionShown = true;
384
501
  }
385
- // Now safe to disconnect — overlay is rendered
386
502
  disconnect();
387
503
  break;
388
504
  }
389
505
  }
390
506
  }
391
507
 
508
+ function updateStatusBar() {
509
+ const state = AppState.get();
510
+ const phaseEl = document.getElementById('status-phase');
511
+ const tasksEl = document.getElementById('status-tasks');
512
+ if (phaseEl) phaseEl.textContent = `phase: ${state.phase}`;
513
+ if (tasksEl) tasksEl.textContent = `tasks: ${state.metrics.tasksComplete}/${state.metrics.totalTasks}`;
514
+ }
515
+
392
516
  function connect(appId) {
393
517
  if (eventSource) {
394
518
  eventSource.close();
@@ -459,13 +583,16 @@ export function initSSEClient(base, key) {
459
583
  apiBase = base || '';
460
584
  apiKey = key || '';
461
585
 
462
- // Share config with question-cards module
463
- configureQuestionCards(apiBase, apiKey);
586
+ // Share config with chat module
464
587
  configureChat(apiBase, apiKey);
465
588
 
466
589
  // Expose connect for manual invocation from bootstrap
467
590
  window.__connectSSE = connect;
468
591
 
592
+ // Status bar updates
593
+ AppState.on('phase_change', updateStatusBar);
594
+ AppState.on('metric_update', updateStatusBar);
595
+
469
596
  // Reset completionShown on new runs
470
597
  AppState.on('reset', () => {
471
598
  completionShown = false;
package/demo/js/state.js CHANGED
@@ -18,7 +18,10 @@ const AppState = (() => {
18
18
  claimsFailed: 0,
19
19
  filesGenerated: 0
20
20
  },
21
- log: []
21
+ log: [],
22
+ containerState: 'cold', // WebContainer lifecycle: cold|warm|building|verifying|done
23
+ previewUrl: null, // iframe URL from WebContainer server-ready event
24
+ fileQueue: [] // files queued before container is ready
22
25
  };
23
26
 
24
27
  function on(event, fn) {
@@ -45,6 +48,7 @@ const AppState = (() => {
45
48
 
46
49
  function update(partial) {
47
50
  const oldPhase = state.phase;
51
+ const oldContainerState = state.containerState;
48
52
 
49
53
  // Merge top-level primitives and objects
50
54
  for (const key of Object.keys(partial)) {
@@ -61,6 +65,9 @@ const AppState = (() => {
61
65
  if (partial.phase !== undefined && partial.phase !== oldPhase) {
62
66
  emit('phase_change', { from: oldPhase, to: state.phase });
63
67
  }
68
+ if (partial.containerState !== undefined && partial.containerState !== oldContainerState) {
69
+ emit('container_state_change', { from: oldContainerState, to: state.containerState });
70
+ }
64
71
  if (partial.currentTask !== undefined) {
65
72
  emit('task_update', state.currentTask);
66
73
  }
@@ -151,6 +158,9 @@ const AppState = (() => {
151
158
  state.codeFiles = [];
152
159
  state.metrics = { tasksComplete: 0, totalTasks: 0, claimsVerified: 0, claimsFailed: 0, filesGenerated: 0 };
153
160
  state.log = [];
161
+ state.containerState = 'cold';
162
+ state.previewUrl = null;
163
+ state.fileQueue = [];
154
164
  emit('reset', null);
155
165
  }
156
166