codehere 0.7.1 → 1.0.1

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 (139) hide show
  1. package/README.md +22 -13
  2. package/dist/application/services/verdict-service.js +154 -0
  3. package/dist/application/use-cases/assess-cross-vendor.js +126 -0
  4. package/dist/application/use-cases/build-cross-vendor-verdict.js +118 -0
  5. package/dist/application/use-cases/export-aat.js +107 -0
  6. package/dist/application/use-cases/generate-acceptance-criteria.js +223 -0
  7. package/dist/application/use-cases/generate-final-report.js +140 -15
  8. package/dist/application/use-cases/generate-narration.js +5 -3
  9. package/dist/application/use-cases/generate-thread-title.js +5 -2
  10. package/dist/application/use-cases/narrator-prompt-template.js +1 -1
  11. package/dist/application/use-cases/plan-auto-review.js +57 -0
  12. package/dist/application/use-cases/reconcile-audit.js +100 -0
  13. package/dist/application/use-cases/rerun-tests.js +226 -0
  14. package/dist/application/use-cases/search-sessions.js +70 -0
  15. package/dist/application/use-cases/should-auto-review.js +71 -0
  16. package/dist/application/use-cases/thread-export.js +90 -0
  17. package/dist/application/use-cases/verify-claim.js +187 -5
  18. package/dist/domain/audit.js +11 -0
  19. package/dist/domain/services/codebase-detector.js +0 -6
  20. package/dist/domain/task.js +91 -0
  21. package/dist/error-handler.js +3 -115
  22. package/dist/index.js +279 -31
  23. package/dist/infrastructure/completion/bash-completion.js +7 -3
  24. package/dist/infrastructure/completion/fish-completion.js +7 -12
  25. package/dist/infrastructure/completion/zsh-completion.js +7 -12
  26. package/dist/infrastructure/config/config-manager.js +4 -3
  27. package/dist/infrastructure/config/product-context.js +1 -19
  28. package/dist/infrastructure/context/adapters/figma-source.js +37 -2
  29. package/dist/infrastructure/context/adapters/github-issue-source.js +3 -2
  30. package/dist/infrastructure/context/github-api.js +110 -0
  31. package/dist/infrastructure/context/github-device-flow.js +65 -0
  32. package/dist/infrastructure/context/oauth-store.js +5 -2
  33. package/dist/infrastructure/context/registry-bootstrap.js +0 -4
  34. package/dist/infrastructure/cost/budget-config.js +4 -3
  35. package/dist/infrastructure/cost/cost-router.js +16 -23
  36. package/dist/infrastructure/cost/spend-aggregator.js +91 -16
  37. package/dist/infrastructure/cost/team-ledger.js +355 -0
  38. package/dist/infrastructure/discovery/discovery-log.js +85 -0
  39. package/dist/infrastructure/hooks/audit-tip-signature.js +336 -0
  40. package/dist/infrastructure/hooks/claude-sdk-hook.js +139 -3
  41. package/dist/infrastructure/hooks/hook-mode.js +20 -4
  42. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +8 -2
  43. package/dist/infrastructure/hooks/pre-prompt-scan.js +3 -3
  44. package/dist/infrastructure/observability/execution-tracer.js +1 -1
  45. package/dist/infrastructure/orchestration/agents-config-seed.js +23 -11
  46. package/dist/infrastructure/orchestration/aider-adapter.js +188 -24
  47. package/dist/infrastructure/orchestration/artifacts-extractor.js +60 -2
  48. package/dist/infrastructure/orchestration/cassette-recorder.js +1 -1
  49. package/dist/infrastructure/orchestration/claude-code-adapter.js +117 -3
  50. package/dist/infrastructure/orchestration/codex-adapter.js +52 -16
  51. package/dist/infrastructure/orchestration/conductor-adapter.js +46 -7
  52. package/dist/infrastructure/orchestration/context-handoff.js +32 -14
  53. package/dist/infrastructure/orchestration/detect-ollama.js +75 -0
  54. package/dist/infrastructure/orchestration/failover-config.js +1 -11
  55. package/dist/infrastructure/orchestration/gemini-adapter.js +92 -12
  56. package/dist/infrastructure/orchestration/generic-cli-adapter.js +134 -3
  57. package/dist/infrastructure/orchestration/graphify-context.js +14 -59
  58. package/dist/infrastructure/orchestration/memory-injector.js +4 -1
  59. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +167 -9
  60. package/dist/infrastructure/orchestration/opencode-adapter.js +84 -14
  61. package/dist/infrastructure/orchestration/progress-aggregator.js +275 -23
  62. package/dist/infrastructure/orchestration/session-manager.js +593 -94
  63. package/dist/infrastructure/orchestration/session-persistence.js +177 -41
  64. package/dist/infrastructure/orchestration/thread-key-store.js +1 -1
  65. package/dist/infrastructure/orchestration/write-pass-store.js +0 -8
  66. package/dist/infrastructure/protocols/cloud-sync.js +2 -2
  67. package/dist/infrastructure/protocols/mcp-helpers.js +1 -1
  68. package/dist/infrastructure/protocols/mcp-server-impl.js +1498 -354
  69. package/dist/infrastructure/protocols/mcp-tools.js +401 -0
  70. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +121 -0
  71. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +44 -0
  72. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +56 -0
  73. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +588 -0
  74. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +168 -0
  75. package/dist/infrastructure/protocols/ui/app-script-conversation.js +67 -0
  76. package/dist/infrastructure/protocols/ui/app-script-drawer.js +115 -0
  77. package/dist/infrastructure/protocols/ui/app-script-failover.js +133 -0
  78. package/dist/infrastructure/protocols/ui/app-script-github.js +113 -0
  79. package/dist/infrastructure/protocols/ui/app-script-observer.js +104 -0
  80. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +80 -0
  81. package/dist/infrastructure/protocols/ui/app-script-overlay.js +203 -0
  82. package/dist/infrastructure/protocols/ui/app-script-recovery.js +136 -0
  83. package/dist/infrastructure/protocols/ui/app-script-render.js +166 -0
  84. package/dist/infrastructure/protocols/ui/app-script-share.js +78 -0
  85. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +194 -0
  86. package/dist/infrastructure/protocols/ui/app-script-tour.js +78 -0
  87. package/dist/infrastructure/protocols/ui/app-script-wpass.js +312 -0
  88. package/dist/infrastructure/protocols/ui/app-script.js +2998 -0
  89. package/dist/infrastructure/protocols/ui/font-faces.js +7 -0
  90. package/dist/infrastructure/protocols/ui/styles.js +2147 -0
  91. package/dist/infrastructure/protocols/ui/tokens.js +96 -0
  92. package/dist/infrastructure/protocols/ui-page-v2.js +116 -4126
  93. package/dist/infrastructure/protocols/verify-page.js +884 -148
  94. package/dist/infrastructure/security/fix-code.js +144 -0
  95. package/dist/infrastructure/security/fix-secret.js +89 -0
  96. package/dist/infrastructure/security/permissions-config.js +3 -2
  97. package/dist/infrastructure/security/scan-types.js +10 -0
  98. package/dist/infrastructure/security/ship-scan.js +195 -0
  99. package/dist/infrastructure/security/trust-scorer.js +70 -3
  100. package/dist/infrastructure/security/vibe-checks.js +99 -0
  101. package/dist/infrastructure/storage/atomic-write.js +86 -0
  102. package/dist/infrastructure/storage/project-thread-mirror.js +151 -4
  103. package/dist/infrastructure/storage/project-thread-reader.js +262 -25
  104. package/dist/infrastructure/storage/project-thread-title-backfill.js +1 -1
  105. package/dist/monitoring.js +2 -2
  106. package/dist/presentation/cli/commands/audit-command.js +79 -4
  107. package/dist/presentation/cli/commands/budget-command.js +2 -2
  108. package/dist/presentation/cli/commands/check-command.js +96 -0
  109. package/dist/presentation/cli/commands/context-source-command.js +20 -1
  110. package/dist/presentation/cli/commands/help-command.js +5 -5
  111. package/dist/presentation/cli/commands/hook-command.js +5 -5
  112. package/dist/presentation/cli/commands/init-command.js +89 -66
  113. package/dist/presentation/cli/commands/memory-command.js +28 -28
  114. package/dist/presentation/cli/commands/permission-command.js +1 -12
  115. package/dist/presentation/cli/commands/protocols-builder.js +2 -2
  116. package/dist/presentation/cli/commands/protocols-command.js +4 -4
  117. package/dist/presentation/cli/commands/repo-map-builder.js +1 -1
  118. package/dist/presentation/cli/commands/report-command.js +48 -32
  119. package/dist/presentation/cli/commands/scan-command.js +52 -0
  120. package/dist/presentation/cli/commands/scan-tool-command.js +24 -8
  121. package/dist/presentation/cli/commands/search-command.js +51 -0
  122. package/dist/presentation/cli/commands/setup-command.js +31 -8
  123. package/dist/presentation/cli/commands/status-command.js +39 -86
  124. package/dist/presentation/cli/commands/team-command.js +256 -0
  125. package/dist/presentation/cli/commands/thread-command.js +87 -5
  126. package/dist/presentation/cli/commands/trust-command.js +53 -31
  127. package/dist/presentation/cli/commands/verify-command.js +74 -9
  128. package/dist/presentation/cli/error-display.js +0 -8
  129. package/dist/presentation/cli/repl.js +15 -7
  130. package/dist/presentation/cli/task-deliverable.js +36 -10
  131. package/dist/testing/replay-adapter.js +0 -19
  132. package/dist/ui.js +51 -239
  133. package/dist/utils/version.js +0 -6
  134. package/package.json +30 -10
  135. package/dist/domain/entities/task.js +0 -61
  136. package/dist/infrastructure/protocols/ui-page.js +0 -2377
  137. package/dist/infrastructure/security/input-validator.js +0 -152
  138. package/dist/infrastructure/storage/embeddings-path.js +0 -37
  139. package/dist/infrastructure/storage/task-repository.js +0 -206
