agent-yes 1.228.0 → 1.229.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/dist/{JsonlStore-Bm5Nj6v_.js → JsonlStore-CWOA3aDH.js} +1 -1
- package/dist/{SUPPORTED_CLIS-C5d64Xqj.js → SUPPORTED_CLIS-CvLtp6LS.js} +2 -2
- package/dist/SUPPORTED_CLIS-DJEnqsp2.js +11 -0
- package/dist/{agentShare-CpglhRYb.js → agentShare-CQwvWWfd.js} +4 -4
- package/dist/{callback-B3N2VJ1i.js → callback-BFZDARQq.js} +4 -4
- package/dist/{callback-CCEQtwXX.js → callback-BN-tH9dB.js} +3 -3
- package/dist/cli.js +9 -9
- package/dist/{cwdConflictWarn-CJZRp4S0.js → cwdConflictWarn-CKUsQ5_o.js} +2 -2
- package/dist/{globalPidIndex-CoNr7tS8.js → globalPidIndex-Ca4sTr1b.js} +1 -1
- package/dist/index.js +8 -8
- package/dist/{messageLog-Bb_Ie3Ck.js → messageLog-zVGWLX7w.js} +1 -1
- package/dist/{notifyDaemon-CfQN5blR.js → notifyDaemon-CENKHs1M.js} +4 -4
- package/dist/pidStore-Cp3HekUh.js +6 -0
- package/dist/{pidStore-DCqpU_bI.js → pidStore-Hjba3K6Q.js} +3 -3
- package/dist/{reaper-BNKvujYy.js → reaper-iQXyufgp.js} +1 -1
- package/dist/{reaper-BBc2AIKA.js → reaper-pJpMYKmj.js} +1 -1
- package/dist/{runningLock-CNMl13dC.js → runningLock-BFk3Mo8K.js} +1 -1
- package/dist/{rustBinary-B8IcqSw1.js → rustBinary-BjApGser.js} +2 -2
- package/dist/{schedule-B-RjgOOR.js → schedule-D0O3sb5A.js} +9 -9
- package/dist/{serve-DwF03sgB.js → serve-m-WChRrO.js} +67 -21
- package/dist/{setup-cZFxfS89.js → setup-fuwC5wlF.js} +2 -2
- package/dist/spawnGate-5jC6eiqk.js +5 -0
- package/dist/{spawnGate-CeFYH-Ib.js → spawnGate-CMlGqVN4.js} +2 -2
- package/dist/{subcommands-DSyCT0vl.js → subcommands-DzJbnYAx.js} +12 -12
- package/dist/{subcommands-UseeaeYM.js → subcommands-lwgJxJex.js} +3 -3
- package/dist/{todoAutomation-Cwnh4aWo.js → todoAutomation-x6guaQXc.js} +115 -6
- package/dist/{todoCli-6v1CYdxN.js → todoCli-Cjhx3DqQ.js} +38 -6
- package/dist/{tray-DBt8X-Qj.js → tray-C8VfnS7s.js} +2 -2
- package/dist/{ts-Cl3CmLH9.js → ts-oYrTk4Z_.js} +103 -20
- package/dist/{versionChecker-Dhjyjxs_.js → versionChecker-CgCXc8CJ.js} +2 -2
- package/dist/{ws-Dw2fGxTI.js → ws-D5q3R1kY.js} +4 -4
- package/package.json +1 -1
- package/ts/askApi.spec.ts +358 -0
- package/ts/askApi.ts +255 -0
- package/ts/index.ts +2 -0
- package/ts/serve.ts +131 -0
- package/ts/todoBlock.spec.ts +10 -0
- package/ts/todoBlock.ts +14 -2
- package/ts/todoCli.spec.ts +101 -0
- package/ts/todoCli.ts +69 -1
- package/ts/todoStore.spec.ts +126 -0
- package/ts/todoStore.ts +130 -2
- package/dist/SUPPORTED_CLIS-Bfresz1n.js +0 -11
- package/dist/pidStore-CatWvn5W.js +0 -6
- package/dist/spawnGate-CIC7b7nm.js +0 -5
package/ts/todoStore.ts
CHANGED
|
@@ -87,6 +87,19 @@ export interface TodoRecord extends JsonlDoc {
|
|
|
87
87
|
orphanedFrom?: string;
|
|
88
88
|
/** Up to a few currently-idle agent ids suggested as replacements, computed at the moment of orphaning — a snapshot, not live; re-run reconcile for a fresh list. */
|
|
89
89
|
reassignCandidates?: string[];
|
|
90
|
+
/**
|
|
91
|
+
* Incremented every time `block` is written (set OR cleared) — never
|
|
92
|
+
* reset, never derived from `block`'s own content. Exists specifically so
|
|
93
|
+
* a caller that read a block, did other work, and now wants to clear
|
|
94
|
+
* EXACTLY that block instance (`clearBlockIfMatches`) can detect an ABA
|
|
95
|
+
* race: another process replacing the block with a byte-for-byte
|
|
96
|
+
* IDENTICAL `{type, who, question/options/actionLink}` value would be
|
|
97
|
+
* invisible to a content-only comparison, silently erasing that (distinct,
|
|
98
|
+
* newer) block instance as if it were the one originally read
|
|
99
|
+
* (codex-review round-17 Important). Absent on records written before
|
|
100
|
+
* this field existed — treated as `0`.
|
|
101
|
+
*/
|
|
102
|
+
blockRev?: number;
|
|
90
103
|
createdAt: string;
|
|
91
104
|
updatedAt: string;
|
|
92
105
|
}
|
|
@@ -621,7 +634,11 @@ export class TodoStore {
|
|
|
621
634
|
// reload-and-recompute-from-fresh dance the way `blockedBy` does below —
|
|
622
635
|
// it still goes through the write lock via `rawUpdate` for existence
|
|
623
636
|
// validation to run against a fresh record.
|
|
624
|
-
|
|
637
|
+
//
|
|
638
|
+
// `blockRev` bumps on every write regardless of whether `block` actually
|
|
639
|
+
// changed value — see the field's doc comment for why identity, not
|
|
640
|
+
// content, is what `clearBlockIfMatches` needs.
|
|
641
|
+
return this.rawUpdate(id, (fresh) => ({ block, blockRev: (fresh.blockRev ?? 0) + 1 }));
|
|
625
642
|
}
|
|
626
643
|
|
|
627
644
|
/**
|
|
@@ -641,7 +658,118 @@ export class TodoStore {
|
|
|
641
658
|
`task ${id}: block changed since this was decided (expected waiting-on-agent for "${expectedAgentId}") — not clearing`,
|
|
642
659
|
);
|
|
643
660
|
}
|
|
644
|
-
return { block: null };
|
|
661
|
+
return { block: null, blockRev: (fresh.blockRev ?? 0) + 1 };
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Clears `block` ONLY if the FRESH record's `blockRev` still equals
|
|
667
|
+
* `expectedBlockRev` — the generalized form of `clearWaitingOnAgentBlock`
|
|
668
|
+
* above, for a caller (e.g. `askApi.ts`'s `answerAsk`) that decided to
|
|
669
|
+
* clear a specific `blocked-by-human` block instance from a snapshot
|
|
670
|
+
* taken at the start of a longer operation.
|
|
671
|
+
*
|
|
672
|
+
* Compares `blockRev`, NOT the block's own content: an earlier version of
|
|
673
|
+
* this method compared `JSON.stringify(fresh.block)` against the snapshot,
|
|
674
|
+
* which missed an ABA race — another process could replace the block with
|
|
675
|
+
* a byte-for-byte IDENTICAL `{type, who, question/options/actionLink}`
|
|
676
|
+
* value (e.g. the exact same question asked again) between the caller's
|
|
677
|
+
* read and this call, and the content-only check would see no difference,
|
|
678
|
+
* silently erasing that distinct, newer block instance as if it were the
|
|
679
|
+
* one the caller actually decided to answer (codex-review round-17
|
|
680
|
+
* Important). `blockRev` increments on every write to `block` regardless
|
|
681
|
+
* of whether the new value differs from the old one, so it uniquely
|
|
682
|
+
* identifies THIS PARTICULAR block-setting event, not just its shape.
|
|
683
|
+
*
|
|
684
|
+
* Clearing unconditionally by `id` alone (or by content alone) could
|
|
685
|
+
* silently erase a genuinely different, newer block that another process
|
|
686
|
+
* set in the meantime — the task would then look unblocked even though a
|
|
687
|
+
* real, later block exists (codex-review Important). Throws instead of
|
|
688
|
+
* silently no-op-ing so the caller can report the conflict rather than
|
|
689
|
+
* claim success.
|
|
690
|
+
*/
|
|
691
|
+
clearBlockIfMatches(id: string, expectedBlockRev: number): Promise<TodoRecord> {
|
|
692
|
+
return this.rawUpdate(id, (fresh) => {
|
|
693
|
+
if ((fresh.blockRev ?? 0) !== expectedBlockRev) {
|
|
694
|
+
throw new Error(`task ${id}: block changed since this was decided — not clearing`);
|
|
695
|
+
}
|
|
696
|
+
return { block: null, blockRev: (fresh.blockRev ?? 0) + 1 };
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Atomically clears a `blocked-by-human` block AND, if `gate` is given,
|
|
702
|
+
* appends its evidence and advances the transition it names — all inside
|
|
703
|
+
* ONE `rawUpdate` write (one lock acquisition, one reload, one write).
|
|
704
|
+
*
|
|
705
|
+
* This exists specifically so `askApi.ts`'s `answerAsk()` never composes
|
|
706
|
+
* "verify blockRev, approve() a gate, transition(), clearBlockIfMatches()"
|
|
707
|
+
* as separate locked writes again. That composition (this method's
|
|
708
|
+
* predecessor) had a real gap: `approve()`/`transition()` could durably
|
|
709
|
+
* succeed — evidence appended, state advanced — and ONLY THEN could the
|
|
710
|
+
* final `clearBlockIfMatches()` fail because another process replaced the
|
|
711
|
+
* block in between. The task was left in a state no caller intended:
|
|
712
|
+
* already transitioned/evidenced for the OLD block, while `block` still
|
|
713
|
+
* held a DIFFERENT, newer `blocked-by-human` ask that was never gated at
|
|
714
|
+
* all (codex-review round-18 Important — the exact race this module's
|
|
715
|
+
* whole `blockRev` mechanism exists to close, just one step further out
|
|
716
|
+
* than `clearBlockIfMatches` alone could reach). Folding everything into
|
|
717
|
+
* one `rawUpdate` cycle makes the two outcomes (nothing changed / gate +
|
|
718
|
+
* transition + clear all changed together) the only two possible ones —
|
|
719
|
+
* there is no window in which one succeeded and the other didn't.
|
|
720
|
+
*
|
|
721
|
+
* `blockRev` re-validates `canTransition` against the FRESH state (not a
|
|
722
|
+
* value captured before this call), matching `transition()`'s own
|
|
723
|
+
* concurrent-state-drift defense — a state change unrelated to `block`
|
|
724
|
+
* (and so invisible to the `blockRev` check alone) must still be caught.
|
|
725
|
+
*
|
|
726
|
+
* Independent verification (this module's single most important
|
|
727
|
+
* invariant — see the file's own header comment) is re-checked here
|
|
728
|
+
* against the FRESH owner, exactly as `approve()` checks it: `gate`, when
|
|
729
|
+
* given, is never applied if its `validator` is the task's own owner.
|
|
730
|
+
* This method does not call `approve()` (it is not a separate write), so
|
|
731
|
+
* skipping this check here would silently let a human answer their own
|
|
732
|
+
* gated ask with no independent-verification enforcement at all.
|
|
733
|
+
*/
|
|
734
|
+
answerHumanBlock(
|
|
735
|
+
id: string,
|
|
736
|
+
expectedBlockRev: number,
|
|
737
|
+
gate: { name: string; toState: string; validator: string; note: string } | null,
|
|
738
|
+
): Promise<TodoRecord> {
|
|
739
|
+
return this.rawUpdate(id, (fresh) => {
|
|
740
|
+
if ((fresh.blockRev ?? 0) !== expectedBlockRev) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
`task ${id}: this ask has changed since it was loaded (expected blockRev ${expectedBlockRev}, current ${fresh.blockRev ?? 0}) — refresh and try again`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
const patch: Partial<Omit<TodoRecord, "_id">> = {
|
|
746
|
+
block: null,
|
|
747
|
+
blockRev: (fresh.blockRev ?? 0) + 1,
|
|
748
|
+
};
|
|
749
|
+
if (gate) {
|
|
750
|
+
if (
|
|
751
|
+
fresh.owner &&
|
|
752
|
+
fresh.owner.trim().toLowerCase() === gate.validator.trim().toLowerCase()
|
|
753
|
+
) {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`task ${id}: independent verification required — validator "${gate.validator}" is the same identity as owner "${fresh.owner}"; the worker cannot certify their own work`,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
if (!canTransition(fresh.kind, fresh.state, gate.toState)) {
|
|
759
|
+
throw new Error(
|
|
760
|
+
`task ${id}: no transition ${fresh.state} -> ${gate.toState} for kind "${fresh.kind}" (state changed concurrently) — not clearing`,
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
const evidenceEntry: GateEvidence = {
|
|
764
|
+
gate: gate.name,
|
|
765
|
+
passedAt: new Date().toISOString(),
|
|
766
|
+
validator: gate.validator,
|
|
767
|
+
note: gate.note,
|
|
768
|
+
};
|
|
769
|
+
patch.verifyEvidence = [...fresh.verifyEvidence, evidenceEntry];
|
|
770
|
+
patch.state = gate.toState;
|
|
771
|
+
}
|
|
772
|
+
return patch;
|
|
645
773
|
});
|
|
646
774
|
}
|
|
647
775
|
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import "./ts-Cl3CmLH9.js";
|
|
2
|
-
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-Dhjyjxs_.js";
|
|
4
|
-
import "./JsonlStore-Bm5Nj6v_.js";
|
|
5
|
-
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
-
import "./pidStore-DCqpU_bI.js";
|
|
7
|
-
import "./messageLog-Bb_Ie3Ck.js";
|
|
8
|
-
import "./todoAutomation-Cwnh4aWo.js";
|
|
9
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C5d64Xqj.js";
|
|
10
|
-
|
|
11
|
-
export { SUPPORTED_CLIS };
|