@codyswann/lisa 4.80.2 → 4.81.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-contents/gitignore +5 -0
- package/dist/cli/doctor-session-manager-trust.d.ts +62 -0
- package/dist/cli/doctor-session-manager-trust.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-trust.js +174 -0
- package/dist/cli/doctor-session-manager-trust.js.map +1 -0
- package/dist/cli/doctor-session-manager.d.ts +35 -0
- package/dist/cli/doctor-session-manager.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager.js +209 -0
- package/dist/cli/doctor-session-manager.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/lisa-test-run-bootstrap.js +28 -0
- package/dist/cli/lisa-test-run-bootstrap.js.map +1 -1
- package/dist/cli/session-manager-config-merge.d.ts +25 -0
- package/dist/cli/session-manager-config-merge.d.ts.map +1 -0
- package/dist/cli/session-manager-config-merge.js +54 -0
- package/dist/cli/session-manager-config-merge.js.map +1 -0
- 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 +257 -12
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- 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/.codex-plugin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +287 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +158 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa/commands/implement.md +2 -2
- package/plugins/lisa/commands/session-manager.md +7 -0
- package/plugins/lisa/commands/setup/session-manager.md +6 -0
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-agy/commands/lisa/implement.md +2 -2
- package/plugins/lisa-agy/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-agy/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-agy/skills/lisa-tear-down-automations/SKILL.md +10 -0
- 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-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/commands/lisa/implement.md +2 -2
- package/plugins/lisa-copilot/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-copilot/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/implement.md +2 -2
- package/plugins/lisa-cursor/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +14 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +58 -0
- package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-cursor/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-devin/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-devin/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- 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-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-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-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-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-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-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-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-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-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-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-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-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-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-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/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/commands/implement.md +2 -2
- package/plugins/src/base/commands/session-manager.md +7 -0
- package/plugins/src/base/commands/setup/session-manager.md +6 -0
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/src/base/rules/reference/config-resolution.md +58 -0
- package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/src/base/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/src/base/scripts/ready-queue-order.mjs +352 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/src/base/skills/lisa-tear-down-automations/SKILL.md +10 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker bookkeeping for the session manager: names, live detection, spawn
|
|
3
|
+
* argv and stall classification.
|
|
4
|
+
*
|
|
5
|
+
* A worker is a background Claude Code session (`claude --bg`) running
|
|
6
|
+
* `/lisa:implement <REF> managedBy=<manager>` for one work item. The manager
|
|
7
|
+
* never keeps a handle on a worker; it rediscovers every worker each cycle
|
|
8
|
+
* from `claude agents --json` by name. So the name is the join key between the
|
|
9
|
+
* tracker item, the ledger row and the live session, and everything here is
|
|
10
|
+
* about keeping that join exact.
|
|
11
|
+
*
|
|
12
|
+
* Every export is pure. `session-manager.mjs` owns the real `process.env`,
|
|
13
|
+
* `child_process` and clock, and passes them in.
|
|
14
|
+
* @module workers
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Settings blob every worker gets, so the manager's messages reach it. */
|
|
18
|
+
export const WORKER_SETTINGS = JSON.stringify({
|
|
19
|
+
crossSessionInbound: "accept",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Stall policy used when the caller passes none. Mirrors the
|
|
24
|
+
* `sessionManager.stall` defaults, so a missing config block still classifies.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_STALL = Object.freeze({
|
|
27
|
+
noOutputMinutes: 60,
|
|
28
|
+
requestionHours: 4,
|
|
29
|
+
maxAttempts: 3,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/** Every worker session name starts with this. Anything else is not ours. */
|
|
33
|
+
const WORKER_PREFIX = "wk-";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `wk-<token>-<MMDD>-<HHMM>[-<n>]`, read from the right. The token is greedy,
|
|
37
|
+
* so it is everything before the LAST stamp group: a ref whose number has four
|
|
38
|
+
* or more digits (`PROJ-1234`) or that itself ends in `-dddd-dddd` keeps its
|
|
39
|
+
* whole token. A lazy token read `wk-PROJ-1234-1012-1530` as `PROJ`, and the
|
|
40
|
+
* live-worker guard never fired. The collision suffix is capped at three
|
|
41
|
+
* digits so it can never be mistaken for the `HHMM` half of a stamp. The name
|
|
42
|
+
* is anchored so a token is compared exactly — never by `startsWith`, which
|
|
43
|
+
* would let an `ABC-12` worker answer for `ABC-1`.
|
|
44
|
+
*/
|
|
45
|
+
const WORKER_NAME_PATTERN = /^wk-(.+)-(\d{4}-\d{4})(?:-([1-9]\d{0,2}))?$/;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* `claude agents --json --all` states a finished row carries. Never enough on
|
|
49
|
+
* its own: an idle worker whose turn ended is alive but also reads `done`.
|
|
50
|
+
*/
|
|
51
|
+
const TERMINAL_AGENT_STATES = new Set(["done", "stopped"]);
|
|
52
|
+
|
|
53
|
+
/** Ledger statuses that mean the worker reported its own outcome. */
|
|
54
|
+
const FINISHED_STATUSES = new Set(["DONE", "FAILED"]);
|
|
55
|
+
|
|
56
|
+
/** The `managedBy=` value contract `lisa-implement` enforces. */
|
|
57
|
+
const MANAGER_NAME_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
|
|
58
|
+
|
|
59
|
+
/** A full session UUID, which `--resume` accepts directly. */
|
|
60
|
+
const SESSION_UUID_PATTERN =
|
|
61
|
+
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
|
|
62
|
+
|
|
63
|
+
/** The short id `claude --bg` prints and `claude agents` lists. */
|
|
64
|
+
const SHORT_ID_PATTERN = /\b[0-9a-f]{8}\b/i;
|
|
65
|
+
|
|
66
|
+
const MINUTE_MS = 60_000;
|
|
67
|
+
const HOUR_MS = 60 * MINUTE_MS;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reduce a work-item ref to the `[A-Za-z0-9-]` token used in worker names.
|
|
71
|
+
*
|
|
72
|
+
* A GitHub ref `owner/repo#12` becomes `repo-12`: the owner adds nothing
|
|
73
|
+
* inside one project and `/` and `#` are not safe in a session name. Jira and
|
|
74
|
+
* Linear keys already fit and pass through unchanged.
|
|
75
|
+
* @param {string} ref - Tracker ref as the queue reported it
|
|
76
|
+
* @returns {string} Name-safe token
|
|
77
|
+
*/
|
|
78
|
+
export function sanitizeRefToken(ref) {
|
|
79
|
+
if (typeof ref !== "string") {
|
|
80
|
+
throw new TypeError(`ref must be a string, got ${typeof ref}`);
|
|
81
|
+
}
|
|
82
|
+
const github = /^(?:[^/\s]+\/)?([^/#\s]*)#(\d+)$/.exec(ref.trim());
|
|
83
|
+
const raw = github ? [github[1], github[2]].filter(Boolean).join("-") : ref;
|
|
84
|
+
const token = raw
|
|
85
|
+
.replace(/[^A-Za-z0-9-]+/g, "-")
|
|
86
|
+
.replace(/-{2,}/g, "-")
|
|
87
|
+
.replace(/^-|-$/g, "");
|
|
88
|
+
if (token === "") {
|
|
89
|
+
throw new Error(`ref ${JSON.stringify(ref)} yields an empty worker token`);
|
|
90
|
+
}
|
|
91
|
+
return token;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Format a clock reading as local `MMDD-HHMM`.
|
|
96
|
+
* @param {Date | number} now - Clock reading
|
|
97
|
+
* @returns {string} Stamp
|
|
98
|
+
*/
|
|
99
|
+
function formatStamp(now) {
|
|
100
|
+
const date = now instanceof Date ? now : new Date(now);
|
|
101
|
+
const pad = value => String(value).padStart(2, "0");
|
|
102
|
+
return `${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Name a new worker for `ref`, unique against every name already known.
|
|
107
|
+
*
|
|
108
|
+
* A retry in the same minute would otherwise reuse the name of the worker it
|
|
109
|
+
* replaces, and `claude agents` could no longer tell them apart, so a
|
|
110
|
+
* collision gets `-2`, `-3`, ….
|
|
111
|
+
* @param {string} ref - Tracker ref
|
|
112
|
+
* @param {Date | number} now - Clock reading
|
|
113
|
+
* @param {Iterable<string>} [existingNames] - Every session name in use or on record
|
|
114
|
+
* @returns {string} Worker session name
|
|
115
|
+
*/
|
|
116
|
+
export function buildWorkerName(ref, now, existingNames = []) {
|
|
117
|
+
const known = new Set(existingNames);
|
|
118
|
+
const base = `${WORKER_PREFIX}${sanitizeRefToken(ref)}-${formatStamp(now)}`;
|
|
119
|
+
if (!known.has(base)) return base;
|
|
120
|
+
const suffix = Array.from(
|
|
121
|
+
{ length: known.size + 1 },
|
|
122
|
+
(_, index) => index + 2
|
|
123
|
+
).find(candidate => !known.has(`${base}-${candidate}`));
|
|
124
|
+
return `${base}-${suffix}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Split a worker session name into its parts.
|
|
129
|
+
* @param {string} name - Session name
|
|
130
|
+
* @returns {{token: string, stamp: string, suffix: number | null} | null} Parts, or null when the name is not a worker's
|
|
131
|
+
*/
|
|
132
|
+
export function parseWorkerName(name) {
|
|
133
|
+
const match =
|
|
134
|
+
typeof name === "string" ? WORKER_NAME_PATTERN.exec(name) : null;
|
|
135
|
+
if (!match) return null;
|
|
136
|
+
return {
|
|
137
|
+
token: match[1],
|
|
138
|
+
stamp: match[2],
|
|
139
|
+
suffix: match[3] === undefined ? null : Number(match[3]),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Read a value only when it is a string.
|
|
145
|
+
* @param {unknown} value - Candidate
|
|
146
|
+
* @returns {string | null} The string, or null
|
|
147
|
+
*/
|
|
148
|
+
function stringOrNull(value) {
|
|
149
|
+
return typeof value === "string" ? value : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Normalize `claude agents --json` output (with or without `--all`).
|
|
154
|
+
*
|
|
155
|
+
* The shapes differ: an active row carries `{pid,status}`; a completed row
|
|
156
|
+
* from `--all` carries `{id,state}` with `state` of `done` or `stopped` and no
|
|
157
|
+
* `pid` or `status`. Claude Code 2.1.296 also prints hybrids: an idle worker
|
|
158
|
+
* whose turn ended is `{pid,status:"idle",state:"done"}` while its process is
|
|
159
|
+
* alive, a worker waiting on a question is `state:"blocked"`, and a worker
|
|
160
|
+
* that has just been spawned is `{pid:null,status:null,state:"working"}`. So a
|
|
161
|
+
* row is alive when it has a pid or a status (the active shape) whatever its
|
|
162
|
+
* `state`, or when its state is not terminal; only a row with neither, in a
|
|
163
|
+
* terminal state, has finished. Reading `state` alone undercounted capacity,
|
|
164
|
+
* let `spawn` duplicate a live worker and made `classify` say `dead`.
|
|
165
|
+
* Every row comes back with all fields present (null when absent) and an
|
|
166
|
+
* `active` flag, so callers never branch on shape.
|
|
167
|
+
* @param {unknown} parsed - `JSON.parse` of the command's stdout
|
|
168
|
+
* @returns {Array<{name: string|null, sessionId: string|null, id: string|null, pid: number|null, cwd: string|null, kind: string|null, startedAt: number|null, status: string|null, state: string|null, active: boolean}>} Rows
|
|
169
|
+
*/
|
|
170
|
+
export function normalizeAgents(parsed) {
|
|
171
|
+
if (!Array.isArray(parsed)) {
|
|
172
|
+
throw new TypeError("claude agents --json must return an array");
|
|
173
|
+
}
|
|
174
|
+
return parsed
|
|
175
|
+
.filter(row => row !== null && typeof row === "object")
|
|
176
|
+
.map(row => {
|
|
177
|
+
const state = stringOrNull(row.state);
|
|
178
|
+
const pid = typeof row.pid === "number" ? row.pid : null;
|
|
179
|
+
const status = stringOrNull(row.status);
|
|
180
|
+
const activeShape = pid !== null || status !== null;
|
|
181
|
+
return {
|
|
182
|
+
name: stringOrNull(row.name),
|
|
183
|
+
sessionId: stringOrNull(row.sessionId),
|
|
184
|
+
id: stringOrNull(row.id),
|
|
185
|
+
pid,
|
|
186
|
+
cwd: stringOrNull(row.cwd),
|
|
187
|
+
kind: stringOrNull(row.kind),
|
|
188
|
+
startedAt: typeof row.startedAt === "number" ? row.startedAt : null,
|
|
189
|
+
status,
|
|
190
|
+
state,
|
|
191
|
+
active:
|
|
192
|
+
activeShape || state === null || !TERMINAL_AGENT_STATES.has(state),
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether a normalized row is a running worker.
|
|
199
|
+
* @param {{active: boolean, name: string|null}} agent - Normalized row
|
|
200
|
+
* @returns {boolean} True for an active `wk-` session
|
|
201
|
+
*/
|
|
202
|
+
function isRunningWorker(agent) {
|
|
203
|
+
return agent.active && parseWorkerName(agent.name) !== null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Count running workers, the `running` input to capacity.
|
|
208
|
+
* @param {ReturnType<typeof normalizeAgents>} agents - Normalized rows
|
|
209
|
+
* @returns {number} Active `wk-` sessions
|
|
210
|
+
*/
|
|
211
|
+
export function countRunningWorkers(agents) {
|
|
212
|
+
return agents.filter(isRunningWorker).length;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Find the running worker for `ref`, by exact token.
|
|
217
|
+
* @param {ReturnType<typeof normalizeAgents>} agents - Normalized rows
|
|
218
|
+
* @param {string} ref - Tracker ref
|
|
219
|
+
* @returns {ReturnType<typeof normalizeAgents>[number] | null} The row, or null
|
|
220
|
+
*/
|
|
221
|
+
export function findLiveWorker(agents, ref) {
|
|
222
|
+
const token = sanitizeRefToken(ref);
|
|
223
|
+
return (
|
|
224
|
+
agents.find(
|
|
225
|
+
agent =>
|
|
226
|
+
isRunningWorker(agent) && parseWorkerName(agent.name)?.token === token
|
|
227
|
+
) ?? null
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Copy an environment without any `TELEGRAM_*` key.
|
|
233
|
+
*
|
|
234
|
+
* The manager runs with `TELEGRAM_STATE_DIR` (and possibly
|
|
235
|
+
* `TELEGRAM_BOT_TOKEN`) set. A worker that inherited either could start a
|
|
236
|
+
* second poller on the same bot and take the manager's messages, so every
|
|
237
|
+
* `TELEGRAM_*` key goes — a superset of the two that matter today.
|
|
238
|
+
* @param {Record<string, string | undefined>} env - Source environment
|
|
239
|
+
* @returns {Record<string, string | undefined>} New environment
|
|
240
|
+
*/
|
|
241
|
+
export function scrubWorkerEnv(env) {
|
|
242
|
+
return Object.fromEntries(
|
|
243
|
+
Object.entries(env).filter(([key]) => !key.startsWith("TELEGRAM_"))
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Throw unless `value` is a non-empty string.
|
|
249
|
+
* @param {string} label - Field name for the message
|
|
250
|
+
* @param {unknown} value - Candidate
|
|
251
|
+
*/
|
|
252
|
+
function requireText(label, value) {
|
|
253
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
254
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Build the spawn for a new worker. Passed as argv to `spawnSync`, never a
|
|
260
|
+
* shell string, so no ref or name is ever interpreted by a shell.
|
|
261
|
+
*
|
|
262
|
+
* The prompt is exactly `/lisa:implement <REF> managedBy=<manager>`: any
|
|
263
|
+
* trailing prose would make `lisa-track` classify the arguments as free text.
|
|
264
|
+
* @param {{ref: string, name: string, manager: string, repoDir: string, env: Record<string, string | undefined>}} input - Spawn inputs
|
|
265
|
+
* @returns {{command: string, args: string[], cwd: string, env: Record<string, string | undefined>}} Spawn spec
|
|
266
|
+
*/
|
|
267
|
+
export function buildWorkerSpawn({ ref, name, manager, repoDir, env }) {
|
|
268
|
+
requireText("ref", ref);
|
|
269
|
+
if (/\s/.test(ref)) {
|
|
270
|
+
throw new Error(`ref ${JSON.stringify(ref)} must not contain whitespace`);
|
|
271
|
+
}
|
|
272
|
+
if (typeof manager !== "string" || !MANAGER_NAME_PATTERN.test(manager)) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`manager ${JSON.stringify(manager)} must match ${MANAGER_NAME_PATTERN}`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
if (parseWorkerName(name) === null) {
|
|
278
|
+
throw new Error(`name ${JSON.stringify(name)} is not a worker name`);
|
|
279
|
+
}
|
|
280
|
+
requireText("repoDir", repoDir);
|
|
281
|
+
return {
|
|
282
|
+
command: "claude",
|
|
283
|
+
args: [
|
|
284
|
+
"--bg",
|
|
285
|
+
"-n",
|
|
286
|
+
name,
|
|
287
|
+
"--dangerously-skip-permissions",
|
|
288
|
+
"--settings",
|
|
289
|
+
WORKER_SETTINGS,
|
|
290
|
+
`/lisa:implement ${ref} managedBy=${manager}`,
|
|
291
|
+
],
|
|
292
|
+
cwd: repoDir,
|
|
293
|
+
env: scrubWorkerEnv(env),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Build the spawn that resumes a worker with one message (an answer, or a
|
|
299
|
+
* nudge after a stall), with the same cwd and environment scrub as a spawn.
|
|
300
|
+
* @param {{sessionId: string, text: string, repoDir: string, env: Record<string, string | undefined>}} input - Resume inputs
|
|
301
|
+
* @returns {{command: string, args: string[], cwd: string, env: Record<string, string | undefined>}} Spawn spec
|
|
302
|
+
*/
|
|
303
|
+
export function buildWorkerResume({ sessionId, text, repoDir, env }) {
|
|
304
|
+
requireText("sessionId", sessionId);
|
|
305
|
+
if (sessionId.startsWith("-")) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`sessionId ${JSON.stringify(sessionId)} must not look like a flag`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
requireText("text", text);
|
|
311
|
+
requireText("repoDir", repoDir);
|
|
312
|
+
return {
|
|
313
|
+
command: "claude",
|
|
314
|
+
// `--` ends option parsing: an answer that starts with `--` (claude
|
|
315
|
+
// 2.1.296 reads `--x` there as an unknown option) stays message text.
|
|
316
|
+
args: ["--bg", "--resume", sessionId, "--", text],
|
|
317
|
+
cwd: repoDir,
|
|
318
|
+
env: scrubWorkerEnv(env),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Pull the session id out of `claude --bg` stdout. A full UUID wins; else the
|
|
324
|
+
* short id `claude --bg` prints. The manager can always map either back to
|
|
325
|
+
* the full `sessionId` through `claude agents --json` by worker name.
|
|
326
|
+
* @param {string} stdout - Command output
|
|
327
|
+
* @returns {string | null} Session id, or null
|
|
328
|
+
*/
|
|
329
|
+
export function parseSessionId(stdout) {
|
|
330
|
+
const text = typeof stdout === "string" ? stdout : "";
|
|
331
|
+
return (
|
|
332
|
+
SESSION_UUID_PATTERN.exec(text)?.[0] ??
|
|
333
|
+
SHORT_ID_PATTERN.exec(text)?.[0] ??
|
|
334
|
+
null
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Read a timestamp that may be an ISO string, epoch milliseconds or a Date.
|
|
340
|
+
* @param {unknown} value - Candidate
|
|
341
|
+
* @returns {number | null} Epoch milliseconds, or null
|
|
342
|
+
*/
|
|
343
|
+
function toMillis(value) {
|
|
344
|
+
if (value instanceof Date) return value.getTime();
|
|
345
|
+
if (typeof value === "number") return value;
|
|
346
|
+
if (typeof value === "string") {
|
|
347
|
+
const parsed = Date.parse(value);
|
|
348
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Classify one dispatched item for this cycle.
|
|
355
|
+
*
|
|
356
|
+
* Order matters: a recorded DONE/FAILED wins; a pending question means the
|
|
357
|
+
* worker ended its turn on purpose, so it is `waiting` even when its row
|
|
358
|
+
* reads `done`; only then is liveness judged. `stalled` and `dead` become
|
|
359
|
+
* `exhausted` at `maxAttempts`, which is the manager's cue to move the item to
|
|
360
|
+
* Blocked instead of retrying it again.
|
|
361
|
+
* @param {{agent: ReturnType<typeof normalizeAgents>[number] | null, entry: {status?: string, startedAt?: string|number, lastProgressAt?: string|number|null, logsHash?: string|null, pendingQuestion?: {askedAt?: string|number, lastPingAt?: string|number|null} | null, attempts?: number}, logsHash?: string | null | undefined, now: Date | number, stall?: {noOutputMinutes: number, requestionHours: number, maxAttempts: number}}} input - Classification inputs
|
|
362
|
+
* @returns {"finished"|"waiting"|"requestion"|"working"|"stalled"|"dead"|"exhausted"} Classification
|
|
363
|
+
*/
|
|
364
|
+
export function classifyWorker({
|
|
365
|
+
agent,
|
|
366
|
+
entry,
|
|
367
|
+
logsHash,
|
|
368
|
+
now,
|
|
369
|
+
stall = DEFAULT_STALL,
|
|
370
|
+
}) {
|
|
371
|
+
if (FINISHED_STATUSES.has(entry.status ?? "")) return "finished";
|
|
372
|
+
const nowMs = toMillis(now) ?? 0;
|
|
373
|
+
const question = entry.pendingQuestion;
|
|
374
|
+
if (question) {
|
|
375
|
+
const lastPing =
|
|
376
|
+
toMillis(question.lastPingAt) ?? toMillis(question.askedAt) ?? nowMs;
|
|
377
|
+
return nowMs - lastPing > stall.requestionHours * HOUR_MS
|
|
378
|
+
? "requestion"
|
|
379
|
+
: "waiting";
|
|
380
|
+
}
|
|
381
|
+
const outcome = agent?.active
|
|
382
|
+
? judgeActive(entry, logsHash, nowMs, stall)
|
|
383
|
+
: "dead";
|
|
384
|
+
if (outcome !== "working" && (entry.attempts ?? 0) >= stall.maxAttempts) {
|
|
385
|
+
return "exhausted";
|
|
386
|
+
}
|
|
387
|
+
return outcome;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Decide whether an active worker is making progress.
|
|
392
|
+
* @param {{logsHash?: string|null, lastProgressAt?: string|number|null, startedAt?: string|number}} entry - Ledger row
|
|
393
|
+
* @param {string | null | undefined} logsHash - This cycle's hash of `claude logs <name>`
|
|
394
|
+
* @param {number} nowMs - Clock reading
|
|
395
|
+
* @param {{noOutputMinutes: number}} stall - Stall policy
|
|
396
|
+
* @returns {"working"|"stalled"} Classification
|
|
397
|
+
*/
|
|
398
|
+
function judgeActive(entry, logsHash, nowMs, stall) {
|
|
399
|
+
if (typeof logsHash === "string" && logsHash !== entry.logsHash)
|
|
400
|
+
return "working";
|
|
401
|
+
const lastProgress =
|
|
402
|
+
toMillis(entry.lastProgressAt) ?? toMillis(entry.startedAt) ?? nowMs;
|
|
403
|
+
return nowMs - lastProgress <= stall.noOutputMinutes * MINUTE_MS
|
|
404
|
+
? "working"
|
|
405
|
+
: "stalled";
|
|
406
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-setup-session-manager
|
|
3
|
+
description: "Set up the local session manager for this project: writes the non-secret `sessionManager` block to `.lisa.config.json` (detecting a multi-repo `projects/*` layout), stores the project's own Telegram bot token outside the repo, checks the Telegram plugin is installed but not enabled at user scope, keeps the manager's state out of git, seeds the checked-in runbook, starts the manager in tmux, walks the operator through pairing, and finishes with doctor's session-manager check. Idempotent."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Write", "Skill", "AskUserQuestion"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Setup session manager
|
|
8
|
+
|
|
9
|
+
After this skill the project has a `sessionManager` block, a bot token in its own state directory, a
|
|
10
|
+
runbook at `.lisa/automations/session-manager.runbook.md`, and a running manager paired with the
|
|
11
|
+
operator's Telegram chat. Re-running is safe: every step keeps what is already there.
|
|
12
|
+
|
|
13
|
+
The manager itself always runs on Claude Code (see `lisa-session-manager` "Runtime parity"). Setup
|
|
14
|
+
can run from any agent that can reach the scripts; `start` launches the Claude Code manager.
|
|
15
|
+
|
|
16
|
+
## Step 0: Preconditions
|
|
17
|
+
|
|
18
|
+
Stop with the named gap if any of these fail:
|
|
19
|
+
|
|
20
|
+
- **Tracker configured.** `.tracker` in `.lisa.config.json` is `github`, `jira` or `linear`.
|
|
21
|
+
Otherwise stop: "No tracker configured. Run the matching /lisa:setup:<tracker> first."
|
|
22
|
+
- **Tools.** `claude --version` is at least 2.1.224; `tmux -V` and `bun --version` succeed. Name the
|
|
23
|
+
missing or outdated tool and stop.
|
|
24
|
+
|
|
25
|
+
## Step 1: Locate the scripts
|
|
26
|
+
|
|
27
|
+
Resolve the scripts directory with the same ladder as `lisa-session-manager`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
SM="${SM_SCRIPTS_DIR:-}"
|
|
31
|
+
[ -z "$SM" ] && [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && SM="$CLAUDE_PLUGIN_ROOT/skills/lisa-session-manager/scripts"
|
|
32
|
+
[ -n "$SM" ] && [ -f "$SM/session-manager.mjs" ] || SM="$(claude plugin list --json 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>{s+=d}).on("end",()=>{let l=[];try{l=JSON.parse(s)}catch{}const c=l.filter(p=>String(p.id).startsWith("lisa@"));const p=c.find(p=>p.projectPath===process.cwd())||c.find(p=>p.scope==="user");process.stdout.write(p?p.installPath:"")})')/skills/lisa-session-manager/scripts"
|
|
33
|
+
[ -f "$SM/session-manager.mjs" ] || SM="$PWD/node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-session-manager/scripts"
|
|
34
|
+
[ -f "$SM/session-manager.mjs" ] || { echo "lisa-session-manager scripts not found" >&2; exit 1; }
|
|
35
|
+
echo "$SM"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Shell variables do not survive between Bash calls: use the printed absolute path wherever this skill
|
|
39
|
+
writes `$SM`, and pass `--project-root "$ROOT"` (the absolute project root) on every call.
|
|
40
|
+
|
|
41
|
+
## Step 2: Write the config block
|
|
42
|
+
|
|
43
|
+
1. Preview: `node "$SM/session-manager.mjs" init-config --project-root "$ROOT"`. It detects
|
|
44
|
+
`projects/*/.lisa.config.json` and proposes `name`, `interval`, `repos`, `capacity`, `stall` and a
|
|
45
|
+
per-project `notify.stateDir`. Show the block to the operator.
|
|
46
|
+
2. Write it: the same command with `--write`. It adds only non-secret fields and never overwrites an
|
|
47
|
+
existing block (`reason: "exists"` means keep it).
|
|
48
|
+
3. Ask the operator to replace each repo's starter `hints` with the technologies and areas that
|
|
49
|
+
identify it (e.g. `api`: `["NestJS","migrations"]`, `web`: `["Expo","screens"]`), and whether to
|
|
50
|
+
set `sessionManager.trustedAuthorIds`. Without it, any author's Ready item reaches a
|
|
51
|
+
bypass-permissions worker; say so plainly. The list holds stable tracker user ids as strings,
|
|
52
|
+
never logins: the GitHub numeric user id (`gh api users/<login> --jq .id`), the Jira
|
|
53
|
+
`accountId`, or the Linear user id. A login matches no one and silently excludes every item.
|
|
54
|
+
4. `node "$SM/session-manager.mjs" validate --project-root "$ROOT"` must print `valid: true`.
|
|
55
|
+
|
|
56
|
+
## Step 3: Store the bot token
|
|
57
|
+
|
|
58
|
+
The operator creates a bot with @BotFather, one per project. Then run
|
|
59
|
+
`node "$SM/session-manager.mjs" store-token --project-root "$ROOT"`.
|
|
60
|
+
|
|
61
|
+
- `stored: true`: done.
|
|
62
|
+
- Exit 2 because a token already exists: keep it (re-running setup is safe). Only when the operator
|
|
63
|
+
says the stored token is wrong or belongs to another project, re-run with `store-token --replace`.
|
|
64
|
+
- `reason: "no-tty"` (an agent's Bash has no terminal): print `command` from the output and ask the
|
|
65
|
+
operator to run it in their own terminal, where it prompts with echo off. Wait for them to confirm.
|
|
66
|
+
- `empty` / `invalid`: say what was wrong and ask again.
|
|
67
|
+
|
|
68
|
+
Never ask for the token in chat, never echo, log or commit it, and never put it in
|
|
69
|
+
`.lisa.config.json`. It lives only in `<notify.stateDir>/.env`.
|
|
70
|
+
|
|
71
|
+
## Step 4: Telegram plugin scope
|
|
72
|
+
|
|
73
|
+
Read `claude plugin list --json` and look at the `telegram@claude-plugins-official` entries.
|
|
74
|
+
|
|
75
|
+
- **Not installed:** ask the operator to install it with
|
|
76
|
+
`claude plugin install telegram@claude-plugins-official -s user`, then re-check.
|
|
77
|
+
- **Enabled at user scope** (`scope: "user"` and `enabled: true`): every session on this machine
|
|
78
|
+
would start its own poller and take the bot away from the manager. Print the remedy and stop until
|
|
79
|
+
it is fixed:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
claude plugin disable telegram@claude-plugins-official -s user
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Installed and not enabled at user scope:** correct. The manager enables it for itself only.
|
|
86
|
+
|
|
87
|
+
## Step 5: Keep manager state out of git
|
|
88
|
+
|
|
89
|
+
At the project root, when it is a git repo:
|
|
90
|
+
`git check-ignore -q .lisa/session-manager/ledger.json || printf '%s\n' '.lisa/session-manager/' >> "$(git rev-parse --git-common-dir)/info/exclude"`,
|
|
91
|
+
then re-run `git check-ignore -q .lisa/session-manager/ledger.json`. If it is still not ignored, stop
|
|
92
|
+
and report. A root that is not a git repo needs nothing.
|
|
93
|
+
|
|
94
|
+
## Step 6: Seed the runbook
|
|
95
|
+
|
|
96
|
+
If `.lisa/automations/session-manager.runbook.md` does not exist, copy
|
|
97
|
+
`$SM/../references/runbook-template.md` there. Never overwrite an existing runbook: it is the
|
|
98
|
+
project's. It is checked in, like every automation runbook (`automation-runbook-contract`).
|
|
99
|
+
|
|
100
|
+
## Step 7: Coexistence warning
|
|
101
|
+
|
|
102
|
+
Tell the operator, before starting: if this project also runs the scheduled `intake-repair` loop, it
|
|
103
|
+
can re-dispatch an item a local worker is still building (it treats a claimed item with no tracker
|
|
104
|
+
activity for `intake.repair.staleAfterHours` as stalled). `intake-tickets` competes for the same
|
|
105
|
+
Ready items. Recommend tearing down `intake-tickets` (`/lisa:tear-down-automations intake-tickets`)
|
|
106
|
+
and either raising `intake.repair.staleAfterHours` above `stall.noOutputMinutes × stall.maxAttempts`
|
|
107
|
+
or tearing down `intake-repair`.
|
|
108
|
+
|
|
109
|
+
## Step 8: Accept workspace trust in each worker repo
|
|
110
|
+
|
|
111
|
+
Workers are background sessions, and a background session cannot show Claude Code's "trust this
|
|
112
|
+
folder?" prompt: until someone has accepted it in a directory, `claude --bg` refuses to start there
|
|
113
|
+
and `spawn` exits 2 saying so. Do this once per routed repo (or once for the root on a single-repo
|
|
114
|
+
project), on every machine that runs the manager:
|
|
115
|
+
|
|
116
|
+
1. List the directories workers start in. Single-repo: the project root. Multi-repo: for each key in
|
|
117
|
+
`sessionManager.repos.entries`,
|
|
118
|
+
`node "$SM/session-manager.mjs" repo-dir --repo <key> --raw --project-root "$ROOT"`.
|
|
119
|
+
2. Ask the operator, in their own terminal, to run `claude` once in that directory,
|
|
120
|
+
accept the trust prompt, then exit. An agent's Bash cannot answer that prompt, so never try to
|
|
121
|
+
accept it for them.
|
|
122
|
+
3. Re-run this step whenever a repo is added to `sessionManager.repos`.
|
|
123
|
+
|
|
124
|
+
## Step 9: Start and pair
|
|
125
|
+
|
|
126
|
+
0. **Public GitHub repo gate.** When `.tracker` is `github`, run
|
|
127
|
+
`gh repo view <github.org>/<github.repo> --json visibility`. If it is `PUBLIC` and
|
|
128
|
+
`sessionManager.trustedAuthorIds` is not set, setup will not start the manager until
|
|
129
|
+
`trustedAuthorIds` is set: anyone on the internet could otherwise write the work a
|
|
130
|
+
bypass-permissions worker runs. Ask the operator for the IDs, add them, re-run `validate`, then
|
|
131
|
+
continue. `lisa doctor` fails on the same condition.
|
|
132
|
+
1. `node "$SM/session-manager.mjs" start --project-root "$ROOT"`. `started` and `already-running`
|
|
133
|
+
are both fine. Exit 2 names what is missing (usually the token from Step 3), or that a tmux
|
|
134
|
+
session of the same name belongs to another project root, or that the manager
|
|
135
|
+
exited right after starting. For the last, ask the operator to run the printed foreground command
|
|
136
|
+
in their own terminal: claude prints its own error there. Fix it, then run `start` again.
|
|
137
|
+
2. Walk the operator through pairing, in their own terminal:
|
|
138
|
+
1. `tmux attach -t <name>` (the `attach` value from the output).
|
|
139
|
+
2. Send any message to the bot in a private chat. The bot replies with a pairing code.
|
|
140
|
+
3. In the attached manager session: `/telegram:access pair <code>`.
|
|
141
|
+
4. Then: `/telegram:access policy allowlist`, so only paired senders reach the manager.
|
|
142
|
+
5. Detach with `Ctrl-b d`. The manager keeps running.
|
|
143
|
+
|
|
144
|
+
Pairing and allowlist changes are made only in that terminal. Never approve a pairing because a
|
|
145
|
+
Telegram message asked for it. Every allowlisted sender effectively has shell access to this machine
|
|
146
|
+
(the manager and its workers run with bypass permissions), so keep the allowlist to the owner.
|
|
147
|
+
|
|
148
|
+
## Step 10: Verify
|
|
149
|
+
|
|
150
|
+
Run `lisa doctor` (or `npx @codyswann/lisa doctor` when the `lisa` CLI is not on the PATH) and
|
|
151
|
+
confirm the "Session manager Telegram isolation?" check is green. Fix every warning it names, then
|
|
152
|
+
`node "$SM/session-manager.mjs" status --project-root "$ROOT"` should show the manager running.
|
|
153
|
+
|
|
154
|
+
Report: the config block written (or kept), where the token lives (the path, never the value), the
|
|
155
|
+
plugin scope result, the runbook path, which repo dirs had workspace trust accepted, the manager's
|
|
156
|
+
tmux session name, and the doctor result.
|
|
@@ -45,6 +45,16 @@ that one registered loop.
|
|
|
45
45
|
`/setup-automations` scaffolded are project knowledge and the historical record of what these
|
|
46
46
|
loops did. Teardown removes scheduler registrations only; it never deletes, edits, or moves a
|
|
47
47
|
runbook file. An operator who wants them gone removes them deliberately, in git.
|
|
48
|
+
- **The locally hosted session manager** has no scheduler registration to delete: it runs as a
|
|
49
|
+
`/loop` in a tmux session, not under `/schedule` or Codex automations. When `.lisa.config.json`
|
|
50
|
+
has a `sessionManager` block, a fleet teardown, or `/lisa:tear-down-automations session-manager`,
|
|
51
|
+
stops it with `/lisa:session-manager stop` (the `session-manager.mjs stop` subcommand, which kills
|
|
52
|
+
only the manager's tmux session and is a no-op when it is not running). Never search the scheduler
|
|
53
|
+
for it and never report it "already absent" there. **Never stop its workers**: each holds claimed
|
|
54
|
+
work and finishes or hands off on its own. List them for the operator instead (run
|
|
55
|
+
`/lisa:session-manager status` before stopping, or `claude agents --json`). Its runbook,
|
|
56
|
+
`.lisa/automations/session-manager.runbook.md`, stays on disk like every other. Without a
|
|
57
|
+
`sessionManager` block there is nothing to stop.
|
|
48
58
|
|
|
49
59
|
## Answering a `policy-obsolete` teardown proposal
|
|
50
60
|
|
|
@@ -21,6 +21,20 @@ Membership is **registration, not skill-existence**: a loop is under this contra
|
|
|
21
21
|
registered as a scheduled automation, and registering a new one pulls it in automatically. There is
|
|
22
22
|
no hardcoded roster of loops anywhere.
|
|
23
23
|
|
|
24
|
+
### Locally hosted loops
|
|
25
|
+
|
|
26
|
+
A loop the runtime's scheduler does not host is still a member. The session manager
|
|
27
|
+
(`lisa-session-manager`) is the case: its registration is the tmux session
|
|
28
|
+
`/lisa:setup:session-manager` starts, running a `/loop`, not a `/schedule` routine or a Codex
|
|
29
|
+
automation. It is a member exactly when `.lisa.config.json` has a `sessionManager` block. It keeps
|
|
30
|
+
the same obligations as a scheduled loop: a checked-in runbook at
|
|
31
|
+
`.lisa/automations/session-manager.runbook.md`, one of the six outcomes per cycle, and a run record
|
|
32
|
+
written through `automation-run-record.mjs` under loop id `session-manager`. Only the scheduler-facing
|
|
33
|
+
surfaces differ. `lisa-automation-status` lists it `UNSUPPORTED`, with a reason naming the tmux
|
|
34
|
+
session, because no scheduler can see it, and still shows its runbook and recorded cycles.
|
|
35
|
+
`lisa-tear-down-automations` stops the tmux session instead of deleting a scheduler entry, never
|
|
36
|
+
stops its workers, and leaves the runbook.
|
|
37
|
+
|
|
24
38
|
### Interactive flows are members too
|
|
25
39
|
|
|
26
40
|
The outcome vocabulary is **not cron-specific** — it answers "did this need me?", which an operator
|