@codyswann/lisa 4.85.0 → 4.86.1
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/lib/completion-proof-content.mjs +63 -22
- package/all/copy-overwrite/scripts/lib/npm-update-hosted-hook.mjs +6 -1
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +19 -3
- package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +2 -2
- package/dist/cli/doctor-session-manager-pending.d.ts +17 -0
- package/dist/cli/doctor-session-manager-pending.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-pending.js +130 -0
- package/dist/cli/doctor-session-manager-pending.js.map +1 -0
- package/dist/cli/doctor-session-manager-token.d.ts +69 -0
- package/dist/cli/doctor-session-manager-token.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-token.js +264 -0
- package/dist/cli/doctor-session-manager-token.js.map +1 -0
- package/dist/cli/doctor-session-manager.d.ts +3 -1
- package/dist/cli/doctor-session-manager.d.ts.map +1 -1
- package/dist/cli/doctor-session-manager.js +168 -146
- package/dist/cli/doctor-session-manager.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +16 -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 +168 -26
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +19 -3
- 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-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa/commands/session-manager.md +1 -1
- package/plugins/lisa/commands/setup/session-manager.md +1 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +19 -3
- package/plugins/lisa/rules/reference/config-resolution.md +39 -7
- package/plugins/lisa/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa-agy/commands/lisa/session-manager.md +1 -1
- package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +19 -3
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +82 -19
- 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/session-manager.md +1 -1
- package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +19 -3
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +39 -7
- package/plugins/lisa-copilot/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/session-manager.md +1 -1
- package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +19 -3
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +39 -7
- package/plugins/lisa-cursor/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/hooks/parity-safety-net.sh +19 -3
- package/plugins/lisa-devin/rules/reference/config-resolution.md +39 -7
- package/plugins/lisa-devin/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +82 -19
- 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/session-manager.md +1 -1
- package/plugins/src/base/commands/setup/session-manager.md +1 -1
- package/plugins/src/base/hooks/parity-safety-net.sh +19 -3
- package/plugins/src/base/rules/reference/config-resolution.md +39 -7
- package/plugins/src/base/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/src/base/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/src/base/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +82 -19
|
@@ -30,8 +30,10 @@ export { detectRepoLayout, detectRepos, draftHints } from "./repo-detect.mjs";
|
|
|
30
30
|
/**
|
|
31
31
|
* Documented defaults for every optional key.
|
|
32
32
|
*
|
|
33
|
-
* `
|
|
34
|
-
*
|
|
33
|
+
* `notify.provider` defaults to `"none"`: a notification channel is optional
|
|
34
|
+
* (#4555), and an absent `notify` block means the manager runs without one.
|
|
35
|
+
* `name` and `notify.stateDir` have no default: both are per-project, and a
|
|
36
|
+
* shared default for either is how two projects end up fighting over one bot.
|
|
35
37
|
* `buildDefaultBlock` derives them from the project slug at setup time.
|
|
36
38
|
*/
|
|
37
39
|
export const SESSION_MANAGER_DEFAULTS = Object.freeze({
|
|
@@ -48,9 +50,24 @@ export const SESSION_MANAGER_DEFAULTS = Object.freeze({
|
|
|
48
50
|
maxAttempts: 3,
|
|
49
51
|
requestionHours: 4,
|
|
50
52
|
}),
|
|
51
|
-
notify: Object.freeze({ provider: "
|
|
53
|
+
notify: Object.freeze({ provider: "none" }),
|
|
52
54
|
});
|
|
53
55
|
|
|
56
|
+
/** The `notify` choices `buildDefaultBlock` (and `init-config --notify`) take. */
|
|
57
|
+
export const NOTIFY_CHOICES = Object.freeze(["none", "telegram"]);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The notification provider a resolved (or raw) block runs with.
|
|
61
|
+
* @param {{ notify?: { provider?: unknown } } | null | undefined} config
|
|
62
|
+
* Resolved or raw `sessionManager` block.
|
|
63
|
+
* @returns {string} `"telegram"`, `"none"`, or whatever unsupported value
|
|
64
|
+
* was declared (validation refuses it).
|
|
65
|
+
*/
|
|
66
|
+
export function notifyProvider(config) {
|
|
67
|
+
const provider = config?.notify?.provider;
|
|
68
|
+
return provider === undefined || provider === null ? "none" : provider;
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
/**
|
|
55
72
|
* @typedef {{ root: string, entries: Record<string, { hints: string[] }> }} ReposBlock
|
|
56
73
|
* @typedef {{ maxWorkers: number, gbPerWorker: number, reserveGb: number,
|
|
@@ -75,7 +92,7 @@ export const SESSION_MANAGER_DEFAULTS = Object.freeze({
|
|
|
75
92
|
* repos?: ReposBlock,
|
|
76
93
|
* capacity: CapacityBlock,
|
|
77
94
|
* stall: StallBlock,
|
|
78
|
-
* notify: { provider: string, stateDir
|
|
95
|
+
* notify: { provider: string, stateDir?: string },
|
|
79
96
|
* }} SessionManagerBlock
|
|
80
97
|
*/
|
|
81
98
|
|
|
@@ -160,10 +177,14 @@ function normalizeSlug(projectSlug) {
|
|
|
160
177
|
* name: valid either way, and setup asks the operator to review them.
|
|
161
178
|
* `reposRoot` is where the repos were found (`detectRepoLayout`): `projects`
|
|
162
179
|
* by default, or `.` when they sit directly under the project root.
|
|
180
|
+
* `notify` is the operator's channel choice: `"none"` (the default) writes
|
|
181
|
+
* `{provider:"none"}` and no state dir; `"telegram"` adds the per-project
|
|
182
|
+
* state dir derived from the slug.
|
|
163
183
|
* @param {{ projectSlug: string, detectedRepos?: string[],
|
|
164
|
-
* hints?: Record<string, string[]>, reposRoot?: string
|
|
165
|
-
* slug, the output of `detectRepos`,
|
|
166
|
-
* root they were found under
|
|
184
|
+
* hints?: Record<string, string[]>, reposRoot?: string,
|
|
185
|
+
* notify?: string }} input Project slug, the output of `detectRepos`,
|
|
186
|
+
* drafted hints per repo, the repos root they were found under, and the
|
|
187
|
+
* notification channel.
|
|
167
188
|
* @returns {SessionManagerBlock} A block `validateSessionManager` accepts.
|
|
168
189
|
*/
|
|
169
190
|
export function buildDefaultBlock({
|
|
@@ -171,7 +192,14 @@ export function buildDefaultBlock({
|
|
|
171
192
|
detectedRepos = [],
|
|
172
193
|
hints = {},
|
|
173
194
|
reposRoot = DEFAULT_REPOS_ROOT,
|
|
195
|
+
notify = SESSION_MANAGER_DEFAULTS.notify.provider,
|
|
174
196
|
}) {
|
|
197
|
+
if (!NOTIFY_CHOICES.includes(notify)) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`notify ${JSON.stringify(notify)} is not supported. Use one of: ` +
|
|
200
|
+
`${NOTIFY_CHOICES.join(", ")}.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
175
203
|
const slug = normalizeSlug(projectSlug);
|
|
176
204
|
const repos = [...detectedRepos].sort();
|
|
177
205
|
return {
|
|
@@ -192,10 +220,10 @@ export function buildDefaultBlock({
|
|
|
192
220
|
: {}),
|
|
193
221
|
capacity: { ...SESSION_MANAGER_DEFAULTS.capacity },
|
|
194
222
|
stall: { ...SESSION_MANAGER_DEFAULTS.stall },
|
|
195
|
-
notify:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
223
|
+
notify:
|
|
224
|
+
notify === "telegram"
|
|
225
|
+
? { provider: notify, stateDir: `~/.claude/channels/telegram-${slug}` }
|
|
226
|
+
: { provider: notify },
|
|
199
227
|
};
|
|
200
228
|
}
|
|
201
229
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a human's answer back off a work item (CodySwannGT/lisa#4555,
|
|
3
|
+
* part 3). Pure: no I/O, no clock, no config reads.
|
|
4
|
+
*
|
|
5
|
+
* Tracker text is untrusted, and in a single-user setup the operator's
|
|
6
|
+
* tracker identity is also the one the workers and the manager post as. So
|
|
7
|
+
* an operator-id match alone proves nothing: {@link isMachineAuthoredComment}
|
|
8
|
+
* runs first and is the ONE place that decides a comment is a machine's.
|
|
9
|
+
* It excludes a comment that opens with a Lisa or Claude machine tag
|
|
10
|
+
* (`[claude-sync] …`, `[lisa-…] …`), holds a `[lisa-` marker or a
|
|
11
|
+
* `<!-- lisa:` marker, says "Written by Claude", or carries the Claude Code
|
|
12
|
+
* attribution footer; and any comment whose id the manager recorded as one it
|
|
13
|
+
* posted itself (`ledger marked --comment-id`), whatever it says. Markdown
|
|
14
|
+
* blockquote lines are ignored before the markers are tested, so a human's
|
|
15
|
+
* "Quote reply" of the question (or of a machine comment) is still a human's
|
|
16
|
+
* answer, and an ordinary reply such as `[x] option 1` is not a machine tag
|
|
17
|
+
* (#4561). Only then is the author matched, on stable ids only: a GitHub
|
|
18
|
+
* login or numeric id, a Jira accountId, a Linear user id, never a display
|
|
19
|
+
* name or email, which anyone can set. The text is carried as data.
|
|
20
|
+
*
|
|
21
|
+
* The question the answer must follow is the comment whose id the manager
|
|
22
|
+
* recorded last; with no recorded id, the latest comment that STARTS with
|
|
23
|
+
* the question marker and is by an allowed answerer. A marker merely quoted,
|
|
24
|
+
* or posted by anyone else, never moves that anchor, so nobody can hide an
|
|
25
|
+
* answer by posting the marker after it.
|
|
26
|
+
* @module session-manager/human-answer
|
|
27
|
+
*/
|
|
28
|
+
import { QUESTION_MARKER } from "./human-needed.mjs";
|
|
29
|
+
|
|
30
|
+
/** Body markers that make a comment a machine's, never a human's answer. */
|
|
31
|
+
const MACHINE_MARKERS = [
|
|
32
|
+
/^\s*\[(lisa|claude)-[a-z0-9-]*\]/i,
|
|
33
|
+
/\[lisa-/i,
|
|
34
|
+
/<!--\s*lisa:/i,
|
|
35
|
+
/Written by Claude/i,
|
|
36
|
+
/Generated with Claude Code/i,
|
|
37
|
+
/🤖 Generated with/,
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** A Markdown blockquote line: quoted text, never the commenter's own. */
|
|
41
|
+
const QUOTE_LINE = /^\s*>/;
|
|
42
|
+
|
|
43
|
+
/** The comment fields that carry its tracker id. */
|
|
44
|
+
const COMMENT_ID_KEYS = ["id", "node_id"];
|
|
45
|
+
|
|
46
|
+
/** The author fields that are stable ids, never settable display text. */
|
|
47
|
+
const STABLE_ID_KEYS = ["login", "id", "accountId"];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The string or number values of `keys` on `value`, as strings.
|
|
51
|
+
* @param {unknown} value - Object to read
|
|
52
|
+
* @param {readonly string[]} keys - Keys to read
|
|
53
|
+
* @returns {string[]} Values
|
|
54
|
+
*/
|
|
55
|
+
function idsOf(value, keys) {
|
|
56
|
+
if (value === null || typeof value !== "object") return [];
|
|
57
|
+
return keys
|
|
58
|
+
.map(key => value[key])
|
|
59
|
+
.filter(id => typeof id === "string" || typeof id === "number")
|
|
60
|
+
.map(String);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The comment body, whatever the tracker calls it.
|
|
65
|
+
* @param {Record<string, unknown>} comment - One comment
|
|
66
|
+
* @returns {string} Body text
|
|
67
|
+
*/
|
|
68
|
+
function bodyOf(comment) {
|
|
69
|
+
const body = comment?.body ?? comment?.text ?? "";
|
|
70
|
+
return typeof body === "string" ? body : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The comment body without its Markdown blockquote lines.
|
|
75
|
+
* @param {string} text - Body text
|
|
76
|
+
* @returns {string} The commenter's own text
|
|
77
|
+
*/
|
|
78
|
+
function unquoted(text) {
|
|
79
|
+
return text
|
|
80
|
+
.split("\n")
|
|
81
|
+
.filter(line => !QUOTE_LINE.test(line))
|
|
82
|
+
.join("\n");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether a comment was written by a machine (an agent, a Lisa flow, or the
|
|
87
|
+
* manager itself) and so can never be a human's answer.
|
|
88
|
+
* @param {Record<string, unknown>} comment - One tracker comment
|
|
89
|
+
* @param {{managerCommentIds?: readonly (string|number)[]}} [options] - Ids
|
|
90
|
+
* of comments the manager posted (from the ledger)
|
|
91
|
+
* @returns {boolean} True for a machine's comment
|
|
92
|
+
*/
|
|
93
|
+
export function isMachineAuthoredComment(comment, options = {}) {
|
|
94
|
+
const mine = new Set((options.managerCommentIds ?? []).map(String));
|
|
95
|
+
if (idsOf(comment, COMMENT_ID_KEYS).some(id => mine.has(id))) return true;
|
|
96
|
+
const text = unquoted(bodyOf(comment));
|
|
97
|
+
return MACHINE_MARKERS.some(pattern => pattern.test(text));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Index of the question comment an answer must follow: the comment whose id
|
|
102
|
+
* the manager recorded last, when it recorded any; otherwise the latest
|
|
103
|
+
* comment that starts with the question marker and is by an allowed author.
|
|
104
|
+
* @param {Record<string, unknown>[]} rows - Comments, chronological
|
|
105
|
+
* @param {readonly (string|number)[]} managerCommentIds - Recorded ids
|
|
106
|
+
* @param {(row: Record<string, unknown>) => string|undefined} authorOf -
|
|
107
|
+
* The row's allowed stable author id, if any
|
|
108
|
+
* @returns {number} Index, or -1 when there is no question comment
|
|
109
|
+
*/
|
|
110
|
+
function questionIndex(rows, managerCommentIds, authorOf) {
|
|
111
|
+
if (managerCommentIds.length > 0) {
|
|
112
|
+
const latest = String(managerCommentIds.at(-1));
|
|
113
|
+
return rows.findLastIndex(row =>
|
|
114
|
+
idsOf(row, COMMENT_ID_KEYS).includes(latest)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return rows.findLastIndex(
|
|
118
|
+
row =>
|
|
119
|
+
bodyOf(row).trimStart().startsWith(QUESTION_MARKER) &&
|
|
120
|
+
authorOf(row) !== undefined
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Pick the human's answer from an item's comments: the first comment after
|
|
126
|
+
* the manager's latest question comment ({@link questionIndex}) that is not
|
|
127
|
+
* a machine's ({@link isMachineAuthoredComment}), has text of its own
|
|
128
|
+
* outside quoted lines (a comment made only of `>` lines quotes someone
|
|
129
|
+
* else and answers nothing), and whose author's stable id is allowed. Comments are taken in the order given (chronological).
|
|
130
|
+
* @param {{comments: unknown, allowed: readonly string[],
|
|
131
|
+
* managerCommentIds?: readonly (string|number)[]}} input - The item's
|
|
132
|
+
* comments, the answerer ids, and the ids the manager posted
|
|
133
|
+
* @returns {{answer: {text: string, author: string, at: string|null} | null,
|
|
134
|
+
* reason: string}} The answer, or null with why there is none
|
|
135
|
+
*/
|
|
136
|
+
export function pickItemAnswer({ comments, allowed, managerCommentIds = [] }) {
|
|
137
|
+
if (!Array.isArray(comments)) {
|
|
138
|
+
throw new Error('stdin must be JSON {"comments":[...]}');
|
|
139
|
+
}
|
|
140
|
+
const rows = comments.filter(row => row && typeof row === "object");
|
|
141
|
+
const allow = new Set(allowed.map(String));
|
|
142
|
+
const authorOf = row =>
|
|
143
|
+
idsOf(row.author ?? row.user, STABLE_ID_KEYS).find(id => allow.has(id));
|
|
144
|
+
const asked = questionIndex(rows, managerCommentIds, authorOf);
|
|
145
|
+
if (asked === -1) return { answer: null, reason: "no-question-comment" };
|
|
146
|
+
const reply = rows
|
|
147
|
+
.slice(asked + 1)
|
|
148
|
+
.filter(row => !isMachineAuthoredComment(row, { managerCommentIds }))
|
|
149
|
+
.find(
|
|
150
|
+
row => authorOf(row) !== undefined && unquoted(bodyOf(row)).trim() !== ""
|
|
151
|
+
);
|
|
152
|
+
if (!reply) return { answer: null, reason: "no-answer-yet" };
|
|
153
|
+
const at = reply.createdAt ?? reply.created ?? reply.created_at ?? null;
|
|
154
|
+
return {
|
|
155
|
+
answer: {
|
|
156
|
+
text: bodyOf(reply).trim(),
|
|
157
|
+
author: authorOf(reply),
|
|
158
|
+
at: typeof at === "string" ? at : null,
|
|
159
|
+
},
|
|
160
|
+
reason: "answered",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Mark and move on" (CodySwannGT/lisa#4555, part 3): what the manager writes
|
|
3
|
+
* on a work item that cannot progress without a human, and how it reads the
|
|
4
|
+
* human's answer back off the item. Pure: no I/O, no clock, no config reads.
|
|
5
|
+
*
|
|
6
|
+
* Four cases need a human: a worker's question, an ambiguous repo route, an
|
|
7
|
+
* item `exhausted` after `stall.maxAttempts`, and a design or product
|
|
8
|
+
* decision. In each the manager marks the item with the configured
|
|
9
|
+
* human-needed marker, posts the question on the item with enough context to
|
|
10
|
+
* answer it there, records it pending in the ledger, and keeps dispatching
|
|
11
|
+
* other work. One item waiting on a person never stops the queue.
|
|
12
|
+
*
|
|
13
|
+
* The marker is the tracker's configured human-needed label; with no label
|
|
14
|
+
* for the tracker it is the configured `blocked` role. An `exhausted` item is
|
|
15
|
+
* also moved to the `blocked` role, because no worker is building it any
|
|
16
|
+
* more. Names always come from config, never from this file.
|
|
17
|
+
*
|
|
18
|
+
* Reading the answer back off the item lives in human-answer.mjs.
|
|
19
|
+
* @module session-manager/human-needed
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** The four reasons an item can need a human. */
|
|
23
|
+
export const HUMAN_NEEDED_KINDS = Object.freeze([
|
|
24
|
+
"worker-question",
|
|
25
|
+
"repo-route",
|
|
26
|
+
"exhausted",
|
|
27
|
+
"decision",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/** Marks the manager's question comment, so its answer can be found after it. */
|
|
31
|
+
export const QUESTION_MARKER = "[lisa-session-manager-question]";
|
|
32
|
+
|
|
33
|
+
/** What each kind is called in the comment's first line. */
|
|
34
|
+
const KIND_TITLES = {
|
|
35
|
+
"worker-question": "the worker building it has a question",
|
|
36
|
+
"repo-route": "it is not clear which repository it belongs to",
|
|
37
|
+
exhausted: "every automatic attempt to build it stopped without finishing",
|
|
38
|
+
decision: "it needs a design or product decision",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Credential shapes refused in question and answer text. Best effort: a match
|
|
43
|
+
* is refused, a miss is not proof the text is safe. A secret belongs in the
|
|
44
|
+
* project's secrets provider, never in a tracker comment or the ledger.
|
|
45
|
+
*/
|
|
46
|
+
const CREDENTIAL_SHAPES = [
|
|
47
|
+
["Telegram bot token", /\b\d{8,10}:[A-Za-z0-9_-]{30,}\b/],
|
|
48
|
+
["GitHub token", /\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_\w{30,})/],
|
|
49
|
+
["AWS access key", /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/],
|
|
50
|
+
["Slack token", /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
|
|
51
|
+
["API key", /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}/],
|
|
52
|
+
["private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
|
|
53
|
+
["bearer token", /\bbearer\s+[A-Za-z0-9._~+/=-]{20,}/i],
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The name of the first credential shape found in `text`, or null.
|
|
58
|
+
* @param {string} text - Question or answer text
|
|
59
|
+
* @returns {string | null} Shape name
|
|
60
|
+
*/
|
|
61
|
+
export function findCredentialShape(text) {
|
|
62
|
+
const found = CREDENTIAL_SHAPES.find(([, pattern]) => pattern.test(text));
|
|
63
|
+
return found ? found[0] : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The short label an operator starts a Telegram answer with: `#12` for a
|
|
68
|
+
* GitHub ref (`owner/repo#12`), the key itself for Jira and Linear.
|
|
69
|
+
* @param {string} ref - Item ref
|
|
70
|
+
* @returns {string} Answer label
|
|
71
|
+
*/
|
|
72
|
+
export function answerLabel(ref) {
|
|
73
|
+
const hash = ref.lastIndexOf("#");
|
|
74
|
+
return hash > 0 ? ref.slice(hash) : ref;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The comment the manager posts on the item. It names the ref, the reason,
|
|
79
|
+
* the question verbatim, and every way to answer, in plain English with
|
|
80
|
+
* nothing a reader has to paste and no local path. A reply on the item is
|
|
81
|
+
* offered only when someone may answer there (`answerers`): with no
|
|
82
|
+
* operator or trusted author configured, replies are never picked up.
|
|
83
|
+
* @param {{ref: string, kind: string, text: string, label: string,
|
|
84
|
+
* markerText: string, channel: boolean, answerers: boolean}} input - What
|
|
85
|
+
* to say
|
|
86
|
+
* @returns {string} Comment body
|
|
87
|
+
*/
|
|
88
|
+
function questionComment({
|
|
89
|
+
ref,
|
|
90
|
+
kind,
|
|
91
|
+
text,
|
|
92
|
+
label,
|
|
93
|
+
markerText,
|
|
94
|
+
channel,
|
|
95
|
+
answerers,
|
|
96
|
+
}) {
|
|
97
|
+
const ways = [
|
|
98
|
+
answerers
|
|
99
|
+
? `- Reply in a comment on this item, in plain words (do not start it with a bracketed tag). The session manager reads the first reply from an operator or trusted author on its next cycle.`
|
|
100
|
+
: `- Replies on this item are not picked up: no operator or trusted author is configured for the session manager.`,
|
|
101
|
+
`- On the machine running the session manager, run \`session-manager.mjs status\` and use the \`answerCommand\` it lists for this item.`,
|
|
102
|
+
...(channel
|
|
103
|
+
? [`- Or on Telegram: \`Answer with: ${label} <your answer>\`.`]
|
|
104
|
+
: []),
|
|
105
|
+
];
|
|
106
|
+
return [
|
|
107
|
+
`${QUESTION_MARKER} ${ref} needs a human: ${KIND_TITLES[kind]}.`,
|
|
108
|
+
"",
|
|
109
|
+
text,
|
|
110
|
+
"",
|
|
111
|
+
"How to answer:",
|
|
112
|
+
...ways,
|
|
113
|
+
"",
|
|
114
|
+
`The session manager keeps working on other items meanwhile. Once the answer is delivered it removes ${markerText} and says so here.`,
|
|
115
|
+
].join("\n");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The marker for an item: the human-needed label, else the `blocked` role.
|
|
120
|
+
* @param {string | undefined} label - Configured human-needed label
|
|
121
|
+
* @param {string | undefined} blocked - Configured `blocked` role value
|
|
122
|
+
* @returns {{type: "label"|"role", value: string}} Marker
|
|
123
|
+
*/
|
|
124
|
+
function chooseMarker(label, blocked) {
|
|
125
|
+
if (label) return { type: "label", value: label };
|
|
126
|
+
if (blocked) return { type: "role", value: blocked };
|
|
127
|
+
throw new Error(
|
|
128
|
+
"no human-needed label and no blocked role is configured for this tracker; configure one (see the config-resolution rule) so the manager can mark the item"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Plan how to mark one item human-needed. Throws (a setup refusal) on an
|
|
134
|
+
* unknown kind, empty or credential-shaped text, no marker, or an `exhausted`
|
|
135
|
+
* item with no `blocked` role to move it to.
|
|
136
|
+
* @param {{ref: string, kind: string, text: string, label?: string,
|
|
137
|
+
* blocked?: string, channel?: boolean, answerers?: boolean}} input - The
|
|
138
|
+
* item, why, the question, the configured human-needed label and `blocked`
|
|
139
|
+
* role value, whether a notification channel exists, and whether anyone
|
|
140
|
+
* (operatorIds / trustedAuthorIds) may answer on the item
|
|
141
|
+
* @returns {{ref: string, kind: string, marker: {type: string, value: string},
|
|
142
|
+
* move?: {role: "blocked", value: string}, comment: string,
|
|
143
|
+
* answerWith: {item: string|null, cli: string, channel: string|null}}} Plan
|
|
144
|
+
*/
|
|
145
|
+
export function planMark({
|
|
146
|
+
ref,
|
|
147
|
+
kind,
|
|
148
|
+
text,
|
|
149
|
+
label,
|
|
150
|
+
blocked,
|
|
151
|
+
channel = false,
|
|
152
|
+
answerers = true,
|
|
153
|
+
}) {
|
|
154
|
+
if (!HUMAN_NEEDED_KINDS.includes(kind)) {
|
|
155
|
+
throw new Error(`--kind must be one of: ${HUMAN_NEEDED_KINDS.join(", ")}.`);
|
|
156
|
+
}
|
|
157
|
+
const question = String(text ?? "").trim();
|
|
158
|
+
if (question === "") throw new Error("the question or reason is empty");
|
|
159
|
+
const shape = findCredentialShape(question);
|
|
160
|
+
if (shape) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`the text looks like it holds a credential (${shape}); it would be posted on the item. Remove it and keep secrets in the secrets provider.`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const marker = chooseMarker(label, blocked);
|
|
166
|
+
if (kind === "exhausted" && !blocked) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"an exhausted item moves to the blocked role, and none is configured for this tracker"
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const move =
|
|
172
|
+
kind === "exhausted" ? { role: "blocked", value: blocked } : undefined;
|
|
173
|
+
const short = answerLabel(ref);
|
|
174
|
+
const markerText =
|
|
175
|
+
marker.type === "label"
|
|
176
|
+
? `the \`${marker.value}\` label`
|
|
177
|
+
: `the hold (it moves the item out of \`${marker.value}\`)`;
|
|
178
|
+
return {
|
|
179
|
+
ref,
|
|
180
|
+
kind,
|
|
181
|
+
marker,
|
|
182
|
+
...(move ? { move } : {}),
|
|
183
|
+
comment: questionComment({
|
|
184
|
+
ref,
|
|
185
|
+
kind,
|
|
186
|
+
text: question,
|
|
187
|
+
label: short,
|
|
188
|
+
markerText,
|
|
189
|
+
channel,
|
|
190
|
+
answerers,
|
|
191
|
+
}),
|
|
192
|
+
answerWith: {
|
|
193
|
+
item: answerers ? `a comment on ${ref}` : null,
|
|
194
|
+
cli: `the answerCommand that session-manager.mjs status lists for ${ref} (answer on stdin)`,
|
|
195
|
+
channel: channel ? `Answer with: ${short} <your answer>` : null,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON input for the session-manager CLI: parse text or read a file named by
|
|
3
|
+
* a flag, exiting 2 with the source named when it is not valid JSON. Split
|
|
4
|
+
* out of cli-queue.mjs to keep it under the line budget (#4555); cli-queue.mjs
|
|
5
|
+
* re-exports both, so callers keep their import.
|
|
6
|
+
* @module session-manager/json-input
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
|
|
10
|
+
import { CliError, asSetupError } from "./cli-runtime.mjs";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse JSON, or exit 2 naming the source.
|
|
14
|
+
* @param {string} text - JSON text
|
|
15
|
+
* @param {string} source - Where it came from
|
|
16
|
+
* @returns {any} Parsed value
|
|
17
|
+
*/
|
|
18
|
+
export function parseJson(text, source) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(text);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new CliError(`${source} is not valid JSON: ${error.message}`, 2);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read a JSON file named by a flag, or exit 2.
|
|
28
|
+
* @param {string} file - Path
|
|
29
|
+
* @returns {any} Parsed value
|
|
30
|
+
*/
|
|
31
|
+
export function readJsonFile(file) {
|
|
32
|
+
const text = asSetupError(() => readFileSync(file, "utf8"));
|
|
33
|
+
return parseJson(text, file);
|
|
34
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two shell-facing renderings of the manager's tmux launch, split out of
|
|
3
|
+
* launch.mjs to keep it under the line budget (#4555):
|
|
4
|
+
* - {@link buildForegroundCommand}: the display-only line an operator runs to
|
|
5
|
+
* start the manager in the foreground and see claude's own error;
|
|
6
|
+
* - {@link buildSourcedLaunch}: the `tmux start-server ; source-file -` call
|
|
7
|
+
* that carries secret session variables (the Telegram bot token) on stdin,
|
|
8
|
+
* never in argv.
|
|
9
|
+
*
|
|
10
|
+
* Both take the argv `buildManagerLaunch` returned. Everything here is pure:
|
|
11
|
+
* nothing reads the environment or runs a process. launch.mjs re-exports
|
|
12
|
+
* every export, so callers keep one import.
|
|
13
|
+
* @module launch-shell
|
|
14
|
+
*/
|
|
15
|
+
import { LaunchError } from "./state-dir.mjs";
|
|
16
|
+
|
|
17
|
+
/** A `'` inside a single-quoted shell word: close, escaped quote, reopen. */
|
|
18
|
+
const ESCAPED_QUOTE = "'\\''";
|
|
19
|
+
|
|
20
|
+
/** An argument a POSIX shell reads back unchanged without quotes. */
|
|
21
|
+
const SHELL_SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Quote one argument for a POSIX shell, for display only.
|
|
25
|
+
* @param {string} value - Argument
|
|
26
|
+
* @returns {string} Single-quoted argument
|
|
27
|
+
*/
|
|
28
|
+
function shellQuote(value) {
|
|
29
|
+
return `'${value.replaceAll("'", ESCAPED_QUOTE)}'`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The shell line an operator runs to start the manager in the foreground,
|
|
34
|
+
* from the tmux argv `buildManagerLaunch` (launch.mjs) returned: same cwd, the same
|
|
35
|
+
* two session variables, the same claude argv. `start` prints it when the
|
|
36
|
+
* tmux session dies at once, because tmux closes the window and claude's own
|
|
37
|
+
* error goes with it. It is display text only and is never executed here.
|
|
38
|
+
* The token is not in the argv, so it is not in this line either. When the
|
|
39
|
+
* token comes from a secrets provider, `shellVars` adds it as a command the
|
|
40
|
+
* operator's own shell runs (`$(node …/resolve-secret.mjs get <NAME>)`), so
|
|
41
|
+
* the line names the secret and never holds its value (#4555).
|
|
42
|
+
* @param {readonly string[]} argv - Result of `buildManagerLaunch` (launch.mjs)
|
|
43
|
+
* @param {Record<string, string>} [shellVars] - Extra variables, each value
|
|
44
|
+
* double-quoted so the operator's shell expands it
|
|
45
|
+
* @returns {string} One shell line
|
|
46
|
+
*/
|
|
47
|
+
export function buildForegroundCommand(argv, shellVars = {}) {
|
|
48
|
+
const claudeAt = argv.indexOf("claude");
|
|
49
|
+
const head = argv.slice(0, claudeAt);
|
|
50
|
+
const cwd = head[head.indexOf("-c") + 1];
|
|
51
|
+
const vars = head
|
|
52
|
+
.filter((_, index) => head[index - 1] === "-e")
|
|
53
|
+
.map(entry => {
|
|
54
|
+
const at = entry.indexOf("=");
|
|
55
|
+
return `${entry.slice(0, at)}=${shellQuote(entry.slice(at + 1))}`;
|
|
56
|
+
});
|
|
57
|
+
const expanded = Object.entries(shellVars).map(
|
|
58
|
+
([name, value]) => `${name}="${value}"`
|
|
59
|
+
);
|
|
60
|
+
const command = argv
|
|
61
|
+
.slice(claudeAt)
|
|
62
|
+
.map(arg => (SHELL_SAFE.test(arg) ? arg : shellQuote(arg)));
|
|
63
|
+
return `cd ${shellQuote(cwd)} && ${[...vars, ...expanded, ...command].join(" ")}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Quote one word for tmux's command parser: single quotes, inside which
|
|
68
|
+
* nothing is special, and `'"'"'` for a quote (close, a double-quoted quote,
|
|
69
|
+
* reopen; tmux joins adjacent quoted runs into one word).
|
|
70
|
+
* @param {string} word - Word
|
|
71
|
+
* @returns {string} Quoted word
|
|
72
|
+
*/
|
|
73
|
+
export function tmuxQuote(word) {
|
|
74
|
+
return `'${String(word).replaceAll("'", `'"'"'`)}'`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The tmux call that starts the manager with secret session variables
|
|
79
|
+
* (the Telegram bot token from the secrets provider, #4555) that must never
|
|
80
|
+
* reach argv, where `ps` shows them to every user on the host.
|
|
81
|
+
*
|
|
82
|
+
* `tmux start-server ; source-file -` reads the `new-session` command from
|
|
83
|
+
* stdin, so the argv holds no value. The `-e` variables join the session's
|
|
84
|
+
* own environment only, the same as the ones `buildManagerLaunch` (launch.mjs)
|
|
85
|
+
* already passes. A newline in any word would end the command early and run
|
|
86
|
+
* the rest as a second tmux command, so it is refused.
|
|
87
|
+
* @param {readonly string[]} argv - Result of `buildManagerLaunch` (launch.mjs)
|
|
88
|
+
* @param {Record<string, string>} sessionVars - Extra session variables
|
|
89
|
+
* @returns {{ argv: string[], input: string }} argv for `spawnSync("tmux")`
|
|
90
|
+
* and the line for its stdin
|
|
91
|
+
*/
|
|
92
|
+
export function buildSourcedLaunch(argv, sessionVars) {
|
|
93
|
+
if (argv[0] !== "new-session") {
|
|
94
|
+
throw new LaunchError("buildSourcedLaunch needs a new-session argv.");
|
|
95
|
+
}
|
|
96
|
+
const claudeAt = argv.indexOf("claude");
|
|
97
|
+
const words = [
|
|
98
|
+
...argv.slice(0, claudeAt),
|
|
99
|
+
...Object.entries(sessionVars).flatMap(([name, value]) => [
|
|
100
|
+
"-e",
|
|
101
|
+
`${name}=${value}`,
|
|
102
|
+
]),
|
|
103
|
+
...argv.slice(claudeAt),
|
|
104
|
+
];
|
|
105
|
+
if (words.some(word => /[\r\n]/.test(word))) {
|
|
106
|
+
throw new LaunchError(
|
|
107
|
+
"A launch argument contains a newline, which would end the tmux " +
|
|
108
|
+
"command early. Nothing was started."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
argv: ["start-server", ";", "source-file", "-"],
|
|
113
|
+
input: `${words.map(tmuxQuote).join(" ")}\n`,
|
|
114
|
+
};
|
|
115
|
+
}
|