@kky42/pievo-server 4.2.0 → 5.0.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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{MarkdownView-C9-AzIkl.js → MarkdownView-T28pBEgh.js} +1 -1
- package/.output/public/assets/{actionUi-D47jwMfh.js → actionUi-CY207nNM.js} +1 -1
- package/.output/public/assets/app-Cye1TgwS.css +2 -0
- package/.output/public/assets/index-BxXrF3gW.js +10 -0
- package/.output/public/assets/loops._loopId-DuSEcu38.js +2 -0
- package/.output/public/assets/{loops._loopId_.runs._runId-DW2HPjtj.js → loops._loopId_.runs._runId-vjQaPXEe.js} +2 -2
- package/.output/public/assets/{preload-helper-CbYqBpY9.js → preload-helper-B7p1iHUP.js} +2 -2
- package/.output/public/assets/routes-BUMEAWuS.js +1 -0
- package/.output/public/assets/routes-BrqOpUEi.js +1 -0
- package/.output/server/_ssr/{MachinesModal-DwHy2cis.mjs → MachinesModal-CB8rpDG2.mjs} +24 -12
- package/.output/server/_ssr/{actionUi-CszQXDSN.mjs → actionUi-CPYgZIr7.mjs} +40 -62
- package/.output/server/_ssr/{artifactFiles-DJiK99sF.mjs → artifactFiles-DLfSRhrV.mjs} +3 -3
- package/.output/server/_ssr/{artifactKind-DYMU0q3N.mjs → artifactKind-C0oXLA23.mjs} +2 -2
- package/.output/server/_ssr/{boot-CecCDRoH.mjs → boot-jtVCf_pN.mjs} +27 -91
- package/.output/server/_ssr/{loopApi-uEiBQ5YX.mjs → loopApi-0nR35Kh2.mjs} +8 -8
- package/.output/server/_ssr/{loopProjection-Bo54jjGd.mjs → loopProjection-ZNfSmA5e.mjs} +2 -2
- package/.output/server/_ssr/{loops._loopId-c1oRP7cr.mjs → loops._loopId-CcJ3rxCU.mjs} +9 -65
- package/.output/server/_ssr/{loops._loopId_.runs._runId-czMVvNsk.mjs → loops._loopId_.runs._runId-DFlrV3dJ.mjs} +2 -2
- package/.output/server/_ssr/{loops._loopId_.runs._runId-Dl0SwOlC.mjs → loops._loopId_.runs._runId-XkGF7HKo.mjs} +4 -4
- package/.output/server/_ssr/{machineFns-aw9xe5oz.mjs → machineFns-D-97a_hz.mjs} +2 -2
- package/.output/server/_ssr/{router-3OQ849sN.mjs → router-CZ9CFWS2.mjs} +15 -15
- package/.output/server/_ssr/{routes-CL4y6AEv.mjs → routes-BBmuVs6n.mjs} +2 -2
- package/.output/server/_ssr/{routes-nU2PDK3W.mjs → routes-JxTh5mbj.mjs} +2 -2
- package/.output/server/_ssr/{routes-CT5IdoiP.mjs → routes-fHRtSpkH.mjs} +5 -5
- package/.output/server/_ssr/ssr.mjs +22 -22
- package/.output/server/{_tanstack-start-manifest_v-_nMS2ozp.mjs → _tanstack-start-manifest_v-C9JsxlwC.mjs} +8 -8
- package/.output/server/drizzle/0003_run_status_ok.sql +13 -0
- package/.output/server/drizzle/meta/_journal.json +7 -0
- package/.output/server/index.mjs +47 -47
- package/drizzle/0003_run_status_ok.sql +13 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/.output/public/assets/app-C5xfpeJe.css +0 -2
- package/.output/public/assets/index-BgNjFXWE.js +0 -10
- package/.output/public/assets/loops._loopId-Bo63FXPQ.js +0 -2
- package/.output/public/assets/routes-C88oSApK.js +0 -1
- package/.output/public/assets/routes-CTCh8xYm.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
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
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/actionUi-
|
|
3
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/actionUi-CPYgZIr7.js
|
|
4
4
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
5
5
|
var import_jsx_runtime = require_jsx_runtime();
|
|
6
6
|
var fmt = (t) => t ? new Date(t).toLocaleString() : "—";
|
|
@@ -78,13 +78,9 @@ function humanBytes(n) {
|
|
|
78
78
|
return `${(abs / (1024 * 1024)).toFixed(1)} MB`;
|
|
79
79
|
}
|
|
80
80
|
var ST = {
|
|
81
|
-
|
|
82
|
-
c: "var(--color-run-
|
|
83
|
-
label: "
|
|
84
|
-
},
|
|
85
|
-
"no-change": {
|
|
86
|
-
c: "var(--color-run-no-change)",
|
|
87
|
-
label: "No change"
|
|
81
|
+
ok: {
|
|
82
|
+
c: "var(--color-run-ok)",
|
|
83
|
+
label: "OK"
|
|
88
84
|
},
|
|
89
85
|
block: {
|
|
90
86
|
c: "var(--color-run-block)",
|
|
@@ -94,10 +90,6 @@ var ST = {
|
|
|
94
90
|
c: "var(--color-run-error)",
|
|
95
91
|
label: "Error"
|
|
96
92
|
},
|
|
97
|
-
warning: {
|
|
98
|
-
c: "var(--color-run-warning)",
|
|
99
|
-
label: "Missing status"
|
|
100
|
-
},
|
|
101
93
|
canceled: {
|
|
102
94
|
c: "var(--color-run-canceled)",
|
|
103
95
|
label: "Canceled"
|
|
@@ -107,7 +99,7 @@ var ST = {
|
|
|
107
99
|
label: "Queued"
|
|
108
100
|
},
|
|
109
101
|
reconciling: {
|
|
110
|
-
c: "var(--color-run-
|
|
102
|
+
c: "var(--color-run-reconciling)",
|
|
111
103
|
label: "Reconciling…"
|
|
112
104
|
},
|
|
113
105
|
active: {
|
|
@@ -115,48 +107,45 @@ var ST = {
|
|
|
115
107
|
label: "Running…"
|
|
116
108
|
}
|
|
117
109
|
};
|
|
118
|
-
|
|
110
|
+
/** The run's ink, for text and borders. Fills use `runSwatch`. */
|
|
119
111
|
function dotColor(r) {
|
|
120
112
|
if (r.reconciliation) return ST.reconciling.c;
|
|
121
113
|
if (r.phase === "running") return ST.active.c;
|
|
122
114
|
if (r.phase === "error") return ST.error.c;
|
|
123
|
-
if (r.status === "block") return ST.block.c;
|
|
124
115
|
if (r.phase === "pending") return ST.queued.c;
|
|
125
116
|
if (r.phase === "canceled") return ST.canceled.c;
|
|
126
|
-
|
|
127
|
-
return (statusMeta(r.status) ?? ST["no-change"]).c;
|
|
117
|
+
return r.status === "block" ? ST.block.c : ST.ok.c;
|
|
128
118
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (r.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
var runPulseAnim = { animation: "runPulse 1.1s ease-in-out infinite" };
|
|
120
|
+
/** Fill, outline, and motion for a run block or dot. */
|
|
121
|
+
function runSwatch(r) {
|
|
122
|
+
const ink = dotColor(r);
|
|
123
|
+
if (r.reconciliation) return {
|
|
124
|
+
outline: `1.5px dashed ${ink}`,
|
|
125
|
+
outlineOffset: "-1.5px",
|
|
126
|
+
animation: "runWait 2.4s ease-in-out infinite"
|
|
127
|
+
};
|
|
128
|
+
if (r.phase === "running") return {
|
|
129
|
+
background: ink,
|
|
130
|
+
...runPulseAnim
|
|
131
|
+
};
|
|
132
|
+
if (r.phase === "pending") return { boxShadow: `inset 0 0 0 1.5px ${ink}` };
|
|
133
|
+
if (r.phase === "canceled") return {
|
|
134
|
+
background: ink,
|
|
135
|
+
opacity: .5
|
|
136
|
+
};
|
|
137
|
+
if (r.phase === "error") return { background: `repeating-linear-gradient(135deg, ${ink} 0 3px, var(--color-run-error-stripe) 3px 6px)` };
|
|
138
|
+
return { background: ink };
|
|
137
139
|
}
|
|
138
140
|
function dotLabel(r) {
|
|
139
141
|
if (r.reconciliation === "blocking") return "Waiting for machine recovery";
|
|
140
142
|
if (r.reconciliation === "report-only") return "Awaiting late report";
|
|
141
143
|
if (r.phase === "pending") return ST.queued.label;
|
|
142
|
-
if (r.phase === "running")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
if (r.phase === "error") {
|
|
147
|
-
if (r.cancelRequested) return "Failed while stopping";
|
|
148
|
-
return ST.error.label;
|
|
149
|
-
}
|
|
144
|
+
if (r.phase === "running") return r.cancelRequested ? "Stopping…" : ST.active.label;
|
|
145
|
+
if (r.phase === "error") return r.cancelRequested ? "Failed while stopping" : ST.error.label;
|
|
146
|
+
if (r.phase === "canceled") return ST.canceled.label;
|
|
150
147
|
if (r.status === "block") return ST.block.label;
|
|
151
|
-
|
|
152
|
-
if (r.error === "stopped by user") return "Canceled";
|
|
153
|
-
return "Canceled";
|
|
154
|
-
}
|
|
155
|
-
if (r.cancelRequested && r.phase === "done") return "Succeeded while stopping";
|
|
156
|
-
if (r.status === "keep") return ST.keep.label;
|
|
157
|
-
if (r.status === "no-change") return ST["no-change"].label;
|
|
158
|
-
if (r.phase === "done") return ST.warning.label;
|
|
159
|
-
return ST["no-change"].label;
|
|
148
|
+
return r.cancelRequested ? "Succeeded while stopping" : ST.ok.label;
|
|
160
149
|
}
|
|
161
150
|
/** The newest finished run, whose attention reason the loop card surfaces until a
|
|
162
151
|
* later run finishes without one. */
|
|
@@ -178,14 +167,10 @@ var sectionHeadCls = "text-label font-semibold text-secondary";
|
|
|
178
167
|
var labelCls = "mb-1.5 mt-3 block text-label font-medium text-secondary";
|
|
179
168
|
var inputCls = `w-full rounded-control border border-wire bg-surface px-3 py-2.5 text-sm text-primary outline-none transition-shadow focus:border-transparent focus:shadow-focus`;
|
|
180
169
|
var selectCls = `${inputCls} lp-select cursor-pointer`;
|
|
181
|
-
/**
|
|
182
|
-
*
|
|
183
|
-
* keyframe (app.css). Shared so the in-flight timeline block and the Running
|
|
184
|
-
* badge stay in lockstep. Spread into a `style` prop.
|
|
185
|
-
*/
|
|
186
|
-
var runPulseAnim = { animation: "runPulse 1.1s ease-in-out infinite" };
|
|
170
|
+
/** The live "running" indicator outside the timeline: the same ink fill and
|
|
171
|
+
* `runPulse` breath as the in-flight run block. Spread into a `style` prop. */
|
|
187
172
|
var runPulseStyle = {
|
|
188
|
-
background: "var(--color-
|
|
173
|
+
background: "var(--color-run-active)",
|
|
189
174
|
...runPulseAnim
|
|
190
175
|
};
|
|
191
176
|
function ErrorBanner({ message, onDismiss, className = "mb-2" }) {
|
|
@@ -210,20 +195,16 @@ var PILL_TONES = {
|
|
|
210
195
|
neutral: "bg-raised text-secondary",
|
|
211
196
|
ink: "bg-raised text-primary",
|
|
212
197
|
outline: "border border-hairline text-secondary",
|
|
213
|
-
|
|
214
|
-
attention: "bg-running-soft text-running",
|
|
198
|
+
attention: "bg-attention-soft text-attention",
|
|
215
199
|
success: "bg-success-soft text-success",
|
|
216
200
|
accent: "bg-accent-soft text-accent",
|
|
217
201
|
warn: "bg-warn-soft text-warn"
|
|
218
202
|
};
|
|
219
|
-
function Pill({ tone = "neutral",
|
|
220
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
203
|
+
function Pill({ tone = "neutral", title, className = "", children }) {
|
|
204
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
221
205
|
title,
|
|
222
206
|
className: `inline-flex h-[22px] items-center gap-1.5 rounded-full px-2.5 text-caption font-medium ${PILL_TONES[tone]} ${className}`,
|
|
223
|
-
children
|
|
224
|
-
className: "size-1.5 rounded-full",
|
|
225
|
-
style: dot === "pulse" ? runPulseStyle : { background: "var(--color-rubik-green)" }
|
|
226
|
-
}), children]
|
|
207
|
+
children
|
|
227
208
|
});
|
|
228
209
|
}
|
|
229
210
|
function Loading({ className = "" }) {
|
|
@@ -252,10 +233,7 @@ function StatusPill({ run, colorText }) {
|
|
|
252
233
|
style: colorText ? { color: dotColor(run) } : void 0,
|
|
253
234
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
254
235
|
className: "size-2 rounded-full",
|
|
255
|
-
style:
|
|
256
|
-
background: dotColor(run),
|
|
257
|
-
opacity: dotOpacity(run)
|
|
258
|
-
}
|
|
236
|
+
style: runSwatch(run)
|
|
259
237
|
}), dotLabel(run)]
|
|
260
238
|
});
|
|
261
239
|
}
|
|
@@ -371,4 +349,4 @@ function useFlash() {
|
|
|
371
349
|
return [flash, setFlash];
|
|
372
350
|
}
|
|
373
351
|
//#endregion
|
|
374
|
-
export {
|
|
352
|
+
export { tsShort as A, lastRunOf as C, scheduleText as D, runSwatch as E, useFlash as M, useHydrated as N, sectionHeadCls as O, lastFinishedRun as S, runPulseStyle as T, fnum as _, LoadErrorCard as a, inputCls as b, StatusPill as c, btnPrimary as d, btnQuiet as f, fmt as g, dur as h, FlashLine as i, until as j, selectCls as k, btn as l, dotLabel as m, ConfirmBar as n, Loading as o, btnSm as p, ErrorBanner as r, Pill as s, AttentionMark as t, btnDanger as u, focusRing as v, rel as w, labelCls as x, humanBytes as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as listArtifacts, M as loopFileNames, b as getArtifactFile, c as getLoopFiles, x as getLoop } from "./boot-
|
|
2
|
-
import { n as toArtifactSummary } from "./loopProjection-
|
|
3
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/artifactFiles-
|
|
1
|
+
import { D as listArtifacts, M as loopFileNames, b as getArtifactFile, c as getLoopFiles, x as getLoop } from "./boot-jtVCf_pN.mjs";
|
|
2
|
+
import { n as toArtifactSummary } from "./loopProjection-ZNfSmA5e.mjs";
|
|
3
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/artifactFiles-DLfSRhrV.js
|
|
4
4
|
/** Read-only access to a loop's current files, shared by the dashboard server
|
|
5
5
|
* functions and the download route. Bytes are decoded as text, never interpreted. */
|
|
6
6
|
async function listLoopArtifacts(loopId) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
2
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/artifactKind-
|
|
3
|
-
var $$splitComponentImporter = () => import("./loops._loopId-
|
|
2
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/artifactKind-C0oXLA23.js
|
|
3
|
+
var $$splitComponentImporter = () => import("./loops._loopId-CcJ3rxCU.mjs");
|
|
4
4
|
var Route = createFileRoute("/loops/$loopId")({
|
|
5
5
|
ssr: false,
|
|
6
6
|
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
@@ -11,8 +11,8 @@ import { fileURLToPath } from "node:url";
|
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import os from "node:os";
|
|
13
13
|
import fs$1 from "node:fs/promises";
|
|
14
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/loopTags-
|
|
15
|
-
var
|
|
14
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/loopTags-CI_aMcqh.js
|
|
15
|
+
var loopTags_CI_aMcqh_exports = /* @__PURE__ */ __exportAll$1({
|
|
16
16
|
$: () => loopIdsWithSnapshots,
|
|
17
17
|
$t: () => logger_exports,
|
|
18
18
|
A: () => enqueueRun,
|
|
@@ -217,9 +217,6 @@ var loops = pgTable("loops", {
|
|
|
217
217
|
name: text("name").notNull(),
|
|
218
218
|
tags: text("tags").array().notNull().default(sql`ARRAY[]::text[]`),
|
|
219
219
|
prompt: text("prompt").notNull(),
|
|
220
|
-
statusKeep: text("status_keep").notNull(),
|
|
221
|
-
statusNoChange: text("status_no_change").notNull(),
|
|
222
|
-
statusBlock: text("status_block").notNull(),
|
|
223
220
|
/** Configured artifact names, in addition to the always-present MEMORY.md.
|
|
224
221
|
* See `gateway/loopFiles.ts`. */
|
|
225
222
|
artifacts: jsonb("artifacts").$type().notNull().default([]),
|
|
@@ -283,11 +280,7 @@ var runs = pgTable("runs", {
|
|
|
283
280
|
ts: text("ts").notNull(),
|
|
284
281
|
createdAt: text("created_at").notNull().default(sql`CURRENT_TIMESTAMP::text`),
|
|
285
282
|
updatedAt: text("updated_at").notNull().default(sql`CURRENT_TIMESTAMP::text`),
|
|
286
|
-
status: text("status", { enum: [
|
|
287
|
-
"keep",
|
|
288
|
-
"no-change",
|
|
289
|
-
"block"
|
|
290
|
-
] }),
|
|
283
|
+
status: text("status", { enum: ["ok", "block"] }),
|
|
291
284
|
message: text("message"),
|
|
292
285
|
/** Owner-facing reason from the final response's ATTENTION line. Independent
|
|
293
286
|
* of status: it never pauses the loop. */
|
|
@@ -321,7 +314,8 @@ var runs = pgTable("runs", {
|
|
|
321
314
|
check("runs_agent_check", sql`${t.agent} IS NULL OR ${t.agent} IN ('claude-code', 'codex', 'pi')`),
|
|
322
315
|
check("runs_phase_check", sql`${t.phase} IN ('pending', 'running', 'done', 'error', 'canceled')`),
|
|
323
316
|
check("runs_requested_by_check", sql`${t.requestedBy} IN ('owner', 'system')`),
|
|
324
|
-
check("runs_status_check", sql`${t.status} IS NULL OR ${t.status} IN ('
|
|
317
|
+
check("runs_status_check", sql`${t.status} IS NULL OR ${t.status} IN ('ok', 'block')`),
|
|
318
|
+
check("runs_done_status_check", sql`(${t.phase} = 'done') = (${t.status} IS NOT NULL)`)
|
|
325
319
|
]);
|
|
326
320
|
var runLeases = pgTable("run_leases", {
|
|
327
321
|
tokenHash: text("token_hash").primaryKey(),
|
|
@@ -2268,8 +2262,8 @@ function validateLoopTags(value) {
|
|
|
2268
2262
|
};
|
|
2269
2263
|
}
|
|
2270
2264
|
//#endregion
|
|
2271
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/boot-
|
|
2272
|
-
var
|
|
2265
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/boot-jtVCf_pN.js
|
|
2266
|
+
var boot_jtVCf_pN_exports = /* @__PURE__ */ __exportAll$1({
|
|
2273
2267
|
_: () => validOptionalPollString,
|
|
2274
2268
|
a: () => POLL_HTTP_BODY_FIELDS,
|
|
2275
2269
|
c: () => MIN_DAEMON_VERSION,
|
|
@@ -2372,11 +2366,11 @@ function clipText(s, cap) {
|
|
|
2372
2366
|
/** Literal placeholder the daemon replaces with the run's absolute loop-file
|
|
2373
2367
|
* directory before launch (and exports as an env var of the same name). */
|
|
2374
2368
|
var LOOP_DIR_PLACEHOLDER = "$PIEVO_LOOP_DIR";
|
|
2375
|
-
var STATUS_LINE_RE = /^STATUS:\s*(
|
|
2369
|
+
var STATUS_LINE_RE = /^STATUS:\s*(ok|block)\.?$/i;
|
|
2376
2370
|
var ATTENTION_LINE_RE = /^ATTENTION:\s*(.*)$/i;
|
|
2377
2371
|
/**
|
|
2378
|
-
* Deliver the user's prompt unchanged, followed by the
|
|
2379
|
-
*
|
|
2372
|
+
* Deliver the user's prompt unchanged, followed by the loop-file contract and the
|
|
2373
|
+
* final-line status contract. The run index
|
|
2380
2374
|
* intentionally remains an internal history fact.
|
|
2381
2375
|
*/
|
|
2382
2376
|
function buildRunTask(loop, previousNotices = []) {
|
|
@@ -2384,12 +2378,6 @@ function buildRunTask(loop, previousNotices = []) {
|
|
|
2384
2378
|
const notices = previousNotices.length ? `\n\nNotices from the previous run:\n${previousNotices.map((notice) => `- ${notice}`).join("\n")}` : "";
|
|
2385
2379
|
return `${loop.prompt}
|
|
2386
2380
|
|
|
2387
|
-
Status definitions:
|
|
2388
|
-
- keep: ${loop.statusKeep}
|
|
2389
|
-
- no-change: ${loop.statusNoChange}
|
|
2390
|
-
- block: ${loop.statusBlock}
|
|
2391
|
-
A block pauses the loop until the owner restarts it.
|
|
2392
|
-
|
|
2393
2381
|
Loop files in ${LOOP_DIR_PLACEHOLDER}:
|
|
2394
2382
|
${files.join("\n")}
|
|
2395
2383
|
A file over its limit is rejected and its previous version kept. Other files in that directory are discarded.${notices}
|
|
@@ -2397,7 +2385,9 @@ A file over its limit is rejected and its previous version kept. Other files in
|
|
|
2397
2385
|
Be concise in everything you write: lead with what changed or needs action; prefer tables and short lists; no filler or restated instructions.
|
|
2398
2386
|
|
|
2399
2387
|
Your final response is only: a summary of at most five short lines (no headings, no repeated tables or file contents); if the owner should look at something, one line \`ATTENTION: <reason>\`; then exactly one last line:
|
|
2400
|
-
STATUS:
|
|
2388
|
+
STATUS: ok|block
|
|
2389
|
+
- ok: the loop keeps running on schedule. Findings and alerts are ok; flag them with ATTENTION.
|
|
2390
|
+
- block: the loop must stop until the owner acts, because the task says to stop or the next run cannot proceed safely (missing access, broken environment, unsafe next step). Block pauses the loop.`;
|
|
2401
2391
|
}
|
|
2402
2392
|
var bareLine = (line) => line.trim().replace(/^[*_`>\s]+|[*_`\s]+$/g, "");
|
|
2403
2393
|
/**
|
|
@@ -2640,7 +2630,6 @@ var LOOP_CONFIG_FIELDS = [
|
|
|
2640
2630
|
"model",
|
|
2641
2631
|
"reasoningEffort",
|
|
2642
2632
|
"prompt",
|
|
2643
|
-
"statusDefinitions",
|
|
2644
2633
|
"artifacts",
|
|
2645
2634
|
"enabled"
|
|
2646
2635
|
];
|
|
@@ -2816,36 +2805,6 @@ function validateSchedule(value) {
|
|
|
2816
2805
|
detail: "schedule.mode must be cron|continuous"
|
|
2817
2806
|
};
|
|
2818
2807
|
}
|
|
2819
|
-
function validateStatusDefinitions(value) {
|
|
2820
|
-
const raw = object(value);
|
|
2821
|
-
if (!raw) return {
|
|
2822
|
-
ok: false,
|
|
2823
|
-
detail: "statusDefinitions is required"
|
|
2824
|
-
};
|
|
2825
|
-
const unknown = Object.keys(raw).filter((key) => ![
|
|
2826
|
-
"keep",
|
|
2827
|
-
"noChange",
|
|
2828
|
-
"block"
|
|
2829
|
-
].includes(key));
|
|
2830
|
-
if (unknown.length) return {
|
|
2831
|
-
ok: false,
|
|
2832
|
-
detail: `statusDefinitions has unknown field(s): ${unknown.join(", ")}`
|
|
2833
|
-
};
|
|
2834
|
-
const keep = requiredText(raw.keep, "statusDefinitions.keep", true);
|
|
2835
|
-
if (!keep.ok) return keep;
|
|
2836
|
-
const noChange = requiredText(raw.noChange, "statusDefinitions.noChange", true);
|
|
2837
|
-
if (!noChange.ok) return noChange;
|
|
2838
|
-
const block = requiredText(raw.block, "statusDefinitions.block", true);
|
|
2839
|
-
if (!block.ok) return block;
|
|
2840
|
-
return {
|
|
2841
|
-
ok: true,
|
|
2842
|
-
value: {
|
|
2843
|
-
keep: keep.value,
|
|
2844
|
-
noChange: noChange.value,
|
|
2845
|
-
block: block.value
|
|
2846
|
-
}
|
|
2847
|
-
};
|
|
2848
|
-
}
|
|
2849
2808
|
function validateArtifactNames(value) {
|
|
2850
2809
|
if (value === void 0 || value === null) return {
|
|
2851
2810
|
ok: true,
|
|
@@ -2910,18 +2869,10 @@ function scheduleFromLoop(loop) {
|
|
|
2910
2869
|
}
|
|
2911
2870
|
throw new Error(`invariant: loop ${loop.id} has unknown schedule mode: ${String(loop.scheduleMode)}`);
|
|
2912
2871
|
}
|
|
2913
|
-
function statusDefinitionsFromLoop(loop) {
|
|
2914
|
-
return {
|
|
2915
|
-
keep: loop.statusKeep,
|
|
2916
|
-
noChange: loop.statusNoChange,
|
|
2917
|
-
block: loop.statusBlock
|
|
2918
|
-
};
|
|
2919
|
-
}
|
|
2920
2872
|
function canonicalLoopEnvelope(loop) {
|
|
2921
2873
|
return {
|
|
2922
2874
|
...canonicalLoopSummary(loop),
|
|
2923
2875
|
prompt: loop.prompt,
|
|
2924
|
-
statusDefinitions: statusDefinitionsFromLoop(loop),
|
|
2925
2876
|
artifacts: loop.artifacts,
|
|
2926
2877
|
enabled: loop.enabled
|
|
2927
2878
|
};
|
|
@@ -2959,8 +2910,6 @@ function validateLoopCreate(value) {
|
|
|
2959
2910
|
if (!prompt.ok) return prompt;
|
|
2960
2911
|
const schedule = validateSchedule(raw.schedule);
|
|
2961
2912
|
if (!schedule.ok) return schedule;
|
|
2962
|
-
const statusDefinitions = validateStatusDefinitions(raw.statusDefinitions);
|
|
2963
|
-
if (!statusDefinitions.ok) return statusDefinitions;
|
|
2964
2913
|
const artifacts = validateArtifactNames(raw.artifacts);
|
|
2965
2914
|
if (!artifacts.ok) return artifacts;
|
|
2966
2915
|
const model = optionalProvider(raw.model, "model");
|
|
@@ -2985,7 +2934,6 @@ function validateLoopCreate(value) {
|
|
|
2985
2934
|
model: model.value,
|
|
2986
2935
|
reasoningEffort: reasoningEffort.value,
|
|
2987
2936
|
prompt: prompt.value,
|
|
2988
|
-
statusDefinitions: statusDefinitions.value,
|
|
2989
2937
|
artifacts: artifacts.value,
|
|
2990
2938
|
enabled: raw.enabled !== false
|
|
2991
2939
|
};
|
|
@@ -3002,9 +2950,6 @@ function validateLoopCreate(value) {
|
|
|
3002
2950
|
model: config.model,
|
|
3003
2951
|
reasoningEffort: config.reasoningEffort,
|
|
3004
2952
|
prompt: config.prompt,
|
|
3005
|
-
statusKeep: config.statusDefinitions.keep,
|
|
3006
|
-
statusNoChange: config.statusDefinitions.noChange,
|
|
3007
|
-
statusBlock: config.statusDefinitions.block,
|
|
3008
2953
|
artifacts: config.artifacts,
|
|
3009
2954
|
enabled: config.enabled
|
|
3010
2955
|
}
|
|
@@ -3048,13 +2993,6 @@ function validateLoopEdit(loop, value) {
|
|
|
3048
2993
|
if (!result.ok) return result;
|
|
3049
2994
|
Object.assign(update, rowSchedule(result.value, loop.cron));
|
|
3050
2995
|
}
|
|
3051
|
-
if (raw.statusDefinitions !== void 0) {
|
|
3052
|
-
const result = validateStatusDefinitions(raw.statusDefinitions);
|
|
3053
|
-
if (!result.ok) return result;
|
|
3054
|
-
update.statusKeep = result.value.keep;
|
|
3055
|
-
update.statusNoChange = result.value.noChange;
|
|
3056
|
-
update.statusBlock = result.value.block;
|
|
3057
|
-
}
|
|
3058
2996
|
if (raw.artifacts !== void 0) {
|
|
3059
2997
|
const result = validateArtifactNames(raw.artifacts);
|
|
3060
2998
|
if (!result.ok) return result;
|
|
@@ -3284,7 +3222,7 @@ function incidentDiagnosis(code, issues, reportId, payloadDigest) {
|
|
|
3284
3222
|
recommendedAction
|
|
3285
3223
|
};
|
|
3286
3224
|
}
|
|
3287
|
-
var MISSING_STATUS_ERROR = "run protocol incomplete: the final response has no \"STATUS:
|
|
3225
|
+
var MISSING_STATUS_ERROR = "run protocol incomplete: the final response has no \"STATUS: ok|block\" line";
|
|
3288
3226
|
/** The run outcome a terminal report establishes. Only a successful provider
|
|
3289
3227
|
* exit is read for a status; a successful exit without one is a protocol error. */
|
|
3290
3228
|
function reportOutcome(body) {
|
|
@@ -4356,10 +4294,8 @@ function renderLoopsText(loops, fields) {
|
|
|
4356
4294
|
return doc(countLine(loops.length), listBlock("loops", fields, loops.map((l) => fields.map((f) => loopCell(l, f)))), helpBlock(["Run `pievo show <id>` to see a loop's full config", "Run `pievo log <id>` to see a loop's recent runs"]));
|
|
4357
4295
|
}
|
|
4358
4296
|
function runResultToken(r) {
|
|
4359
|
-
if (r.
|
|
4360
|
-
|
|
4361
|
-
const base = r.phase === "error" ? "failed" : r.phase === "done" ? "ok" : r.phase;
|
|
4362
|
-
return r.status ? `${base}/${r.status}` : `${base}/missing-status`;
|
|
4297
|
+
if (r.phase === "error") return "failed";
|
|
4298
|
+
return r.status ?? r.phase;
|
|
4363
4299
|
}
|
|
4364
4300
|
/** Stable JSON with recursively sorted object keys — so two structurally-equal values
|
|
4365
4301
|
* serialize identically regardless of key ordering (pg `jsonb` normalizes key order). */
|
|
@@ -4589,11 +4525,7 @@ var TERMINAL_PHASES = [
|
|
|
4589
4525
|
"error",
|
|
4590
4526
|
"canceled"
|
|
4591
4527
|
];
|
|
4592
|
-
var STATUSES = [
|
|
4593
|
-
"keep",
|
|
4594
|
-
"no-change",
|
|
4595
|
-
"block"
|
|
4596
|
-
];
|
|
4528
|
+
var STATUSES = ["ok", "block"];
|
|
4597
4529
|
var LOG_FLAGS = new Set([
|
|
4598
4530
|
"_",
|
|
4599
4531
|
"loop",
|
|
@@ -4651,7 +4583,7 @@ function parseHistoryFlags(flags) {
|
|
|
4651
4583
|
const status = flags.status;
|
|
4652
4584
|
if (status !== void 0 && (typeof status !== "string" || !STATUSES.includes(status))) return {
|
|
4653
4585
|
ok: false,
|
|
4654
|
-
error: "--status must be
|
|
4586
|
+
error: "--status must be ok|block"
|
|
4655
4587
|
};
|
|
4656
4588
|
const phase = flags.phase;
|
|
4657
4589
|
if (phase !== void 0 && (typeof phase !== "string" || !TERMINAL_PHASES.includes(phase))) return {
|
|
@@ -4720,6 +4652,7 @@ function boundedText(value, cap = HISTORY_DETAIL_TEXT_CAP) {
|
|
|
4720
4652
|
}
|
|
4721
4653
|
function normalizedListRun(run) {
|
|
4722
4654
|
const message = boundedText(run.message ?? null, 500);
|
|
4655
|
+
const error = boundedText(run.error ?? null, 500);
|
|
4723
4656
|
return {
|
|
4724
4657
|
runIndex: run.runIndex,
|
|
4725
4658
|
terminalAt: run.ts,
|
|
@@ -4730,6 +4663,8 @@ function normalizedListRun(run) {
|
|
|
4730
4663
|
tokenUsage: totalTokenUsage(run.usage ?? null),
|
|
4731
4664
|
message: message.value,
|
|
4732
4665
|
messageTruncated: message.truncated,
|
|
4666
|
+
error: error.value,
|
|
4667
|
+
errorTruncated: error.truncated,
|
|
4733
4668
|
finalTextAvailable: !!run.finalText?.trim()
|
|
4734
4669
|
};
|
|
4735
4670
|
}
|
|
@@ -4784,6 +4719,7 @@ async function listHistory(loop, q) {
|
|
|
4784
4719
|
"durationMs",
|
|
4785
4720
|
"tokenUsage",
|
|
4786
4721
|
"message",
|
|
4722
|
+
"error",
|
|
4787
4723
|
"finalTextAvailable"
|
|
4788
4724
|
], rows.map((run) => [
|
|
4789
4725
|
run.runIndex,
|
|
@@ -4794,6 +4730,7 @@ async function listHistory(loop, q) {
|
|
|
4794
4730
|
run.durationMs ?? null,
|
|
4795
4731
|
totalTokenUsage(run.usage ?? null),
|
|
4796
4732
|
run.message ? truncate(run.message, 500, "use --run for detail").value : null,
|
|
4733
|
+
run.error ? truncate(run.error, 500, "use --run for detail").value : null,
|
|
4797
4734
|
!!run.finalText?.trim()
|
|
4798
4735
|
])) : emptyList("runs");
|
|
4799
4736
|
return response(doc(`loop: ${scalar(loop.name)} (${loop.id})`, `count: ${rows.length} of ${Number(counted[0]?.n ?? 0)} matching`, table, helpBlock(["Use `pievo log --run <index>` for one run"])));
|
|
@@ -5286,7 +5223,7 @@ var CliGateway = class {
|
|
|
5286
5223
|
})), scoped.elsewhere, await recentMachineRuns(loops, 3));
|
|
5287
5224
|
}
|
|
5288
5225
|
};
|
|
5289
|
-
var RUN_HAS_NO_CLI = "pievo commands are not available inside a run; end your final response with a summary and a last line `STATUS:
|
|
5226
|
+
var RUN_HAS_NO_CLI = "pievo commands are not available inside a run; end your final response with a summary and a last line `STATUS: ok|block`";
|
|
5290
5227
|
var FILE_USAGE = "pievo file list <loop> | file get <loop> <name> | file set <loop> <name> --from <path>";
|
|
5291
5228
|
var PAUSED_FINISHING = "loop paused; current run is finishing";
|
|
5292
5229
|
var STOP_UPGRADE_REQUIRED = "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`.";
|
|
@@ -5356,7 +5293,7 @@ var DEVICE_VERB_HELP = {
|
|
|
5356
5293
|
new: {
|
|
5357
5294
|
syntax: "new --json '<config>' [--dry-run]",
|
|
5358
5295
|
summary: `create a loop from the canonical envelope (keys: ${[...EDITABLE_LOOP_FIELDS].join(", ")}; agent required)`,
|
|
5359
|
-
help: ["Run `pievo new --json '{\"name\":\"Daily check\",\"schedule\":{\"mode\":\"cron\",\"cron\":\"0 8 * * *\",\"timezone\":\"UTC\",\"overlap\":\"skip\"},\"workdir\":\"<path>\",\"agent\":\"claude-code\",\"prompt\":\"Check the project.\"
|
|
5296
|
+
help: ["Run `pievo new --json '{\"name\":\"Daily check\",\"schedule\":{\"mode\":\"cron\",\"cron\":\"0 8 * * *\",\"timezone\":\"UTC\",\"overlap\":\"skip\"},\"workdir\":\"<path>\",\"agent\":\"claude-code\",\"prompt\":\"Check the project.\"}'` to create a loop", "Run `pievo new --json '{...}' --dry-run` to validate without creating"]
|
|
5360
5297
|
},
|
|
5361
5298
|
loops: {
|
|
5362
5299
|
syntax: "loops [--fields a,b] [--json]",
|
|
@@ -5404,7 +5341,7 @@ var DEVICE_VERB_HELP = {
|
|
|
5404
5341
|
help: ["Run `pievo file get <id> MEMORY.md` to read the loop's memory", "An edit made while a run is executing wins over that run's version of the file"]
|
|
5405
5342
|
},
|
|
5406
5343
|
log: {
|
|
5407
|
-
syntax: "log [<id>] [--limit 1..20] [--since ISO] [--until ISO] [--status
|
|
5344
|
+
syntax: "log [<id>] [--limit 1..20] [--since ISO] [--until ISO] [--status ok|block] [--phase done|error|canceled] [--run <index|UUID> [--diff]] [--json]",
|
|
5408
5345
|
summary: "bounded terminal history or one detailed run",
|
|
5409
5346
|
help: ["Run `pievo log <id> --json` for structured history", "Run `pievo log <id> --run 12 --diff` for bounded run detail and artifact diff"]
|
|
5410
5347
|
}
|
|
@@ -5441,7 +5378,6 @@ function renderShowText(loop, env, totalRuns, lastRun, opts = {}) {
|
|
|
5441
5378
|
["model", env.model == null ? { raw: "default" } : env.model],
|
|
5442
5379
|
["reasoningEffort", env.reasoningEffort == null ? { raw: "default" } : env.reasoningEffort],
|
|
5443
5380
|
["prompt", contentField(loop.prompt, full)],
|
|
5444
|
-
["statusDefinitions", { raw: JSON.stringify(env.statusDefinitions) }],
|
|
5445
5381
|
["artifacts", { raw: JSON.stringify(env.artifacts) }],
|
|
5446
5382
|
["enabled", env.enabled]
|
|
5447
5383
|
]);
|
|
@@ -5727,4 +5663,4 @@ async function getCliGateway() {
|
|
|
5727
5663
|
return (await ensureServer()).cliGateway;
|
|
5728
5664
|
}
|
|
5729
5665
|
//#endregion
|
|
5730
|
-
export { listRuns as A, reconciliationStatesForRuns as B, getRun as C, listArtifacts as D, isOutdated as E, machineIdFromToken as F, stopLoop as G, requestRunCancel as H, machinePresence as I, updateMachine as J, tryDeleteLoop as K, mintDeviceToken as L, loopFileNames as M,
|
|
5666
|
+
export { listRuns as A, reconciliationStatesForRuns as B, getRun as C, listArtifacts as D, isOutdated as E, machineIdFromToken as F, stopLoop as G, requestRunCancel as H, machinePresence as I, updateMachine as J, tryDeleteLoop as K, mintDeviceToken as L, loopFileNames as M, loopTags_CI_aMcqh_exports as N, listLoops as O, loopsForMachine as P, pauseLoop as R, getMachine as S, hasRunningRun as T, sha256 as U, requestDeleteLoop as V, startLoop as W, validateLoopTags as Y, createMachine as _, canonicalLoopSummary as a, getArtifactFile as b, getLoopFiles as c, validOptionalPollString as d, validPollCurrentRun as f, countRuns as g, CODING_AGENTS as h, canonicalLoopEnvelope as i, listRunsBefore as j, listMachines as k, readByteBody as l, validateLoopEdit as m, POLL_HTTP_BODY_FIELDS as n, daemonNeedsUpdate as o, validPollWireId as p, updateLoop as q, boot_jtVCf_pN_exports as r, ensureServer as s, MIN_DAEMON_VERSION as t, readJsonBody as u, forceDeleteLoop as v, hasPendingRun as w, getLoop as x, forceDeleteMachine as y, recentLoopUsage as z };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { C as getRun, G as stopLoop$1, H as requestRunCancel, I as machinePresence, K as tryDeleteLoop, O as listLoops$1, R as pauseLoop$1, S as getMachine, T as hasRunningRun, V as requestDeleteLoop, W as startLoop$1, j as listRunsBefore, k as listMachines, m as validateLoopEdit, q as updateLoop, s as ensureServer, v as forceDeleteLoop, x as getLoop } from "./boot-
|
|
2
|
-
import { a as toRunSummaries, i as toLoopSummaries, r as toLoopDetail, t as sortLoopSummariesByRecentRun } from "./loopProjection-
|
|
1
|
+
import { C as getRun, G as stopLoop$1, H as requestRunCancel, I as machinePresence, K as tryDeleteLoop, O as listLoops$1, R as pauseLoop$1, S as getMachine, T as hasRunningRun, V as requestDeleteLoop, W as startLoop$1, j as listRunsBefore, k as listMachines, m as validateLoopEdit, q as updateLoop, s as ensureServer, v as forceDeleteLoop, x as getLoop } from "./boot-jtVCf_pN.mjs";
|
|
2
|
+
import { a as toRunSummaries, i as toLoopSummaries, r as toLoopDetail, t as sortLoopSummariesByRecentRun } from "./loopProjection-ZNfSmA5e.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-
|
|
5
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/loopApi-0nR35Kh2.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
|
|
@@ -75,7 +75,7 @@ var getArtifacts_createServerFn_handler = createServerRpc({
|
|
|
75
75
|
var getArtifacts = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifacts_createServerFn_handler, async ({ data }) => {
|
|
76
76
|
await backend();
|
|
77
77
|
if (!await findLoop(data.loopId)) return [];
|
|
78
|
-
const { listLoopArtifacts } = await import("./artifactFiles-
|
|
78
|
+
const { listLoopArtifacts } = await import("./artifactFiles-DLfSRhrV.mjs");
|
|
79
79
|
return listLoopArtifacts(data.loopId);
|
|
80
80
|
});
|
|
81
81
|
var getArtifact_createServerFn_handler = createServerRpc({
|
|
@@ -86,7 +86,7 @@ var getArtifact_createServerFn_handler = createServerRpc({
|
|
|
86
86
|
var getArtifact = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifact_createServerFn_handler, async ({ data }) => {
|
|
87
87
|
await backend();
|
|
88
88
|
if (!await findLoop(data.loopId)) return { error: "file not found" };
|
|
89
|
-
const { readLoopArtifact } = await import("./artifactFiles-
|
|
89
|
+
const { readLoopArtifact } = await import("./artifactFiles-DLfSRhrV.mjs");
|
|
90
90
|
return readLoopArtifact(data.loopId, data.path);
|
|
91
91
|
});
|
|
92
92
|
var getRunDiff_createServerFn_handler = createServerRpc({
|
|
@@ -105,7 +105,7 @@ var getRunDiff = createServerFn({ method: "GET" }).validator((d) => d).handler(g
|
|
|
105
105
|
hasSnapshot: false,
|
|
106
106
|
files: []
|
|
107
107
|
};
|
|
108
|
-
const { computeRunDiff } = await import("./boot-
|
|
108
|
+
const { computeRunDiff } = await import("./boot-jtVCf_pN.mjs").then((n) => n.r).then((n) => n.i);
|
|
109
109
|
return computeRunDiff(data.runId);
|
|
110
110
|
});
|
|
111
111
|
var patchLoop_createServerFn_handler = createServerRpc({
|
|
@@ -191,7 +191,7 @@ var deleteLoop = createServerFn({ method: "POST" }).validator((id) => id).handle
|
|
|
191
191
|
if (!await requestDeleteLoop(id)) return { error: "not found" };
|
|
192
192
|
if (unreachable) {
|
|
193
193
|
if (!await forceDeleteLoop(id)) return { error: "delete failed; server data was not deleted" };
|
|
194
|
-
const { logger } = await import("./boot-
|
|
194
|
+
const { logger } = await import("./boot-jtVCf_pN.mjs").then((n) => n.N).then((n) => n.$t);
|
|
195
195
|
logger.child({ mod: "loop-lifecycle" }).warn({
|
|
196
196
|
action: "unreachable-delete",
|
|
197
197
|
loopId: id,
|
|
@@ -250,7 +250,7 @@ var mintConnectKey_createServerFn_handler = createServerRpc({
|
|
|
250
250
|
}, (opts) => mintConnectKey.__executeServer(opts));
|
|
251
251
|
var mintConnectKey = createServerFn({ method: "POST" }).handler(mintConnectKey_createServerFn_handler, async () => {
|
|
252
252
|
await backend();
|
|
253
|
-
const { mintDeviceToken, rememberConnectKey } = await import("./boot-
|
|
253
|
+
const { mintDeviceToken, rememberConnectKey } = await import("./boot-jtVCf_pN.mjs").then((n) => n.N).then((n) => n.g);
|
|
254
254
|
const token = mintDeviceToken();
|
|
255
255
|
await rememberConnectKey(token);
|
|
256
256
|
return { token };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as listRuns, B as reconciliationStatesForRuns, I as machinePresence, 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 recentLoopUsage } from "./boot-
|
|
2
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-
|
|
1
|
+
import { A as listRuns, B as reconciliationStatesForRuns, I as machinePresence, 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 recentLoopUsage } from "./boot-jtVCf_pN.mjs";
|
|
2
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-ZNfSmA5e.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
|