@kky42/pievo-server 3.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/MarkdownView-BuFKr9Jg.js +66 -0
  3. package/.output/public/assets/app-x1CeeY7N.css +2 -0
  4. package/.output/public/assets/{index-Di_wpAtL.js → index-D0FIKiiW.js} +3 -3
  5. package/.output/public/assets/loops._loopId-DTkIz4C0.js +2 -0
  6. package/.output/public/assets/loops._loopId_.runs._runId-CsV2Ih4Z.js +3 -0
  7. package/.output/public/assets/{preload-helper-hRTYB_ju.js → preload-helper-BSxGu4d0.js} +1 -1
  8. package/.output/public/assets/routes-m4Z90DTR.js +1 -0
  9. package/.output/server/_ssr/{MachinesModal-CmVMec6B.mjs → MachinesModal-CLBZxZkq.mjs} +4 -4
  10. package/.output/server/_ssr/MarkdownView-D1J7ImYF.mjs +70 -0
  11. package/.output/server/_ssr/{artifactFiles-Di1xDKvX.mjs → artifactFiles-B0NPRDOR.mjs} +14 -19
  12. package/.output/server/_ssr/{artifactKind-DqpmtxDU.mjs → artifactKind-a2oxT2wQ.mjs} +4 -4
  13. package/.output/server/_ssr/{boot-CYRi-wez.mjs → boot-DmpKtb0A.mjs} +810 -842
  14. package/.output/server/_ssr/{lifecycleUi-BsbUsi5K.mjs → lifecycleUi-DEjM8Etm.mjs} +5 -5
  15. package/.output/server/_ssr/{loopApi-Bez1rHV0.mjs → loopApi-DK1lQPrx.mjs} +13 -13
  16. package/.output/server/_ssr/{loopProjection-esCjyZJg.mjs → loopProjection-DdFivf7-.mjs} +4 -3
  17. package/.output/server/_ssr/{loops._loopId-KUezNY69.mjs → loops._loopId-DVCvGSEc.mjs} +34 -99
  18. package/.output/server/_ssr/{loops._loopId_.runs._runId-DOkUEesG.mjs → loops._loopId_.runs._runId-BXbTfLlC.mjs} +20 -9
  19. package/.output/server/_ssr/{loops._loopId_.runs._runId-BFP9oMrp.mjs → loops._loopId_.runs._runId-Dr6E-FA5.mjs} +3 -3
  20. package/.output/server/_ssr/{machineFns-CniGcY8s.mjs → machineFns-DLRHgDdS.mjs} +2 -2
  21. package/.output/server/_ssr/{router-BFIH2UAE.mjs → router-BhHsTGqu.mjs} +95 -102
  22. package/.output/server/_ssr/{routes-BSVBANI3.mjs → routes-CCallyCT.mjs} +2 -2
  23. package/.output/server/_ssr/{routes-DnE7XDe_.mjs → routes-CL2Kef-Q.mjs} +4 -4
  24. package/.output/server/_ssr/ssr.mjs +22 -22
  25. package/.output/server/{_tanstack-start-manifest_v-DnwAupbZ.mjs → _tanstack-start-manifest_v-BPiJ4yWT.mjs} +8 -9
  26. package/.output/server/drizzle/0001_loop_files.sql +9 -0
  27. package/.output/server/drizzle/meta/_journal.json +8 -1
  28. package/.output/server/index.mjs +49 -42
  29. package/drizzle/0001_loop_files.sql +9 -0
  30. package/drizzle/meta/_journal.json +8 -1
  31. package/package.json +1 -1
  32. package/.output/public/assets/app-DXLuQNb9.css +0 -2
  33. package/.output/public/assets/loops._loopId-X3ClNPMY.js +0 -67
  34. package/.output/public/assets/loops._loopId_.runs._runId-ByU2BSyF.js +0 -3
  35. package/.output/public/assets/routes-K8D5uBiV.js +0 -1
