synartesis 0.6.16 → 0.6.19
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/CHANGELOG.md +98 -0
- package/dist/{chunk-OODDXM36.js → chunk-SZHVLCJR.js} +24 -1
- package/dist/cli.js +79 -15
- package/dist/proxy.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
What changed, and why it mattered. Dates are release dates.
|
|
4
4
|
|
|
5
|
+
## 0.6.19 — 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Both guides showed output the command no longer produces.** The worked
|
|
10
|
+
examples of `show` were still the pre-0.6.18 single-line row, and the session
|
|
11
|
+
lists were still the pre-0.6.17 table of full uuids -- in the runbook, whose
|
|
12
|
+
pdf had just been rebuilt from that stale text, and in the user guide, whose
|
|
13
|
+
pdf had not been rebuilt at all, so the two shipped documents disagreed with
|
|
14
|
+
each other as well as with the terminal. Regenerated from real runs and both
|
|
15
|
+
pdfs built together.
|
|
16
|
+
|
|
17
|
+
- **Four tap targets touching on a phone.** A rule added with the last release
|
|
18
|
+
set `gap:0` on the screenshot tabs, overriding the deliberate `gap:8px` set
|
|
19
|
+
with the two columns further up the same stylesheet. The buttons carry their
|
|
20
|
+
own border, so the four of them closed into one block with doubled seams and
|
|
21
|
+
no separation between adjacent targets. Measured at 375px: they abutted
|
|
22
|
+
exactly, at x=22 and x=188 across a width of 166.
|
|
23
|
+
|
|
24
|
+
- `badgeOf` and `statusOf` took a `pad` that defaulted to true, and the one
|
|
25
|
+
call site of each passes it explicitly -- so the default could never apply,
|
|
26
|
+
and it was the wrong answer for the ordinary case anyway. Required now.
|
|
27
|
+
|
|
28
|
+
## 0.6.18 — 2026-09-15
|
|
29
|
+
|
|
30
|
+
A pass over the terminal and the site, most of it drafted by another model and
|
|
31
|
+
reviewed here.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **`show` leads each action with the tool that ran.** It used to begin the row
|
|
36
|
+
with sequence, class and status, putting the tool name at column thirty-eight
|
|
37
|
+
where a long server name could push it off. The class and status now sit on
|
|
38
|
+
the line below it, and each action is separated by a blank line rather than
|
|
39
|
+
each action's recovery data being separated from the action.
|
|
40
|
+
|
|
41
|
+
- **A held call is set in the accent colour**, which the palette reserves for
|
|
42
|
+
the one thing that wants a person. It was set in ink, like an ordinary fact.
|
|
43
|
+
|
|
44
|
+
- **The site keeps its columns on a phone.** At 375px the interactive example's
|
|
45
|
+
caption left the undo control stranded alone on a second line; it takes its
|
|
46
|
+
own row now, with a tap target that meets the usual minimum. Figures in the
|
|
47
|
+
evidence blocks are set in tabular figures so they hold their columns as the
|
|
48
|
+
numbers change, and the install guide's two opening columns share a top edge.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **Trailing whitespace on every action in `show`.** The padding that used to
|
|
53
|
+
line up the next column had nothing after it once the tool name moved, so it
|
|
54
|
+
dangled at the end of each line -- inside the escape codes, where trimming
|
|
55
|
+
the finished line cannot reach. Padded now only where a third column follows,
|
|
56
|
+
which is `--live` and usually absent.
|
|
57
|
+
|
|
58
|
+
### Kept
|
|
59
|
+
|
|
60
|
+
- **The turning mark in `watch` and the screen stays.** It was read as implying
|
|
61
|
+
an agent was at work on an empty journal. It sits beside "watching" and "no
|
|
62
|
+
journal here yet", which are statements about the command rather than about
|
|
63
|
+
an agent, and it answers the only question a quiet screen raises -- whether
|
|
64
|
+
this is still running. Nothing else in the frame changes to answer it: every
|
|
65
|
+
time shown is a wall clock reading of something that already happened, so
|
|
66
|
+
without the mark a live `watch` on a quiet journal is indistinguishable from
|
|
67
|
+
a crashed one. That is the command meant to be left running. There is now a
|
|
68
|
+
test.
|
|
69
|
+
|
|
70
|
+
## 0.6.17 — 2026-09-15
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- **The session list says what each session did.** Three sessions a second
|
|
75
|
+
apart -- one that wrote a file, one that read one, one that did nothing --
|
|
76
|
+
printed as three identical rows distinguished only by a uuid, so the command
|
|
77
|
+
you run to find the session you want told you nothing about which session you
|
|
78
|
+
want. There are two new columns and one fewer:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
session started did state agent
|
|
82
|
+
331e43de 16:41:21 write_file report.txt done, can undo agent
|
|
83
|
+
ac635294 16:41:21 read only agent
|
|
84
|
+
5856e14a 16:41:21 create_directory x waiting for you agent
|
|
85
|
+
e52213e6 16:41:20 write_file notes.md undone agent
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`did` is the last action that changed something, named the way `watch` and
|
|
89
|
+
the screen already name one. `state` is where that leaves it, including the
|
|
90
|
+
two that look identical otherwise: a run that has been undone and one that
|
|
91
|
+
has not.
|
|
92
|
+
|
|
93
|
+
Ids are printed at eight characters, widened only if that would not tell the
|
|
94
|
+
runs apart -- any unambiguous prefix has always been accepted, and thirty-six
|
|
95
|
+
characters of hex on every line was the price of a collision that has not
|
|
96
|
+
happened. The `status` column is gone: `complete` is true of nearly every
|
|
97
|
+
line and pushed the column that varies off the edge. `active` is not true of
|
|
98
|
+
nearly every line, so it survives as "still open" -- it means a proxy still
|
|
99
|
+
working or one that was killed, and it is the whole reason `close` exists.
|
|
100
|
+
|
|
101
|
+
`--json` is untouched: full ids, same fields. That is what scripts read.
|
|
102
|
+
|
|
5
103
|
## 0.6.16 — 2026-09-15
|
|
6
104
|
|
|
7
105
|
### Fixed
|
|
@@ -313,6 +313,18 @@ CREATE INDEX IF NOT EXISTS actions_undoable ON actions(run_id)
|
|
|
313
313
|
-- rows it saves reading are the ones carrying the snapshots.
|
|
314
314
|
CREATE INDEX IF NOT EXISTS actions_unresolved ON actions(run_id, server, tool)
|
|
315
315
|
WHERE status = 'pending';
|
|
316
|
+
|
|
317
|
+
-- Partial again, and for the third time the partial half is the point. The
|
|
318
|
+
-- session list says what each session did, which means the last action in it
|
|
319
|
+
-- that changed something. Without this, a run that read ten thousand files and
|
|
320
|
+
-- wrote once is walked backwards through all ten thousand reads to find the
|
|
321
|
+
-- write -- and those rows carry the snapshots.
|
|
322
|
+
--
|
|
323
|
+
-- The one of these three the planner finds on its own, so lastWrite does not
|
|
324
|
+
-- name it: the query's WHERE clause is this predicate exactly, and seq is the
|
|
325
|
+
-- ordering it asks for, so the index both filters and sorts.
|
|
326
|
+
CREATE INDEX IF NOT EXISTS actions_writes ON actions(run_id, seq)
|
|
327
|
+
WHERE class <> 'readonly';
|
|
316
328
|
`;
|
|
317
329
|
|
|
318
330
|
// src/journal/journal.ts
|
|
@@ -824,6 +836,17 @@ var SqliteJournal = class {
|
|
|
824
836
|
return raw === void 0 ? void 0 : toRun(raw);
|
|
825
837
|
});
|
|
826
838
|
}
|
|
839
|
+
lastWrite(runId) {
|
|
840
|
+
return this.#run("lastWrite", () => {
|
|
841
|
+
const raw = this.#db.prepare(
|
|
842
|
+
`SELECT * FROM actions
|
|
843
|
+
WHERE run_id = ? AND class <> 'readonly'
|
|
844
|
+
ORDER BY seq DESC
|
|
845
|
+
LIMIT 1`
|
|
846
|
+
).get(runId);
|
|
847
|
+
return raw === void 0 ? void 0 : toAction(raw);
|
|
848
|
+
});
|
|
849
|
+
}
|
|
827
850
|
recentActions(limit) {
|
|
828
851
|
return this.#run(
|
|
829
852
|
"recentActions",
|
|
@@ -1907,4 +1930,4 @@ export {
|
|
|
1907
1930
|
observeState,
|
|
1908
1931
|
connectStdioUpstream
|
|
1909
1932
|
};
|
|
1910
|
-
//# sourceMappingURL=chunk-
|
|
1933
|
+
//# sourceMappingURL=chunk-SZHVLCJR.js.map
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
verifyAgainstServers,
|
|
37
37
|
warnUntested,
|
|
38
38
|
wasRefused
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-SZHVLCJR.js";
|
|
40
40
|
import {
|
|
41
41
|
DriftConflict,
|
|
42
42
|
ManifestError,
|
|
@@ -3279,6 +3279,62 @@ function hint(candidate) {
|
|
|
3279
3279
|
out(hintLine(candidate, pathArgs(candidate.needs)));
|
|
3280
3280
|
out("");
|
|
3281
3281
|
}
|
|
3282
|
+
function idWidth(runs) {
|
|
3283
|
+
for (let width = 8; width < 36; width += 1) {
|
|
3284
|
+
const seen = new Set(runs.map((run) => run.id.slice(0, width)));
|
|
3285
|
+
if (seen.size === runs.length) {
|
|
3286
|
+
return width;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
return 36;
|
|
3290
|
+
}
|
|
3291
|
+
function didWhat(journal, run, total) {
|
|
3292
|
+
const write = journal.lastWrite(run.id);
|
|
3293
|
+
if (write === void 0) {
|
|
3294
|
+
return {
|
|
3295
|
+
what: style.quiet(total === 0 ? "nothing" : "read only"),
|
|
3296
|
+
state: style.quiet(run.status === "active" ? "still open" : "")
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
const what = subject(write.args);
|
|
3300
|
+
const said = plainly(write);
|
|
3301
|
+
const rest = total - 1;
|
|
3302
|
+
return {
|
|
3303
|
+
what: `${style.strong(write.tool)}${what === "" ? "" : ` ${what}`}` + (rest > 0 ? style.quiet(` +${String(rest)} more`) : ""),
|
|
3304
|
+
// A run still open outranks whatever its last action did. `complete` was
|
|
3305
|
+
// dropped as a column because it is true of nearly every line, but
|
|
3306
|
+
// `active` is not -- it is a proxy still working or one that was killed,
|
|
3307
|
+
// and it is the whole reason `close` exists. Losing it with the column it
|
|
3308
|
+
// shared would have been a real loss hidden inside a tidier table.
|
|
3309
|
+
//
|
|
3310
|
+
// Otherwise the action's own state, and always, not only when it asks
|
|
3311
|
+
// something of the reader: a run that has been undone and one that has not
|
|
3312
|
+
// look identical otherwise, and which of the two this is happens to be the
|
|
3313
|
+
// question the command gets asked.
|
|
3314
|
+
state: run.status === "active" ? style.accent("still open") : said.needs ? style.accent(said.text) : style.quiet(said.text)
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
function laid(text, width) {
|
|
3318
|
+
const codes = /\u001b\[[0-9;]*m/g;
|
|
3319
|
+
const bare = text.replace(codes, "");
|
|
3320
|
+
if (bare.length <= width) {
|
|
3321
|
+
return text + " ".repeat(width - bare.length);
|
|
3322
|
+
}
|
|
3323
|
+
let kept = "";
|
|
3324
|
+
let shown = 0;
|
|
3325
|
+
let at = 0;
|
|
3326
|
+
for (const match of text.matchAll(codes)) {
|
|
3327
|
+
const plain = text.slice(at, match.index);
|
|
3328
|
+
const room = width - 1 - shown;
|
|
3329
|
+
if (plain.length >= room) {
|
|
3330
|
+
return `${kept}${plain.slice(0, room)}\u2026\x1B[0m`;
|
|
3331
|
+
}
|
|
3332
|
+
kept += plain + match[0];
|
|
3333
|
+
shown += plain.length;
|
|
3334
|
+
at = match.index + match[0].length;
|
|
3335
|
+
}
|
|
3336
|
+
return `${kept}${text.slice(at, at + width - 1 - shown)}\u2026`;
|
|
3337
|
+
}
|
|
3282
3338
|
function runList(journal, asJson, journalPath) {
|
|
3283
3339
|
const runs = [...journal.listRuns()].reverse();
|
|
3284
3340
|
const tally2 = journal.tallyRuns();
|
|
@@ -3291,13 +3347,14 @@ function runList(journal, asJson, journalPath) {
|
|
|
3291
3347
|
out("no runs recorded");
|
|
3292
3348
|
return 0;
|
|
3293
3349
|
}
|
|
3350
|
+
const width = idWidth(runs);
|
|
3294
3351
|
out("");
|
|
3295
3352
|
out(` ${style.label("sessions")} ${style.quiet("most recent first")}`);
|
|
3296
|
-
out(` ${rule(
|
|
3353
|
+
out(` ${rule(88)}`);
|
|
3297
3354
|
out("");
|
|
3298
3355
|
out(
|
|
3299
3356
|
style.quiet(
|
|
3300
|
-
` ${"session".padEnd(
|
|
3357
|
+
` ${"session".padEnd(width)} ${"started".padEnd(13)} ${"did".padEnd(26)} ${"state".padEnd(26)} agent`
|
|
3301
3358
|
)
|
|
3302
3359
|
);
|
|
3303
3360
|
for (const run of runs) {
|
|
@@ -3305,11 +3362,12 @@ function runList(journal, asJson, journalPath) {
|
|
|
3305
3362
|
const { unknown, waiting } = actions;
|
|
3306
3363
|
const notes = [
|
|
3307
3364
|
unknown === 0 ? "" : `${String(unknown)} of unknown outcome`,
|
|
3308
|
-
waiting
|
|
3365
|
+
waiting > 1 ? `${String(waiting)} awaiting approval` : ""
|
|
3309
3366
|
].filter((note2) => note2 !== "");
|
|
3310
3367
|
const note = notes.length === 0 ? "" : ` ${style.accent(`(${notes.join("; ")})`)}`;
|
|
3368
|
+
const did = didWhat(journal, run, actions.actions);
|
|
3311
3369
|
out(
|
|
3312
|
-
` ${style.strong(run.id)} ${style.quiet(shortTime(run.startedAt).trimEnd().padEnd(13))} ${
|
|
3370
|
+
` ${style.strong(run.id.slice(0, width))} ${style.quiet(shortTime(run.startedAt).trimEnd().padEnd(13))} ${laid(did.what, 26)} ${laid(did.state, 26)} ${style.quiet(run.label ?? "-")}${note}`
|
|
3313
3371
|
);
|
|
3314
3372
|
}
|
|
3315
3373
|
out("");
|
|
@@ -3344,7 +3402,8 @@ async function runShow(argv, journal, asJson) {
|
|
|
3344
3402
|
return 0;
|
|
3345
3403
|
}
|
|
3346
3404
|
out("");
|
|
3347
|
-
out(` ${style.label("session")} ${style.strong(run.label ?? "an agent")}
|
|
3405
|
+
out(` ${style.label("session")} ${style.strong(run.label ?? "an agent")}`);
|
|
3406
|
+
out(` ${style.quiet(run.id)}`);
|
|
3348
3407
|
out(` ${rule(54)}`);
|
|
3349
3408
|
out("");
|
|
3350
3409
|
out(` ${style.quiet("agent ")} ${run.label ?? "-"}`);
|
|
@@ -3359,7 +3418,6 @@ async function runShow(argv, journal, asJson) {
|
|
|
3359
3418
|
return 0;
|
|
3360
3419
|
}
|
|
3361
3420
|
out("");
|
|
3362
|
-
out("");
|
|
3363
3421
|
out(` ${style.label("timeline")}`);
|
|
3364
3422
|
out(` ${rule(72)}`);
|
|
3365
3423
|
out("");
|
|
@@ -3367,7 +3425,11 @@ async function runShow(argv, journal, asJson) {
|
|
|
3367
3425
|
for (const action of actions) {
|
|
3368
3426
|
const now = live.get(action.seq);
|
|
3369
3427
|
out(
|
|
3370
|
-
` ${style.quiet(String(action.seq).padStart(3))} ${
|
|
3428
|
+
` ${style.quiet(String(action.seq).padStart(3))} ${style.strong(`${action.server}.${action.tool}`)}`
|
|
3429
|
+
);
|
|
3430
|
+
const alongside = now !== void 0;
|
|
3431
|
+
out(
|
|
3432
|
+
` ${badgeOf(action, alongside)} ${statusOf(action, alongside)}` + (now === void 0 ? "" : ` ${conditionOf(now)}`)
|
|
3371
3433
|
);
|
|
3372
3434
|
if (now?.diff !== void 0) {
|
|
3373
3435
|
for (const line2 of now.diff.split("\n")) {
|
|
@@ -3404,11 +3466,11 @@ async function runShow(argv, journal, asJson) {
|
|
|
3404
3466
|
out(` ${line2}`);
|
|
3405
3467
|
}
|
|
3406
3468
|
} else {
|
|
3407
|
-
out(` ${style.quiet("undo:")} ${style.
|
|
3469
|
+
out(` ${style.quiet("undo:")} ${style.strong(summariseArgs(inverseArgs(action.inverse), 90))}`);
|
|
3408
3470
|
}
|
|
3409
3471
|
}
|
|
3472
|
+
out("");
|
|
3410
3473
|
}
|
|
3411
|
-
out("");
|
|
3412
3474
|
out(` ${summarise2(actions)}`);
|
|
3413
3475
|
if (inspection !== void 0) {
|
|
3414
3476
|
out("");
|
|
@@ -3462,17 +3524,19 @@ var CLASS_MARK = {
|
|
|
3462
3524
|
unclassified: "?"
|
|
3463
3525
|
};
|
|
3464
3526
|
var BADGE_WIDTH = "irreversible".length + 2;
|
|
3465
|
-
function badgeOf(action) {
|
|
3466
|
-
const
|
|
3527
|
+
function badgeOf(action, pad2) {
|
|
3528
|
+
const name = `${CLASS_MARK[action.class]} ${action.class}`;
|
|
3529
|
+
const plain = pad2 ? name.padEnd(BADGE_WIDTH) : name;
|
|
3467
3530
|
return action.class === "irreversible" ? style.accent(plain) : style.quiet(plain);
|
|
3468
3531
|
}
|
|
3469
|
-
function statusOf(action) {
|
|
3470
|
-
const
|
|
3532
|
+
function statusOf(action, pad2) {
|
|
3533
|
+
const label = labelFor(action);
|
|
3534
|
+
const text = pad2 ? label.padEnd(13) : label;
|
|
3471
3535
|
if (wasRefused(action)) {
|
|
3472
3536
|
return style.accent(text);
|
|
3473
3537
|
}
|
|
3474
3538
|
if (action.status === "gated") {
|
|
3475
|
-
return style.
|
|
3539
|
+
return style.accent(text);
|
|
3476
3540
|
}
|
|
3477
3541
|
return style.quiet(text);
|
|
3478
3542
|
}
|
package/dist/proxy.js
CHANGED