lavish-axi 0.1.78 → 0.1.79

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.
package/README.md CHANGED
@@ -200,6 +200,7 @@ pnpm link
200
200
  If the element an unsent annotation was attached to is gone from the artifact for good, Lavish cannot reopen that card, so it writes your text into the conversation panel under **Unsent annotation** - selectable, never written over anything you have typed, and kept there across reloads; no note is ever dropped to make room for a newer one, and a note the browser refuses to store says so where it is shown.
201
201
  - **Feedback controls** - Native controls (radios, checkboxes, inputs, selects, buttons, labels, disclosure summaries, contenteditable) are interactive automatically, so they do not need `data-lavish-action`.
202
202
  For reversible choices, let option clicks update local state, then queue exactly one final answer from a per-question submit or Queue answer button with `window.lavish.queuePrompt()`.
203
+ If someone may open a decision artifact as a standalone HTML file, `lavish-axi playbook input` provides an optional Copy all answers control they can use to paste their form answers back into chat.
203
204
  Mark only custom (non-native) clickable elements with `data-lavish-action` so Lavish does not annotate them, and use `data-lavish-question` or `queueKey` when pre-send updates for the same question should replace each other.
204
205
  Everything you send is part of the conversation: a note on an element, a text selection, a table cell, a diagram node, a whiteboard, a layout-issue batch, or a composer message each becomes a bubble on your side of the Conversation panel, with an anchor line naming what it was attached to (the element tag, text selection, table cell, diagram node, whiteboard, or layout issues), a quoted excerpt, and, when available, the selector on hover. A note you have queued but not sent sits at the end of the conversation as a dashed bubble labelled **Queued** with a remove control; while its batch is in flight it reads **Sending**, and once the server accepts it, it settles in place as a sent bubble in every tab of the session. The panel keeps the newest stored transcript up to 5 MiB; older bubbles drop from view, but an accepted note still settles and is not sent twice. Agent replies render a small Markdown subset - paragraphs, line breaks, headings as bold lead lines, bullet and numbered lists, fenced and inline code, bold, italic, and http(s) links - while tables, images, and raw HTML stay as the text the agent wrote; your own messages keep their line breaks and are never parsed.
205
206
  On wider screens, the conversation and the queued notes share one scrollable Conversation panel above a sticky composer, so long feedback queues do not push the text box or send controls off screen.
@@ -228,7 +229,7 @@ pnpm link
228
229
  Images are stored under the state dir and the queued prompt carries a server-generated absolute `path` and content-hash `id` (plus mime and dimensions) - never the raw bytes - so `lavish-axi poll` hands the agent a local file path to open.
229
230
  Limits are `LAVISH_AXI_MAX_ATTACHMENT_BYTES` (default 10 MiB per image), `LAVISH_AXI_MAX_ATTACHMENTS_PER_PROMPT` (default 4), and `LAVISH_AXI_MAX_PROMPT_ATTACHMENT_BYTES` (default 25 MiB per prompt); if any image is missing or any prompt breaches a count or byte cap, the entire send batch is rejected, the queue is preserved, and the reason is surfaced in the composer rather than silently dropping images.
230
231
  As a browser-side abuse guard, each chrome page allows 30 upload attempts per rolling minute, 4 uploads in flight at once, and 256 MiB of attempted image bytes over its lifetime; rejected uploads stay visible for retry or removal.
231
- Attachments are cleaned up by `LAVISH_AXI_ATTACHMENT_TTL_MS` (default 7 days; `0`/`off` disables) but only once no pending prompt still references them; `LAVISH_AXI_MAX_ATTACHMENT_DISK_MB` (default 512 MiB; `0`/`off` disables) caps total attachment disk.
232
+ Unreferenced attachments expire after `LAVISH_AXI_ATTACHMENT_TTL_MS` (default 7 days; `0`/`off` disables); `LAVISH_AXI_MAX_ATTACHMENT_DISK_MB` (default 512 MiB; `0`/`off` disables) caps total attachment disk and may evict older unreferenced images sooner.
232
233
  The cap is enforced when an image is uploaded, not just periodically: the upload first reclaims unreferenced files (oldest first, and never one added within the last hour), and if it still would not fit, that upload is refused with a storage-full error on its chip instead of discarding an image the user is about to send.
