@codyswann/lisa 4.65.8 → 4.66.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/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +34 -10
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +8 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +5 -3
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/create-only/.github/workflows/maestro-e2e.yml +6 -0
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +34 -10
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +34 -10
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +34 -10
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/worktree-binding-guard.mjs +34 -10
|
@@ -503,9 +503,32 @@ function reachesOut(script, foreign, bound) {
|
|
|
503
503
|
]);
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
-
function stateFile(
|
|
506
|
+
function stateFile(bindingKey) {
|
|
507
507
|
const home = process.env.LISA_STATE_HOME || join(homedir(), ".lisa");
|
|
508
|
-
return join(home, "worktree-binding", `${
|
|
508
|
+
return join(home, "worktree-binding", `${bindingKey}.json`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* The identity a binding belongs to.
|
|
513
|
+
*
|
|
514
|
+
* Parallel subagents share the session's `session_id`, so keying the binding
|
|
515
|
+
* on it alone made each subagent's guarded call read the binding a sibling
|
|
516
|
+
* recorded — the sibling's worktree then read as "changed underneath it" and
|
|
517
|
+
* its writes were refused, or an acknowledgement rebound the file out from
|
|
518
|
+
* under the sibling (CodySwannGT/lisa#4277). The payload's `agent_id` is what
|
|
519
|
+
* distinguishes a subagent from the main agent, so a caller carrying one gets
|
|
520
|
+
* its own state file. The main agent carries no `agent_id` and keeps the
|
|
521
|
+
* session-scoped key it always had, which is also the fallback for runtimes
|
|
522
|
+
* that never send the field.
|
|
523
|
+
*
|
|
524
|
+
* The key lands in a filename, so the composite is folded into a set that is
|
|
525
|
+
* legal on every filesystem — `:` is not, which rules it out as a separator
|
|
526
|
+
* and as tolerated session_id content on Windows hosts.
|
|
527
|
+
*/
|
|
528
|
+
function bindingKey(payload) {
|
|
529
|
+
const agent = payload?.agent_id;
|
|
530
|
+
if (typeof agent !== "string" || !agent) return payload.session_id;
|
|
531
|
+
return `${payload.session_id}--${agent}`.replaceAll(/[^A-Za-z0-9._-]/g, "_");
|
|
509
532
|
}
|
|
510
533
|
|
|
511
534
|
function readState(sessionId) {
|
|
@@ -637,8 +660,8 @@ function recordBaseline(sessionId, observed) {
|
|
|
637
660
|
function recordClaim(payload, observed) {
|
|
638
661
|
const claimed = claimedRoot(payload.tool_input, payload.cwd);
|
|
639
662
|
if (!claimed || claimed === observed) return;
|
|
640
|
-
const previous = readState(payload
|
|
641
|
-
writeState(payload
|
|
663
|
+
const previous = readState(bindingKey(payload));
|
|
664
|
+
writeState(bindingKey(payload), {
|
|
642
665
|
boundRoot: previous?.boundRoot ?? observed,
|
|
643
666
|
claimedRoot: claimed,
|
|
644
667
|
updatedAt: new Date().toISOString(),
|
|
@@ -678,7 +701,7 @@ function handleAcceptance(payload, observed) {
|
|
|
678
701
|
acceptanceLine(observed),
|
|
679
702
|
]);
|
|
680
703
|
}
|
|
681
|
-
writeState(payload
|
|
704
|
+
writeState(bindingKey(payload), {
|
|
682
705
|
boundRoot: observed,
|
|
683
706
|
claimedRoot: null,
|
|
684
707
|
updatedAt: new Date().toISOString(),
|
|
@@ -880,15 +903,16 @@ function evaluate(payload) {
|
|
|
880
903
|
say("payload carries no session id or cwd; binding is NOT enforced");
|
|
881
904
|
return 0;
|
|
882
905
|
}
|
|
906
|
+
const binding = bindingKey(payload);
|
|
883
907
|
const observed = worktreeRoot(cwd);
|
|
884
908
|
if (!observed) return 0;
|
|
885
909
|
|
|
886
910
|
if (payload.hook_event_name === "SessionStart") {
|
|
887
|
-
recordBaseline(
|
|
911
|
+
recordBaseline(binding, observed);
|
|
888
912
|
// After the baseline, never before: `recordBaseline` writes a fresh state
|
|
889
913
|
// for a session it has not seen, and the notice's record has to go on top
|
|
890
914
|
// of that write rather than under it.
|
|
891
|
-
noticeRuntimeDrift(
|
|
915
|
+
noticeRuntimeDrift(binding, process.env);
|
|
892
916
|
return 0;
|
|
893
917
|
}
|
|
894
918
|
if (payload.tool_name === "EnterWorktree") {
|
|
@@ -900,7 +924,7 @@ function evaluate(payload) {
|
|
|
900
924
|
const accepted = handleAcceptance(payload, observed);
|
|
901
925
|
if (accepted !== null) return accepted;
|
|
902
926
|
|
|
903
|
-
const state = readState(
|
|
927
|
+
const state = readState(binding);
|
|
904
928
|
if (!state?.boundRoot) {
|
|
905
929
|
// No baseline means SessionStart did not run for this session — an older
|
|
906
930
|
// install, a runtime that fires no such event, a harness that skips it.
|
|
@@ -909,7 +933,7 @@ function evaluate(payload) {
|
|
|
909
933
|
// refusing. Treating it as a refusal would turn a floor into a wall on
|
|
910
934
|
// every surface where the event does not fire, and a wall gets switched
|
|
911
935
|
// off, which costs the whole guard.
|
|
912
|
-
writeState(
|
|
936
|
+
writeState(binding, {
|
|
913
937
|
boundRoot: observed,
|
|
914
938
|
claimedRoot: null,
|
|
915
939
|
updatedAt: new Date().toISOString(),
|
|
@@ -920,7 +944,7 @@ function evaluate(payload) {
|
|
|
920
944
|
return unconfirmedSwitch(state.claimedRoot, observed);
|
|
921
945
|
}
|
|
922
946
|
if (state.claimedRoot === observed) {
|
|
923
|
-
writeState(
|
|
947
|
+
writeState(binding, {
|
|
924
948
|
boundRoot: observed,
|
|
925
949
|
claimedRoot: null,
|
|
926
950
|
updatedAt: new Date().toISOString(),
|