@@ -1,5 +1,5 @@
1
1
  import { i as getServerFnById, r as createServerFn, t as TSS_SERVER_FUNCTION } from "./ssr.mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/lifecycleUi-BsbUsi5K.js
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/lifecycleUi-DEjM8Etm.js
3
3
  var createSsrRpc = (functionId) => {
4
4
  const url = "/_serverFn/" + functionId;
5
5
  const serverFnMeta = { id: functionId };
@@ -16,8 +16,8 @@ var createSsrRpc = (functionId) => {
16
16
  * Server functions backing the dashboard. They run server-side (createServerFn
17
17
  * → RPC) and call the IN-PROCESS scheduler + store directly — the scheduler
18
18
  * lives in this same TanStack process (booted on first call via ensureServer).
19
- * The machine-facing poll, CLI, report, and artifact-sync endpoints are sibling
20
- * server routes in this same process, so one process owns the single scheduler.
19
+ * The machine-facing poll, CLI, report, and run-file endpoints are sibling server
20
+ * routes in this same process, so one process owns the single scheduler.
21
21
  */
22
22
  /**
23
23
  * Client config. `pievoCli` is the CLI invocation prefix the skill + connect
@@ -94,9 +94,9 @@ function lifecyclePresentation(loop) {
94
94
  };
95
95
  }
96
96
  function daemonStopSupport(protocol) {
97
- return protocol === 4 ? {
97
+ return protocol === 5 ? {
98
98
  supported: true,
99
- label: `Daemon protocol 4 · Stop supported`
99
+ label: `Daemon protocol 5 · Stop supported`
100
100
  } : {
101
101
  supported: false,
102
102
  label: `Daemon protocol ${protocol ?? "unknown"} · upgrade required`
@@ -1,14 +1,14 @@
1
- import { A as listMachines, C as getMachine, E as hasRunningRun, G as startLoop$1, H as requestRunCancel, I as machinePresence, J as updateLoop, K as stopLoop$1, M as listRunsBefore, R as pauseLoop$1, S as getLoop, V as requestDeleteLoop, c as ensureServer, h as validateLoopEdit, k as listLoops$1, q as tryDeleteLoop, w as getRun, y as forceDeleteLoop } from "./boot-CYRi-wez.mjs";
2
- import { a as toRunSummaries, i as toLoopSummaries, r as toLoopDetail, t as sortLoopSummariesByRecentRun } from "./loopProjection-esCjyZJg.mjs";
1
+ import { B as requestDeleteLoop, C as getRun, F as machinePresence, G as tryDeleteLoop, K as updateLoop, L as pauseLoop$1, O as listLoops$1, S as getMachine, T as hasRunningRun, U as startLoop$1, V as requestRunCancel, W as stopLoop$1, j as listRunsBefore, k as listMachines, m as validateLoopEdit, s as ensureServer, v as forceDeleteLoop, x as getLoop } from "./boot-DmpKtb0A.mjs";
2
+ import { a as toRunSummaries, i as toLoopSummaries, r as toLoopDetail, t as sortLoopSummariesByRecentRun } from "./loopProjection-DdFivf7-.mjs";
3
3
  import { r as createServerFn } from "./ssr.mjs";
4
4
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
5
- //#region node_modules/.nitro/vite/services/ssr/assets/loopApi-Bez1rHV0.js
5
+ //#region node_modules/.nitro/vite/services/ssr/assets/loopApi-DK1lQPrx.js
6
6
  /**
7
7
  * Server functions backing the dashboard. They run server-side (createServerFn
8
8
  * → RPC) and call the IN-PROCESS scheduler + store directly — the scheduler
9
9
  * lives in this same TanStack process (booted on first call via ensureServer).
10
- * The machine-facing poll, CLI, report, and artifact-sync endpoints are sibling
11
- * server routes in this same process, so one process owns the single scheduler.
10
+ * The machine-facing poll, CLI, report, and run-file endpoints are sibling server
11
+ * routes in this same process, so one process owns the single scheduler.
12
12
  */
13
13
  function backend() {
14
14
  return ensureServer();
@@ -81,7 +81,7 @@ var getArtifacts_createServerFn_handler = createServerRpc({
81
81
  var getArtifacts = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifacts_createServerFn_handler, async ({ data }) => {
82
82
  await backend();
83
83
  if (!await findLoop(data.loopId)) return [];
84
- const { listLoopArtifacts } = await import("./artifactFiles-Di1xDKvX.mjs");
84
+ const { listLoopArtifacts } = await import("./artifactFiles-B0NPRDOR.mjs");
85
85
  return listLoopArtifacts(data.loopId);
86
86
  });
87
87
  var getArtifact_createServerFn_handler = createServerRpc({
@@ -92,7 +92,7 @@ var getArtifact_createServerFn_handler = createServerRpc({
92
92
  var getArtifact = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifact_createServerFn_handler, async ({ data }) => {
93
93
  await backend();
94
94
  if (!await findLoop(data.loopId)) return { error: "file not found" };
95
- const { readLoopArtifact } = await import("./artifactFiles-Di1xDKvX.mjs");
95
+ const { readLoopArtifact } = await import("./artifactFiles-B0NPRDOR.mjs");
96
96
  return readLoopArtifact(data.loopId, data.path);
97
97
  });
98
98
  var getRunDiff_createServerFn_handler = createServerRpc({
@@ -111,7 +111,7 @@ var getRunDiff = createServerFn({ method: "GET" }).validator((d) => d).handler(g
111
111
  hasSnapshot: false,
112
112
  files: []
113
113
  };
114
- const { computeRunDiff } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.i);
114
+ const { computeRunDiff } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.i);
115
115
  return computeRunDiff(data.runId);
116
116
  });
117
117
  var patchLoop_createServerFn_handler = createServerRpc({
@@ -171,7 +171,7 @@ var stopLoop = createServerFn({ method: "POST" }).validator((id) => id).handler(
171
171
  const found = await findLoop(id);
172
172
  if (!found) return { error: "not found" };
173
173
  if (await hasRunningRun(id)) {
174
- if ((await getMachine(found.loop.machineId))?.daemonProtocol !== 4) return { error: "Daemon upgrade required to stop a running process" };
174
+ if ((await getMachine(found.loop.machineId))?.daemonProtocol !== 5) return { error: "Daemon upgrade required to stop a running process" };
175
175
  }
176
176
  const stopped = await stopLoop$1(id);
177
177
  if (!stopped) return { error: "not found" };
@@ -192,12 +192,12 @@ var deleteLoop = createServerFn({ method: "POST" }).validator((id) => id).handle
192
192
  if (!found) return { error: "not found" };
193
193
  const machine = await getMachine(found.loop.machineId);
194
194
  const unreachable = machinePresence(machine?.online, machine?.lastSeen) !== "online";
195
- if (!unreachable && await hasRunningRun(id) && machine?.daemonProtocol !== 4) return { error: "Daemon upgrade required to stop a running process" };
195
+ if (!unreachable && await hasRunningRun(id) && machine?.daemonProtocol !== 5) return { error: "Daemon upgrade required to stop a running process" };
196
196
  scheduler.removeLoop(id);
197
197
  if (!await requestDeleteLoop(id)) return { error: "not found" };
198
198
  if (unreachable) {
199
199
  if (!await forceDeleteLoop(id)) return { error: "delete failed; server data was not deleted" };
200
- const { logger } = await import("./boot-CYRi-wez.mjs").then((n) => n.N).then((n) => n.Jt);
200
+ const { logger } = await import("./boot-DmpKtb0A.mjs").then((n) => n.M).then((n) => n.Qt);
201
201
  logger.child({ mod: "loop-lifecycle" }).warn({
202
202
  action: "unreachable-delete",
203
203
  loopId: id,
@@ -242,7 +242,7 @@ var stopRun = createServerFn({ method: "POST" }).validator((id) => id).handler(s
242
242
  const run = await getRun(id);
243
243
  const found = run ? await findLoop(run.loopId) : void 0;
244
244
  if (!run || !found) return { error: "run not found" };
245
- if (run.phase === "running" && (await getMachine(found.loop.machineId))?.daemonProtocol !== 4) return { error: "Daemon upgrade required to stop a running process" };
245
+ if (run.phase === "running" && (await getMachine(found.loop.machineId))?.daemonProtocol !== 5) return { error: "Daemon upgrade required to stop a running process" };
246
246
  const result = await requestRunCancel(run.loopId, id);
247
247
  return result ? {
248
248
  ok: true,
@@ -256,7 +256,7 @@ var mintConnectKey_createServerFn_handler = createServerRpc({
256
256
  }, (opts) => mintConnectKey.__executeServer(opts));
257
257
  var mintConnectKey = createServerFn({ method: "POST" }).handler(mintConnectKey_createServerFn_handler, async () => {
258
258
  await backend();
259
- const { mintDeviceToken, rememberConnectKey } = await import("./boot-CYRi-wez.mjs").then((n) => n.N).then((n) => n.g);
259
+ const { mintDeviceToken, rememberConnectKey } = await import("./boot-DmpKtb0A.mjs").then((n) => n.M).then((n) => n.g);
260
260
  const token = mintDeviceToken();
261
261
  await rememberConnectKey(token);
262
262
  return { token };
@@ -1,5 +1,5 @@
1
- import { B as reconciliationStatesForRuns, C as getMachine, E as hasRunningRun, I as machinePresence, T as hasPendingRun, _ as countRuns, a as canonicalLoopEnvelope, j as listRuns, n as MIN_DAEMON_VERSION, o as canonicalLoopSummary, s as daemonNeedsUpdate, z as recentLoopUsage } from "./boot-CYRi-wez.mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-esCjyZJg.js
1
+ import { A as listRuns, F as machinePresence, R as recentLoopUsage, S as getMachine, T as hasRunningRun, a as canonicalLoopSummary, g as countRuns, i as canonicalLoopEnvelope, o as daemonNeedsUpdate, t as MIN_DAEMON_VERSION, w as hasPendingRun, z as reconciliationStatesForRuns } from "./boot-DmpKtb0A.mjs";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-DdFivf7-.js
3
3
  var SUMMARY_RUNS = 18;
4
4
  var RECENT_USAGE_WINDOW_MS = 10080 * 60 * 1e3;
5
5
  /** Dashboard order: loops with runs first, most recent run first. The incoming
@@ -39,7 +39,8 @@ function toRunSummary(r, reconciliation) {
39
39
  } : null,
40
40
  error: r.error ?? null,
41
41
  sessionId: r.sessionId ?? null,
42
- reportIncident: r.reportIncident ?? null
42
+ reportIncident: r.reportIncident ?? null,
43
+ fileNotices: r.fileNotices ?? null
43
44
  };
44
45
  }
45
46
  async function toRunSummaries(loopId, rows) {
@@ -1,18 +1,17 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
- import { X as validateLoopTags, g as CODING_AGENTS } from "./boot-CYRi-wez.mjs";
2
+ import { J as validateLoopTags, h as CODING_AGENTS } from "./boot-DmpKtb0A.mjs";
3
3
  import { g as require_react, h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
4
4
  import { d as useNavigate, u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
5
- import { _ as runLoop, a as deriveLoopLifecycle, g as pauseLoop, h as patchLoop, i as deleteLoop, l as getLoopDetail, o as getArtifact, p as loadOlderRuns, r as daemonStopSupport, s as getArtifacts, t as DAEMON_UPGRADE_REQUIRED, v as startLoop, y as stopLoop } from "./lifecycleUi-BsbUsi5K.mjs";
5
+ import { _ as runLoop, a as deriveLoopLifecycle, g as pauseLoop, h as patchLoop, i as deleteLoop, l as getLoopDetail, o as getArtifact, p as loadOlderRuns, r as daemonStopSupport, s as getArtifacts, t as DAEMON_UPGRADE_REQUIRED, v as startLoop, y as stopLoop } from "./lifecycleUi-DEjM8Etm.mjs";
6
6
  import { A as tsShort, E as runPulseStyle, M as useFlash, O as sectionHeadCls, S as labelCls, _ as fmt, a as Loading, b as humanBytes, c as btn, d as btnQuiet, g as dur, i as LoadErrorCard, k as selectCls, l as btnDanger, m as dotLabel, n as ErrorBanner, p as dotColor, r as FlashLine, t as ConfirmBar, u as btnPrimary, w as rel, x as inputCls, y as focusRing } from "./actionUi-DE-Pb2tv.mjs";
7
- import { a as Timeline, l as mergeRuns, n as MachinesModal, t as LoopOverview } from "./MachinesModal-CmVMec6B.mjs";
8
- import { n as artifactKind, t as Route } from "./artifactKind-DqpmtxDU.mjs";
9
- import { t as purify } from "../_libs/dompurify.mjs";
10
- import { t as g } from "../_libs/marked.mjs";
11
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-KUezNY69.js
7
+ import { a as Timeline, l as mergeRuns, n as MachinesModal, t as LoopOverview } from "./MachinesModal-CLBZxZkq.mjs";
8
+ import { n as artifactKind, t as Route } from "./artifactKind-a2oxT2wQ.mjs";
9
+ import { t as MarkdownView } from "./MarkdownView-D1J7ImYF.mjs";
10
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-DVCvGSEc.js
12
11
  var import_react = /* @__PURE__ */ __toESM(require_react());
13
12
  var import_jsx_runtime = require_jsx_runtime();
14
- /** Download URL for one artifact. Each path segment is encoded independently so
15
- * nested artifact paths continue to match the catch-all route. */
13
+ /** Download URL for one loop file. Each path segment is encoded independently so
14
+ * the name continues to match the catch-all route. */
16
15
  function downloadHref(loopId, path) {
17
16
  const encodedPath = path.split("/").map(encodeURIComponent).join("/");
18
17
  return `/api/artifact/${encodeURIComponent(loopId)}/${encodedPath}`;
@@ -21,67 +20,6 @@ function inlineHref(loopId, path) {
21
20
  return `${downloadHref(loopId, path)}?view=inline`;
22
21
  }
23
22
  /**
24
- * Shared markdown → sanitized HTML pipeline: marked (GFM) → DOMPurify (an
25
- * allowlisted prose subset, NO script/handlers). ONE sanitizer config for every
26
- * artifact markdown surface so the allowlist cannot drift between viewers.
27
- * Render the output under
28
- * the `.artifact-markdown` styles.
29
- */
30
- var MD_SANITIZE = {
31
- ALLOWED_TAGS: [
32
- "h1",
33
- "h2",
34
- "h3",
35
- "h4",
36
- "h5",
37
- "h6",
38
- "p",
39
- "br",
40
- "hr",
41
- "strong",
42
- "b",
43
- "em",
44
- "i",
45
- "del",
46
- "s",
47
- "code",
48
- "pre",
49
- "blockquote",
50
- "ul",
51
- "ol",
52
- "li",
53
- "a",
54
- "span",
55
- "table",
56
- "thead",
57
- "tbody",
58
- "tr",
59
- "th",
60
- "td",
61
- "img"
62
- ],
63
- ALLOWED_ATTR: [
64
- "href",
65
- "title",
66
- "target",
67
- "rel",
68
- "src",
69
- "alt"
70
- ]
71
- };
72
- function renderMarkdown(content) {
73
- return purify.sanitize(g.parse(content, {
74
- async: false,
75
- gfm: true
76
- }), MD_SANITIZE);
77
- }
78
- function MarkdownView({ content }) {
79
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
80
- className: "artifact-markdown px-5 py-4",
81
- dangerouslySetInnerHTML: { __html: (0, import_react.useMemo)(() => renderMarkdown(content), [content]) }
82
- });
83
- }
84
- /**
85
23
  * Shared pieces of the artifact content viewer. The Files panel uses this one
86
24
  * dispatch so binary/oversize copy and type-appropriate rendering cannot drift.
87
25
  *
@@ -96,8 +34,8 @@ function MarkdownView({ content }) {
96
34
  * rides as an image, which does not execute embedded scripts).
97
35
  * - markdown → the shared markdown pipeline (Preview) with a raw Source toggle.
98
36
  * - text → monospace source.
99
- * Everything keeps a Download fallback; an oversize (metadata-only) artifact has
100
- * no synced bytes to render, so it says so rather than showing an empty pane.
37
+ * Everything keeps a Download fallback; a legacy oversize/binary entry has no
38
+ * renderable bytes, so it says so rather than showing an empty pane.
101
39
  */
102
40
  function ViewerHead({ path, meta, action }) {
103
41
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -276,7 +214,6 @@ function ArtifactBody({ loopId, file }) {
276
214
  kind
277
215
  }, file.path);
278
216
  }
279
- var basename = (path) => path.split("/").pop() || path;
280
217
  function LoopFilesPanel({ loopId, configuredPaths, running }) {
281
218
  const [artifacts, setArtifacts] = (0, import_react.useState)(null);
282
219
  const [selected, setSelected] = (0, import_react.useState)(null);
@@ -309,14 +246,19 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
309
246
  className: "mb-2.5 flex items-end justify-between gap-3 border-b border-hairline pb-1.5",
310
247
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", {
311
248
  className: "text-label font-semibold text-secondary",
312
- children: ["Artifacts", artifacts ? ` (${files.length})` : ""]
249
+ children: ["Files", artifacts ? ` (${files.length})` : ""]
313
250
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
314
251
  className: "text-caption font-medium text-disabled",
315
- children: ["Configured exact paths: ", configuredPaths.length]
252
+ children: [
253
+ "MEMORY.md + ",
254
+ configuredPaths.length,
255
+ " artifact",
256
+ configuredPaths.length === 1 ? "" : "s"
257
+ ]
316
258
  })]
317
259
  }), files.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
318
260
  className: "rounded-card border border-hairline bg-surface px-5 py-10 text-center text-body text-disabled shadow-card",
319
- children: artifacts == null ? "Loading…" : configuredPaths.length ? "No configured artifact has been collected yet." : "No artifact paths configured."
261
+ children: artifacts == null ? "Loading…" : "No loop file has been written yet. Runs create MEMORY.md and any configured artifacts."
320
262
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
321
263
  className: "grid h-[min(600px,68vh)] grid-cols-1 overflow-hidden rounded-card border border-hairline bg-surface shadow-card sm:grid-cols-[210px_1fr]",
322
264
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", {
@@ -329,17 +271,10 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
329
271
  type: "button",
330
272
  onClick: () => setSelected(file.path),
331
273
  className: `flex w-full items-center border-l-2 px-3 py-2 text-left transition-colors ${activeRow ? "border-display bg-raised" : "border-transparent hover:bg-raised/60"}`,
332
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
333
- className: "min-w-0 flex-1",
334
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
335
- className: `block truncate font-mono text-label ${activeRow ? "text-display" : "text-primary"}`,
336
- title: file.path,
337
- children: basename(file.path)
338
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
339
- className: "block truncate font-mono text-micro text-disabled",
340
- title: file.path,
341
- children: file.path
342
- })]
274
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
275
+ className: `block min-w-0 flex-1 truncate font-mono text-label ${activeRow ? "text-display" : "text-primary"}`,
276
+ title: file.path,
277
+ children: file.path
343
278
  })
344
279
  }) }, file.path);