@@ -0,0 +1,312 @@
1
+ /**
2
+ * W-PASS approval modal — the browser half of mediated tool approval: poll for a pending
3
+ * pass, render what the agent wants to do in human words, take the decision back.
4
+ *
5
+ * Split out of app-script.ts (strangler fig, split 5). This is a verbatim slice of the same
6
+ * template literal and is interpolated back at exactly the position it was cut from, so it
7
+ * still shares the surrounding IIFE scope — `wpCloseModal` is called by the Escape handler
8
+ * that stayed behind, and that keeps working precisely because nothing about scope moved.
9
+ * Extraction is verified by hashing the composed APP_SCRIPT before and after: same bytes.
10
+ */
11
+ export const WPASS_MODAL = ` /* v0.9 S6 part C · W-PASS approval modal · poll -> render -> decide */
12
+ let _wpCurrent = null; // current pending pass shown in modal
13
+ let _wpEditing = false;
14
+ const wpModal = document.getElementById('wp-modal');
15
+ const wpBadge = document.getElementById('wp-pending-badge');
16
+
17
+ function wpPretty(v){
18
+ try { return JSON.stringify(v, null, 2); }
19
+ catch { return String(v); }
20
+ }
21
+ // Demo v12 diff-style payload coloring · render JSON with keys grey,
22
+ // string vals green, "from->to" patterns colored. When the value is a
23
+ // string starting with "FROM:VAL -> TO:VAL" or {from, to} pair, render
24
+ // the FROM with strike-through red and the TO with green.
25
+ function wpPayloadHtml(v){
26
+ function esc(s){ return String(s).replace(/[&<>"]/g, c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
27
+ function renderVal(val, indent){
28
+ const pad = ' '.repeat(indent);
29
+ if(val === null) return '<span class="val">null</span>';
30
+ if(typeof val === 'number' || typeof val === 'boolean') return '<span class="val">'+esc(String(val))+'</span>';
31
+ if(typeof val === 'string'){
32
+ const m = val.match(/^(.+?)\\s*\\u2192\\s*(.+)$/);
33
+ if(m) return '<span class="from">"'+esc(m[1])+'"</span><span class="arrow">\\u203A</span><span class="val">"'+esc(m[2])+'"</span>';
34
+ return '<span class="val">"'+esc(val)+'"</span>';
35
+ }
36
+ if(Array.isArray(val)){
37
+ if(val.length === 0) return '[]';
38
+ const parts = val.map(x => pad + ' ' + renderVal(x, indent+1));
39
+ return '[\\n' + parts.join(',\\n') + '\\n' + pad + ']';
40
+ }
41
+ if(typeof val === 'object'){
42
+ // Special-case {from, to} -> render as colored diff
43
+ if('from' in val && 'to' in val){
44
+ return '<span class="from">"'+esc(String(val.from))+'"</span><span class="arrow">\\u203A</span><span class="val">"'+esc(String(val.to))+'"</span>';
45
+ }
46
+ const keys = Object.keys(val);
47
+ if(keys.length === 0) return '{}';
48
+ const parts = keys.map(k => pad + ' <span class="key">"'+esc(k)+'"</span>: ' + renderVal(val[k], indent+1));
49
+ return '{\\n' + parts.join(',\\n') + '\\n' + pad + '}';
50
+ }
51
+ return esc(String(val));
52
+ }
53
+ try { return renderVal(v, 0); }
54
+ catch { return esc(wpPretty(v)); }
55
+ }
56
+ function wpHumanTitle(intent){
57
+ const target = String(intent.target||'');
58
+ const method = String(intent.method||'POST');
59
+ const tail = target.replace(/^[a-z]+:\\/\\//,'');
60
+ return method + ' · ' + tail;
61
+ }
62
+ function wpHumanTarget(intent){
63
+ return String(intent.target||'');
64
+ }
65
+ let _wpTrustGrade = null; // last-known trust grade for current pass
66
+ function wpRender(status){
67
+ if(!status || status.kind !== 'pending') return Promise.resolve();
68
+ const it = status.intent;
69
+ _wpCurrent = status;
70
+ _wpEditing = false;
71
+ document.getElementById('wp-title').textContent = wpHumanTitle(it);
72
+ document.getElementById('wp-reason').textContent = it.reason || '(no reason given)';
73
+ document.getElementById('wp-target').textContent = wpHumanTarget(it);
74
+ document.getElementById('wp-emitter').textContent = it.emittedBy || 'agent';
75
+ document.getElementById('wp-session').textContent = (it.sessionId||'').slice(0,12);
76
+ document.getElementById('wp-method').textContent = it.method || 'POST';
77
+ // v0.9 · fetch + render per-domain trust grade · returned promise lets
78
+ // callers (e.g., openWpModal demo synth) chain reliably instead of
79
+ // racing setTimeout. Resolves whether the fetch succeeds, 404s, or
80
+ // network-fails.
81
+ const trustPromise = fetch('/api/writepass/'+encodeURIComponent(it.id)+'/trust')
82
+ .then(r=>r.json()).then(t=>{
83
+ _wpTrustGrade = t;
84
+ const row = document.getElementById('wp-trust-row');
85
+ if(!row || !t || !t.host) { if(row) row.style.display='none'; return; }
86
+ row.style.display = '';
87
+ document.getElementById('wp-trust-host').textContent = t.host;
88
+ const ge = document.getElementById('wp-trust-grade');
89
+ ge.textContent = t.grade;
90
+ ge.className = 'wp-trust-grade ' + t.grade;
91
+ document.getElementById('wp-trust-reason').textContent = ' · ' + (t.reason||'');
92
+ })
93
+ .catch(()=>{ /* no daemon trust info · ignore */ });
94
+ document.getElementById('wp-targetline').textContent = wpHumanTarget(it);
95
+ document.getElementById('wp-payload').innerHTML = wpPayloadHtml(it.payload);
96
+ document.getElementById('wp-payload-wrap').style.display = '';
97
+ document.getElementById('wp-payload-edit').style.display = 'none';
98
+ document.getElementById('wp-actions').style.display = '';
99
+ document.getElementById('wp-actions-editing').style.display = 'none';
100
+ // S11 · a synthesized demo pass has no server-side intent — Approve would
101
+ // 404 every time (a dead-end the atomic audit found by clicking it).
102
+ // Disabled-with-reason, never a button that always fails.
103
+ const isSynth = !!status.synth || /^demo-wp-/.test(String(it.id||''));
104
+ const aBtn=document.getElementById('wp-approve'), eBtn=document.getElementById('wp-edit');
105
+ if(aBtn){ aBtn.disabled=isSynth; aBtn.title=isSynth?'demo pass \\u00b7 not sendable (no real write awaits)':''; }
106
+ if(eBtn){ eBtn.disabled=isSynth; eBtn.title=isSynth?'demo pass \\u00b7 nothing to edit-and-send':''; }
107
+ const result = document.getElementById('wp-result');
108
+ result.style.display = 'none';
109
+ result.className = 'wp-result';
110
+ result.textContent = '';
111
+ wpModal.classList.add('open');
112
+ wpBadge.classList.remove('show');
113
+ return trustPromise;
114
+ }
115
+ function wpCloseModal(){
116
+ wpModal.classList.remove('open');
117
+ _wpCurrent = null;
118
+ _wpEditing = false;
119
+ // Fix D9 (2026-05-23) · re-poll immediately so the badge re-appears
120
+ // for any still-pending pass without waiting up to 4s. Without this,
121
+ // closing the modal without deciding left the badge hidden for one
122
+ // poll cycle even though the pass was still pending.
123
+ wpPoll();
124
+ }
125
+ async function wpPoll(){
126
+ try{
127
+ const r = await fetch('/api/writepass/pending');
128
+ if(!r.ok) return;
129
+ const j = await r.json();
130
+ const passes = (j && j.passes) || [];
131
+ // S13 · the decision queue renders from the SAME pending list the
132
+ // modal polls — one truth, two surfaces.
133
+ window._wpPendingAll = passes;
134
+ try{ renderDecisions(); }catch(e){}
135
+ if(passes.length === 0){
136
+ if(!wpModal.classList.contains('open')) wpBadge.classList.remove('show');
137
+ return;
138
+ }
139
+ // If modal already open with this pass, skip · otherwise badge it.
140
+ const top = passes[0];
141
+ if(_wpCurrent && _wpCurrent.intent.id === top.intent.id) return;
142
+ if(wpModal.classList.contains('open')) return;
143
+ _wpCurrent = top;
144
+ wpBadge.classList.add('show');
145
+ }catch{
146
+ // Daemon hiccup · ignore · next poll catches up.
147
+ }
148
+ }
149
+ async function wpDecide(kind, editedPayload, trustThisDomain){
150
+ if(!_wpCurrent) return;
151
+ const passId = _wpCurrent.intent.id;
152
+ // Synth demo passes exist only client-side — the daemon has no record, so
153
+ // a POST can only error. Cancel just closes the modal locally.
154
+ if(_wpCurrent.synth || /^demo-wp-/.test(String(passId||''))){ wpCloseModal(); return; }
155
+ const body = { kind };
156
+ if(kind === 'edit') body.editedPayload = editedPayload;
157
+ if(trustThisDomain) body.trustThisDomain = true;
158
+ const result = document.getElementById('wp-result');
159
+ result.style.display = 'block';
160
+ result.className = 'wp-result';
161
+ result.textContent = kind === 'cancel' ? 'cancelling…' : 'sending…';
162
+ try{
163
+ const r = await fetch('/api/writepass/'+encodeURIComponent(passId)+'/decide', {
164
+ method: 'POST',
165
+ headers: { 'Content-Type': 'application/json' },
166
+ body: JSON.stringify(body),
167
+ });
168
+ const j = await r.json();
169
+ // v0.9 · first-write domain trust gate
170
+ if(j && j.status === 'requires_domain_trust'){
171
+ result.className = 'wp-result fail';
172
+ result.innerHTML = 'first-write to <b>'+orEscape(j.host)+'</b> (grade '+orEscape(j.grade)+') requires trust · '+orEscape(j.reason||'')+
173
+ '<div class="wp-trust-prompt"><label><input type="checkbox" id="wp-trust-confirm"/> Trust <b>'+orEscape(j.host)+'</b> for future writes · then approve + send</label>'+
174
+ ' <button class="wp-btn-approve" id="wp-trust-approve" style="margin-left:8px;padding:5px 10px;font-size:12px">Approve + trust</button></div>';
175
+ document.getElementById('wp-trust-approve').onclick = ()=>{
176
+ if(!document.getElementById('wp-trust-confirm').checked){ result.textContent='Tick the checkbox to confirm trust first.'; return; }
177
+ wpDecide('approve', undefined, true);
178
+ };
179
+ return;
180
+ }
181
+ const finalStatus = j && j.kind ? j : null;
182
+ if(!finalStatus){
183
+ result.className = 'wp-result fail';
184
+ result.textContent = 'unexpected response · check daemon logs';
185
+ return;
186
+ }
187
+ if(finalStatus.kind === 'cancelled'){
188
+ result.className = 'wp-result cancelled';
189
+ result.textContent = 'cancelled · audit chain captured the refusal';
190
+ } else if(finalStatus.kind === 'sent'){
191
+ const code = finalStatus.response && finalStatus.response.status;
192
+ result.className = 'wp-result ok';
193
+ result.textContent = '✓ sent · HTTP '+code+' · audit chain captured payload + response';
194
+ } else if(finalStatus.kind === 'failed'){
195
+ const code = finalStatus.response && finalStatus.response.status;
196
+ const err = finalStatus.response && finalStatus.response.error;
197
+ result.className = 'wp-result fail';
198
+ result.textContent = '\\u2715 failed · HTTP '+code+(err?' · '+err:'')+' · audit captured';
199
+ }
200
+ _wpCurrent = null;
201
+ setTimeout(wpCloseModal, 2400);
202
+ }catch(err){
203
+ result.className = 'wp-result fail';
204
+ result.textContent = 'request failed · '+((err&&err.message)||String(err));
205
+ }
206
+ }
207
+
208
+ wpBadge?.addEventListener('click', ()=>{
209
+ if(_wpCurrent && _wpCurrent.kind === 'pending') wpRender(_wpCurrent);
210
+ else wpPoll().then(()=>{ if(_wpCurrent) wpRender(_wpCurrent); });
211
+ });
212
+ // Expose an opener so the inline .write-pass primitive's "Approve + send"
213
+ // button (rendered inside the step timeline · see toolCardsHtml · gap 8)
214
+ // can open the modal. If a real pending pass is already polled, use it.
215
+ // Artifacts drawer open/close (task #51). Global so inline onclick resolves.
216
+ window.openArtD = function(){ _artOpen=true; var d=document.getElementById('artDrawer'),s=document.getElementById('artScrim'); if(d)d.classList.add('open'); if(s)s.classList.add('open'); };
217
+ window.closeArtD = function(){ _artOpen=false; var d=document.getElementById('artDrawer'),s=document.getElementById('artScrim'); if(d)d.classList.remove('open'); if(s)s.classList.remove('open'); };
218
+
219
+ // Otherwise synthesize a demo-shape pass from the open detail view so the
220
+ // modal isn't a dead button on the seed session.
221
+ window.openWpModal = function(){
222
+ if(_wpCurrent && _wpCurrent.kind === 'pending') {
223
+ wpRender(_wpCurrent);
224
+ return;
225
+ }
226
+ // Synthesize from the current detail view if one is open · the demo
227
+ // seed's W-PASS step has the payload encoded in step.output.
228
+ const id = (document.getElementById('detail-view') && window._detailId) || 'demo-pending';
229
+ const synth = {
230
+ kind: 'pending',
231
+ synth: true,
232
+ intent: {
233
+ id: 'demo-wp-' + Date.now(),
234
+ sessionId: id,
235
+ emittedBy: 'conductor',
236
+ method: 'PATCH',
237
+ target: 'linear://INFRA-247',
238
+ reason: 'Close ticket once tests pass + benchmark holds · human approval required for third-party writes.',
239
+ payload: { status: { from: 'in-progress', to: 'done' } },
240
+ },
241
+ };
242
+ // Synthesize the trust-grade row too so the demo modal shows the full
243
+ // affordance. wpRender now returns the fetch promise · chain on .then()
244
+ // so the override fires AFTER the async fetch settles (no setTimeout
245
+ // race · was 300ms guess, now deterministic). The trust-reason includes
246
+ // a "(demo data)" suffix per the 2026-05-24 retro: synth data must be
247
+ // visibly honest about being synth, per [[no-screen-lies]] +
248
+ // [[demo-synth-modal-pattern]] rules.
249
+ wpRender(synth).then(() => {
250
+ const row = document.getElementById('wp-trust-row');
251
+ if(!row) return;
252
+ row.style.display = '';
253
+ document.getElementById('wp-trust-host').textContent = 'linear.app';
254
+ const ge = document.getElementById('wp-trust-grade');
255
+ ge.textContent = 'A';
256
+ ge.className = 'wp-trust-grade A';
257
+ document.getElementById('wp-trust-reason').textContent =
258
+ ' \\u00B7 18mo on this domain \\u00B7 no overrides \\u00B7 (demo data \\u00B7 linear adapter not configured)';
259
+ });
260
+ };
261
+ // S13 · inline Approve/Deny from the decision queue — the SAME wpDecide
262
+ // the modal uses; the result is mirrored inline + toasted, never silent.
263
+ window.wpQueueDecide = function(passId, kind, btn){
264
+ const p=(window._wpPendingAll||[]).find(function(x){ return x.intent&&x.intent.id===passId; });
265
+ if(!p){ al('That write-pass is no longer pending'); return; }
266
+ _wpCurrent=p;
267
+ const row=btn&&btn.closest?btn.closest('.dq-row'):null;
268
+ if(row) row.querySelectorAll('.dq-btn').forEach(function(b){ b.disabled=true; });
269
+ Promise.resolve(wpDecide(kind)).then(function(){
270
+ // First-write trust gate -> the interactive confirm lives in the modal;
271
+ // open it rather than leaving a dead inline row.
272
+ if(document.getElementById('wp-trust-approve')){ wpRender(p); return; }
273
+ const res=document.getElementById('wp-result');
274
+ const txt=(res&&res.textContent)||'';
275
+ if(row){ const rl=row.querySelector('.dq-result'); if(rl&&txt){ rl.textContent=txt; rl.style.display='block'; } }
276
+ if(txt) al(txt);
277
+ wpPoll();
278
+ });
279
+ };
280
+ document.getElementById('wp-approve')?.addEventListener('click', ()=>wpDecide('approve'));
281
+ document.getElementById('wp-cancel')?.addEventListener('click', ()=>wpDecide('cancel'));
282
+ document.getElementById('wp-edit')?.addEventListener('click', ()=>{
283
+ if(!_wpCurrent) return;
284
+ _wpEditing = true;
285
+ document.getElementById('wp-payload-wrap').style.display = 'none';
286
+ const ta = document.getElementById('wp-payload-edit');
287
+ ta.style.display = 'block';
288
+ ta.value = wpPretty(_wpCurrent.intent.payload);
289
+ document.getElementById('wp-actions').style.display = 'none';
290
+ document.getElementById('wp-actions-editing').style.display = '';
291
+ });
292
+ document.getElementById('wp-edit-cancel')?.addEventListener('click', ()=>{
293
+ if(!_wpCurrent) return;
294
+ wpRender(_wpCurrent); // re-render the read-only view
295
+ });
296
+ document.getElementById('wp-save')?.addEventListener('click', ()=>{
297
+ const ta = document.getElementById('wp-payload-edit');
298
+ let parsed;
299
+ try{ parsed = JSON.parse(ta.value); }
300
+ catch(err){
301
+ const result = document.getElementById('wp-result');
302
+ result.style.display = 'block';
303
+ result.className = 'wp-result fail';
304
+ result.textContent = 'edited payload is not valid JSON · '+((err&&err.message)||'parse error');
305
+ return;
306
+ }
307
+ wpDecide('edit', parsed);
308
+ });
309
+ // Poll on load + every 4s. Cheap.
310
+ wpPoll();
311
+ setInterval(wpPoll, 4000);`;
312
+ //# sourceMappingURL=app-script-wpass.js.map