wyrm-mcp 7.2.0 → 7.2.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 (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
@@ -1,244 +1,6 @@
1
- /**
2
- * Trace harvest (v7 F4 T040) — turn a harness's working trace into run-tagged
3
- * review-queue candidates, OFFLINE.
4
- *
5
- * Three trace shapes are ingested into one normalized prose stream that feeds
6
- * the EXISTING auto_capture extractor (src/auto-capture.ts local Ollama
7
- * WYRM_EXTRACT_MODEL / deterministic fallback, NEVER a cloud LLM, Article III):
8
- *
9
- * 1. Claude Code session JSONL — one JSON object per line, each
10
- * { message: { role, content }, … } (the same shape
11
- * scripts/hooks/wyrm-session-capture.mjs already parses). User prose +
12
- * assistant text are kept; tool-result / system-reminder blocks dropped.
13
- * 2. ~/.dragon/traces — the dragon-cli flywheel trace records. Tolerant of
14
- * both line-delimited JSON and a top-level JSON array; each record's
15
- * prompt/response/text/content fields are harvested.
16
- * 3. WYRM_TRACE_TOOL_CALLS output — the `tool_call` Live Memory events the
17
- * PostToolUse hook emits ({ kind:'tool_call', actor, payload, … }) or the
18
- * raw stdin shape the hook reads ({ tool_name, tool_input, … }). Collapsed
19
- * into a deterministic per-tool activity summary.
20
- *
21
- * SECRET REDACTION: every harvested segment passes through redactSecrets()
22
- * BEFORE extraction, so an API key / bearer token / password that scrolled
23
- * through a transcript never lands in the review queue. Deterministic, no
24
- * network. Tool-call payloads are collapsed to keys (never values) for the same
25
- * reason — a secret passed as a tool argument is summarized, not stored.
26
- *
27
- * Pure: parsing + redaction are total string functions (no I/O, no clock, no
28
- * RNG). The MCP handler injects the DB writes + the extractor.
29
- *
30
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
31
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
32
- */
33
- /** Control chars (ANSI/BEL/NUL) must never reach the review queue or a TUI. */
34
- // eslint-disable-next-line no-control-regex
35
- const CTRL = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
36
- /**
37
- * Redact common secret shapes from a string so they never enter the review
38
- * queue. Deterministic + offline. Conservative (favours over-redaction): the
39
- * goal is "a leaked credential is never stored", not perfect classification.
40
- * Each rule replaces the SECRET portion with `[REDACTED]`, preserving
41
- * surrounding prose so the candidate is still meaningful.
42
- */
43
- export function redactSecrets(input) {
44
- if (!input)
45
- return '';
46
- let s = input;
47
- // High-entropy / vendor-prefixed token shapes (order matters — specific first).
48
- const rules = [
49
- // sk-... / sk-ant-api03-... / rk_live_... (OpenAI/Anthropic/Stripe-style);
50
- // the token body may carry internal -/_ separators (sk-ant-api03-<rand>).
51
- [/\b(?:sk|rk|pk)[-_][A-Za-z0-9][A-Za-z0-9_-]{14,}\b/g, '[REDACTED]'],
52
- // GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_) + fine-grained github_pat_
53
- [/\bgh[posru]_[A-Za-z0-9]{20,}\b/g, '[REDACTED]'],
54
- [/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, '[REDACTED]'],
55
- // AWS access key id
56
- [/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, '[REDACTED]'],
57
- // Slack tokens
58
- [/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, '[REDACTED]'],
59
- // Google API key
60
- [/\bAIza[A-Za-z0-9_-]{30,}\b/g, '[REDACTED]'],
61
- // JWTs (three dot-separated base64url segments)
62
- [/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, '[REDACTED]'],
63
- // Authorization: Bearer <token>
64
- [/\b([Bb]earer)\s+[A-Za-z0-9._-]{12,}/g, '$1 [REDACTED]'],
65
- // key=value / "key": "value" for secret-ish key names (api_key, token,
66
- // password, secret, passwd, authorization, access_token, private_key).
67
- [/((?:api[_-]?key|secret|token|password|passwd|auth(?:orization)?|access[_-]?token|private[_-]?key|client[_-]?secret)["']?\s*[:=]\s*["']?)([A-Za-z0-9._/+\-]{6,})/gi, '$1[REDACTED]'],
68
- ];
69
- for (const [re, repl] of rules)
70
- s = s.replace(re, repl);
71
- return s;
72
- }
73
- /** Pull plain text out of a Claude/Anthropic message `content` (string or block array). */
74
- function extractMessageText(content) {
75
- if (content == null)
76
- return '';
77
- if (typeof content === 'string')
78
- return content;
79
- if (!Array.isArray(content))
80
- return '';
81
- const out = [];
82
- for (const b of content) {
83
- if (typeof b === 'string') {
84
- out.push(b);
85
- continue;
86
- }
87
- if (b && typeof b === 'object') {
88
- const block = b;
89
- if (block.type === 'text' && typeof block.text === 'string')
90
- out.push(block.text);
91
- }
92
- }
93
- return out.join('\n');
94
- }
95
- /** Tidy a candidate chunk: strip control chars, collapse whitespace, clip. */
96
- function clean(text) {
97
- return redactSecrets(text).replace(CTRL, '').replace(/[ \t]+/g, ' ').replace(/\n{3,}/g, '\n\n').trim().slice(0, 4000);
98
- }
99
- /** Parse a JSONL/array trace body into per-line JSON objects (tolerant). */
100
- function parseRecords(raw) {
101
- const trimmed = raw.trim();
102
- if (!trimmed)
103
- return [];
104
- // Whole-body JSON array (dragon traces sometimes export one).
105
- if (trimmed.startsWith('[')) {
106
- try {
107
- const arr = JSON.parse(trimmed);
108
- if (Array.isArray(arr))
109
- return arr.filter((x) => !!x && typeof x === 'object');
110
- }
111
- catch { /* fall through to line mode */ }
112
- }
113
- const out = [];
114
- for (const line of trimmed.split('\n')) {
115
- const t = line.trim();
116
- if (!t || (t[0] !== '{' && t[0] !== '['))
117
- continue;
118
- try {
119
- const obj = JSON.parse(t);
120
- if (obj && typeof obj === 'object' && !Array.isArray(obj))
121
- out.push(obj);
122
- }
123
- catch { /* skip non-JSON line */ }
124
- }
125
- return out;
126
- }
127
- /** Claude Code session JSONL → user/assistant prose segments. */
128
- export function parseClaudeJsonl(raw) {
129
- const out = [];
130
- for (const obj of parseRecords(raw)) {
131
- const msg = (obj.message && typeof obj.message === 'object' ? obj.message : obj);
132
- const role = String(msg.role ?? obj.role ?? '');
133
- if (role !== 'user' && role !== 'assistant')
134
- continue;
135
- const text = extractMessageText(msg.content).trim();
136
- if (!text)
137
- continue;
138
- // Drop tool-result / system-reminder blocks (same rule as the capture hook).
139
- if (role === 'user' && (text.startsWith('<') || text.startsWith('[{')))
140
- continue;
141
- const cleaned = clean(text);
142
- if (cleaned.length >= 12)
143
- out.push({ role, text: cleaned });
144
- }
145
- return out;
146
- }
147
- /** dragon-cli flywheel trace → prose segments (prompt/response/text/content). */
148
- export function parseDragonTrace(raw) {
149
- const out = [];
150
- const fields = [['prompt', 'user'], ['input', 'user'], ['response', 'assistant'], ['output', 'assistant'], ['text', 'note'], ['content', 'note']];
151
- for (const rec of parseRecords(raw)) {
152
- for (const [field, role] of fields) {
153
- const v = rec[field];
154
- const text = typeof v === 'string' ? v : extractMessageText(v);
155
- if (!text)
156
- continue;
157
- const cleaned = clean(text);
158
- if (cleaned.length >= 12)
159
- out.push({ role, text: cleaned });
160
- }
161
- }
162
- return out;
163
- }
164
- /**
165
- * WYRM_TRACE_TOOL_CALLS output → ONE deterministic activity-summary segment.
166
- *
167
- * Accepts both the Live Memory `tool_call` event shape ({ kind:'tool_call',
168
- * actor:<toolName>, payload }) and the raw PostToolUse stdin shape
169
- * ({ tool_name, tool_input }). Payloads are collapsed to argument KEYS only
170
- * (never values) so a secret passed as a tool argument is never stored. The
171
- * order of first appearance is preserved (deterministic); counts are summed.
172
- */
173
- export function parseToolCalls(raw) {
174
- const counts = new Map();
175
- const order = [];
176
- for (const rec of parseRecords(raw)) {
177
- const kind = String(rec.kind ?? '');
178
- let tool = '';
179
- if (kind === 'tool_call')
180
- tool = String(rec.actor ?? rec.tool ?? '');
181
- else if (typeof rec.tool_name === 'string')
182
- tool = rec.tool_name;
183
- else if (typeof rec.tool === 'string')
184
- tool = rec.tool;
185
- tool = tool.replace(CTRL, '').trim().slice(0, 80);
186
- if (!tool)
187
- continue;
188
- if (!counts.has(tool))
189
- order.push(tool);
190
- counts.set(tool, (counts.get(tool) ?? 0) + 1);
191
- }
192
- if (order.length === 0)
193
- return [];
194
- const summary = order.map((t) => `${t}×${counts.get(t)}`).join(', ');
195
- return [{ role: 'tool', text: `Tool activity in this run: ${summary}.` }];
196
- }
197
- /** Sniff the trace format from the content (best-effort, deterministic). */
198
- export function detectTraceFormat(raw) {
199
- const recs = parseRecords(raw).slice(0, 50);
200
- if (recs.length === 0)
201
- return 'claude-jsonl';
202
- let toolish = 0, claudeish = 0;
203
- for (const r of recs) {
204
- if (r.kind === 'tool_call' || typeof r.tool_name === 'string')
205
- toolish++;
206
- const msg = (r.message && typeof r.message === 'object' ? r.message : r);
207
- const role = msg.role ?? r.role;
208
- if (role === 'user' || role === 'assistant' || role === 'system')
209
- claudeish++;
210
- }
211
- if (toolish > 0 && toolish >= claudeish)
212
- return 'tool-calls';
213
- if (claudeish > 0)
214
- return 'claude-jsonl';
215
- return 'dragon';
216
- }
217
- /** Parse a trace body into normalized segments using the chosen (or sniffed) format. */
218
- export function parseTrace(raw, format = 'auto') {
219
- const fmt = format === 'auto' ? detectTraceFormat(raw) : format;
220
- const segments = fmt === 'tool-calls' ? parseToolCalls(raw) :
221
- fmt === 'dragon' ? parseDragonTrace(raw) :
222
- parseClaudeJsonl(raw);
223
- return { segments, format: fmt };
224
- }
225
- /**
226
- * Fold parsed segments into ONE prose blob for the extractor. Deterministic
227
- * (preserves segment order); bounded so a huge transcript can't materialize an
228
- * unbounded segment array downstream (the deterministic extractor splits the
229
- * FULL text). Caps total chars; drops the tail past the cap rather than
230
- * silently mangling.
231
- */
232
- export function segmentsToText(segments, maxChars = 24_000) {
233
- const parts = [];
234
- let total = 0;
235
- for (const s of segments) {
236
- const line = s.text;
237
- if (total + line.length > maxChars)
238
- break;
239
- parts.push(line);
240
- total += line.length + 1;
241
- }
242
- return parts.join('\n');
243
- }
244
- //# sourceMappingURL=trace-harvest.js.map
1
+ const l=/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;function g(s){if(!s)return"";let o=s;const e=[[/\b(?:sk|rk|pk)[-_][A-Za-z0-9][A-Za-z0-9_-]{14,}\b/g,"[REDACTED]"],[/\bgh[posru]_[A-Za-z0-9]{20,}\b/g,"[REDACTED]"],[/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,"[REDACTED]"],[/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,"[REDACTED]"],[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,"[REDACTED]"],[/\bAIza[A-Za-z0-9_-]{30,}\b/g,"[REDACTED]"],[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,"[REDACTED]"],[/\b([Bb]earer)\s+[A-Za-z0-9._-]{12,}/g,"$1 [REDACTED]"],[/((?:api[_-]?key|secret|token|password|passwd|auth(?:orization)?|access[_-]?token|private[_-]?key|client[_-]?secret)["']?\s*[:=]\s*["']?)([A-Za-z0-9._/+\-]{6,})/gi,"$1[REDACTED]"]];for(const[r,t]of e)o=o.replace(r,t);return o}function u(s){if(s==null)return"";if(typeof s=="string")return s;if(!Array.isArray(s))return"";const o=[];for(const e of s){if(typeof e=="string"){o.push(e);continue}if(e&&typeof e=="object"){const r=e;r.type==="text"&&typeof r.text=="string"&&o.push(r.text)}}return o.join(`
2
+ `)}function p(s){return g(s).replace(l,"").replace(/[ \t]+/g," ").replace(/\n{3,}/g,`
3
+
4
+ `).trim().slice(0,4e3)}function i(s){const o=s.trim();if(!o)return[];if(o.startsWith("["))try{const r=JSON.parse(o);if(Array.isArray(r))return r.filter(t=>!!t&&typeof t=="object")}catch{}const e=[];for(const r of o.split(`
5
+ `)){const t=r.trim();if(!(!t||t[0]!=="{"&&t[0]!=="["))try{const c=JSON.parse(t);c&&typeof c=="object"&&!Array.isArray(c)&&e.push(c)}catch{}}return e}function A(s){const o=[];for(const e of i(s)){const r=e.message&&typeof e.message=="object"?e.message:e,t=String(r.role??e.role??"");if(t!=="user"&&t!=="assistant")continue;const c=u(r.content).trim();if(!c||t==="user"&&(c.startsWith("<")||c.startsWith("[{")))continue;const n=p(c);n.length>=12&&o.push({role:t,text:n})}return o}function m(s){const o=[],e=[["prompt","user"],["input","user"],["response","assistant"],["output","assistant"],["text","note"],["content","note"]];for(const r of i(s))for(const[t,c]of e){const n=r[t],a=typeof n=="string"?n:u(n);if(!a)continue;const f=p(a);f.length>=12&&o.push({role:c,text:f})}return o}function b(s){const o=new Map,e=[];for(const t of i(s)){const c=String(t.kind??"");let n="";c==="tool_call"?n=String(t.actor??t.tool??""):typeof t.tool_name=="string"?n=t.tool_name:typeof t.tool=="string"&&(n=t.tool),n=n.replace(l,"").trim().slice(0,80),n&&(o.has(n)||e.push(n),o.set(n,(o.get(n)??0)+1))}return e.length===0?[]:[{role:"tool",text:`Tool activity in this run: ${e.map(t=>`${t}\xD7${o.get(t)}`).join(", ")}.`}]}function x(s){const o=i(s).slice(0,50);if(o.length===0)return"claude-jsonl";let e=0,r=0;for(const t of o){(t.kind==="tool_call"||typeof t.tool_name=="string")&&e++;const n=(t.message&&typeof t.message=="object"?t.message:t).role??t.role;(n==="user"||n==="assistant"||n==="system")&&r++}return e>0&&e>=r?"tool-calls":r>0?"claude-jsonl":"dragon"}function y(s,o="auto"){const e=o==="auto"?x(s):o;return{segments:e==="tool-calls"?b(s):e==="dragon"?m(s):A(s),format:e}}function h(s,o=24e3){const e=[];let r=0;for(const t of s){const c=t.text;if(r+c.length>o)break;e.push(c),r+=c.length+1}return e.join(`
6
+ `)}export{x as detectTraceFormat,A as parseClaudeJsonl,m as parseDragonTrace,b as parseToolCalls,y as parseTrace,g as redactSecrets,h as segmentsToText};
package/dist/types.js CHANGED
@@ -1,30 +1 @@
1
- /**
2
- * Wyrm Types - Core type definitions
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- * @module types
7
- * @version 3.0.0
8
- */
9
- export const DEFAULT_CONFIG = {
10
- database: {
11
- wal: true,
12
- cacheSize: 64000,
13
- },
14
- encryption: {
15
- enabled: false,
16
- },
17
- logging: {
18
- level: 'info',
19
- console: true,
20
- },
21
- http: {
22
- port: 3333,
23
- host: '127.0.0.1',
24
- },
25
- sync: {
26
- autoImport: true,
27
- autoExport: false,
28
- },
29
- };
30
- //# sourceMappingURL=types.js.map
1
+ const e={database:{wal:!0,cacheSize:64e3},encryption:{enabled:!1},logging:{level:"info",console:!0},http:{port:3333,host:"127.0.0.1"},sync:{autoImport:!0,autoExport:!1}};export{e as DEFAULT_CONFIG};
@@ -1,11 +1,4 @@
1
- /**
2
- * Wyrm Visual Web Dashboard — Self-contained HTML SPA
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- */
7
- export function getUIDashboardHTML() {
8
- return `<!DOCTYPE html>
1
+ function e(){return`<!DOCTYPE html>
9
2
  <html lang="en">
10
3
  <head>
11
4
  <meta charset="UTF-8">
@@ -261,7 +254,7 @@ main { padding: calc(var(--nav-h) + 24px) 24px 24px; max-width: 1200px; margin:
261
254
  (function() {
262
255
  'use strict';
263
256
 
264
- // ── Helpers ─────────────────────────────────────────────────────────────────
257
+ // \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
265
258
  function el(tag, cls, text) {
266
259
  const e = document.createElement(tag);
267
260
  if (cls) e.className = cls;
@@ -313,7 +306,7 @@ async function apiPost(path) {
313
306
 
314
307
  function truncate(s, n) {
315
308
  if (!s) return '';
316
- return s.length > n ? s.slice(0, n) + '' : s;
309
+ return s.length > n ? s.slice(0, n) + '\u2026' : s;
317
310
  }
318
311
 
319
312
  function clearEl(e) {
@@ -381,7 +374,7 @@ function staleBar(staleness) {
381
374
  return wrap;
382
375
  }
383
376
 
384
- // ── Router ───────────────────────────────────────────────────────────────────
377
+ // \u2500\u2500 Router \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
385
378
  const TABS = ['overview', 'impact', 'memories', 'quests', 'truths', 'skills', 'review'];
386
379
  let currentTab = '';
387
380
  // Set from GET /ui/account.readonly at boot (WYRM_UI_READONLY=1 on the server).
@@ -413,7 +406,7 @@ document.querySelectorAll('.tab-btn').forEach(btn => {
413
406
  });
414
407
  window.addEventListener('hashchange', router);
415
408
 
416
- // ── Overview ─────────────────────────────────────────────────────────────────
409
+ // \u2500\u2500 Overview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
417
410
  async function loadOverview() {
418
411
  const pane = document.getElementById('pane-overview');
419
412
  clearEl(pane);
@@ -456,13 +449,13 @@ async function loadOverview() {
456
449
  card.replaceChild(vEl, skel);
457
450
  }
458
451
  });
459
- // Recent sessions remove skeleton list, then insert real data
452
+ // Recent sessions \u2014 remove skeleton list, then insert real data
460
453
  pane.querySelector('.list')?.remove();
461
454
  const sl = el('div', 'sessions-list');
462
455
  const sessions = data.recent_sessions || [];
463
456
  if (sessions.length === 0) {
464
457
  const em = el('div', 'empty');
465
- em.appendChild(el('div', 'empty-icon', '📭'));
458
+ em.appendChild(el('div', 'empty-icon', '\u{1F4ED}'));
466
459
  em.appendChild(el('div', null, 'No sessions yet'));
467
460
  sl.appendChild(em);
468
461
  } else {
@@ -477,7 +470,7 @@ async function loadOverview() {
477
470
  } catch(e) { showToast(String(e), 'error'); }
478
471
  }
479
472
 
480
- // ── Memories ─────────────────────────────────────────────────────────────────
473
+ // \u2500\u2500 Memories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
481
474
  let memPage = 1, memKind = '', memSearch = '', memDebounce = null;
482
475
 
483
476
  async function loadMemories(page, kind, search) {
@@ -503,7 +496,7 @@ async function loadMemories(page, kind, search) {
503
496
  const newList = el('div', 'list');
504
497
  if (!data.items || data.items.length === 0) {
505
498
  const em = el('div', 'empty');
506
- em.appendChild(el('div', 'empty-icon', '🧠'));
499
+ em.appendChild(el('div', 'empty-icon', '\u{1F9E0}'));
507
500
  em.appendChild(el('div', null, 'No memory artifacts found'));
508
501
  newList.appendChild(em);
509
502
  } else {
@@ -551,7 +544,7 @@ function initMemoriesPane() {
551
544
  const filters = el('div', 'filters');
552
545
  const si = el('input', 'filter-input');
553
546
  si.type = 'search';
554
- si.placeholder = 'Search memories';
547
+ si.placeholder = 'Search memories\u2026';
555
548
  si.addEventListener('input', () => {
556
549
  memSearch = si.value;
557
550
  clearTimeout(memDebounce);
@@ -578,7 +571,7 @@ function initMemoriesPane() {
578
571
  pane.insertBefore(filters, pane.firstChild);
579
572
  }
580
573
 
581
- // ── Quests ───────────────────────────────────────────────────────────────────
574
+ // \u2500\u2500 Quests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
582
575
  async function loadQuests() {
583
576
  const pane = document.getElementById('pane-quests');
584
577
  clearEl(pane);
@@ -590,7 +583,7 @@ async function loadQuests() {
590
583
  const col = el('div', 'kanban-col');
591
584
  const hdr = el('div', 'kanban-header');
592
585
  hdr.appendChild(el('span', 'kanban-title', colLabels[status]));
593
- const cnt = el('span', 'kanban-count', '');
586
+ const cnt = el('span', 'kanban-count', '\u2026');
594
587
  hdr.appendChild(cnt);
595
588
  col.appendChild(hdr);
596
589
  col.dataset.status = status;
@@ -627,7 +620,7 @@ async function loadQuests() {
627
620
  } catch(e) { showToast(String(e), 'error'); }
628
621
  }
629
622
 
630
- // ── Truths ───────────────────────────────────────────────────────────────────
623
+ // \u2500\u2500 Truths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
631
624
  let truthPage = 1;
632
625
 
633
626
  async function loadTruths(page) {
@@ -646,7 +639,7 @@ async function loadTruths(page) {
646
639
  const list = el('div', 'list');
647
640
  if (!data.items || data.items.length === 0) {
648
641
  const em = el('div', 'empty');
649
- em.appendChild(el('div', 'empty-icon', '📚'));
642
+ em.appendChild(el('div', 'empty-icon', '\u{1F4DA}'));
650
643
  em.appendChild(el('div', null, 'No ground truths yet'));
651
644
  list.appendChild(em);
652
645
  } else {
@@ -656,7 +649,7 @@ async function loadTruths(page) {
656
649
  row1.appendChild(el('span', 'truth-cat', t.category));
657
650
  row1.appendChild(el('span', 'truth-key', t.key));
658
651
  if (t.staleness != null && t.staleness > 0.7) {
659
- row1.appendChild(el('span', null, '⚠️'));
652
+ row1.appendChild(el('span', null, '\u26A0\uFE0F'));
660
653
  }
661
654
  item.appendChild(row1);
662
655
  item.appendChild(el('div', 'truth-value', truncate(t.value, 150)));
@@ -689,7 +682,7 @@ async function loadTruths(page) {
689
682
  } catch(e) { showToast(String(e), 'error'); }
690
683
  }
691
684
 
692
- // ── Review ───────────────────────────────────────────────────────────────────
685
+ // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
693
686
  async function loadReview() {
694
687
  const pane = document.getElementById('pane-review');
695
688
  clearEl(pane);
@@ -701,7 +694,7 @@ async function loadReview() {
701
694
  const items = data.items || [];
702
695
  if (items.length === 0) {
703
696
  const em = el('div', 'empty');
704
- em.appendChild(el('div', 'empty-icon', ''));
697
+ em.appendChild(el('div', 'empty-icon', '\u2705'));
705
698
  em.appendChild(el('div', null, 'Review queue is empty'));
706
699
  pane.appendChild(em);
707
700
  return;
@@ -719,8 +712,8 @@ async function loadReview() {
719
712
  // Approve/reject mutate the queue, withheld in read-only/public view.
720
713
  if (!READONLY) {
721
714
  const actions = el('div', 'review-actions');
722
- const approveBtn = el('button', 'btn btn-approve', ' Approve');
723
- const rejectBtn = el('button', 'btn btn-reject', ' Reject');
715
+ const approveBtn = el('button', 'btn btn-approve', '\u2713 Approve');
716
+ const rejectBtn = el('button', 'btn btn-reject', '\u2715 Reject');
724
717
  approveBtn.addEventListener('click', async () => {
725
718
  try {
726
719
  await apiPost('/ui/review/' + m.id + '/approve');
@@ -729,7 +722,7 @@ async function loadReview() {
729
722
  if (list.querySelectorAll('.review-item').length === 0) {
730
723
  list.remove();
731
724
  const em = el('div', 'empty');
732
- em.appendChild(el('div', 'empty-icon', ''));
725
+ em.appendChild(el('div', 'empty-icon', '\u2705'));
733
726
  em.appendChild(el('div', null, 'Review queue is empty'));
734
727
  pane.appendChild(em);
735
728
  }
@@ -743,7 +736,7 @@ async function loadReview() {
743
736
  if (list.querySelectorAll('.review-item').length === 0) {
744
737
  list.remove();
745
738
  const em = el('div', 'empty');
746
- em.appendChild(el('div', 'empty-icon', ''));
739
+ em.appendChild(el('div', 'empty-icon', '\u2705'));
747
740
  em.appendChild(el('div', null, 'Review queue is empty'));
748
741
  pane.appendChild(em);
749
742
  }
@@ -759,7 +752,7 @@ async function loadReview() {
759
752
  } catch(e) { showToast(String(e), 'error'); }
760
753
  }
761
754
 
762
- // ── Skills ─────────────────────────────────────────────────────────────────
755
+ // \u2500\u2500 Skills \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
763
756
  async function loadSkills() {
764
757
  const pane = document.getElementById('pane-skills');
765
758
  clearEl(pane);
@@ -770,7 +763,7 @@ async function loadSkills() {
770
763
  const items = data.items || [];
771
764
  if (items.length === 0) {
772
765
  const em = el('div', 'empty');
773
- em.appendChild(el('div', 'empty-icon', '🧩'));
766
+ em.appendChild(el('div', 'empty-icon', '\u{1F9E9}'));
774
767
  em.appendChild(el('div', null, 'No skills registered yet'));
775
768
  pane.appendChild(em);
776
769
  return;
@@ -798,7 +791,7 @@ async function loadSkills() {
798
791
  } catch(e) { showToast(String(e), 'error'); }
799
792
  }
800
793
 
801
- // ── Impact ───────────────────────────────────────────────────────────────────
794
+ // \u2500\u2500 Impact \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
802
795
  function fmtNum(n) {
803
796
  if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\\.0$/, '') + 'k';
804
797
  return String(n || 0);
@@ -857,15 +850,15 @@ async function loadImpact() {
857
850
  } catch(e) { showToast(String(e), 'error'); }
858
851
  }
859
852
 
860
- // ── Memory detail modal ───────────────────────────────────────────────────────
853
+ // \u2500\u2500 Memory detail modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
861
854
  let modalReq = 0;
862
855
  async function openMemoryModal(id) {
863
856
  const myReq = ++modalReq;
864
857
  const ov = document.getElementById('modal-overlay');
865
858
  const card = document.getElementById('modal-card');
866
859
  clearEl(card);
867
- const close = el('span', 'modal-close', ''); close.onclick = closeModal; card.appendChild(close);
868
- card.appendChild(el('div', 'item-sub', 'Loading'));
860
+ const close = el('span', 'modal-close', '\u2715'); close.onclick = closeModal; card.appendChild(close);
861
+ card.appendChild(el('div', 'item-sub', 'Loading\u2026'));
869
862
  ov.classList.add('open');
870
863
  try {
871
864
  const d = await apiFetch('/ui/memory?id=' + id);
@@ -873,7 +866,7 @@ async function openMemoryModal(id) {
873
866
  if (myReq !== modalReq || !ov.classList.contains('open')) return;
874
867
  const it = d.item;
875
868
  clearEl(card);
876
- const cl = el('span', 'modal-close', ''); cl.onclick = closeModal; card.appendChild(cl);
869
+ const cl = el('span', 'modal-close', '\u2715'); cl.onclick = closeModal; card.appendChild(cl);
877
870
  if (!it) { card.appendChild(el('div', 'item-sub', 'Memory not found.')); return; }
878
871
  const row = el('div', 'item-row');
879
872
  if (it.kind) row.appendChild(kindBadge(it.kind));
@@ -902,12 +895,12 @@ async function openMemoryModal(id) {
902
895
  const used = (it.access_count || 0), reused = (it.reuse_count || 0);
903
896
  // "Surfaced" = times recalled (auto); "Confirmed helpful" = times an agent
904
897
  // called wyrm_feedback after applying it. 0 here means no feedback yet, not broken.
905
- card.appendChild(el('div', 'item-sub', 'Surfaced ' + used + '× · confirmed helpful ' + reused + '×'));
898
+ card.appendChild(el('div', 'item-sub', 'Surfaced ' + used + '\xD7 \xB7 confirmed helpful ' + reused + '\xD7'));
906
899
  } catch(e) { if (myReq === modalReq) closeModal(); }
907
900
  }
908
901
  function closeModal() { const ov = document.getElementById('modal-overlay'); if (ov) ov.classList.remove('open'); }
909
902
 
910
- // ── Account badge ────────────────────────────────────────────────────────────
903
+ // \u2500\u2500 Account badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
911
904
  async function loadAccount() {
912
905
  const box = document.getElementById('acct');
913
906
  if (!box) return;
@@ -927,14 +920,14 @@ async function loadAccount() {
927
920
  box.classList.add('ro');
928
921
  box.title = 'Read-only public view';
929
922
  } else {
930
- box.appendChild(el('span', 'acct-caret', ''));
923
+ box.appendChild(el('span', 'acct-caret', '\u25BE'));
931
924
  box.title = 'Click to switch account / home';
932
925
  box.onclick = toggleAcctMenu;
933
926
  }
934
927
  } catch(e) { /* badge stays empty on failure */ }
935
928
  }
936
929
 
937
- // ── Account switcher dropdown ─────────────────────────────────────────────────
930
+ // \u2500\u2500 Account switcher dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
938
931
  let acctMenuOpen = false;
939
932
  async function toggleAcctMenu(ev) {
940
933
  if (ev) ev.stopPropagation();
@@ -943,7 +936,7 @@ async function toggleAcctMenu(ev) {
943
936
  if (acctMenuOpen) { menu.classList.remove('open'); acctMenuOpen = false; return; }
944
937
  clearEl(menu);
945
938
  menu.appendChild(el('div', 'acct-menu-label', 'Switch account / home'));
946
- menu.appendChild(el('div', 'acct-menu-label', 'Loading'));
939
+ menu.appendChild(el('div', 'acct-menu-label', 'Loading\u2026'));
947
940
  menu.classList.add('open'); acctMenuOpen = true;
948
941
  try {
949
942
  const r = await fetch('/ui/homes');
@@ -956,9 +949,9 @@ async function toggleAcctMenu(ev) {
956
949
  const it = el('div', 'acct-menu-item' + (h.active ? ' active' : ''));
957
950
  const a = el('div', 'ai-acct');
958
951
  a.appendChild(el('span', null, h.account || h.name));
959
- if (h.active) a.appendChild(el('span', 'ai-check', ' active'));
952
+ if (h.active) a.appendChild(el('span', 'ai-check', '\u25CF active'));
960
953
  it.appendChild(a);
961
- it.appendChild(el('div', 'ai-meta', (h.tier || 'free') + ' · ' + h.name));
954
+ it.appendChild(el('div', 'ai-meta', (h.tier || 'free') + ' \xB7 ' + h.name));
962
955
  if (!h.active) it.addEventListener('click', () => switchHome(h.dbPath));
963
956
  menu.appendChild(it);
964
957
  });
@@ -990,22 +983,22 @@ document.getElementById('modal-overlay').addEventListener('click', (e) => {
990
983
  });
991
984
  document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); });
992
985
 
993
- // ── Pagination helper ─────────────────────────────────────────────────────────
986
+ // \u2500\u2500 Pagination helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
994
987
  function buildPagination(current, total, onPage) {
995
988
  const pgn = el('div', 'pagination');
996
- const prev = el('button', 'page-btn', ' Prev');
989
+ const prev = el('button', 'page-btn', '\u2190 Prev');
997
990
  prev.disabled = current <= 1;
998
991
  prev.addEventListener('click', () => onPage(current - 1));
999
992
  pgn.appendChild(prev);
1000
993
  pgn.appendChild(el('span', 'page-info', 'Page ' + current + ' of ' + total));
1001
- const next = el('button', 'page-btn', 'Next ');
994
+ const next = el('button', 'page-btn', 'Next \u2192');
1002
995
  next.disabled = current >= total;
1003
996
  next.addEventListener('click', () => onPage(current + 1));
1004
997
  pgn.appendChild(next);
1005
998
  return pgn;
1006
999
  }
1007
1000
 
1008
- // ── Tab loader ────────────────────────────────────────────────────────────────
1001
+ // \u2500\u2500 Tab loader \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1009
1002
  function loadTab(name) {
1010
1003
  if (name === 'overview') loadOverview();
1011
1004
  else if (name === 'impact') loadImpact();
@@ -1021,8 +1014,6 @@ function loadTab(name) {
1021
1014
  // so the Review pane knows whether to show approve/reject controls.
1022
1015
  loadAccount().then(router);
1023
1016
  })();
1024
- </script>
1017
+ <\/script>
1025
1018
  </body>
1026
- </html>`;
1027
- }
1028
- //# sourceMappingURL=ui-dashboard.js.map
1019
+ </html>`}export{e as getUIDashboardHTML};