@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
|
@@ -112,6 +112,12 @@ jobs:
|
|
|
112
112
|
# MAESTRO_TEST_PHONE=0000000000
|
|
113
113
|
# MAESTRO_TEST_OTP=555555
|
|
114
114
|
#
|
|
115
|
+
# MAESTRO_RUN_ID namespaces the throwaway accounts and uploads a run
|
|
116
|
+
# mints (convention: tester+<runId><ordinal>) so a sweep can delete
|
|
117
|
+
# exactly one run's state. It defaults to the run's own id; uncomment to
|
|
118
|
+
# pin a shared namespace across invocations you will sweep together.
|
|
119
|
+
# run_id: 'my-sweep-namespace'
|
|
120
|
+
#
|
|
115
121
|
# Once per run, before either platform starts — resets, sweeps, seeding.
|
|
116
122
|
# pre_suite_command: |
|
|
117
123
|
# node scripts/maestro/prune-e2e-fixtures.mjs
|
package/package.json
CHANGED
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
"zod-validation-error": "^4.0.0"
|
|
188
188
|
},
|
|
189
189
|
"name": "@codyswann/lisa",
|
|
190
|
-
"version": "4.
|
|
190
|
+
"version": "4.66.0",
|
|
191
191
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
192
192
|
"main": "dist/index.js",
|
|
193
193
|
"exports": {
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
"test": "tests"
|
|
337
337
|
},
|
|
338
338
|
"types": "./dist/index.d.ts",
|
|
339
|
-
"lisaReleaseCommit": "
|
|
340
|
-
"gitHead": "
|
|
341
|
-
"lisaReleaseTag": "v4.
|
|
339
|
+
"lisaReleaseCommit": "719eb2d8b19f05e8f6dab62790533cfba50c1f32",
|
|
340
|
+
"gitHead": "719eb2d8b19f05e8f6dab62790533cfba50c1f32",
|
|
341
|
+
"lisaReleaseTag": "v4.66.0"
|
|
342
342
|
}
|
|
@@ -506,9 +506,32 @@ function reachesOut(script, foreign, bound) {
|
|
|
506
506
|
]);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
function stateFile(
|
|
509
|
+
function stateFile(bindingKey) {
|
|
510
510
|
const home = process.env.LISA_STATE_HOME || join(homedir(), ".lisa");
|
|
511
|
-
return join(home, "worktree-binding", `${
|
|
511
|
+
return join(home, "worktree-binding", `${bindingKey}.json`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The identity a binding belongs to.
|
|
516
|
+
*
|
|
517
|
+
* Parallel subagents share the session's `session_id`, so keying the binding
|
|
518
|
+
* on it alone made each subagent's guarded call read the binding a sibling
|
|
519
|
+
* recorded — the sibling's worktree then read as "changed underneath it" and
|
|
520
|
+
* its writes were refused, or an acknowledgement rebound the file out from
|
|
521
|
+
* under the sibling (CodySwannGT/lisa#4277). The payload's `agent_id` is what
|
|
522
|
+
* distinguishes a subagent from the main agent, so a caller carrying one gets
|
|
523
|
+
* its own state file. The main agent carries no `agent_id` and keeps the
|
|
524
|
+
* session-scoped key it always had, which is also the fallback for runtimes
|
|
525
|
+
* that never send the field.
|
|
526
|
+
*
|
|
527
|
+
* The key lands in a filename, so the composite is folded into a set that is
|
|
528
|
+
* legal on every filesystem — `:` is not, which rules it out as a separator
|
|
529
|
+
* and as tolerated session_id content on Windows hosts.
|
|
530
|
+
*/
|
|
531
|
+
function bindingKey(payload) {
|
|
532
|
+
const agent = payload?.agent_id;
|
|
533
|
+
if (typeof agent !== "string" || !agent) return payload.session_id;
|
|
534
|
+
return `${payload.session_id}--${agent}`.replaceAll(/[^A-Za-z0-9._-]/g, "_");
|
|
512
535
|
}
|
|
513
536
|
|
|
514
537
|
function readState(sessionId) {
|
|
@@ -640,8 +663,8 @@ function recordBaseline(sessionId, observed) {
|
|
|
640
663
|
function recordClaim(payload, observed) {
|
|
641
664
|
const claimed = claimedRoot(payload.tool_input, payload.cwd);
|
|
642
665
|
if (!claimed || claimed === observed) return;
|
|
643
|
-
const previous = readState(payload
|
|
644
|
-
writeState(payload
|
|
666
|
+
const previous = readState(bindingKey(payload));
|
|
667
|
+
writeState(bindingKey(payload), {
|
|
645
668
|
boundRoot: previous?.boundRoot ?? observed,
|
|
646
669
|
claimedRoot: claimed,
|
|
647
670
|
updatedAt: new Date().toISOString(),
|
|
@@ -681,7 +704,7 @@ function handleAcceptance(payload, observed) {
|
|
|
681
704
|
acceptanceLine(observed),
|
|
682
705
|
]);
|
|
683
706
|
}
|
|
684
|
-
writeState(payload
|
|
707
|
+
writeState(bindingKey(payload), {
|
|
685
708
|
boundRoot: observed,
|
|
686
709
|
claimedRoot: null,
|
|
687
710
|
updatedAt: new Date().toISOString(),
|
|
@@ -883,15 +906,16 @@ function evaluate(payload) {
|
|
|
883
906
|
say("payload carries no session id or cwd; binding is NOT enforced");
|
|
884
907
|
return 0;
|
|
885
908
|
}
|
|
909
|
+
const binding = bindingKey(payload);
|
|
886
910
|
const observed = worktreeRoot(cwd);
|
|
887
911
|
if (!observed) return 0;
|
|
888
912
|
|
|
889
913
|
if (payload.hook_event_name === "SessionStart") {
|
|
890
|
-
recordBaseline(
|
|
914
|
+
recordBaseline(binding, observed);
|
|
891
915
|
// After the baseline, never before: `recordBaseline` writes a fresh state
|
|
892
916
|
// for a session it has not seen, and the notice's record has to go on top
|
|
893
917
|
// of that write rather than under it.
|
|
894
|
-
noticeRuntimeDrift(
|
|
918
|
+
noticeRuntimeDrift(binding, process.env);
|
|
895
919
|
return 0;
|
|
896
920
|
}
|
|
897
921
|
if (payload.tool_name === "EnterWorktree") {
|
|
@@ -903,7 +927,7 @@ function evaluate(payload) {
|
|
|
903
927
|
const accepted = handleAcceptance(payload, observed);
|
|
904
928
|
if (accepted !== null) return accepted;
|
|
905
929
|
|
|
906
|
-
const state = readState(
|
|
930
|
+
const state = readState(binding);
|
|
907
931
|
if (!state?.boundRoot) {
|
|
908
932
|
// No baseline means SessionStart did not run for this session — an older
|
|
909
933
|
// install, a runtime that fires no such event, a harness that skips it.
|
|
@@ -912,7 +936,7 @@ function evaluate(payload) {
|
|
|
912
936
|
// refusing. Treating it as a refusal would turn a floor into a wall on
|
|
913
937
|
// every surface where the event does not fire, and a wall gets switched
|
|
914
938
|
// off, which costs the whole guard.
|
|
915
|
-
writeState(
|
|
939
|
+
writeState(binding, {
|
|
916
940
|
boundRoot: observed,
|
|
917
941
|
claimedRoot: null,
|
|
918
942
|
updatedAt: new Date().toISOString(),
|
|
@@ -923,7 +947,7 @@ function evaluate(payload) {
|
|
|
923
947
|
return unconfirmedSwitch(state.claimedRoot, observed);
|
|
924
948
|
}
|
|
925
949
|
if (state.claimedRoot === observed) {
|
|
926
|
-
writeState(
|
|
950
|
+
writeState(binding, {
|
|
927
951
|
boundRoot: observed,
|
|
928
952
|
claimedRoot: null,
|
|
929
953
|
updatedAt: new Date().toISOString(),
|
|
@@ -506,9 +506,32 @@ function reachesOut(script, foreign, bound) {
|
|
|
506
506
|
]);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
function stateFile(
|
|
509
|
+
function stateFile(bindingKey) {
|
|
510
510
|
const home = process.env.LISA_STATE_HOME || join(homedir(), ".lisa");
|
|
511
|
-
return join(home, "worktree-binding", `${
|
|
511
|
+
return join(home, "worktree-binding", `${bindingKey}.json`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The identity a binding belongs to.
|
|
516
|
+
*
|
|
517
|
+
* Parallel subagents share the session's `session_id`, so keying the binding
|
|
518
|
+
* on it alone made each subagent's guarded call read the binding a sibling
|
|
519
|
+
* recorded — the sibling's worktree then read as "changed underneath it" and
|
|
520
|
+
* its writes were refused, or an acknowledgement rebound the file out from
|
|
521
|
+
* under the sibling (CodySwannGT/lisa#4277). The payload's `agent_id` is what
|
|
522
|
+
* distinguishes a subagent from the main agent, so a caller carrying one gets
|
|
523
|
+
* its own state file. The main agent carries no `agent_id` and keeps the
|
|
524
|
+
* session-scoped key it always had, which is also the fallback for runtimes
|
|
525
|
+
* that never send the field.
|
|
526
|
+
*
|
|
527
|
+
* The key lands in a filename, so the composite is folded into a set that is
|
|
528
|
+
* legal on every filesystem — `:` is not, which rules it out as a separator
|
|
529
|
+
* and as tolerated session_id content on Windows hosts.
|
|
530
|
+
*/
|
|
531
|
+
function bindingKey(payload) {
|
|
532
|
+
const agent = payload?.agent_id;
|
|
533
|
+
if (typeof agent !== "string" || !agent) return payload.session_id;
|
|
534
|
+
return `${payload.session_id}--${agent}`.replaceAll(/[^A-Za-z0-9._-]/g, "_");
|
|
512
535
|
}
|
|
513
536
|
|
|
514
537
|
function readState(sessionId) {
|
|
@@ -640,8 +663,8 @@ function recordBaseline(sessionId, observed) {
|
|
|
640
663
|
function recordClaim(payload, observed) {
|
|
641
664
|
const claimed = claimedRoot(payload.tool_input, payload.cwd);
|
|
642
665
|
if (!claimed || claimed === observed) return;
|
|
643
|
-
const previous = readState(payload
|
|
644
|
-
writeState(payload
|
|
666
|
+
const previous = readState(bindingKey(payload));
|
|
667
|
+
writeState(bindingKey(payload), {
|
|
645
668
|
boundRoot: previous?.boundRoot ?? observed,
|
|
646
669
|
claimedRoot: claimed,
|
|
647
670
|
updatedAt: new Date().toISOString(),
|
|
@@ -681,7 +704,7 @@ function handleAcceptance(payload, observed) {
|
|
|
681
704
|
acceptanceLine(observed),
|
|
682
705
|
]);
|
|
683
706
|
}
|
|
684
|
-
writeState(payload
|
|
707
|
+
writeState(bindingKey(payload), {
|
|
685
708
|
boundRoot: observed,
|
|
686
709
|
claimedRoot: null,
|
|
687
710
|
updatedAt: new Date().toISOString(),
|
|
@@ -883,15 +906,16 @@ function evaluate(payload) {
|
|
|
883
906
|
say("payload carries no session id or cwd; binding is NOT enforced");
|
|
884
907
|
return 0;
|
|
885
908
|
}
|
|
909
|
+
const binding = bindingKey(payload);
|
|
886
910
|
const observed = worktreeRoot(cwd);
|
|
887
911
|
if (!observed) return 0;
|
|
888
912
|
|
|
889
913
|
if (payload.hook_event_name === "SessionStart") {
|
|
890
|
-
recordBaseline(
|
|
914
|
+
recordBaseline(binding, observed);
|
|
891
915
|
// After the baseline, never before: `recordBaseline` writes a fresh state
|
|
892
916
|
// for a session it has not seen, and the notice's record has to go on top
|
|
893
917
|
// of that write rather than under it.
|
|
894
|
-
noticeRuntimeDrift(
|
|
918
|
+
noticeRuntimeDrift(binding, process.env);
|
|
895
919
|
return 0;
|
|
896
920
|
}
|
|
897
921
|
if (payload.tool_name === "EnterWorktree") {
|
|
@@ -903,7 +927,7 @@ function evaluate(payload) {
|
|
|
903
927
|
const accepted = handleAcceptance(payload, observed);
|
|
904
928
|
if (accepted !== null) return accepted;
|
|
905
929
|
|
|
906
|
-
const state = readState(
|
|
930
|
+
const state = readState(binding);
|
|
907
931
|
if (!state?.boundRoot) {
|
|
908
932
|
// No baseline means SessionStart did not run for this session — an older
|
|
909
933
|
// install, a runtime that fires no such event, a harness that skips it.
|
|
@@ -912,7 +936,7 @@ function evaluate(payload) {
|
|
|
912
936
|
// refusing. Treating it as a refusal would turn a floor into a wall on
|
|
913
937
|
// every surface where the event does not fire, and a wall gets switched
|
|
914
938
|
// off, which costs the whole guard.
|
|
915
|
-
writeState(
|
|
939
|
+
writeState(binding, {
|
|
916
940
|
boundRoot: observed,
|
|
917
941
|
claimedRoot: null,
|
|
918
942
|
updatedAt: new Date().toISOString(),
|
|
@@ -923,7 +947,7 @@ function evaluate(payload) {
|
|
|
923
947
|
return unconfirmedSwitch(state.claimedRoot, observed);
|
|
924
948
|
}
|
|
925
949
|
if (state.claimedRoot === observed) {
|
|
926
|
-
writeState(
|
|
950
|
+
writeState(binding, {
|
|
927
951
|
boundRoot: observed,
|
|
928
952
|
claimedRoot: null,
|
|
929
953
|
updatedAt: new Date().toISOString(),
|
|
@@ -506,9 +506,32 @@ function reachesOut(script, foreign, bound) {
|
|
|
506
506
|
]);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
function stateFile(
|
|
509
|
+
function stateFile(bindingKey) {
|
|
510
510
|
const home = process.env.LISA_STATE_HOME || join(homedir(), ".lisa");
|
|
511
|
-
return join(home, "worktree-binding", `${
|
|
511
|
+
return join(home, "worktree-binding", `${bindingKey}.json`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The identity a binding belongs to.
|
|
516
|
+
*
|
|
517
|
+
* Parallel subagents share the session's `session_id`, so keying the binding
|
|
518
|
+
* on it alone made each subagent's guarded call read the binding a sibling
|
|
519
|
+
* recorded — the sibling's worktree then read as "changed underneath it" and
|
|
520
|
+
* its writes were refused, or an acknowledgement rebound the file out from
|
|
521
|
+
* under the sibling (CodySwannGT/lisa#4277). The payload's `agent_id` is what
|
|
522
|
+
* distinguishes a subagent from the main agent, so a caller carrying one gets
|
|
523
|
+
* its own state file. The main agent carries no `agent_id` and keeps the
|
|
524
|
+
* session-scoped key it always had, which is also the fallback for runtimes
|
|
525
|
+
* that never send the field.
|
|
526
|
+
*
|
|
527
|
+
* The key lands in a filename, so the composite is folded into a set that is
|
|
528
|
+
* legal on every filesystem — `:` is not, which rules it out as a separator
|
|
529
|
+
* and as tolerated session_id content on Windows hosts.
|
|
530
|
+
*/
|
|
531
|
+
function bindingKey(payload) {
|
|
532
|
+
const agent = payload?.agent_id;
|
|
533
|
+
if (typeof agent !== "string" || !agent) return payload.session_id;
|
|
534
|
+
return `${payload.session_id}--${agent}`.replaceAll(/[^A-Za-z0-9._-]/g, "_");
|
|
512
535
|
}
|
|
513
536
|
|
|
514
537
|
function readState(sessionId) {
|
|
@@ -640,8 +663,8 @@ function recordBaseline(sessionId, observed) {
|
|
|
640
663
|
function recordClaim(payload, observed) {
|
|
641
664
|
const claimed = claimedRoot(payload.tool_input, payload.cwd);
|
|
642
665
|
if (!claimed || claimed === observed) return;
|
|
643
|
-
const previous = readState(payload
|
|
644
|
-
writeState(payload
|
|
666
|
+
const previous = readState(bindingKey(payload));
|
|
667
|
+
writeState(bindingKey(payload), {
|
|
645
668
|
boundRoot: previous?.boundRoot ?? observed,
|
|
646
669
|
claimedRoot: claimed,
|
|
647
670
|
updatedAt: new Date().toISOString(),
|
|
@@ -681,7 +704,7 @@ function handleAcceptance(payload, observed) {
|
|
|
681
704
|
acceptanceLine(observed),
|
|
682
705
|
]);
|
|
683
706
|
}
|
|
684
|
-
writeState(payload
|
|
707
|
+
writeState(bindingKey(payload), {
|
|
685
708
|
boundRoot: observed,
|
|
686
709
|
claimedRoot: null,
|
|
687
710
|
updatedAt: new Date().toISOString(),
|
|
@@ -883,15 +906,16 @@ function evaluate(payload) {
|
|
|
883
906
|
say("payload carries no session id or cwd; binding is NOT enforced");
|
|
884
907
|
return 0;
|
|
885
908
|
}
|
|
909
|
+
const binding = bindingKey(payload);
|
|
886
910
|
const observed = worktreeRoot(cwd);
|
|
887
911
|
if (!observed) return 0;
|
|
888
912
|
|
|
889
913
|
if (payload.hook_event_name === "SessionStart") {
|
|
890
|
-
recordBaseline(
|
|
914
|
+
recordBaseline(binding, observed);
|
|
891
915
|
// After the baseline, never before: `recordBaseline` writes a fresh state
|
|
892
916
|
// for a session it has not seen, and the notice's record has to go on top
|
|
893
917
|
// of that write rather than under it.
|
|
894
|
-
noticeRuntimeDrift(
|
|
918
|
+
noticeRuntimeDrift(binding, process.env);
|
|
895
919
|
return 0;
|
|
896
920
|
}
|
|
897
921
|
if (payload.tool_name === "EnterWorktree") {
|
|
@@ -903,7 +927,7 @@ function evaluate(payload) {
|
|
|
903
927
|
const accepted = handleAcceptance(payload, observed);
|
|
904
928
|
if (accepted !== null) return accepted;
|
|
905
929
|
|
|
906
|
-
const state = readState(
|
|
930
|
+
const state = readState(binding);
|
|
907
931
|
if (!state?.boundRoot) {
|
|
908
932
|
// No baseline means SessionStart did not run for this session — an older
|
|
909
933
|
// install, a runtime that fires no such event, a harness that skips it.
|
|
@@ -912,7 +936,7 @@ function evaluate(payload) {
|
|
|
912
936
|
// refusing. Treating it as a refusal would turn a floor into a wall on
|
|
913
937
|
// every surface where the event does not fire, and a wall gets switched
|
|
914
938
|
// off, which costs the whole guard.
|
|
915
|
-
writeState(
|
|
939
|
+
writeState(binding, {
|
|
916
940
|
boundRoot: observed,
|
|
917
941
|
claimedRoot: null,
|
|
918
942
|
updatedAt: new Date().toISOString(),
|
|
@@ -923,7 +947,7 @@ function evaluate(payload) {
|
|
|
923
947
|
return unconfirmedSwitch(state.claimedRoot, observed);
|
|
924
948
|
}
|
|
925
949
|
if (state.claimedRoot === observed) {
|
|
926
|
-
writeState(
|
|
950
|
+
writeState(binding, {
|
|
927
951
|
boundRoot: observed,
|
|
928
952
|
claimedRoot: null,
|
|
929
953
|
updatedAt: new Date().toISOString(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.66.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.66.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.66.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.66.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.66.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|