345
280
  })
@@ -348,7 +283,7 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
348
283
  className: "min-w-0 overflow-y-auto",
349
284
  children: active && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewerHead, {
350
285
  path: active.path,
351
- meta: [active.size != null ? humanBytes(active.size) : "", `collected ${fmt(active.updatedAt)}`].filter(Boolean).join(" · ")
286
+ meta: [active.size != null ? humanBytes(active.size) : "", `updated ${fmt(active.updatedAt)}`].filter(Boolean).join(" · ")
352
287
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactBody, {
353
288
  loopId,
354
289
  file: active
@@ -528,7 +463,7 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
528
463
  noChange: form.noChange,
529
464
  block: form.block
530
465
  },
531
- artifacts: form.artifacts.split(/\r?\n/).filter((path) => path.length > 0)
466
+ artifacts: form.artifacts.split(/\r?\n/).filter((name) => name.length > 0)
532
467
  };
533
468
  } }));
534
469
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -756,10 +691,10 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
756
691
  }),
757
692
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, {
758
693
  title: "Artifacts",
759
- hint: "Optional exact paths relative to the working directory. One path per line; globs are not supported."
694
+ hint: "Optional .md or .html file names (up to 4, 2 MB each) that runs update and the dashboard renders. One name per line. Every loop also has MEMORY.md."
760
695
  }),
