lavish-axi 0.1.78 → 0.1.80
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 +7 -3
- package/dist/cli.mjs +226 -21
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/lavish/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ npx skills add kunchenguid/lavish-axi --skill lavish
|
|
|
53
53
|
|
|
54
54
|
That is the entire setup - no npm install needed.
|
|
55
55
|
The skill teaches your agent to run Lavish through `npx -y lavish-axi`, so the CLI comes along on demand.
|
|
56
|
-
It stays a short stub and sends the agent to `npx -y lavish-axi --help`, `design`, and `playbook` for current instructions, so an installed copy cannot go stale against a newer CLI.
|
|
56
|
+
It stays a short stub and sends the agent to `npx -y lavish-axi --help`, `reply --help`, `design`, and `playbook` for current instructions, so an installed copy cannot go stale against a newer CLI.
|
|
57
57
|
Its frontmatter also includes Hermes Agent metadata, so Hermes-compatible harnesses can categorize and surface it as a first-class productivity skill.
|
|
58
58
|
This installs the public `lavish` skill.
|
|
59
59
|
The repository also contains an internal `lavish-design` brand skill for maintainers; default `npx skills add ... --list` and skills.sh discovery hide it unless `INSTALL_INTERNAL_SKILLS=1` is set.
|
|
@@ -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.
|
|
@@ -209,7 +210,7 @@ pnpm link
|
|
|
209
210
|
- **Keyboard shortcuts** - In the chrome composer, Enter sends queued prompts and Shift+Enter inserts a newline.
|
|
210
211
|
In the annotation card, Enter queues the annotation, Shift+Enter inserts a newline, and Ctrl+Enter (Cmd+Enter on macOS) queues it and sends all queued prompts immediately. Escape closes the card, same as Cancel, but only while it is empty (no text, no attachment); with unsent text or an attachment present, Escape does nothing rather than risk discarding it.
|
|
211
212
|
Cmd+I or Ctrl+I toggles between annotate and explore mode from either the browser chrome or the artifact iframe, including while focus is in a textarea or control.
|
|
212
|
-
- **Agent presence** - The browser shows when no agent is listening, keeps queued feedback for the next successful `lavish-axi poll` send even across reloads, and keeps human feedback actions available while the agent is working because the server queues them for the next poll. An agent reply concludes delivered work and returns presence to waiting. A supervisor-owned process listener is shown as an external listener rather than an idle captain turn.
|
|
213
|
+
- **Agent presence** - The browser shows when no agent is listening, keeps queued feedback for the next successful `lavish-axi poll` send even across reloads, and keeps human feedback actions available while the agent is working because the server queues them for the next poll. An agent reply concludes delivered work and returns presence to waiting (see the CLI Reference for reply and poll commands). A supervisor-owned process listener is shown as an external listener rather than an idle captain turn.
|
|
213
214
|
In a Herdr-managed pane, set `LAVISH_AXI_HERDR_CHIME=1` to show a Herdr request notification and play its chime once an indefinite poll enters the waiting state. Immediate queued feedback and timed debug polls do not trigger a notification. The integration is off unless both `LAVISH_AXI_HERDR_CHIME` and Herdr's `HERDR_ENV` session marker equal `1`; leaving either unset or using any other value opts out. Notification delivery runs without blocking feedback: if Herdr is unavailable, rejects the notification, stalls, or fails, the poll continues unchanged.
|
|
214
215
|
Closing the last review tab while a poll is active starts a 10-second reconnect grace period. A reload or quick reconnect keeps an active poll waiting; if every review stays disconnected, the poll returns `browser_disconnected` without ending the resumable session, and the agent asks whether to reopen or end it instead of acting uninvited.
|
|
215
216
|
The no-timeout poll always writes an immediate stderr banner so it is visibly not hung; it adds the periodic stderr wait ticks only in an interactive terminal, so when stderr is piped (as under agent harnesses) the captured output carries no tick noise. Stdout always stays reserved for the final response; if the poll is interrupted or times out before feedback arrives, re-run it because feedback remains queued until delivery. Poll delivery consumes the response, so read the complete response before truncating or filtering it.
|
|
@@ -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
|
-
|
|
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.
|
|
@@ -262,6 +263,7 @@ pnpm link
|
|
|
262
263
|
| `lavish-axi update` | Check for or apply the latest npm release through the AXI SDK self-updater. |
|
|
263
264
|
| `lavish-axi <html-file>` | Open or resume a Lavish Editor session, with the open-time layout gate enabled by default. Unresolved layout issues from earlier in the session are preserved. Refuses to reopen a session the user explicitly ended from the browser unless `--reopen` is passed. |
|
|
264
265
|
| `lavish-axi poll <html-file>` | Long-poll until the user sends feedback, ends the session, or leaves every review disconnected past the reconnect grace period; detected layout issues arrive only when queued. On `browser_disconnected`, ask before reopening or ending the still-resumable session. On `ended`, stop polling and do not reopen uninvited. |
|
|
266
|
+
| `lavish-axi reply <html-file>` | Post an agent reply and exit 0 only after the server accepts it, so the review page stops showing Working. Exits non-zero if the server does not confirm within 10 seconds. Use this when handing a result back without starting a long-poll. `poll --agent-reply` still posts a reply and then waits. |
|
|
265
267
|
| `lavish-axi end <html-file>` | End a session as the agent; unlike a user-initiated end from the browser, this still allows a plain reopen later. |
|
|
266
268
|
| `lavish-axi export <html-file>` | Write a portable copy of the artifact: one HTML file with its local assets inlined, so it opens with no server and no sibling files. Remote CDN/font references are left as links. |
|
|
267
269
|
| `lavish-axi share <html-file>` | Publish the artifact (local assets inlined) to [ht-ml.app](https://ht-ml.app), a third-party host not part of Lavish, and print a visitable URL plus a secret update key; shares are public by default, `--private` locks one behind a generated password, and the same command republishes or unpublishes an existing page with `--site`/`--update-key`. |
|
|
@@ -296,6 +298,8 @@ For flows, architecture, state, or sequence diagrams, open the diagram playbook
|
|
|
296
298
|
| `lavish-axi poll` | `--agent-reply "..."` | Show a concise agent reply in the existing browser chat, conclude delivered work, and return presence to waiting before polling again. Keep replies concise by default. |
|
|
297
299
|
| `lavish-axi poll` | `--agent-reply-file <path>` | When a longer reply is genuinely necessary, read Markdown from a file (`-` for stdin) so newlines survive quoting. Use blank-line paragraphs, `- ` / `1. ` lists, and `## ` headings so the Conversation panel renders it scannably. The Markdown subset is under Feedback controls. Cannot be combined with `--agent-reply`. |
|
|
298
300
|
| `lavish-axi poll` | `--timeout-ms <ms>` | Test/debug escape hatch only; agents should normally omit it and leave the long poll running. |
|
|
301
|
+
| `lavish-axi reply` | `--agent-reply "..."` | Concise reply text. Required unless `--agent-reply-file` is set. The command exits 0 only after the server accepts it. Empty text is refused. Prefer this over `poll --agent-reply` when you are not about to long-poll. |
|
|
302
|
+
| `lavish-axi reply` | `--agent-reply-file <path>` | Read the reply from a file (`-` for stdin) so newlines survive quoting. Cannot be combined with `--agent-reply`. Same Markdown guidance as `poll --agent-reply-file`. |
|
|
299
303
|
| `lavish-axi stop` | `--port <port>` | Shut down a server running on a non-default port. |
|
|
300
304
|
| `lavish-axi server` | `--verbose` | Log session and watcher events to stderr; can also be enabled with `LAVISH_AXI_DEBUG=1`. Detached server output is appended with UTC timestamps to `~/.lavish-axi/server.log` (or `LAVISH_AXI_STATE_DIR/server.log`) for startup, shutdown-cause, and crash diagnostics. |
|
|
301
305
|
| `lavish-axi server` | `--also-listen <host>` | Also listen on this concrete address (repeatable). The CLI passes it when it replaces a running server, so the replacement keeps every address the old one served; see One server per port. |
|
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.",
|
|
@@ -8832,13 +8911,14 @@ var SessionStore = class {
|
|
|
8832
8911
|
return session;
|
|
8833
8912
|
});
|
|
8834
8913
|
}
|
|
8835
|
-
async addAgentReply(key, text) {
|
|
8914
|
+
async addAgentReply(key, text, { requireOpen = false } = {}) {
|
|
8836
8915
|
return this.runExclusive(async () => {
|
|
8837
8916
|
const state = await this.readState();
|
|
8838
8917
|
const session = state.sessions[key];
|
|
8839
8918
|
if (!session) {
|
|
8840
8919
|
return null;
|
|
8841
8920
|
}
|
|
8921
|
+
if (requireOpen && session.status === "ended") return session;
|
|
8842
8922
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8843
8923
|
session.chat = [...session.chat || [], { role: "agent", text: String(text || ""), at }];
|
|
8844
8924
|
applyTranscriptBound(session);
|
|
@@ -10149,9 +10229,9 @@ async function serve({
|
|
|
10149
10229
|
next(error);
|
|
10150
10230
|
}
|
|
10151
10231
|
});
|
|
10152
|
-
async function publishAgentReply(key, text) {
|
|
10153
|
-
const session = await store.addAgentReply(key, text);
|
|
10154
|
-
if (!session) return
|
|
10232
|
+
async function publishAgentReply(key, text, { requireOpen = false } = {}) {
|
|
10233
|
+
const session = await store.addAgentReply(key, text, { requireOpen });
|
|
10234
|
+
if (!session || requireOpen && session.status === "ended") return session;
|
|
10155
10235
|
const lastEntry = session.chat?.at(-1);
|
|
10156
10236
|
const entry = serializeChat([
|
|
10157
10237
|
lastEntry?.role === "agent" ? lastEntry : { role: "agent", text, at: session.updated_at }
|
|
@@ -10550,11 +10630,17 @@ async function serve({
|
|
|
10550
10630
|
});
|
|
10551
10631
|
app.post("/api/:key/agent-reply", async (req, res, next) => {
|
|
10552
10632
|
try {
|
|
10553
|
-
const session = await publishAgentReply(req.params.key, String(req.body?.text || "")
|
|
10633
|
+
const session = await publishAgentReply(req.params.key, String(req.body?.text || ""), {
|
|
10634
|
+
requireOpen: true
|
|
10635
|
+
});
|
|
10554
10636
|
if (!session) {
|
|
10555
10637
|
res.status(404).json({ error: "session not found" });
|
|
10556
10638
|
return;
|
|
10557
10639
|
}
|
|
10640
|
+
if (session.status === "ended") {
|
|
10641
|
+
res.status(409).json({ status: "ended", ended_by: session.ended_by });
|
|
10642
|
+
return;
|
|
10643
|
+
}
|
|
10558
10644
|
res.json({ status: "sent" });
|
|
10559
10645
|
} catch (error) {
|
|
10560
10646
|
next(error);
|
|
@@ -12194,7 +12280,19 @@ function normalizePagePath(path9) {
|
|
|
12194
12280
|
|
|
12195
12281
|
// src/cli.js
|
|
12196
12282
|
var SHARE_VALUE_FLAGS = ["--password", "--token", "--site", "--update-key"];
|
|
12197
|
-
var COMMANDS = /* @__PURE__ */ new Set([
|
|
12283
|
+
var COMMANDS = /* @__PURE__ */ new Set([
|
|
12284
|
+
"open",
|
|
12285
|
+
"poll",
|
|
12286
|
+
"reply",
|
|
12287
|
+
"end",
|
|
12288
|
+
"stop",
|
|
12289
|
+
"server",
|
|
12290
|
+
"playbook",
|
|
12291
|
+
"design",
|
|
12292
|
+
"setup",
|
|
12293
|
+
"export",
|
|
12294
|
+
"share"
|
|
12295
|
+
]);
|
|
12198
12296
|
var RESERVED = new Set(RESERVED_COMMANDS);
|
|
12199
12297
|
var DESCRIPTION = "Lavish Editor helps agents turn rich HTML artifacts into collaborative human review surfaces. Whenever you are about to give user a complex response that will be easier to understand via a rich / interactive page, consider using Lavish Editor. First generate an interactive HTML artifact according to user request, then run `lavish-axi <html-file>` so the user can visually review it, annotate elements or selected text, queue prompts, and send feedback back through `lavish-axi poll`.";
|
|
12200
12298
|
var POLL_WAKE_PATH_RULES = Object.freeze([
|
|
@@ -12217,7 +12315,7 @@ var AGENT_REPLY_INPUT_LIMIT_BYTES = AGENT_REPLY_JSON_LIMIT_BYTES - AGENT_REPLY_J
|
|
|
12217
12315
|
var AGENT_REPLY_LIMIT_LABEL = "2 MB JSON request limit";
|
|
12218
12316
|
var POLL_STATE_HEADER = "lavish-poll-state";
|
|
12219
12317
|
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.
|
|
12318
|
+
var VERSION = "0.1.80";
|
|
12221
12319
|
function detectInvokingAgent(env = process.env) {
|
|
12222
12320
|
return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
|
|
12223
12321
|
}
|
|
@@ -12307,6 +12405,7 @@ async function run(argv) {
|
|
|
12307
12405
|
commands: {
|
|
12308
12406
|
open: openCommand,
|
|
12309
12407
|
poll: pollCommand,
|
|
12408
|
+
reply: replyCommand,
|
|
12310
12409
|
end: endCommand,
|
|
12311
12410
|
stop: stopCommand,
|
|
12312
12411
|
playbook: playbookCommand,
|
|
@@ -12377,6 +12476,7 @@ function createHomeOutput({ bin, sessions, includeSessions = true, agent = "gene
|
|
|
12377
12476
|
"Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/`",
|
|
12378
12477
|
"Lavish serves the html file through a local express.js server. If your html needs to reference other filesystem assets such as images, CSS, fonts, and local scripts, copy them into the same directory as the HTML file, then reference them with relative paths from that directory. Never prepend `/` to those asset paths - root paths won't work",
|
|
12379
12478
|
`Run \`lavish-axi poll <html-file>\` to wait for user feedback. It long-polls and stays silent until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period, so leave it running - never kill it. Detected layout issues never return this poll: the browser files them in the user's Layout issues inbox in the Lavish top bar, and they arrive as an ordinary tag "layout-warnings" prompt only when the user selects them and queues the fixes. Never edit the artifact to chase a layout issue the user has not queued. The only exception is a fatal artifact_failures response, which means the review surface itself could not be used. ${pollExecutionGuidance({ agent })} ${POLL_SEND_AND_END_RULE}`,
|
|
12479
|
+
'Run `lavish-axi reply <html-file> --agent-reply "<message>"` to show a concise reply and exit once Lavish Editor accepts it, when you are handing a result back and are not about to long-poll. The board stops showing Working only after this command exits 0. Use `lavish-axi poll <html-file> --agent-reply "<message>"` when that reply should be followed by another wait for feedback. A longer reply uses `--agent-reply-file <path>` (`-` reads stdin) on either command. Run `lavish-axi reply --help` for the receipt contract',
|
|
12380
12480
|
'Mermaid is the whiteboard opt-in, not the diagram default: only when the user asks for an editable whiteboard, author that diagram as Mermaid in a `.mermaid` container. Rendered Mermaid diagrams there become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; an unmodified autosave silently re-converts when a reload changes the Mermaid source. If the reviewer edited the scene, they choose to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back)',
|
|
12381
12481
|
"Run `lavish-axi end <html-file>` to end a session as the agent - ending it this way still allows a plain reopen later. When the user ends it from the browser instead, a later `lavish-axi <html-file>` refuses to reopen it without `--reopen`",
|
|
12382
12482
|
"Run `lavish-axi export <html-file> [--out <path>]` to write a portable copy of the artifact - one HTML file with its LOCAL assets inlined - so it opens with no Lavish server and no sibling files. Remote CDN/font references are left as links, so it needs network to render those. Users can also export from the browser chrome's overflow menu",
|
|
@@ -12418,7 +12518,7 @@ function createOpenOutput({
|
|
|
12418
12518
|
session: { file, url, status },
|
|
12419
12519
|
...networkWarning ? { network_warning: networkWarning } : {},
|
|
12420
12520
|
...selfPaintWarning ? { self_paint_warning: selfPaintWarning } : {},
|
|
12421
|
-
next_step: `${networkPrefix}${selfPaintPrefix}Do not respond to the user just yet. Now you must run \`lavish-axi poll ${file}\`. This command long-polls until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period, and it stays silent the whole time - that is normal, never kill it. Layout issues the browser detects do not return this poll; they wait in the user's Layout issues inbox until the user queues them, then arrive as an ordinary tag "layout-warnings" prompt. Do not pass --timeout-ms during normal agent use. ${pollExecutionGuidance({ agent })} After applying feedback, run \`lavish-axi poll ${file} --agent-reply "<message for the user>"\` without --timeout-ms to show a concise response in Lavish Editor and
|
|
12521
|
+
next_step: `${networkPrefix}${selfPaintPrefix}Do not respond to the user just yet. Now you must run \`lavish-axi poll ${file}\`. This command long-polls until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period, and it stays silent the whole time - that is normal, never kill it. Layout issues the browser detects do not return this poll; they wait in the user's Layout issues inbox until the user queues them, then arrive as an ordinary tag "layout-warnings" prompt. Do not pass --timeout-ms during normal agent use. ${pollExecutionGuidance({ agent })} After applying feedback, run \`lavish-axi poll ${file} --agent-reply "<message for the user>"\` without --timeout-ms to show a concise response in Lavish Editor and keep waiting for more feedback. If instead you are handing back a result without starting another long-poll, run \`lavish-axi reply ${file} --agent-reply "<message for the user>"\` to get an acceptance receipt. ${POLL_AGENT_REPLY_RULE} ${POLL_AGENT_REPLY_NEXT_POINTER} If the user ends the session, stop polling and do not reopen it by re-running \`lavish-axi ${file}\` unless the user asks for further review or something genuinely important needs their visual attention - deliver routine updates directly in this conversation instead. When reopening is warranted, run \`lavish-axi ${file} --reopen\`.`
|
|
12422
12522
|
};
|
|
12423
12523
|
}
|
|
12424
12524
|
function createUserEndedOpenOutput({ file, url, networkWarning = void 0 }) {
|
|
@@ -12543,6 +12643,41 @@ ${pollInterruptedText(absolute)}
|
|
|
12543
12643
|
}
|
|
12544
12644
|
}
|
|
12545
12645
|
}
|
|
12646
|
+
var REPLY_VALUE_FLAGS = ["--agent-reply", "--agent-reply-file"];
|
|
12647
|
+
var REPLY_TOO_LARGE_HELP = "Shorten the reply, then retry `lavish-axi reply <html-file>`";
|
|
12648
|
+
async function replyCommand(args) {
|
|
12649
|
+
const file = firstPositionalArg(args, REPLY_VALUE_FLAGS);
|
|
12650
|
+
if (!file) {
|
|
12651
|
+
throw new AxiError("HTML file path is required", "VALIDATION_ERROR", [
|
|
12652
|
+
'Run `lavish-axi reply <html-file> --agent-reply "<message>"`'
|
|
12653
|
+
]);
|
|
12654
|
+
}
|
|
12655
|
+
const inline = inspectValueFlag(args, "--agent-reply");
|
|
12656
|
+
const fromFile = inspectValueFlag(args, "--agent-reply-file");
|
|
12657
|
+
if (!inline.present && !fromFile.present) {
|
|
12658
|
+
throw new AxiError("An agent reply is required", "VALIDATION_ERROR", [
|
|
12659
|
+
'Pass exactly one of --agent-reply "<message>" or --agent-reply-file <path> (`-` reads stdin)',
|
|
12660
|
+
"Use `lavish-axi poll <html-file> --agent-reply` when the reply should be followed by a wait for feedback"
|
|
12661
|
+
]);
|
|
12662
|
+
}
|
|
12663
|
+
const text = await resolveAgentReply(args, { tooLargeHelp: REPLY_TOO_LARGE_HELP });
|
|
12664
|
+
if (!String(text || "").trim()) {
|
|
12665
|
+
throw new AxiError("Agent reply text was empty", "VALIDATION_ERROR", [
|
|
12666
|
+
'Pass --agent-reply "<message>" or --agent-reply-file <path> with a non-empty body'
|
|
12667
|
+
]);
|
|
12668
|
+
}
|
|
12669
|
+
await assertHtmlFile(file);
|
|
12670
|
+
const absolute = await canonicalFile(file);
|
|
12671
|
+
const baseUrl = await ensureServer();
|
|
12672
|
+
await postAgentReply(`${baseUrl}/api/${sessionKey(absolute)}/agent-reply`, text, absolute);
|
|
12673
|
+
return createReplyOutput(absolute);
|
|
12674
|
+
}
|
|
12675
|
+
function createReplyOutput(file) {
|
|
12676
|
+
return {
|
|
12677
|
+
reply: { file, status: "sent" },
|
|
12678
|
+
next_step: `Lavish Editor accepted the reply for ${file} and is no longer showing Working. Run \`lavish-axi poll ${file}\` when you are ready to wait for more feedback.`
|
|
12679
|
+
};
|
|
12680
|
+
}
|
|
12546
12681
|
function pollWaitBannerText(file) {
|
|
12547
12682
|
return `[lavish-axi] Long-polling for user feedback on ${file}. This stays silent until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period - leave it running. Detected layout issues do NOT return this poll: they wait in the user's Layout issues inbox until the user queues them as ordinary feedback. If it gets killed or times out before feedback arrives, re-run \`lavish-axi poll ${file}\` - feedback remains queued until delivery. Poll delivery consumes the response, so read it completely.`;
|
|
12548
12683
|
}
|
|
@@ -12627,7 +12762,7 @@ function createFeedbackNextStep(file, artifactFailures, sessionEnded, endedBy, p
|
|
|
12627
12762
|
return `${failureNote}${layoutNote}${whiteboardNote}${attachmentNote}This was the last feedback before the Lavish Editor session ended. Stop polling ${file}. Deliver any remaining updates directly in this conversation, or run \`lavish-axi ${file}\` to open a fresh session if the user needs further visual review.`;
|
|
12628
12763
|
}
|
|
12629
12764
|
const prefix = count > 0 ? artifactFailuresPrefix(file, artifactFailures) : `Apply the requested changes to ${file}. `;
|
|
12630
|
-
return `${prefix}${layoutNote}${whiteboardNote}${attachmentNote}Do not respond to the user just yet.
|
|
12765
|
+
return `${prefix}${layoutNote}${whiteboardNote}${attachmentNote}Do not respond to the user just yet. If you are continuing to wait for feedback, run \`lavish-axi poll ${file} --agent-reply "<message for the user>"\` without --timeout-ms to reply and keep polling. If instead you are handing back a result without starting another long-poll, run \`lavish-axi reply ${file} --agent-reply "<message for the user>"\` to post it and receive an acceptance receipt. ${POLL_AGENT_REPLY_RULE} ${POLL_AGENT_REPLY_NEXT_POINTER} The poll waits silently until the user sends more feedback, ends the session, or leaves every review window disconnected past the reconnect grace period - never kill it. ${pollExecutionGuidance({ agent })}`;
|
|
12631
12766
|
}
|
|
12632
12767
|
function artifactFailuresPrefix(file, artifactFailures) {
|
|
12633
12768
|
const count = artifactFailures.length;
|
|
@@ -13843,6 +13978,57 @@ async function postJson(url, body) {
|
|
|
13843
13978
|
}
|
|
13844
13979
|
return response.json();
|
|
13845
13980
|
}
|
|
13981
|
+
var AGENT_REPLY_RECEIPT_TIMEOUT_MS = 1e4;
|
|
13982
|
+
async function postAgentReply(url, text, file, { timeoutMs = AGENT_REPLY_RECEIPT_TIMEOUT_MS } = {}) {
|
|
13983
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
13984
|
+
let response;
|
|
13985
|
+
try {
|
|
13986
|
+
response = await fetch(url, {
|
|
13987
|
+
method: "POST",
|
|
13988
|
+
headers: { "content-type": "application/json" },
|
|
13989
|
+
body: JSON.stringify({ text }),
|
|
13990
|
+
signal
|
|
13991
|
+
});
|
|
13992
|
+
} catch {
|
|
13993
|
+
if (signal.aborted) throw agentReplyReceiptTimeoutError(file, timeoutMs);
|
|
13994
|
+
throw new AxiError("Lavish Editor server connection failed", "SERVER_ERROR", [
|
|
13995
|
+
"Run `lavish-axi server --verbose` or inspect `~/.lavish-axi/server.log` (`LAVISH_AXI_STATE_DIR/server.log` when set) for server startup or crash diagnostics",
|
|
13996
|
+
`Re-run \`lavish-axi reply ${file} --agent-reply "<message>"\` after the server is reachable`
|
|
13997
|
+
]);
|
|
13998
|
+
}
|
|
13999
|
+
let payload = null;
|
|
14000
|
+
try {
|
|
14001
|
+
payload = await response.json();
|
|
14002
|
+
} catch {
|
|
14003
|
+
if (signal.aborted) throw agentReplyReceiptTimeoutError(file, timeoutMs);
|
|
14004
|
+
}
|
|
14005
|
+
if (response.status === 404) {
|
|
14006
|
+
throw new AxiError("No active Lavish Editor session for this file", "NOT_FOUND", [
|
|
14007
|
+
`Run \`lavish-axi ${file}\` first`
|
|
14008
|
+
]);
|
|
14009
|
+
}
|
|
14010
|
+
if (response.status === 409 && payload?.status === "ended") {
|
|
14011
|
+
throw new AxiError("Lavish Editor session has ended; reply was not sent", "SESSION_ENDED", [
|
|
14012
|
+
createEndedNextStep(file, payload.ended_by)
|
|
14013
|
+
]);
|
|
14014
|
+
}
|
|
14015
|
+
if (!response.ok || payload?.status !== "sent") {
|
|
14016
|
+
throw new AxiError(`Lavish Editor did not accept the agent reply (${response.status})`, "SERVER_ERROR", [
|
|
14017
|
+
`Confirm the session is open with \`lavish-axi ${file}\`, then re-run \`lavish-axi reply ${file}\``
|
|
14018
|
+
]);
|
|
14019
|
+
}
|
|
14020
|
+
return payload;
|
|
14021
|
+
}
|
|
14022
|
+
function agentReplyReceiptTimeoutError(file, timeoutMs) {
|
|
14023
|
+
return new AxiError(
|
|
14024
|
+
`Lavish Editor did not confirm the agent reply within ${timeoutMs}ms, so acceptance is unknown`,
|
|
14025
|
+
"SERVER_ERROR",
|
|
14026
|
+
[
|
|
14027
|
+
"Run `lavish-axi server --verbose` or inspect `~/.lavish-axi/server.log` (`LAVISH_AXI_STATE_DIR/server.log` when set) for server diagnostics",
|
|
14028
|
+
`Check the Lavish Editor conversation for the reply before re-running \`lavish-axi reply ${file}\`, so it is not posted twice`
|
|
14029
|
+
]
|
|
14030
|
+
);
|
|
14031
|
+
}
|
|
13846
14032
|
function serverConnectionError() {
|
|
13847
14033
|
return new AxiError("Lavish Editor server connection failed", "SERVER_ERROR", [
|
|
13848
14034
|
"Run `lavish-axi server --verbose` or inspect `~/.lavish-axi/server.log` (`LAVISH_AXI_STATE_DIR/server.log` when set) for server startup or crash diagnostics",
|
|
@@ -13912,12 +14098,12 @@ function inspectValueFlag(args, flag) {
|
|
|
13912
14098
|
return { present: false };
|
|
13913
14099
|
}
|
|
13914
14100
|
var AGENT_REPLY_FILE_HINT = "Pass --agent-reply-file <path>, or --agent-reply-file - to read stdin";
|
|
13915
|
-
function agentReplyTooLargeError() {
|
|
14101
|
+
function agentReplyTooLargeError(suggestion) {
|
|
13916
14102
|
return new AxiError(`Agent reply exceeds the ${AGENT_REPLY_LIMIT_LABEL}`, "VALIDATION_ERROR", [
|
|
13917
|
-
"Shorten the reply, then retry the same poll command"
|
|
14103
|
+
suggestion || "Shorten the reply, then retry the same poll command"
|
|
13918
14104
|
]);
|
|
13919
14105
|
}
|
|
13920
|
-
async function readAgentReplyStream(stream) {
|
|
14106
|
+
async function readAgentReplyStream(stream, tooLargeHelp) {
|
|
13921
14107
|
const chunks = [];
|
|
13922
14108
|
let bytes = 0;
|
|
13923
14109
|
for await (const value of stream) {
|
|
@@ -13925,17 +14111,22 @@ async function readAgentReplyStream(stream) {
|
|
|
13925
14111
|
bytes += chunk.length;
|
|
13926
14112
|
if (bytes > AGENT_REPLY_INPUT_LIMIT_BYTES) {
|
|
13927
14113
|
stream.destroy();
|
|
13928
|
-
throw agentReplyTooLargeError();
|
|
14114
|
+
throw agentReplyTooLargeError(tooLargeHelp);
|
|
13929
14115
|
}
|
|
13930
14116
|
chunks.push(chunk);
|
|
13931
14117
|
}
|
|
13932
14118
|
const text = Buffer.concat(chunks, bytes).toString("utf8");
|
|
13933
14119
|
if (Buffer.byteLength(JSON.stringify({ agent_reply: text })) > AGENT_REPLY_JSON_LIMIT_BYTES) {
|
|
13934
|
-
throw agentReplyTooLargeError();
|
|
14120
|
+
throw agentReplyTooLargeError(tooLargeHelp);
|
|
13935
14121
|
}
|
|
13936
14122
|
return text;
|
|
13937
14123
|
}
|
|
13938
|
-
async function resolveAgentReply(args, {
|
|
14124
|
+
async function resolveAgentReply(args, {
|
|
14125
|
+
createReadStreamFn = createReadStream,
|
|
14126
|
+
stdin = process.stdin,
|
|
14127
|
+
stdinIsTTY = process.stdin.isTTY === true,
|
|
14128
|
+
tooLargeHelp = void 0
|
|
14129
|
+
} = {}) {
|
|
13939
14130
|
const inline = inspectValueFlag(args, "--agent-reply");
|
|
13940
14131
|
const fromFile = inspectValueFlag(args, "--agent-reply-file");
|
|
13941
14132
|
if (inline.present && fromFile.present) {
|
|
@@ -13944,12 +14135,12 @@ async function resolveAgentReply(args, { createReadStreamFn = createReadStream,
|
|
|
13944
14135
|
]);
|
|
13945
14136
|
}
|
|
13946
14137
|
if (fromFile.present) {
|
|
13947
|
-
return readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIsTTY });
|
|
14138
|
+
return readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIsTTY, tooLargeHelp });
|
|
13948
14139
|
}
|
|
13949
14140
|
if (!inline.present) return null;
|
|
13950
14141
|
return inline.value || null;
|
|
13951
14142
|
}
|
|
13952
|
-
async function readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIsTTY }) {
|
|
14143
|
+
async function readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIsTTY, tooLargeHelp }) {
|
|
13953
14144
|
if (fromFile.swallows && typeof fromFile.value === "string" && fromFile.value.startsWith("--")) {
|
|
13954
14145
|
throw new AxiError(
|
|
13955
14146
|
`--agent-reply-file was given no value: the next argument ${fromFile.value} is another flag, so it would have been used as the path`,
|
|
@@ -13969,7 +14160,7 @@ async function readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIs
|
|
|
13969
14160
|
]);
|
|
13970
14161
|
}
|
|
13971
14162
|
try {
|
|
13972
|
-
text = await readAgentReplyStream(stdin);
|
|
14163
|
+
text = await readAgentReplyStream(stdin, tooLargeHelp);
|
|
13973
14164
|
} catch (error) {
|
|
13974
14165
|
if (error instanceof AxiError) throw error;
|
|
13975
14166
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -13979,7 +14170,7 @@ async function readAgentReplyFile(fromFile, { createReadStreamFn, stdin, stdinIs
|
|
|
13979
14170
|
}
|
|
13980
14171
|
} else {
|
|
13981
14172
|
try {
|
|
13982
|
-
text = await readAgentReplyStream(createReadStreamFn(spec));
|
|
14173
|
+
text = await readAgentReplyStream(createReadStreamFn(spec), tooLargeHelp);
|
|
13983
14174
|
} catch (error) {
|
|
13984
14175
|
if (error instanceof AxiError) throw error;
|
|
13985
14176
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -14014,6 +14205,7 @@ Usage:
|
|
|
14014
14205
|
lavish-axi
|
|
14015
14206
|
lavish-axi <html-file> [--no-open] [--no-gate] [--reopen]
|
|
14016
14207
|
lavish-axi poll <html-file> [--owner <label>] [--takeover] [--agent-reply "..."] [--agent-reply-file <path>]
|
|
14208
|
+
lavish-axi reply <html-file> (--agent-reply "..." | --agent-reply-file <path>)
|
|
14017
14209
|
lavish-axi end <html-file>
|
|
14018
14210
|
lavish-axi export <html-file> [--out <path>]
|
|
14019
14211
|
lavish-axi share <html-file> [--private | --password <pw>] [--token <t>]
|
|
@@ -14027,7 +14219,7 @@ Usage:
|
|
|
14027
14219
|
|
|
14028
14220
|
${DESIGN_SYSTEM_HINT}
|
|
14029
14221
|
|
|
14030
|
-
Note: poll long-polls until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period, staying silent while it waits - never kill it. Layout issues the browser detects are passive: they collect in the user's Layout issues inbox in the Lavish top bar and reach the agent only when the user selects them and queues the fixes, as an ordinary tag "layout-warnings" prompt. Do not pass --timeout-ms during normal agent use; it is for tests and debugging only. ${pollExecutionGuidance({ agent })} ${POLL_SEND_AND_END_RULE}
|
|
14222
|
+
Note: poll long-polls until the user sends feedback, ends the session, or leaves every review window disconnected past the reconnect grace period, staying silent while it waits - never kill it. Layout issues the browser detects are passive: they collect in the user's Layout issues inbox in the Lavish top bar and reach the agent only when the user selects them and queues the fixes, as an ordinary tag "layout-warnings" prompt. Do not pass --timeout-ms during normal agent use; it is for tests and debugging only. ${pollExecutionGuidance({ agent })} ${POLL_SEND_AND_END_RULE} Use \`lavish-axi reply\` when handing a result back without waiting for more feedback: it exits 0 only after the server accepts the reply, so the board stops showing Working. \`poll --agent-reply\` posts a reply and then keeps waiting.
|
|
14031
14223
|
|
|
14032
14224
|
`;
|
|
14033
14225
|
}
|
|
@@ -14041,7 +14233,7 @@ Open or resume a Lavish Editor review session for an HTML artifact. Use --no-ope
|
|
|
14041
14233
|
|
|
14042
14234
|
This command exclusively long-polls for queued user prompts. Pass --owner <label> to make the active listener visible in session listings; --takeover displaces an existing listener, which receives LISTENER_REPLACED. A second poll without --takeover fails with LISTENER_ACTIVE instead of silently returning waiting.
|
|
14043
14235
|
|
|
14044
|
-
This command long-polls indefinitely for queued user prompts. It stays silent while it waits - that is normal, never kill it. Browser-detected layout issues do NOT return this poll: they are filed passively in the user's Layout issues inbox and arrive as an ordinary tag "layout-warnings" prompt only after the user selects them and queues the fixes. Warning lifecycle: an issue stays unresolved and counted while queued, becomes recurring if a newer artifact revision still shows it, and is resolved only after a newer artifact load plus a complete diagnostic pass at the same viewport no longer detects it. A failed or incomplete pass preserves it as unverified rather than clearing it. The only response that arrives without user action is artifact_failures - a fatal failure that made the review surface itself unusable. Do not pass --timeout-ms during normal agent use; it is for tests and debugging only. In a Herdr-managed pane, set LAVISH_AXI_HERDR_CHIME=1 to request attention when the poll has entered its waiting state; unset it or use any other value to keep the current silent behavior. Notification failures never interrupt the poll. ${pollExecutionGuidance({ agent })} Use --agent-reply after applying prior feedback to display a concise response in Lavish Editor before waiting again. ${POLL_AGENT_REPLY_RULE} ${POLL_AGENT_REPLY_HELP_POINTER} Do not combine --agent-reply with --agent-reply-file.
|
|
14236
|
+
This command long-polls indefinitely for queued user prompts. It stays silent while it waits - that is normal, never kill it. Browser-detected layout issues do NOT return this poll: they are filed passively in the user's Layout issues inbox and arrive as an ordinary tag "layout-warnings" prompt only after the user selects them and queues the fixes. Warning lifecycle: an issue stays unresolved and counted while queued, becomes recurring if a newer artifact revision still shows it, and is resolved only after a newer artifact load plus a complete diagnostic pass at the same viewport no longer detects it. A failed or incomplete pass preserves it as unverified rather than clearing it. The only response that arrives without user action is artifact_failures - a fatal failure that made the review surface itself unusable. Do not pass --timeout-ms during normal agent use; it is for tests and debugging only. In a Herdr-managed pane, set LAVISH_AXI_HERDR_CHIME=1 to request attention when the poll has entered its waiting state; unset it or use any other value to keep the current silent behavior. Notification failures never interrupt the poll. ${pollExecutionGuidance({ agent })} Use --agent-reply after applying prior feedback to display a concise response in Lavish Editor before waiting again. When you are handing a result back and are not about to long-poll, run \`lavish-axi reply <html-file> --agent-reply "..."\` instead: that command exits 0 only after the server accepts the reply, so the board stops showing Working without this poll staying open. ${POLL_AGENT_REPLY_RULE} ${POLL_AGENT_REPLY_HELP_POINTER} Do not combine --agent-reply with --agent-reply-file.
|
|
14045
14237
|
|
|
14046
14238
|
Examples:
|
|
14047
14239
|
lavish-axi poll report.html --agent-reply "Renamed the payment step."
|
|
@@ -14049,6 +14241,19 @@ Examples:
|
|
|
14049
14241
|
lavish-axi poll report.html --agent-reply-file -
|
|
14050
14242
|
|
|
14051
14243
|
${POLL_SEND_AND_END_RULE}
|
|
14244
|
+
`,
|
|
14245
|
+
reply: `Usage: lavish-axi reply <html-file> (--agent-reply "..." | --agent-reply-file <path>)
|
|
14246
|
+
|
|
14247
|
+
Post an agent reply to the open Lavish Editor session and exit once the server confirms it was accepted. Exit 0 only when the server answers that the reply was sent, which is when the board stops showing Working. If that answer does not arrive within ${AGENT_REPLY_RECEIPT_TIMEOUT_MS / 1e3} seconds, exit non-zero with a timeout error. Use this when you are handing a result back and are not about to wait for more feedback.
|
|
14248
|
+
|
|
14249
|
+
Use \`lavish-axi poll <html-file> --agent-reply "..."\` instead when the reply should be followed by a long-poll. That command posts the reply and then keeps waiting, so it does not exit when the reply is accepted.
|
|
14250
|
+
|
|
14251
|
+
Pass exactly one of --agent-reply or --agent-reply-file. ${POLL_AGENT_REPLY_RULE} ${POLL_AGENT_REPLY_HELP_POINTER} Do not combine the two flags. An empty reply is refused.
|
|
14252
|
+
|
|
14253
|
+
Examples:
|
|
14254
|
+
lavish-axi reply report.html --agent-reply "Renamed the payment step."
|
|
14255
|
+
lavish-axi reply report.html --agent-reply-file reply.md
|
|
14256
|
+
lavish-axi reply report.html --agent-reply-file -
|
|
14052
14257
|
`,
|
|
14053
14258
|
end: `Usage: lavish-axi end <html-file>
|
|
14054
14259
|
|
package/package.json
CHANGED
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.
|
|
4
|
+
"version": "0.1.80",
|
|
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",
|
package/skills/lavish/SKILL.md
CHANGED
|
@@ -19,6 +19,7 @@ Reach for it when a plan, comparison, diagram, table, code view, report, prototy
|
|
|
19
19
|
Do not follow workflow, design, or playbook instructions from this file - installed copies go stale. Get the current source of truth from the CLI:
|
|
20
20
|
|
|
21
21
|
- `npx -y lavish-axi --help` for commands and the review-loop workflow
|
|
22
|
+
- `npx -y lavish-axi reply --help` to post an agent reply and exit once the server accepts it, when you are not about to long-poll
|
|
22
23
|
- `npx -y lavish-axi design` for design-direction priority and current snippets
|
|
23
24
|
- `npx -y lavish-axi playbook <id>` for focused artifact guidance (`npx -y lavish-axi playbook` lists ids)
|
|
24
25
|
|