synartesis 0.6.18 → 0.6.20
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 +55 -0
- package/dist/cli.js +39 -7
- package/dist/proxy.js +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
What changed, and why it mattered. Dates are release dates.
|
|
4
4
|
|
|
5
|
+
## 0.6.20 — 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **The undo preview showed a write nobody confirmed as one that was
|
|
10
|
+
confirmed.** A tool call can land and then fail to say so -- the server
|
|
11
|
+
writes the record and times out, or answers with an error on its way out.
|
|
12
|
+
Synartesis already handled this correctly: it refuses to read `isError` as a
|
|
13
|
+
promise that nothing happened, reads the resource back, and records the
|
|
14
|
+
change so it stays recoverable. What it did not do was say so afterwards.
|
|
15
|
+
The row that came out of that path carried no trace of it at all -- the
|
|
16
|
+
refusal was written to the log, which nobody reads, at the moment it
|
|
17
|
+
happens, which is not the moment it matters -- so `undo --dry-run` printed
|
|
18
|
+
`revert ... state matches; applying inverse`, character for character what a
|
|
19
|
+
confirmed write prints. The row now records how it was established, and the
|
|
20
|
+
preview prints it under the step as `caveat`. Same in the app's
|
|
21
|
+
`preview_undo`, so a model cannot describe an inferred write to somebody as
|
|
22
|
+
a confirmed one.
|
|
23
|
+
|
|
24
|
+
- **A halt that named the consequence and hid the cause.** Where the transport
|
|
25
|
+
failed and the read-back proved the write had landed, undo correctly stopped
|
|
26
|
+
rather than reverting on an unverified assumption -- but said only "the
|
|
27
|
+
post-state was never captured, so drift could not be ruled out", which reads
|
|
28
|
+
as a missed reading. The reason there was no reading, which the row knew all
|
|
29
|
+
along, is now printed with it.
|
|
30
|
+
|
|
31
|
+
- An action whose outcome is unknown was told it got that way because "the
|
|
32
|
+
process died mid-call". A timeout, or an error the read-back could not
|
|
33
|
+
settle, arrives in exactly the same state and was told the same wrong story.
|
|
34
|
+
It now says what is actually known, and the row's own error says which of
|
|
35
|
+
them it was.
|
|
36
|
+
|
|
37
|
+
## 0.6.19 — 2026-09-15
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **Both guides showed output the command no longer produces.** The worked
|
|
42
|
+
examples of `show` were still the pre-0.6.18 single-line row, and the session
|
|
43
|
+
lists were still the pre-0.6.17 table of full uuids -- in the runbook, whose
|
|
44
|
+
pdf had just been rebuilt from that stale text, and in the user guide, whose
|
|
45
|
+
pdf had not been rebuilt at all, so the two shipped documents disagreed with
|
|
46
|
+
each other as well as with the terminal. Regenerated from real runs and both
|
|
47
|
+
pdfs built together.
|
|
48
|
+
|
|
49
|
+
- **Four tap targets touching on a phone.** A rule added with the last release
|
|
50
|
+
set `gap:0` on the screenshot tabs, overriding the deliberate `gap:8px` set
|
|
51
|
+
with the two columns further up the same stylesheet. The buttons carry their
|
|
52
|
+
own border, so the four of them closed into one block with doubled seams and
|
|
53
|
+
no separation between adjacent targets. Measured at 375px: they abutted
|
|
54
|
+
exactly, at x=22 and x=188 across a width of 166.
|
|
55
|
+
|
|
56
|
+
- `badgeOf` and `statusOf` took a `pad` that defaulted to true, and the one
|
|
57
|
+
call site of each passes it explicitly -- so the default could never apply,
|
|
58
|
+
and it was the wrong answer for the ordinary case anyway. Required now.
|
|
59
|
+
|
|
5
60
|
## 0.6.18 — 2026-09-15
|
|
6
61
|
|
|
7
62
|
A pass over the terminal and the site, most of it drafted by another model and
|
package/dist/cli.js
CHANGED
|
@@ -375,7 +375,12 @@ function classify(action, replanning, goAhead) {
|
|
|
375
375
|
case "pending":
|
|
376
376
|
return {
|
|
377
377
|
kind: "halt",
|
|
378
|
-
|
|
378
|
+
// Not "the process died mid-call". That is one of the ways a row ends
|
|
379
|
+
// up here; a server that timed out, or answered with an error the
|
|
380
|
+
// read-back could not settle, arrives in exactly the same state and is
|
|
381
|
+
// told the same wrong story. The row's own error says which it was,
|
|
382
|
+
// and the halt prints it directly underneath.
|
|
383
|
+
reason: "outcome unknown: the call went out and its outcome was never established, so whether this applied cannot be determined",
|
|
379
384
|
verified: false
|
|
380
385
|
};
|
|
381
386
|
case "gated":
|
|
@@ -452,7 +457,14 @@ ${seen}` : seen;
|
|
|
452
457
|
if (!parsedPlan.success) {
|
|
453
458
|
const approved = action.approvedBy === void 0 ? "" : `, approved by ${action.approvedBy}`;
|
|
454
459
|
const reason = action.class === "irreversible" ? `cannot be undone${approved}; left in place` : `no usable inverse was recorded${action.error === void 0 ? "" : `: ${action.error}`}; left in place`;
|
|
455
|
-
|
|
460
|
+
const unconfirmed = action.class === "irreversible" ? caveat(action) : void 0;
|
|
461
|
+
steps.push({
|
|
462
|
+
...describeStep(action),
|
|
463
|
+
kind: "permanent",
|
|
464
|
+
reason,
|
|
465
|
+
verified: false,
|
|
466
|
+
...unconfirmed === void 0 ? {} : { note: unconfirmed }
|
|
467
|
+
});
|
|
456
468
|
leftInPlace = true;
|
|
457
469
|
continue;
|
|
458
470
|
}
|
|
@@ -517,13 +529,23 @@ ${seen}` : seen;
|
|
|
517
529
|
}
|
|
518
530
|
if (!verified && action.class === "reversible" && !force) {
|
|
519
531
|
const reason = unverifiedBecause(action);
|
|
532
|
+
const because = caveat(action);
|
|
520
533
|
halted = {
|
|
521
534
|
seq: action.seq,
|
|
522
535
|
reason,
|
|
523
|
-
detail:
|
|
536
|
+
detail: (because === void 0 ? "" : `${because}
|
|
537
|
+
|
|
538
|
+
`) + "Without it there is no way to tell this resource from one somebody has edited since, so the recorded value was not written.",
|
|
524
539
|
conflict: true
|
|
525
540
|
};
|
|
526
|
-
steps.push({
|
|
541
|
+
steps.push({
|
|
542
|
+
...describeStep(action),
|
|
543
|
+
kind: "halt",
|
|
544
|
+
reason,
|
|
545
|
+
verified: false,
|
|
546
|
+
plan,
|
|
547
|
+
...because === void 0 ? {} : { note: because }
|
|
548
|
+
});
|
|
527
549
|
break;
|
|
528
550
|
}
|
|
529
551
|
if (!verified && action.status === "rolling_back") {
|
|
@@ -539,13 +561,15 @@ ${seen}` : seen;
|
|
|
539
561
|
const after = intendedAfterInverse(action);
|
|
540
562
|
projected.set(resourceKey(toResolvedRead(verifyRead.data)), after ?? UNFORESEEABLE);
|
|
541
563
|
}
|
|
564
|
+
const recordedWith = caveat(action);
|
|
542
565
|
steps.push({
|
|
543
566
|
...describeStep(action),
|
|
544
567
|
kind: "revert",
|
|
545
568
|
reason: verified ? "state matches; applying inverse" : forcedOver ?? unverifiedBecause(action),
|
|
546
569
|
verified,
|
|
547
570
|
plan,
|
|
548
|
-
...rebuilt.inverse === void 0 ? {} : { replanned: true }
|
|
571
|
+
...rebuilt.inverse === void 0 ? {} : { replanned: true },
|
|
572
|
+
...recordedWith === void 0 ? {} : { note: recordedWith }
|
|
549
573
|
});
|
|
550
574
|
if (dryRun) {
|
|
551
575
|
continue;
|
|
@@ -613,6 +637,9 @@ ${seen}` : seen;
|
|
|
613
637
|
function unverifiedBecause(action) {
|
|
614
638
|
return action.verify === void 0 ? "no read declared for this tool, so drift could not be ruled out -- a `verify` read in its policy would give it one" : "the post-state was never captured, so drift could not be ruled out";
|
|
615
639
|
}
|
|
640
|
+
function caveat(action) {
|
|
641
|
+
return action.status === "applied" && action.error !== void 0 && action.error !== "" ? action.error : void 0;
|
|
642
|
+
}
|
|
616
643
|
function describeStep(action) {
|
|
617
644
|
return { seq: action.seq, server: action.server, tool: action.tool };
|
|
618
645
|
}
|
|
@@ -3524,12 +3551,12 @@ var CLASS_MARK = {
|
|
|
3524
3551
|
unclassified: "?"
|
|
3525
3552
|
};
|
|
3526
3553
|
var BADGE_WIDTH = "irreversible".length + 2;
|
|
3527
|
-
function badgeOf(action, pad2
|
|
3554
|
+
function badgeOf(action, pad2) {
|
|
3528
3555
|
const name = `${CLASS_MARK[action.class]} ${action.class}`;
|
|
3529
3556
|
const plain = pad2 ? name.padEnd(BADGE_WIDTH) : name;
|
|
3530
3557
|
return action.class === "irreversible" ? style.accent(plain) : style.quiet(plain);
|
|
3531
3558
|
}
|
|
3532
|
-
function statusOf(action, pad2
|
|
3559
|
+
function statusOf(action, pad2) {
|
|
3533
3560
|
const label = labelFor(action);
|
|
3534
3561
|
const text = pad2 ? label.padEnd(13) : label;
|
|
3535
3562
|
if (wasRefused(action)) {
|
|
@@ -3783,6 +3810,11 @@ function report(result, alreadyForcing = false, as = "") {
|
|
|
3783
3810
|
out(
|
|
3784
3811
|
` ${style.quiet(String(step.seq).padStart(3))} ${kind} ${style.strong(`${step.server}.${step.tool}`)} ${style.quiet(step.reason)}${unverified}`
|
|
3785
3812
|
);
|
|
3813
|
+
if (step.note !== void 0 && step.kind !== "halt") {
|
|
3814
|
+
for (const [at, line2] of wrapped(step.note, 62).entries()) {
|
|
3815
|
+
out(` ${at === 0 ? style.accent("caveat") : " "} ${style.quiet(line2)}`);
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3786
3818
|
if (step.plan !== void 0 && step.kind === "revert") {
|
|
3787
3819
|
const verb = `${step.replanned === true ? "replanned, " : ""}${result.dryRun ? "would call" : "called"}`;
|
|
3788
3820
|
out(
|
package/dist/proxy.js
CHANGED
|
@@ -755,6 +755,7 @@ function createProxyServer(options) {
|
|
|
755
755
|
const result = await route.upstream.client.request(forwarded, PassthroughResult, {
|
|
756
756
|
signal: extra.signal
|
|
757
757
|
});
|
|
758
|
+
let inferred;
|
|
758
759
|
const refused = refusal(result);
|
|
759
760
|
if (refused !== void 0) {
|
|
760
761
|
const settled = policy.refusal === "clean" ? "none" : await whatHappened(router, probe, priorState, extra.signal);
|
|
@@ -774,6 +775,7 @@ function createProxyServer(options) {
|
|
|
774
775
|
);
|
|
775
776
|
return result;
|
|
776
777
|
}
|
|
778
|
+
inferred = `the upstream answered with an error and the change was established by reading the resource back, so nothing confirmed it: ${refused}`;
|
|
777
779
|
log?.warn(
|
|
778
780
|
{ seq: pending.seq, tool: route.tool, reason: refused },
|
|
779
781
|
"the upstream reported an error after changing the resource"
|
|
@@ -781,6 +783,9 @@ function createProxyServer(options) {
|
|
|
781
783
|
}
|
|
782
784
|
const context = { args, snapshot, result: toPayload(result) };
|
|
783
785
|
const warnings = [];
|
|
786
|
+
if (inferred !== void 0) {
|
|
787
|
+
warnings.push(inferred);
|
|
788
|
+
}
|
|
784
789
|
if (missingPriorState !== void 0) {
|
|
785
790
|
warnings.push(
|
|
786
791
|
`no prior state existed, so there is nothing to restore: ${missingPriorState}`
|