233
234
  - **Mermaid diagrams** - Whiteboards are an opt-in: agents author a diagram as Mermaid only when you ask for an editable whiteboard, and hand-authored inline SVG illustrations are the default figure medium otherwise.
234
235
  In the Lavish browser, every rendered Mermaid diagram in a `.mermaid` container becomes an embedded editable Excalidraw whiteboard.
package/dist/cli.mjs CHANGED
@@ -245,6 +245,85 @@ var PLAYBOOKS = [
245
245
  "Lavish is strongest when the artifact becomes a focused review surface and not just a static page.",
246
246
  `A native single-choice question should submit the final value: \`<form data-lavish-question="plan" onsubmit="event.preventDefault(); const choice = new FormData(event.currentTarget).get('plan'); if (choice) window.lavish.queuePrompt('Use the ' + choice + ' plan', { tag: 'choice', text: 'Plan: ' + choice, element: event.currentTarget, data: { question: 'plan', answer: choice } });"><label><input type="radio" name="plan" value="Starter"> Starter</label><label><input type="radio" name="plan" value="Pro"> Pro</label><button type="submit">Queue this answer</button></form>\`.`,
247
247
  `A tracked batch should submit the final selected set once: <form data-lavish-question="tracked-review" onsubmit="event.preventDefault(); const selected = [...event.currentTarget.querySelectorAll('input[name=items]:checked')].map((input) => ({ id: input.value, label: input.dataset.label, disposition: input.dataset.disposition })); if (selected.length) window.lavish.queuePrompt('Act on every selected item and return an item-by-item receipt. Account for every submitted ID before reporting completion.', { tag: 'tracked-batch', text: 'Apply ' + selected.length + ' selected review items', element: event.currentTarget, data: { items: selected } });"><label><input type="checkbox" name="items" value="R-03" data-label="Preserve rollback behavior" data-disposition="must-address"> R-03 \u2014 Preserve rollback behavior</label><label><input type="checkbox" name="items" value="R-08" data-label="Reuse the existing error surface" data-disposition="must-address"> R-08 \u2014 Reuse the existing error surface</label><button type="submit">Queue selected items</button></form>`,
248
+ `For a decision artifact a human may open by double-clicking the HTML, optionally include this standalone answer-copy control: it copies every question's answers as text the human pastes back into the chat for the agent to read. It works without Lavish and remains absent unless you write it into the artifact:
249
+ \`\`\`html
250
+ <div>
251
+ <button type="button" onclick="(async (btn) => {
252
+ const copyRun = btn._lavishCopyRun = (btn._lavishCopyRun || 0) + 1;
253
+ const qs = [...document.querySelectorAll('[data-lavish-question]')];
254
+ const status = btn.parentElement.querySelector('[data-lavish-copy-all-status]');
255
+ btn.parentElement.querySelector('[data-lavish-copy-all-manual]')?.remove();
256
+ const out = [];
257
+ for (const q of qs) {
258
+ const key = q.getAttribute('data-lavish-question')?.trim() || 'Question';
259
+ const lines = [];
260
+ for (const el of q.querySelectorAll('input, select, textarea')) {
261
+ if (el.matches(':disabled')) continue;
262
+ if (el.closest('[data-lavish-question]') !== q) continue;
263
+ const isChoice = el.matches('input[type=checkbox], input[type=radio]');
264
+ if (isChoice && !el.checked) continue;
265
+ if (el.matches('input[type=button], input[type=submit], input[type=reset], input[type=image], input[type=hidden], input[type=file], input[type=range], input[type=color]')) continue;
266
+ let vals;
267
+ if (el instanceof HTMLSelectElement) {
268
+ vals = [...el.selectedOptions].filter((o) => !o.disabled && !o.closest('optgroup[disabled]')).map((o) => o.value.trim()).filter(Boolean);
269
+ } else {
270
+ const v = isChoice && (!el.hasAttribute('value') || !el.value.trim())
271
+ ? el.labels?.[0]?.textContent?.trim() || el.getAttribute('aria-label')?.trim() || el.name || ''
272
+ : el.value.trim();
273
+ vals = v ? [v] : [];
274
+ }
275
+ const label = el.name || el.labels?.[0]?.textContent?.trim() || el.getAttribute('aria-label')?.trim() || el.getAttribute('placeholder')?.trim() || el.tagName.toLowerCase();
276
+ for (const v of vals) lines.push(' ' + label + ': ' + v.split('\\n').join('\\n '));
277
+ }
278
+ if (lines.length) out.push(key + ':\\n' + lines.join('\\n'));
279
+ }
280
+ const text = out.join('\\n\\n');
281
+ if (!text) {
282
+ status.textContent = qs.length ? 'No answers to copy yet.' : 'No questions or answers to copy.';
283
+ return;
284
+ }
285
+ status.textContent = '';
286
+ try {
287
+ if (typeof navigator.clipboard?.writeText !== 'function') throw new Error('Clipboard API unavailable');
288
+ await navigator.clipboard.writeText(text);
289
+ if (copyRun !== btn._lavishCopyRun) return;
290
+ status.textContent = 'Answers copied.';
291
+ return;
292
+ } catch {}
293
+ if (copyRun !== btn._lavishCopyRun) return;
294
+ const tmp = document.createElement('textarea');
295
+ tmp.value = text;
296
+ tmp.readOnly = true;
297
+ tmp.setAttribute('aria-hidden', 'true');
298
+ tmp.style.position = 'fixed';
299
+ tmp.style.left = '-10000px';
300
+ document.body.append(tmp);
301
+ tmp.focus();
302
+ tmp.select();
303
+ let ok = false;
304
+ try {
305
+ ok = document.execCommand('copy');
306
+ } catch {}
307
+ tmp.remove();
308
+ if (ok) {
309
+ status.textContent = 'Answers copied.';
310
+ return;
311
+ }
312
+ btn.parentElement.querySelector('[data-lavish-copy-all-manual]')?.remove();
313
+ const man = document.createElement('textarea');
314
+ man.value = text;
315
+ man.readOnly = true;
316
+ man.rows = Math.min(12, Math.max(3, text.split('\\n').length));
317
+ man.setAttribute('aria-label', 'Answers to copy manually');
318
+ man.dataset.lavishCopyAllManual = '';
319
+ btn.insertAdjacentElement('afterend', man);
320
+ man.focus();
321
+ man.select();
322
+ status.textContent = 'Clipboard unavailable. Copy the answers below manually.';
323
+ })(this)">Copy all answers</button>
324
+ <span data-lavish-copy-all-status role="status" aria-live="polite"></span>
325
+ </div>
326
+ \`\`\``,
248
327
  "The agent's tracked-batch completion receipt must give every submitted ID exactly one outcome: addressed with concrete evidence, deferred with a reason, or rejected with a reason. Evidence may cover several IDs. Before declaring completion, compare the submitted ID set with the receipt ID set and surface every missing ID.",
249
328
  "A custom choice UI should make option buttons update local state, then use a separate Queue answer button with data-lavish-action to queue the final selected value.",
250
329
  "Use window.lavish.queuePrompt for user intent, not internal analytics or UI-only state changes.",
@@ -12217,7 +12296,7 @@ var AGENT_REPLY_INPUT_LIMIT_BYTES = AGENT_REPLY_JSON_LIMIT_BYTES - AGENT_REPLY_J
12217
12296
  var AGENT_REPLY_LIMIT_LABEL = "2 MB JSON request limit";
12218
12297
  var POLL_STATE_HEADER = "lavish-poll-state";
12219
12298
  var CODEX_POLL_WAKE_PATH_GUIDANCE = "Codex detected: completed background tasks may not resume Codex automatically, so keep the poll attached to the active turn.";
12220
- var VERSION = "0.1.78";
12299
+ var VERSION = "0.1.79";
12221
12300
  function detectInvokingAgent(env = process.env) {
12222
12301
  return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
12223
12302
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavish-axi",
3
- "version": "0.1.78",
3
+ "version": "0.1.79",
4
4
  "packageManager": "pnpm@11.1.1",
5
5
  "description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
6
6
  "type": "module",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "lavish-axi",
4
- "version": "0.1.78",
4
+ "version": "0.1.79",
5
5
  "description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
6
6
  "author": {
7
7
  "name": "Kun Chen",