omkit 0.8.13 → 0.8.15
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 -2
- package/dist/core/ExecutionTree.js +32 -0
- package/dist/core/step.js +1 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/output/writers/report/report.css +70 -0
- package/dist/output/writers/report/report.js +2387 -1904
- package/dist/output/writers/views.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -185,11 +185,11 @@ flakyBackgroundJob().result.catch((e) => console.warn("job failed, carrying on",
|
|
|
185
185
|
|
|
186
186
|
Every action's output, events, asserts, snapshots, and lifecycle land on **one timeline** — streamed live to the terminal (curated milestones) and written to a per-run folder:
|
|
187
187
|
|
|
188
|
-
- `result.json` — the run's tree, per-node status, verdict, and curated artifacts. Each node also carries what its definition declared about itself: the `.describe()` summary, the `.mcp()` exposure, and the `.args()` schema as JSON Schema. Each node also records `calledAt`, the line that launched it, so a built-in step (a `prompt`, a `command`) points back at your code rather than omkit's, and a `step(...)` is defined at the line where you wrote it.
|
|
188
|
+
- `result.json` — the run's tree, per-node status, verdict, and curated artifacts. Each node also carries what its definition declared about itself: the `.describe()` summary, the `.mcp()` exposure, and the `.args()` schema as JSON Schema. Each node also records what it returned (`value`, or a `valueNote` when it was too large or not JSON) and `calledAt`, the line that launched it, so a built-in step (a `prompt`, a `command`) points back at your code rather than omkit's, and a `step(...)` is defined at the line where you wrote it.
|
|
189
189
|
- `raw.jsonl` — every entry, machine-readable.
|
|
190
190
|
- `main.log` + one `.log` per action — the human-readable timelines.
|
|
191
191
|
- `events.log` / `asserts.log` / `snapshots.log` / `artifacts.log` — cross-cutting rollups.
|
|
192
|
-
- `report.html` — the whole run as one self-contained page: a timeline of every action with its events, a flame graph, a flow graph of the tree, per-action details, logs, assertions, the run's snapshots and images,
|
|
192
|
+
- `report.html` — the whole run as one self-contained page: a timeline of every action with its events, a flame graph, a flow graph of the tree, per-action details, logs, assertions, the run's snapshots and images, each action's source (the `<>` button next to a block), and an Errors tab that collects failures, failed assertions and error output, and can highlight the actions they came from in every graph. Everything is inlined (data, base64 images, d3), so it opens straight from disk and survives being zipped or attached.
|
|
193
193
|
|
|
194
194
|
`ctx` also gives each action `assert(cond, msg)` (tallies into the run's verdict), `snapshot(name, value)` (captures JSON state to the run folder), and `artifact(name, file, opts?)` (labels a file the action wrote — name, optional description, MIME inferred from the extension — and returns its absolute path) — so a run is an inspectable artifact, not just an exit code. Write the files you label under `ctx.artifactsFolder`, the run's own output folder.
|
|
195
195
|
|
|
@@ -391,6 +391,7 @@ export class ExecutionTree {
|
|
|
391
391
|
duration: node.duration,
|
|
392
392
|
logFile: this.logFile(node.path),
|
|
393
393
|
...this.metaView(node),
|
|
394
|
+
...valueView(node.value),
|
|
394
395
|
children: node.children.map((child) => this.nodeView(child)),
|
|
395
396
|
};
|
|
396
397
|
}
|
|
@@ -416,6 +417,7 @@ export class ExecutionTree {
|
|
|
416
417
|
...(meta.description === undefined ? {} : { description: { ...meta.description } }),
|
|
417
418
|
...(meta.mcp === undefined ? {} : { mcp: { ...meta.mcp } }),
|
|
418
419
|
...(inputSchema === undefined ? {} : { inputSchema }),
|
|
420
|
+
...("inline" in meta && meta.inline ? { kind: "step" } : {}),
|
|
419
421
|
};
|
|
420
422
|
}
|
|
421
423
|
/** Test seam: the projected run view (same object written to result.json). */
|
|
@@ -506,6 +508,36 @@ const messageOf = (error) => error instanceof Error ? (error.stack ?? error.mess
|
|
|
506
508
|
* body may hand back anything — a capability handle, a cyclic object — and the run must
|
|
507
509
|
* still write its record; a value that does not survive JSON is dropped, not fatal.
|
|
508
510
|
*/
|
|
511
|
+
/** A node's return value is recorded up to this many characters of JSON. */
|
|
512
|
+
const VALUE_CAP = 512 * 1024;
|
|
513
|
+
/**
|
|
514
|
+
* What a node returned, for the record: the value itself when JSON can carry it and it fits,
|
|
515
|
+
* otherwise a one-line note saying what it was — so "returned a Page" is still on the record.
|
|
516
|
+
*/
|
|
517
|
+
function valueView(value) {
|
|
518
|
+
if (value === undefined)
|
|
519
|
+
return {};
|
|
520
|
+
const kind = value === null
|
|
521
|
+
? "null"
|
|
522
|
+
: typeof value === "object"
|
|
523
|
+
? (value.constructor?.name ?? "object")
|
|
524
|
+
: typeof value;
|
|
525
|
+
let text;
|
|
526
|
+
try {
|
|
527
|
+
text = JSON.stringify(value);
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
text = undefined;
|
|
531
|
+
}
|
|
532
|
+
if (text === undefined)
|
|
533
|
+
return { valueNote: `${kind} (not JSON)` };
|
|
534
|
+
if (text.length > VALUE_CAP) {
|
|
535
|
+
return {
|
|
536
|
+
valueNote: `${kind}, ${Math.round(text.length / 1024)} KB of JSON — too large to record`,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
return { value: JSON.parse(text) };
|
|
540
|
+
}
|
|
509
541
|
function serialisable(value) {
|
|
510
542
|
if (value === undefined)
|
|
511
543
|
return undefined;
|
package/dist/core/step.js
CHANGED
|
@@ -18,6 +18,7 @@ export function step(name, fn) {
|
|
|
18
18
|
body: fn,
|
|
19
19
|
definedAt: site,
|
|
20
20
|
calledAt: site,
|
|
21
|
+
meta: { inline: true },
|
|
21
22
|
};
|
|
22
23
|
// `.result` already resolves the value and rejects on failure/cancel — exactly step's contract.
|
|
23
24
|
return ExecutionTree.require().launch(spec).result;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -254,4 +254,6 @@ export interface ActionMeta {
|
|
|
254
254
|
readonly mcp?: ResolvedMcpExposure;
|
|
255
255
|
/** Converts the declared args schema; called once, at finalize. */
|
|
256
256
|
readonly describeArgs?: () => DescribedArgs;
|
|
257
|
+
/** Launched by `step(…)` — an inline action with no reusable definition. */
|
|
258
|
+
readonly inline?: boolean;
|
|
257
259
|
}
|
|
@@ -170,10 +170,18 @@ button { font: inherit; color: inherit; }
|
|
|
170
170
|
.axis line, .axis path { stroke: var(--line); }
|
|
171
171
|
|
|
172
172
|
.flow-view, .flame-view { position: absolute; inset: 0; }
|
|
173
|
+
.flame-view { overflow-y: auto; overflow-x: hidden; }
|
|
173
174
|
.flow-view svg, .flame-view svg { width: 100%; height: 100%; display: block; cursor: grab; }
|
|
174
175
|
.flow-card rect.box { fill: var(--raise); stroke: var(--line); rx: 8; }
|
|
175
176
|
.flow-card.selected rect.box { stroke: var(--accent); stroke-width: 2; }
|
|
176
177
|
.flow-card text { fill: var(--text); font-size: 12px; }
|
|
178
|
+
.flow-card text.nm { font-weight: 600; }
|
|
179
|
+
.flow-card .flow-icon { pointer-events: none; }
|
|
180
|
+
.flow-seq { fill: none; stroke: var(--text-3); stroke-width: 1.4; stroke-dasharray: 5 4; }
|
|
181
|
+
.flow-seq.parallel { stroke-dasharray: 1 4; stroke-linecap: round; stroke-width: 2; opacity: 0.8; }
|
|
182
|
+
.flow-arrowhead { fill: var(--text-3); }
|
|
183
|
+
.flow-par { fill: var(--text-3); font-size: 10px; font-style: italic; }
|
|
184
|
+
.flow-shot-t { fill: var(--text-3); font-size: 10.5px; pointer-events: none; }
|
|
177
185
|
.flow-card text.sub { fill: var(--text-3); font-size: 11px; }
|
|
178
186
|
.flow-link { fill: none; stroke: var(--text-3); stroke-width: 1.3; }
|
|
179
187
|
.flame-rect { cursor: pointer; stroke: var(--panel); stroke-width: 1; }
|
|
@@ -337,6 +345,7 @@ a.icon-btn { text-decoration: none; }
|
|
|
337
345
|
.tk-k { color: var(--tk-k); }
|
|
338
346
|
.tk-f { color: var(--tk-f); }
|
|
339
347
|
.tk-t { color: var(--tk-t); }
|
|
348
|
+
.tk-p { color: var(--tk-f); }
|
|
340
349
|
|
|
341
350
|
/* ── popover tabs, prompt panel, flow snapshot strip ───── */
|
|
342
351
|
.code-pop .cp-tabs { display: flex; gap: 2px; padding: 0 8px; border-bottom: 1px solid var(--line); background: var(--panel-2); }
|
|
@@ -355,6 +364,67 @@ a.icon-btn { text-decoration: none; }
|
|
|
355
364
|
.flow-shots .shot:hover rect { stroke: var(--accent); }
|
|
356
365
|
.flow-shots .shot text { fill: var(--text-3); font: 600 9px var(--mono); pointer-events: none; }
|
|
357
366
|
|
|
367
|
+
/* ── returned values ───────────────────────────────────── */
|
|
368
|
+
.code-btn path { fill: none; stroke: var(--text-2); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
|
|
369
|
+
.code-btn:hover path { stroke: var(--accent); }
|
|
370
|
+
.cp-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 10px 6px 14px; border-bottom: 1px solid var(--line-soft); font-size: 12px; }
|
|
371
|
+
.btn.small { padding: 3px 9px; font-size: 12px; }
|
|
372
|
+
.btn.small svg { width: 13px; height: 13px; }
|
|
373
|
+
.code-box { border: 1px solid var(--line-soft); border-radius: 8px; background: var(--panel-2); overflow: auto; max-height: 60vh; }
|
|
374
|
+
.code-box .cp-code, .code-pop .cp-code { font: 11.5px/1.65 var(--mono); padding: 8px 0; min-width: max-content; }
|
|
375
|
+
.code-box .cl { display: grid; grid-template-columns: 40px auto; white-space: pre; }
|
|
376
|
+
.code-box .ln { color: var(--text-3); text-align: right; padding-right: 12px; user-select: none; }
|
|
377
|
+
.code-box .lc { padding-right: 14px; }
|
|
378
|
+
|
|
379
|
+
/* ── errors ────────────────────────────────────────────── */
|
|
380
|
+
.tab-count { display: inline-block; min-width: 18px; padding: 0 6px; border-radius: 999px; background: var(--raise); color: var(--text-2); font-size: 11px; text-align: center; }
|
|
381
|
+
.tab-count.bad { background: rgba(239, 68, 68, 0.16); color: var(--fail); font-weight: 700; }
|
|
382
|
+
.linkish { border: 0; background: none; padding: 0; cursor: pointer; font: inherit; color: var(--accent); }
|
|
383
|
+
.linkish.bad { color: var(--fail); }
|
|
384
|
+
.linkish.warn { color: var(--cancel); }
|
|
385
|
+
.tab-count.warn { background: rgba(245, 158, 11, 0.16); color: var(--cancel); font-weight: 700; }
|
|
386
|
+
.linkish:hover { text-decoration: underline; }
|
|
387
|
+
.err-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
|
|
388
|
+
.segs { display: flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
|
|
389
|
+
.seg { border: 0; background: var(--panel); padding: 4px 10px; cursor: pointer; color: var(--text-2); font-size: 12px; }
|
|
390
|
+
.seg + .seg { border-left: 1px solid var(--line); }
|
|
391
|
+
.seg.active { background: var(--accent-soft); color: var(--text); font-weight: 600; }
|
|
392
|
+
.btn.small.active { background: rgba(239, 68, 68, 0.14); color: var(--fail); border-color: var(--fail); }
|
|
393
|
+
.err-sec { margin-bottom: 10px; }
|
|
394
|
+
.err-sec-h { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: 12px; color: var(--text-2); margin: 6px 0 4px; }
|
|
395
|
+
.err-sec-h svg { width: 13px; height: 13px; color: var(--fail); }
|
|
396
|
+
.err-entry { border: 1px solid var(--line-soft); border-left: 3px solid var(--fail); border-radius: 6px; margin-bottom: 4px; background: var(--panel); }
|
|
397
|
+
.err-entry.output { border-left-color: var(--cancel); }
|
|
398
|
+
.err-entry.assert { border-left-color: #f97316; }
|
|
399
|
+
.err-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; cursor: pointer; min-width: 0; font-size: 12px; }
|
|
400
|
+
.err-row:hover { background: var(--raise); }
|
|
401
|
+
.err-row .twist { width: 14px; height: 14px; display: grid; place-items: center; color: var(--text-3); flex: none; }
|
|
402
|
+
.err-row .twist svg { width: 11px; height: 11px; transition: transform 0.12s; }
|
|
403
|
+
.err-row .twist.open svg { transform: rotate(90deg); }
|
|
404
|
+
.err-row .t { color: var(--text-2); font-variant-numeric: tabular-nums; flex: none; min-width: 44px; }
|
|
405
|
+
.err-row .who { color: var(--text); font-weight: 600; flex: none; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
406
|
+
.err-row .err-src { color: var(--text-3); font-family: var(--mono); font-size: 11px; flex: none; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
407
|
+
.err-row .msg { flex: 1; min-width: 0; font-family: var(--mono); color: var(--fail); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
408
|
+
.err-entry.output .err-row .msg { color: var(--text-2); }
|
|
409
|
+
.err-row .err-n { color: var(--text-3); font-size: 11px; flex: none; }
|
|
410
|
+
.err-row .icon-btn { width: 22px; height: 20px; }
|
|
411
|
+
.err-text { margin: 0 8px 8px 28px; max-height: 260px; }
|
|
412
|
+
.err-kind { color: var(--fail); }
|
|
413
|
+
.errbadge { flex: none; min-width: 16px; height: 16px; padding: 0 5px; border-radius: 999px; background: var(--fail); color: #fff; font-size: 10px; font-weight: 700; display: inline-grid; place-items: center; }
|
|
414
|
+
/* highlight mode — errored nodes in red, the path to them marked, the rest dimmed */
|
|
415
|
+
#app.hl-on .chart g.r:not(.err):not(.err-path),
|
|
416
|
+
#app.hl-on .flame-view g.cell:not(.err):not(.err-path),
|
|
417
|
+
#app.hl-on .flow-card:not(.err):not(.err-path) { opacity: 0.22; }
|
|
418
|
+
#app.hl-on .tree .row:not(.err):not(.err-path) { opacity: 0.4; }
|
|
419
|
+
#app.hl-on .chart g.r.err rect.bar,
|
|
420
|
+
#app.hl-on .flame-view g.cell.err rect { stroke: var(--fail); stroke-width: 2.5; filter: drop-shadow(0 0 5px rgba(239, 68, 68, 0.75)); }
|
|
421
|
+
#app.hl-on .flow-card.err rect.box { stroke: var(--fail); stroke-width: 2.5; filter: drop-shadow(0 0 5px rgba(239, 68, 68, 0.6)); }
|
|
422
|
+
#app.hl-on .chart g.r.err-path rect.bar,
|
|
423
|
+
#app.hl-on .flame-view g.cell.err-path rect,
|
|
424
|
+
#app.hl-on .flow-card.err-path rect.box { stroke: var(--fail); stroke-width: 1.2; stroke-dasharray: 4 3; }
|
|
425
|
+
#app.hl-on .tree .row.err { background: rgba(239, 68, 68, 0.1); }
|
|
426
|
+
.chart g.r, .flame-view g.cell, .flow-card, .tree .row { transition: opacity 0.15s; }
|
|
427
|
+
|
|
358
428
|
@media (max-width: 1100px) {
|
|
359
429
|
.bottom { grid-template-columns: minmax(0, 1fr); }
|
|
360
430
|
.legend, .overview { display: none; }
|