761
696
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextArea, {
762
- label: "Artifact paths",
697
+ label: "Artifact files",
763
698
  value: form.artifacts,
764
699
  onChange: (value) => set("artifacts", value),
765
700
  rows: 6,
@@ -1033,7 +968,7 @@ function LoopDetailView({ id }) {
1033
968
  className: "mt-3",
1034
969
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfirmBar, {
1035
970
  prompt: "Stop this loop and delete its server data?",
1036
- note: "This stops the loop and deletes server history and collected artifacts. Files in the working directory are not deleted. If the machine is unreachable, its local process may continue running.",
971
+ note: "This stops the loop and deletes server history, the loop memory, and artifacts. Files in the working directory are not deleted. If the machine is unreachable, its local process may continue running.",
1037
972
  cta: "Delete",
1038
973
  danger: true,
1039
974
  busy,
@@ -1144,13 +1079,13 @@ function LoopDetailView({ id }) {
1144
1079
  })
1145
1080
  }),
1146
1081
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Config, {
1147
- label: "Artifact paths",
1082
+ label: "Artifacts",
1148
1083
  children: loop.artifacts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
1149
1084
  className: "space-y-1",
1150
- children: loop.artifacts.map((path) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
1085
+ children: loop.artifacts.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
1151
1086
  className: "break-all font-mono",
1152
- children: path
1153
- }) }, path))
1087
+ children: name
1088
+ }) }, name))
1154
1089
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1155
1090
  className: "text-disabled",
