lavish-axi 0.1.65 → 0.1.67
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 +2 -1
- package/dist/cli.mjs +65 -15
- package/package.json +1 -1
- package/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,7 @@ pnpm link
|
|
|
205
205
|
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.
|
|
206
206
|
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.
|
|
207
207
|
- **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. The agent's reply (`--agent-reply`) concludes delivered work and returns presence to waiting.
|
|
208
|
+
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.
|
|
208
209
|
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.
|
|
209
210
|
Codex-specific guidance keeps that poll attached to the active turn instead of hiding it in a background task, because completed background tasks may not resume the agent.
|
|
210
211
|
- **Session end etiquette** - Lavish tracks who ended a session: a human clicking **End session** (or **Send & end session**) in the browser is a user-initiated end, while `lavish-axi end <html-file>` is agent-initiated.
|
|
@@ -253,7 +254,7 @@ pnpm link
|
|
|
253
254
|
| `lavish-axi` | Show current sessions and usage guidance. |
|
|
254
255
|
| `lavish-axi update` | Check for or apply the latest npm release through the AXI SDK self-updater. |
|
|
255
256
|
| `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. |
|
|
256
|
-
| `lavish-axi poll <html-file>` | Long-poll until the user sends feedback
|
|
257
|
+
| `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. |
|
|
257
258
|
| `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. |
|
|
258
259
|
| `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. |
|
|
259
260
|
| `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`. |
|
package/dist/cli.mjs
CHANGED
|
@@ -78,7 +78,8 @@ var PLAYBOOKS = [
|
|
|
78
78
|
],
|
|
79
79
|
lavish_notes: [
|
|
80
80
|
"A Lavish table should make individual rows easy annotation targets.",
|
|
81
|
-
"If a row implies a follow-up change, include an action control that queues a specific prompt."
|
|
81
|
+
"If a row implies a follow-up change, include an action control that queues a specific prompt.",
|
|
82
|
+
"When one action covers multiple rows and completeness matters, also follow the input playbook's tracked batch pattern so the reviewer submits one explicit ID set for item-by-item accounting."
|
|
82
83
|
]
|
|
83
84
|
},
|
|
84
85
|
{
|
|
@@ -201,10 +202,12 @@ var PLAYBOOKS = [
|
|
|
201
202
|
choose: [
|
|
202
203
|
"Use this when the user needs to select, tune, triage, annotate, or edit a structured choice.",
|
|
203
204
|
"Use controls for decisions the user can make faster visually than by writing a prompt.",
|
|
205
|
+
"Use an opt-in tracked batch when the agent must preserve completeness across a multi-item decision, such as findings to fixes, constraints to implementation, or recommendations to follow-up work.",
|
|
204
206
|
"Use plain annotations when the artifact only needs open-ended feedback."
|
|
205
207
|
],
|
|
206
208
|
structure: [
|
|
207
209
|
"Make each decision surface visible: what is being chosen, what the options mean, and what happens next.",
|
|
210
|
+
"For a tracked batch, give every candidate item a short, stable, visible ID and let the reviewer select or disposition items with editable native controls.",
|
|
208
211
|
"Keep reversible selection state local in the artifact until the user explicitly submits that question.",
|
|
209
212
|
"Pair each question with a Submit or Queue answer control that sends exactly one prompt for the final answer.",
|
|
210
213
|
"Show selected state separately from queued state so the user trusts what will be sent back."
|
|
@@ -216,6 +219,7 @@ var PLAYBOOKS = [
|
|
|
216
219
|
"Put data-lavish-action only on custom (non-native) elements that should act like a feedback control - typically a styled div or span you made clickable - so Lavish does not annotate it and shows a pointer cursor instead.",
|
|
217
220
|
"Use data-lavish-question on a question wrapper or pass queueKey when multiple pre-send updates should replace the prior unsent answer for the same question.",
|
|
218
221
|
"Pass options such as tag, text, selector, target, data, queueKey, or element when they help the agent understand exactly what the user chose.",
|
|
222
|
+
"For a tracked batch, queue the final selected set once in a concise, bounded data.items array; include each item's stable ID, concise label, and requested disposition, and explicitly tell the agent to account for every submitted ID before reporting completion.",
|
|
219
223
|
"Call window.lavish.sendQueuedPrompts() only when the control should immediately send committed feedback instead of waiting for the user to press Send to Agent.",
|
|
220
224
|
"Make queued prompts specific enough that the agent can act without asking a follow-up question.",
|
|
221
225
|
"Keep native browser controls accessible and readable on mobile."
|
|
@@ -229,6 +233,8 @@ var PLAYBOOKS = [
|
|
|
229
233
|
lavish_notes: [
|
|
230
234
|
"Lavish is strongest when the artifact becomes a focused review surface and not just a static page.",
|
|
231
235
|
`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>\`.`,
|
|
236
|
+
`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>`,
|
|
237
|
+
"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.",
|
|
232
238
|
"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.",
|
|
233
239
|
"Use window.lavish.queuePrompt for user intent, not internal analytics or UI-only state changes.",
|
|
234
240
|
"End input paths with an obvious way for the user to send feedback back to the agent."
|
|
@@ -8807,6 +8813,7 @@ var WHITEBOARD_CHANNEL_TOKEN_TTL_MS = 5 * 6e4;
|
|
|
8807
8813
|
var NETWORK_RECONCILE_CACHE_MS = 1e3;
|
|
8808
8814
|
var TAILSCALE_BIND_RETRY_DELAYS_MS = [100, 250, 500];
|
|
8809
8815
|
var WEBSOCKET_CLOSE_GRACE_MS = 250;
|
|
8816
|
+
var BROWSER_DISCONNECT_GRACE_MS = 1e4;
|
|
8810
8817
|
var ARTIFACT_CONTENT_SECURITY_POLICY = "sandbox allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads";
|
|
8811
8818
|
var ATTACHMENT_SWEEP_INTERVAL_MS = 60 * 6e4;
|
|
8812
8819
|
var SHUTDOWN_REASONS = /* @__PURE__ */ new Set(["upgrade", "local-build", "stop"]);
|
|
@@ -8904,6 +8911,7 @@ async function serve({
|
|
|
8904
8911
|
debug = false,
|
|
8905
8912
|
log = null,
|
|
8906
8913
|
pollHeartbeatMs = 15e3,
|
|
8914
|
+
browserDisconnectGraceMs = BROWSER_DISCONNECT_GRACE_MS,
|
|
8907
8915
|
idleTimeoutMs = resolveIdleTimeoutMs(),
|
|
8908
8916
|
host = bindHost(env),
|
|
8909
8917
|
hosts,
|
|
@@ -8936,6 +8944,7 @@ async function serve({
|
|
|
8936
8944
|
const activePolls = /* @__PURE__ */ new Map();
|
|
8937
8945
|
const deliveredFeedback = /* @__PURE__ */ new Set();
|
|
8938
8946
|
const liveEventClients = /* @__PURE__ */ new Map();
|
|
8947
|
+
const browserDisconnectTimers = /* @__PURE__ */ new Map();
|
|
8939
8948
|
const whiteboardChannelSecret = crypto6.randomBytes(32);
|
|
8940
8949
|
const outstandingRepairBatches = /* @__PURE__ */ new Set();
|
|
8941
8950
|
const diagnosticViewportClasses = resolveDiagnosticViewportClasses();
|
|
@@ -8948,6 +8957,7 @@ async function serve({
|
|
|
8948
8957
|
let serverReady = false;
|
|
8949
8958
|
let networkReconcileCheckedAt = 0;
|
|
8950
8959
|
let cachedNetworkStale = false;
|
|
8960
|
+
let shuttingDown = false;
|
|
8951
8961
|
let networkReconcilePromise = null;
|
|
8952
8962
|
function broadcastLiveEvent(type, key, data = {}) {
|
|
8953
8963
|
for (const [client, clientKey] of liveEventClients) {
|
|
@@ -8959,7 +8969,30 @@ async function serve({
|
|
|
8959
8969
|
events.on("agent-presence", (key, state) => broadcastLiveEvent("agent-presence", key, { state }));
|
|
8960
8970
|
events.on("layout-warnings", (key, warnings) => broadcastLiveEvent("layout-warnings", key, { warnings }));
|
|
8961
8971
|
events.on("ended", (key, endedBy) => broadcastLiveEvent("ended", key, { ended_by: endedBy || null }));
|
|
8972
|
+
function hasLiveEventClient(key) {
|
|
8973
|
+
for (const clientKey of liveEventClients.values()) {
|
|
8974
|
+
if (clientKey === key) return true;
|
|
8975
|
+
}
|
|
8976
|
+
return false;
|
|
8977
|
+
}
|
|
8978
|
+
function clearBrowserDisconnectTimer(key) {
|
|
8979
|
+
const timer = browserDisconnectTimers.get(key);
|
|
8980
|
+
if (!timer) return;
|
|
8981
|
+
clearTimeout(timer);
|
|
8982
|
+
browserDisconnectTimers.delete(key);
|
|
8983
|
+
}
|
|
8984
|
+
function scheduleBrowserDisconnect(key) {
|
|
8985
|
+
clearBrowserDisconnectTimer(key);
|
|
8986
|
+
if (shuttingDown || hasLiveEventClient(key) || !activePolls.has(key)) return;
|
|
8987
|
+
const timer = setTimeout(() => {
|
|
8988
|
+
browserDisconnectTimers.delete(key);
|
|
8989
|
+
if (!hasLiveEventClient(key) && activePolls.has(key)) events.emit("browser-disconnected", key);
|
|
8990
|
+
}, browserDisconnectGraceMs);
|
|
8991
|
+
timer.unref?.();
|
|
8992
|
+
browserDisconnectTimers.set(key, timer);
|
|
8993
|
+
}
|
|
8962
8994
|
function attachLiveEventClient(client, key, onClose) {
|
|
8995
|
+
clearBrowserDisconnectTimer(key);
|
|
8963
8996
|
liveEventClients.set(client, key);
|
|
8964
8997
|
refreshIdleTimer();
|
|
8965
8998
|
let cleanedUp = false;
|
|
@@ -8967,6 +9000,7 @@ async function serve({
|
|
|
8967
9000
|
if (cleanedUp) return;
|
|
8968
9001
|
cleanedUp = true;
|
|
8969
9002
|
liveEventClients.delete(client);
|
|
9003
|
+
scheduleBrowserDisconnect(key);
|
|
8970
9004
|
refreshIdleTimer();
|
|
8971
9005
|
};
|
|
8972
9006
|
onClose(cleanup);
|
|
@@ -9246,25 +9280,28 @@ async function serve({
|
|
|
9246
9280
|
if (heartbeat) clearInterval(heartbeat);
|
|
9247
9281
|
events.off("feedback", onFeedback);
|
|
9248
9282
|
events.off("ended", onFeedback);
|
|
9283
|
+
events.off("browser-disconnected", onBrowserDisconnected);
|
|
9249
9284
|
setPollActive(key, activePolls, deliveredFeedback, events, false);
|
|
9285
|
+
if (!activePolls.has(key)) clearBrowserDisconnectTimer(key);
|
|
9250
9286
|
refreshIdleTimer();
|
|
9251
9287
|
cleanupPoll = null;
|
|
9252
9288
|
detachRequestClose();
|
|
9253
9289
|
};
|
|
9254
|
-
const respond = async () => {
|
|
9290
|
+
const respond = async (forcedResult = null) => {
|
|
9255
9291
|
if (responding || res.writableEnded) return;
|
|
9256
9292
|
responding = true;
|
|
9257
9293
|
try {
|
|
9258
9294
|
const result = await store.takeFeedback(key);
|
|
9295
|
+
const responseResult = forcedResult && result.status === "waiting" ? forcedResult : result;
|
|
9259
9296
|
if (requestClosed || req.destroyed || res.writableEnded) {
|
|
9260
|
-
await restoreClosedFeedback(key,
|
|
9297
|
+
await restoreClosedFeedback(key, responseResult);
|
|
9261
9298
|
return;
|
|
9262
9299
|
}
|
|
9263
|
-
finishFeedbackDelivery(key,
|
|
9300
|
+
finishFeedbackDelivery(key, responseResult);
|
|
9264
9301
|
if (streamHeartbeat) {
|
|
9265
|
-
res.end(JSON.stringify(
|
|
9302
|
+
res.end(JSON.stringify(responseResult));
|
|
9266
9303
|
} else {
|
|
9267
|
-
res.json(
|
|
9304
|
+
res.json(responseResult);
|
|
9268
9305
|
}
|
|
9269
9306
|
} finally {
|
|
9270
9307
|
cleanup();
|
|
@@ -9276,8 +9313,13 @@ async function serve({
|
|
|
9276
9313
|
}
|
|
9277
9314
|
respond().catch(handleRespondError);
|
|
9278
9315
|
};
|
|
9316
|
+
const onBrowserDisconnected = (changedKey) => {
|
|
9317
|
+
if (changedKey !== key || res.writableEnded) return;
|
|
9318
|
+
respond({ status: "browser_disconnected" }).catch(handleRespondError);
|
|
9319
|
+
};
|
|
9279
9320
|
events.on("feedback", onFeedback);
|
|
9280
9321
|
events.on("ended", onFeedback);
|
|
9322
|
+
events.on("browser-disconnected", onBrowserDisconnected);
|
|
9281
9323
|
cleanupPoll = cleanup;
|
|
9282
9324
|
if (requestClosed || req.destroyed || res.writableEnded) {
|
|
9283
9325
|
cleanup();
|
|
@@ -10039,7 +10081,6 @@ ${body}`
|
|
|
10039
10081
|
}
|
|
10040
10082
|
publicPort = httpServers[0].address().port;
|
|
10041
10083
|
serverReady = true;
|
|
10042
|
-
let shuttingDown = false;
|
|
10043
10084
|
function shutdown(reloadKey = "", reason = "") {
|
|
10044
10085
|
if (shuttingDown) return;
|
|
10045
10086
|
shuttingDown = true;
|
|
@@ -10064,6 +10105,8 @@ ${body}`
|
|
|
10064
10105
|
}
|
|
10065
10106
|
}
|
|
10066
10107
|
liveEventClients.clear();
|
|
10108
|
+
for (const timer of browserDisconnectTimers.values()) clearTimeout(timer);
|
|
10109
|
+
browserDisconnectTimers.clear();
|
|
10067
10110
|
for (const w of watchers.values()) {
|
|
10068
10111
|
w.close().catch(() => {
|
|
10069
10112
|
});
|
|
@@ -10906,11 +10949,12 @@ var POLL_WAKE_PATH_RULES = Object.freeze([
|
|
|
10906
10949
|
"Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive.",
|
|
10907
10950
|
"If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor.",
|
|
10908
10951
|
"Do not tell the user the artifact is being monitored until that wake path is live.",
|
|
10909
|
-
"If the poll gets killed or times out before feedback arrives, re-run it - feedback remains queued until delivery. Poll delivery consumes the response, so read it completely."
|
|
10952
|
+
"If the poll gets killed or times out before feedback arrives, re-run it - feedback remains queued until delivery. Poll delivery consumes the response, so read it completely.",
|
|
10953
|
+
"If it returns browser_disconnected, the review window stayed disconnected past its reconnect grace period but the session remains resumable; ask the user whether to reopen or end it, and do neither uninvited."
|
|
10910
10954
|
]);
|
|
10911
10955
|
var POLL_SEND_AND_END_RULE = "`Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited.";
|
|
10912
10956
|
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.";
|
|
10913
|
-
var VERSION = "0.1.
|
|
10957
|
+
var VERSION = "0.1.67";
|
|
10914
10958
|
function detectInvokingAgent(env = process.env) {
|
|
10915
10959
|
return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
|
|
10916
10960
|
}
|
|
@@ -11026,7 +11070,7 @@ function createHomeOutput({ bin, sessions, includeSessions = true, agent = "gene
|
|
|
11026
11070
|
"Run `lavish-axi <html-file>` to open or resume a Lavish Editor session. If the user explicitly ended the session from the browser, this refuses to reopen it and explains why instead of reopening uninvited - pass `--reopen` only when the user asks for further review or something important needs their visual attention",
|
|
11027
11071
|
"Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/`",
|
|
11028
11072
|
"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",
|
|
11029
|
-
`Run \`lavish-axi poll <html-file>\` to wait for user feedback. It long-polls and stays silent until the user sends feedback
|
|
11073
|
+
`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}`,
|
|
11030
11074
|
'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)',
|
|
11031
11075
|
"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`",
|
|
11032
11076
|
"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",
|
|
@@ -11067,7 +11111,7 @@ function createOpenOutput({
|
|
|
11067
11111
|
session: { file, url, status },
|
|
11068
11112
|
...networkWarning ? { network_warning: networkWarning } : {},
|
|
11069
11113
|
...selfPaintWarning ? { self_paint_warning: selfPaintWarning } : {},
|
|
11070
|
-
next_step: `${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
|
|
11114
|
+
next_step: `${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 your response in Lavish Editor and wait for more feedback. 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\`.`
|
|
11071
11115
|
};
|
|
11072
11116
|
}
|
|
11073
11117
|
function createUserEndedOpenOutput({ file, url, networkWarning = void 0 }) {
|
|
@@ -11168,11 +11212,11 @@ ${pollInterruptedText(absolute)}
|
|
|
11168
11212
|
}
|
|
11169
11213
|
}
|
|
11170
11214
|
function pollWaitBannerText(file) {
|
|
11171
|
-
return `[lavish-axi] Long-polling for user feedback on ${file}. This stays silent until the user sends feedback
|
|
11215
|
+
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.`;
|
|
11172
11216
|
}
|
|
11173
11217
|
function pollWaitTickText(elapsedMs) {
|
|
11174
11218
|
const minutes = Math.round(elapsedMs / 6e4);
|
|
11175
|
-
return `[lavish-axi] Still waiting for user feedback (${minutes}m). Leave this running until the user sends feedback
|
|
11219
|
+
return `[lavish-axi] Still waiting for user feedback (${minutes}m). Leave this running until the user sends feedback, ends the session, or leaves every review window disconnected.`;
|
|
11176
11220
|
}
|
|
11177
11221
|
function pollInterruptedText(file) {
|
|
11178
11222
|
return `[lavish-axi] Poll interrupted before user feedback arrived. The user may still be reviewing - re-run \`lavish-axi poll ${file}\` to keep waiting; feedback remains queued until delivery. Poll delivery consumes the response, so read it completely.`;
|
|
@@ -11226,6 +11270,12 @@ function createPollOutput({ file, response, agent = "generic" }) {
|
|
|
11226
11270
|
next_step: createEndedNextStep(file, response.ended_by)
|
|
11227
11271
|
};
|
|
11228
11272
|
}
|
|
11273
|
+
if (response.status === "browser_disconnected") {
|
|
11274
|
+
return {
|
|
11275
|
+
session: { file, status: "browser_disconnected" },
|
|
11276
|
+
next_step: "The Lavish review window was closed or disconnected. The session remains open and resumable; ask the user whether they want to reopen it or end the session. Do not reopen or end it without their direction."
|
|
11277
|
+
};
|
|
11278
|
+
}
|
|
11229
11279
|
return {
|
|
11230
11280
|
session: { file, status: response.status || "waiting" },
|
|
11231
11281
|
next_step: `No user feedback arrived before the optional timeout. Run \`lavish-axi poll ${file}\` without --timeout-ms to wait indefinitely - feedback remains queued until delivery, so re-running the poll is safe while waiting. Poll delivery consumes the response, so read it completely.`
|
|
@@ -11245,7 +11295,7 @@ function createFeedbackNextStep(file, artifactFailures, sessionEnded, endedBy, p
|
|
|
11245
11295
|
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.`;
|
|
11246
11296
|
}
|
|
11247
11297
|
const prefix = count > 0 ? artifactFailuresPrefix(file, artifactFailures) : `Apply the requested changes to ${file}. `;
|
|
11248
|
-
return `${prefix}${layoutNote}${whiteboardNote}${attachmentNote}Do not respond to the user just yet. Now you must run \`lavish-axi poll ${file} --agent-reply "<message for the user>"\` without --timeout-ms unless the user ended the session. The poll waits silently until the user sends more feedback
|
|
11298
|
+
return `${prefix}${layoutNote}${whiteboardNote}${attachmentNote}Do not respond to the user just yet. Now you must run \`lavish-axi poll ${file} --agent-reply "<message for the user>"\` without --timeout-ms unless the user ended the session. 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 })}`;
|
|
11249
11299
|
}
|
|
11250
11300
|
function artifactFailuresPrefix(file, artifactFailures) {
|
|
11251
11301
|
const count = artifactFailures.length;
|
|
@@ -12310,7 +12360,7 @@ Usage:
|
|
|
12310
12360
|
|
|
12311
12361
|
${DESIGN_SYSTEM_HINT}
|
|
12312
12362
|
|
|
12313
|
-
Note: poll long-polls
|
|
12363
|
+
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}
|
|
12314
12364
|
|
|
12315
12365
|
`;
|
|
12316
12366
|
}
|
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.67",
|
|
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",
|