1156
1091
  children: "None"
@@ -1205,7 +1140,7 @@ function EditHead({ name, onBack }) {
1205
1140
  }),
1206
1141
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1207
1142
  className: "mt-1.5 text-meta text-secondary",
1208
- children: "Edit tags, schedule, execution settings, prompt, status definitions, and exact artifact paths."
1143
+ children: "Edit tags, schedule, execution settings, prompt, status definitions, and artifact files."
1209
1144
  })
1210
1145
  ] });
1211
1146
  }
@@ -1285,8 +1220,8 @@ function RunsSection({ loopId, summary, runs, older, onMore, onPickRun }) {
1285
1220
  }
1286
1221
  /**
1287
1222
  * Loop detail page — `/loops/$loopId`: a loop header and action toolbar, the
1288
- * exact artifact Files panel, and the runs timeline. The view owns its own
1289
- * data + self-poll. Run rows link on to `/loops/$loopId/runs/$runId`.
1223
+ * loop Files panel, and the runs timeline. The view owns its own data + self-poll.
1224
+ * Run rows link on to `/loops/$loopId/runs/$runId`.
1290
1225
  */
1291
1226
  function LoopDetailPage() {
1292
1227
  const { loopId } = Route.useParams();
@@ -1,10 +1,11 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
2
  import { g as require_react, h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { b as stopRun, l as getLoopDetail, p as loadOlderRuns, u as getRunDiff } from "./lifecycleUi-BsbUsi5K.mjs";
4
+ import { b as stopRun, l as getLoopDetail, p as loadOlderRuns, u as getRunDiff } from "./lifecycleUi-DEjM8Etm.mjs";
5
5
  import { E as runPulseStyle, O as sectionHeadCls, _ as fmt, a as Loading, b as humanBytes, c as btn, g as dur, i as LoadErrorCard, l as btnDanger, o as Pill, s as StatusPill, v as fnum } from "./actionUi-DE-Pb2tv.mjs";
6
- import { t as Route } from "./loops._loopId_.runs._runId-BFP9oMrp.mjs";
7
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-DOkUEesG.js
6
+ import { t as MarkdownView } from "./MarkdownView-D1J7ImYF.mjs";
7
+ import { t as Route } from "./loops._loopId_.runs._runId-Dr6E-FA5.mjs";
8
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BXbTfLlC.js
8
9
  var import_react = /* @__PURE__ */ __toESM(require_react());
9
10
  var import_jsx_runtime = require_jsx_runtime();
10
11
  /**
@@ -462,8 +463,8 @@ function RunDetailView({ loopId, runId }) {
462
463
  type: "button",
463
464
  onClick: onStop,
464
465
  className: btnDanger,
465
- disabled: run.phase === "running" && detail.machine.daemonProtocol !== 4,
466
- title: run.phase === "running" && detail.machine.daemonProtocol !== 4 ? "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`." : void 0,
466
+ disabled: run.phase === "running" && detail.machine.daemonProtocol !== 5,
467
+ title: run.phase === "running" && detail.machine.daemonProtocol !== 5 ? "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`." : void 0,
467
468
  children: "Stop run"
468
469
  })]
469
470
  })]
@@ -527,15 +528,25 @@ function RunDetailView({ loopId, runId }) {
527
528
  ]
528
529
  })
529
530
  }),
531
+ run.fileNotices && run.fileNotices.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
532
+ label: "Loop files not kept",
533
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
534
+ className: "list-disc space-y-1.5 pl-5 text-body text-primary",
535
+ children: run.fileNotices.map((notice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
536
+ className: "break-words",
537
+ children: notice
538
+ }, notice))
539
+ })
540
+ }),
530
541
  run.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
531
542
  label: "Report",
532
543
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
533
- className: "whitespace-pre-wrap break-words rounded-control border border-hairline bg-raised px-4 py-3.5 text-body leading-relaxed text-primary",
534
- children: run.message
544
+ className: "break-words rounded-control border border-hairline bg-raised text-body leading-relaxed text-primary",
545
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownView, { content: run.message })
535
546
  })
536
547
  }),
537
548
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Changes, { run }),
538
- run.finalText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
549
+ run.finalText && !run.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
539
550
  label: "Final output",
540
551
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
541
552
  className: "whitespace-pre-wrap break-words text-body text-primary",
@@ -592,7 +603,7 @@ function RunDetailView({ loopId, runId }) {
592
603
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
593
604
  * gets its own full surface, deep-linkable + browser-back friendly) rather than a
594
605
  * modal or an inline panel. It resolves the run from the loop's detail payload
595
- * (reusing getLoopDetail) and the existing artifact diff.
606
+ * (reusing getLoopDetail) and the existing loop-file diff.
596
607
  */
597
608
  function RunDetailPage() {
598
609
  const { loopId, runId } = Route.useParams();
@@ -1,12 +1,12 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BFP9oMrp.js
3
- var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-DOkUEesG.mjs");
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-Dr6E-FA5.js
3
+ var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-BXbTfLlC.mjs");
4
4
  /**
5
5
  * Run detail PAGE — `/loops/$loopId/runs/$runId`. A standalone page (the trailing
6
6
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
7
7
  * gets its own full surface, deep-linkable + browser-back friendly) rather than a
8
8
  * modal or an inline panel. It resolves the run from the loop's detail payload
9
- * (reusing getLoopDetail) and the existing artifact diff.
9
+ * (reusing getLoopDetail) and the existing loop-file diff.
10
10
  */
11
11
  var Route = createFileRoute("/loops/$loopId_/runs/$runId")({
12
12
  ssr: false,
@@ -1,7 +1,7 @@
1
- import { A as listMachines$1, C as getMachine, F as machineIdFromToken, L as mintDeviceToken, P as loopsForMachine, W as sha256, Y as updateMachine, b as forceDeleteMachine, c as ensureServer, n as MIN_DAEMON_VERSION, s as daemonNeedsUpdate, v as createMachine$1 } from "./boot-CYRi-wez.mjs";
1
+ import { H as sha256, I as mintDeviceToken, N as loopsForMachine, P as machineIdFromToken, S as getMachine, _ as createMachine$1, k as listMachines$1, o as daemonNeedsUpdate, q as updateMachine, s as ensureServer, t as MIN_DAEMON_VERSION, y as forceDeleteMachine } from "./boot-DmpKtb0A.mjs";
2
2
  import { r as createServerFn } from "./ssr.mjs";
3
3
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-CniGcY8s.js
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-DLRHgDdS.js
5
5
  /**
6
6
  * Latest published @kky42/pievo version — a cached, fail-silent lookup of the
7
7
  * npm dist-tag `latest`, so the web can tell a user their daemon is outdated and