create-egregore 0.21.6 → 0.22.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/assets/egregore-launcher.js +58 -6
- package/assets/egregore-launcher.sh +15 -2
- package/lib/claude-runtime.js +2 -0
- package/lib/codex-runtime.js +2 -0
- package/lib/setup.js +20 -3
- package/package.json +2 -2
- package/runtime/claude/.claude/context/commit-format.md +7 -4
- package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
- package/runtime/claude/.claude/context/notification-consent.md +12 -9
- package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
- package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
- package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
- package/runtime/claude/.claude/settings.json +1 -1
- package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
- package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
- package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
- package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
- package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
- package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
- package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
- package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
- package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
- package/runtime/claude/CLAUDE.md +59 -95
- package/runtime/claude/bin/agent.sh +231 -7
- package/runtime/claude/bin/api-call.sh +44 -23
- package/runtime/claude/bin/artifact-register.sh +7 -1
- package/runtime/claude/bin/artifact-writeback.sh +1 -1
- package/runtime/claude/bin/artifacts.sh +66 -265
- package/runtime/claude/bin/attendant.sh +4 -1
- package/runtime/claude/bin/boundary-guard.cjs +1848 -0
- package/runtime/claude/bin/boundary-hook.sh +41 -0
- package/runtime/claude/bin/boundary.sh +15 -155
- package/runtime/claude/bin/capture-run.sh +32 -2
- package/runtime/claude/bin/codex-render-spec.mjs +24 -46
- package/runtime/claude/bin/codex-session-start.sh +19 -0
- package/runtime/claude/bin/codex-skill-render.mjs +18 -0
- package/runtime/claude/bin/config-get.sh +10 -1
- package/runtime/claude/bin/connect-refresh.sh +16 -0
- package/runtime/claude/bin/corpus_map.py +5 -0
- package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
- package/runtime/claude/bin/genui.sh +19 -0
- package/runtime/claude/bin/graph-batch.sh +1 -1
- package/runtime/claude/bin/graph-op.sh +14 -0
- package/runtime/claude/bin/graph.sh +1 -1
- package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/claude/bin/handoff-run.sh +11 -6
- package/runtime/claude/bin/ingest-item.sh +1 -1
- package/runtime/claude/bin/ingest-surface.html +22 -4
- package/runtime/claude/bin/issue.sh +1 -1
- package/runtime/claude/bin/knowledge.sh +1 -1
- package/runtime/claude/bin/lib/card-delivery.sh +329 -0
- package/runtime/claude/bin/lib/config.sh +148 -3
- package/runtime/claude/bin/lib/context.sh +38 -67
- package/runtime/claude/bin/lib/genui-entry.py +36 -0
- package/runtime/claude/bin/lib/git-message.sh +25 -6
- package/runtime/claude/bin/lib/git-sync.sh +12 -4
- package/runtime/claude/bin/lib/greeting.sh +255 -28
- package/runtime/claude/bin/lib/handoff_meta.py +294 -0
- package/runtime/claude/bin/lib/health-footer.sh +83 -2
- package/runtime/claude/bin/lib/identity.sh +31 -10
- package/runtime/claude/bin/lib/instance-registry.sh +71 -0
- package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/claude/bin/lib/metrics.sh +121 -8
- package/runtime/claude/bin/lib/notices.sh +36 -0
- package/runtime/claude/bin/lib/record_outcomes.py +729 -0
- package/runtime/claude/bin/lib/reflexes.py +267 -0
- package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/claude/bin/lib/session_record.py +3747 -0
- package/runtime/claude/bin/lib/session_trail.py +1983 -0
- package/runtime/claude/bin/lib/settings-drift.sh +8 -5
- package/runtime/claude/bin/lib/startup-restart.sh +41 -0
- package/runtime/claude/bin/lib/worktree-links.sh +92 -0
- package/runtime/claude/bin/memory-push.sh +6 -0
- package/runtime/claude/bin/merge-gate.sh +498 -0
- package/runtime/claude/bin/notify.sh +5 -2
- package/runtime/claude/bin/page-access.sh +103 -0
- package/runtime/claude/bin/person.sh +13 -2
- package/runtime/claude/bin/pi-render-spec.mjs +0 -1
- package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/claude/bin/post-update.sh +181 -0
- package/runtime/claude/bin/prime-render-spec.mjs +0 -1
- package/runtime/claude/bin/publish-artifact.sh +89 -42
- package/runtime/claude/bin/qa-inventory.sh +2 -2
- package/runtime/claude/bin/quest.sh +1 -1
- package/runtime/claude/bin/records.sh +11 -0
- package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/claude/bin/reflexes/30-cost.py +116 -0
- package/runtime/claude/bin/reflexes/40-shells.py +51 -0
- package/runtime/claude/bin/reflexes/README.md +61 -0
- package/runtime/claude/bin/reflexes.sh +7 -0
- package/runtime/claude/bin/runtime-identity.sh +140 -42
- package/runtime/claude/bin/screenshot.sh +77 -0
- package/runtime/claude/bin/scroll.sh +1 -1
- package/runtime/claude/bin/search.sh +31 -5
- package/runtime/claude/bin/session-autosave.sh +6 -3
- package/runtime/claude/bin/session-id.sh +147 -0
- package/runtime/claude/bin/session-log.sh +19 -0
- package/runtime/claude/bin/session-record.sh +26 -0
- package/runtime/claude/bin/session-start.sh +121 -151
- package/runtime/claude/bin/settings.sh +13 -8
- package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/claude/bin/spirit-discovery.sh +4 -0
- package/runtime/claude/bin/startup-check.sh +1 -1
- package/runtime/claude/bin/telemetry.sh +11 -4
- package/runtime/claude/bin/test-baseline.sh +33 -5
- package/runtime/claude/bin/todo.sh +1 -1
- package/runtime/claude/bin/worktree-create.sh +15 -45
- package/runtime/claude/bin/worktree.sh +6 -0
- package/runtime/claude/docs/spirit-discovery.md +96 -0
- package/runtime/claude/egregore_runtime/__init__.py +18 -12
- package/runtime/claude/egregore_runtime/access.py +608 -0
- package/runtime/claude/egregore_runtime/access_cli.py +190 -0
- package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
- package/runtime/claude/egregore_runtime/artifacts.py +222 -16
- package/runtime/claude/egregore_runtime/contracts.py +27 -0
- package/runtime/claude/egregore_runtime/eligibility.py +5 -12
- package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
- package/runtime/claude/egregore_runtime/github_issues.py +2 -2
- package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
- package/runtime/claude/egregore_runtime/held.py +2436 -0
- package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
- package/runtime/claude/egregore_runtime/identity.py +196 -65
- package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/claude/egregore_runtime/installation.py +119 -0
- package/runtime/claude/egregore_runtime/investigation.py +64 -41
- package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
- package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/claude/egregore_runtime/issues.py +12 -6
- package/runtime/claude/egregore_runtime/knowledge.py +331 -5
- package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
- package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/claude/egregore_runtime/line_ids.py +10 -0
- package/runtime/claude/egregore_runtime/memory_push.py +349 -0
- package/runtime/claude/egregore_runtime/observe.py +64 -25
- package/runtime/claude/egregore_runtime/observers.py +19 -0
- package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
- package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/claude/egregore_runtime/policy.py +651 -24
- package/runtime/claude/egregore_runtime/private_result.py +2 -1
- package/runtime/claude/egregore_runtime/projection.py +946 -0
- package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
- package/runtime/claude/egregore_runtime/question_cli.py +15 -11
- package/runtime/claude/egregore_runtime/questions.py +10 -10
- package/runtime/claude/egregore_runtime/quests.py +10 -10
- package/runtime/claude/egregore_runtime/record_publish.py +548 -0
- package/runtime/claude/egregore_runtime/record_subject.py +564 -0
- package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
- package/runtime/claude/egregore_runtime/runtime.py +169 -60
- package/runtime/claude/egregore_runtime/sandbox.py +194 -0
- package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/claude/egregore_runtime/scrolls.py +23 -15
- package/runtime/claude/egregore_runtime/shadow.py +262 -0
- package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/claude/egregore_runtime/status.py +101 -44
- package/runtime/claude/egregore_runtime/status_cli.py +13 -5
- package/runtime/claude/egregore_runtime/sync.py +39 -21
- package/runtime/claude/egregore_runtime/taste.py +934 -0
- package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
- package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
- package/runtime/claude/egregore_runtime/telemetry.py +26 -3
- package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
- package/runtime/claude/egregore_runtime/todos.py +3 -2
- package/runtime/claude/egregore_runtime/upgrade.py +85 -58
- package/runtime/claude/egregore_runtime/writeback.py +330 -24
- package/runtime/claude/manifest.json +65 -5
- package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
- package/runtime/codex/.claude/context/notification-consent.md +12 -9
- package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
- package/runtime/codex/.codex/hooks.json +11 -0
- package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/codex/.codex/spec-manifest.json +13 -19
- package/runtime/codex/AGENTS.md +54 -105
- package/runtime/codex/bin/agent.sh +231 -7
- package/runtime/codex/bin/api-call.sh +44 -23
- package/runtime/codex/bin/artifact-register.sh +7 -1
- package/runtime/codex/bin/artifact-writeback.sh +1 -1
- package/runtime/codex/bin/artifacts.sh +66 -265
- package/runtime/codex/bin/attendant.sh +4 -1
- package/runtime/codex/bin/boundary-guard.cjs +1848 -0
- package/runtime/codex/bin/boundary-hook.sh +41 -0
- package/runtime/codex/bin/capture-run.sh +32 -2
- package/runtime/codex/bin/codex-render-spec.mjs +24 -46
- package/runtime/codex/bin/codex-session-start.sh +19 -0
- package/runtime/codex/bin/codex-skill-render.mjs +18 -0
- package/runtime/codex/bin/config-get.sh +10 -1
- package/runtime/codex/bin/connect-refresh.sh +16 -0
- package/runtime/codex/bin/corpus_map.py +5 -0
- package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
- package/runtime/codex/bin/genui.sh +19 -0
- package/runtime/codex/bin/graph-batch.sh +1 -1
- package/runtime/codex/bin/graph-op.sh +14 -0
- package/runtime/codex/bin/graph.sh +1 -1
- package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/codex/bin/handoff-run.sh +11 -6
- package/runtime/codex/bin/ingest-item.sh +1 -1
- package/runtime/codex/bin/ingest-surface.html +22 -4
- package/runtime/codex/bin/issue.sh +1 -1
- package/runtime/codex/bin/knowledge.sh +1 -1
- package/runtime/codex/bin/lib/card-delivery.sh +329 -0
- package/runtime/codex/bin/lib/config.sh +148 -3
- package/runtime/codex/bin/lib/context.sh +38 -67
- package/runtime/codex/bin/lib/genui-entry.py +36 -0
- package/runtime/codex/bin/lib/git-message.sh +25 -6
- package/runtime/codex/bin/lib/git-sync.sh +12 -4
- package/runtime/codex/bin/lib/greeting.sh +255 -28
- package/runtime/codex/bin/lib/handoff_meta.py +294 -0
- package/runtime/codex/bin/lib/health-footer.sh +83 -2
- package/runtime/codex/bin/lib/identity.sh +31 -10
- package/runtime/codex/bin/lib/instance-registry.sh +71 -0
- package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/codex/bin/lib/metrics.sh +121 -8
- package/runtime/codex/bin/lib/notices.sh +36 -0
- package/runtime/codex/bin/lib/record_outcomes.py +729 -0
- package/runtime/codex/bin/lib/reflexes.py +267 -0
- package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/codex/bin/lib/session_record.py +3747 -0
- package/runtime/codex/bin/lib/session_trail.py +1983 -0
- package/runtime/codex/bin/lib/settings-drift.sh +8 -5
- package/runtime/codex/bin/lib/startup-restart.sh +41 -0
- package/runtime/codex/bin/lib/worktree-links.sh +92 -0
- package/runtime/codex/bin/memory-push.sh +6 -0
- package/runtime/codex/bin/merge-gate.sh +498 -0
- package/runtime/codex/bin/notify.sh +5 -2
- package/runtime/codex/bin/page-access.sh +103 -0
- package/runtime/codex/bin/person.sh +13 -2
- package/runtime/codex/bin/pi-render-spec.mjs +0 -1
- package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/codex/bin/post-update.sh +181 -0
- package/runtime/codex/bin/prime-render-spec.mjs +0 -1
- package/runtime/codex/bin/publish-artifact.sh +89 -42
- package/runtime/codex/bin/qa-inventory.sh +2 -2
- package/runtime/codex/bin/quest.sh +1 -1
- package/runtime/codex/bin/records.sh +11 -0
- package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/codex/bin/reflexes/30-cost.py +116 -0
- package/runtime/codex/bin/reflexes/40-shells.py +51 -0
- package/runtime/codex/bin/reflexes/README.md +61 -0
- package/runtime/codex/bin/reflexes.sh +7 -0
- package/runtime/codex/bin/runtime-identity.sh +140 -42
- package/runtime/codex/bin/screenshot.sh +77 -0
- package/runtime/codex/bin/scroll.sh +1 -1
- package/runtime/codex/bin/search.sh +31 -5
- package/runtime/codex/bin/session-autosave.sh +6 -3
- package/runtime/codex/bin/session-id.sh +147 -0
- package/runtime/codex/bin/session-log.sh +19 -0
- package/runtime/codex/bin/session-record.sh +26 -0
- package/runtime/codex/bin/settings.sh +13 -8
- package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/codex/bin/spirit-discovery.sh +4 -0
- package/runtime/codex/bin/startup-check.sh +1 -1
- package/runtime/codex/bin/telemetry.sh +11 -4
- package/runtime/codex/bin/test-baseline.sh +33 -5
- package/runtime/codex/bin/todo.sh +1 -1
- package/runtime/codex/bin/worktree-create.sh +15 -45
- package/runtime/codex/bin/worktree.sh +6 -0
- package/runtime/codex/docs/spirit-discovery.md +96 -0
- package/runtime/codex/egregore_runtime/__init__.py +18 -12
- package/runtime/codex/egregore_runtime/access.py +608 -0
- package/runtime/codex/egregore_runtime/access_cli.py +190 -0
- package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
- package/runtime/codex/egregore_runtime/artifacts.py +222 -16
- package/runtime/codex/egregore_runtime/contracts.py +27 -0
- package/runtime/codex/egregore_runtime/eligibility.py +5 -12
- package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
- package/runtime/codex/egregore_runtime/github_issues.py +2 -2
- package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
- package/runtime/codex/egregore_runtime/held.py +2436 -0
- package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
- package/runtime/codex/egregore_runtime/identity.py +196 -65
- package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/codex/egregore_runtime/installation.py +119 -0
- package/runtime/codex/egregore_runtime/investigation.py +64 -41
- package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
- package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/codex/egregore_runtime/issues.py +12 -6
- package/runtime/codex/egregore_runtime/knowledge.py +331 -5
- package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
- package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/codex/egregore_runtime/line_ids.py +10 -0
- package/runtime/codex/egregore_runtime/memory_push.py +349 -0
- package/runtime/codex/egregore_runtime/observe.py +64 -25
- package/runtime/codex/egregore_runtime/observers.py +19 -0
- package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
- package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/codex/egregore_runtime/policy.py +651 -24
- package/runtime/codex/egregore_runtime/private_result.py +2 -1
- package/runtime/codex/egregore_runtime/projection.py +946 -0
- package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
- package/runtime/codex/egregore_runtime/question_cli.py +15 -11
- package/runtime/codex/egregore_runtime/questions.py +10 -10
- package/runtime/codex/egregore_runtime/quests.py +10 -10
- package/runtime/codex/egregore_runtime/record_publish.py +548 -0
- package/runtime/codex/egregore_runtime/record_subject.py +564 -0
- package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
- package/runtime/codex/egregore_runtime/runtime.py +169 -60
- package/runtime/codex/egregore_runtime/sandbox.py +194 -0
- package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/codex/egregore_runtime/scrolls.py +23 -15
- package/runtime/codex/egregore_runtime/shadow.py +262 -0
- package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/codex/egregore_runtime/status.py +101 -44
- package/runtime/codex/egregore_runtime/status_cli.py +13 -5
- package/runtime/codex/egregore_runtime/sync.py +39 -21
- package/runtime/codex/egregore_runtime/taste.py +934 -0
- package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
- package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
- package/runtime/codex/egregore_runtime/telemetry.py +26 -3
- package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
- package/runtime/codex/egregore_runtime/todos.py +3 -2
- package/runtime/codex/egregore_runtime/upgrade.py +85 -58
- package/runtime/codex/egregore_runtime/writeback.py +330 -24
- package/runtime/codex/manifest.json +66 -5
- package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
- package/runtime/pi/.claude/context/notification-consent.md +12 -9
- package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
- package/runtime/pi/.codex/hooks.json +11 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/pi/.codex/spec-manifest.json +13 -19
- package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
- package/runtime/pi/.pi/extensions/egregore.ts +13 -1
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +54 -105
- package/runtime/pi/bin/agent.sh +231 -7
- package/runtime/pi/bin/api-call.sh +44 -23
- package/runtime/pi/bin/artifact-register.sh +7 -1
- package/runtime/pi/bin/artifact-writeback.sh +1 -1
- package/runtime/pi/bin/artifacts.sh +66 -265
- package/runtime/pi/bin/attendant.sh +4 -1
- package/runtime/pi/bin/boundary-guard.cjs +1848 -0
- package/runtime/pi/bin/boundary-hook.sh +41 -0
- package/runtime/pi/bin/capture-run.sh +32 -2
- package/runtime/pi/bin/codex-render-spec.mjs +24 -46
- package/runtime/pi/bin/codex-session-start.sh +19 -0
- package/runtime/pi/bin/codex-skill-render.mjs +18 -0
- package/runtime/pi/bin/config-get.sh +10 -1
- package/runtime/pi/bin/connect-refresh.sh +16 -0
- package/runtime/pi/bin/corpus_map.py +5 -0
- package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
- package/runtime/pi/bin/genui.sh +19 -0
- package/runtime/pi/bin/graph-batch.sh +1 -1
- package/runtime/pi/bin/graph-op.sh +14 -0
- package/runtime/pi/bin/graph.sh +1 -1
- package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/pi/bin/handoff-run.sh +11 -6
- package/runtime/pi/bin/ingest-item.sh +1 -1
- package/runtime/pi/bin/ingest-surface.html +22 -4
- package/runtime/pi/bin/issue.sh +1 -1
- package/runtime/pi/bin/knowledge.sh +1 -1
- package/runtime/pi/bin/lib/card-delivery.sh +329 -0
- package/runtime/pi/bin/lib/config.sh +148 -3
- package/runtime/pi/bin/lib/context.sh +38 -67
- package/runtime/pi/bin/lib/genui-entry.py +36 -0
- package/runtime/pi/bin/lib/git-message.sh +25 -6
- package/runtime/pi/bin/lib/git-sync.sh +12 -4
- package/runtime/pi/bin/lib/greeting.sh +255 -28
- package/runtime/pi/bin/lib/handoff_meta.py +294 -0
- package/runtime/pi/bin/lib/health-footer.sh +83 -2
- package/runtime/pi/bin/lib/identity.sh +31 -10
- package/runtime/pi/bin/lib/instance-registry.sh +71 -0
- package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/pi/bin/lib/metrics.sh +121 -8
- package/runtime/pi/bin/lib/notices.sh +36 -0
- package/runtime/pi/bin/lib/record_outcomes.py +729 -0
- package/runtime/pi/bin/lib/reflexes.py +267 -0
- package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/pi/bin/lib/session_record.py +3747 -0
- package/runtime/pi/bin/lib/session_trail.py +1983 -0
- package/runtime/pi/bin/lib/settings-drift.sh +8 -5
- package/runtime/pi/bin/lib/startup-restart.sh +41 -0
- package/runtime/pi/bin/lib/worktree-links.sh +92 -0
- package/runtime/pi/bin/memory-push.sh +6 -0
- package/runtime/pi/bin/merge-gate.sh +498 -0
- package/runtime/pi/bin/notify.sh +5 -2
- package/runtime/pi/bin/page-access.sh +103 -0
- package/runtime/pi/bin/person.sh +13 -2
- package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
- package/runtime/pi/bin/pi-observe-context.mjs +97 -4
- package/runtime/pi/bin/pi-render-spec.mjs +0 -1
- package/runtime/pi/bin/pi-session-start.sh +4 -1
- package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/pi/bin/post-update.sh +181 -0
- package/runtime/pi/bin/prime-render-spec.mjs +0 -1
- package/runtime/pi/bin/publish-artifact.sh +89 -42
- package/runtime/pi/bin/qa-inventory.sh +2 -2
- package/runtime/pi/bin/quest.sh +1 -1
- package/runtime/pi/bin/records.sh +11 -0
- package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/pi/bin/reflexes/30-cost.py +116 -0
- package/runtime/pi/bin/reflexes/40-shells.py +51 -0
- package/runtime/pi/bin/reflexes/README.md +61 -0
- package/runtime/pi/bin/reflexes.sh +7 -0
- package/runtime/pi/bin/runtime-identity.sh +140 -42
- package/runtime/pi/bin/screenshot.sh +77 -0
- package/runtime/pi/bin/scroll.sh +1 -1
- package/runtime/pi/bin/search.sh +31 -5
- package/runtime/pi/bin/session-autosave.sh +6 -3
- package/runtime/pi/bin/session-id.sh +147 -0
- package/runtime/pi/bin/session-log.sh +19 -0
- package/runtime/pi/bin/session-record.sh +26 -0
- package/runtime/pi/bin/settings.sh +13 -8
- package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/pi/bin/spirit-discovery.sh +4 -0
- package/runtime/pi/bin/startup-check.sh +1 -1
- package/runtime/pi/bin/telemetry.sh +11 -4
- package/runtime/pi/bin/test-baseline.sh +33 -5
- package/runtime/pi/bin/todo.sh +1 -1
- package/runtime/pi/bin/worktree-create.sh +15 -45
- package/runtime/pi/bin/worktree.sh +6 -0
- package/runtime/pi/docs/spirit-discovery.md +96 -0
- package/runtime/pi/egregore_runtime/__init__.py +18 -12
- package/runtime/pi/egregore_runtime/access.py +608 -0
- package/runtime/pi/egregore_runtime/access_cli.py +190 -0
- package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
- package/runtime/pi/egregore_runtime/artifacts.py +222 -16
- package/runtime/pi/egregore_runtime/contracts.py +27 -0
- package/runtime/pi/egregore_runtime/eligibility.py +5 -12
- package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
- package/runtime/pi/egregore_runtime/github_issues.py +2 -2
- package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
- package/runtime/pi/egregore_runtime/held.py +2436 -0
- package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
- package/runtime/pi/egregore_runtime/identity.py +196 -65
- package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/pi/egregore_runtime/installation.py +119 -0
- package/runtime/pi/egregore_runtime/investigation.py +64 -41
- package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
- package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/pi/egregore_runtime/issues.py +12 -6
- package/runtime/pi/egregore_runtime/knowledge.py +331 -5
- package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
- package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/pi/egregore_runtime/line_ids.py +10 -0
- package/runtime/pi/egregore_runtime/memory_push.py +349 -0
- package/runtime/pi/egregore_runtime/observe.py +64 -25
- package/runtime/pi/egregore_runtime/observers.py +19 -0
- package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
- package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/pi/egregore_runtime/policy.py +651 -24
- package/runtime/pi/egregore_runtime/private_result.py +2 -1
- package/runtime/pi/egregore_runtime/projection.py +946 -0
- package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
- package/runtime/pi/egregore_runtime/question_cli.py +15 -11
- package/runtime/pi/egregore_runtime/questions.py +10 -10
- package/runtime/pi/egregore_runtime/quests.py +10 -10
- package/runtime/pi/egregore_runtime/record_publish.py +548 -0
- package/runtime/pi/egregore_runtime/record_subject.py +564 -0
- package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
- package/runtime/pi/egregore_runtime/runtime.py +169 -60
- package/runtime/pi/egregore_runtime/sandbox.py +194 -0
- package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/pi/egregore_runtime/scrolls.py +23 -15
- package/runtime/pi/egregore_runtime/shadow.py +262 -0
- package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/pi/egregore_runtime/status.py +101 -44
- package/runtime/pi/egregore_runtime/status_cli.py +13 -5
- package/runtime/pi/egregore_runtime/sync.py +39 -21
- package/runtime/pi/egregore_runtime/taste.py +934 -0
- package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
- package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
- package/runtime/pi/egregore_runtime/telemetry.py +26 -3
- package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
- package/runtime/pi/egregore_runtime/todos.py +3 -2
- package/runtime/pi/egregore_runtime/upgrade.py +85 -58
- package/runtime/pi/egregore_runtime/writeback.py +330 -24
- package/runtime/pi/manifest.json +66 -5
- package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
- package/runtime/prime/.claude/context/notification-consent.md +12 -9
- package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
- package/runtime/prime/.codex/hooks.json +11 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/prime/.codex/spec-manifest.json +13 -19
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
- package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
- package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
- package/runtime/prime/AGENTS.md +54 -105
- package/runtime/prime/bin/agent.sh +231 -7
- package/runtime/prime/bin/api-call.sh +44 -23
- package/runtime/prime/bin/artifact-register.sh +7 -1
- package/runtime/prime/bin/artifact-writeback.sh +1 -1
- package/runtime/prime/bin/artifacts.sh +66 -265
- package/runtime/prime/bin/attendant.sh +4 -1
- package/runtime/prime/bin/boundary-guard.cjs +1848 -0
- package/runtime/prime/bin/boundary-hook.sh +41 -0
- package/runtime/prime/bin/capture-run.sh +32 -2
- package/runtime/prime/bin/codex-render-spec.mjs +24 -46
- package/runtime/prime/bin/codex-session-start.sh +19 -0
- package/runtime/prime/bin/codex-skill-render.mjs +18 -0
- package/runtime/prime/bin/config-get.sh +10 -1
- package/runtime/prime/bin/connect-refresh.sh +16 -0
- package/runtime/prime/bin/corpus_map.py +5 -0
- package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
- package/runtime/prime/bin/genui.sh +19 -0
- package/runtime/prime/bin/graph-batch.sh +1 -1
- package/runtime/prime/bin/graph-op.sh +14 -0
- package/runtime/prime/bin/graph.sh +1 -1
- package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/prime/bin/handoff-run.sh +11 -6
- package/runtime/prime/bin/ingest-item.sh +1 -1
- package/runtime/prime/bin/ingest-surface.html +22 -4
- package/runtime/prime/bin/issue.sh +1 -1
- package/runtime/prime/bin/knowledge.sh +1 -1
- package/runtime/prime/bin/lib/card-delivery.sh +329 -0
- package/runtime/prime/bin/lib/config.sh +148 -3
- package/runtime/prime/bin/lib/context.sh +38 -67
- package/runtime/prime/bin/lib/genui-entry.py +36 -0
- package/runtime/prime/bin/lib/git-message.sh +25 -6
- package/runtime/prime/bin/lib/git-sync.sh +12 -4
- package/runtime/prime/bin/lib/greeting.sh +255 -28
- package/runtime/prime/bin/lib/handoff_meta.py +294 -0
- package/runtime/prime/bin/lib/health-footer.sh +83 -2
- package/runtime/prime/bin/lib/identity.sh +31 -10
- package/runtime/prime/bin/lib/instance-registry.sh +71 -0
- package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/prime/bin/lib/metrics.sh +121 -8
- package/runtime/prime/bin/lib/notices.sh +36 -0
- package/runtime/prime/bin/lib/record_outcomes.py +729 -0
- package/runtime/prime/bin/lib/reflexes.py +267 -0
- package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/prime/bin/lib/session_record.py +3747 -0
- package/runtime/prime/bin/lib/session_trail.py +1983 -0
- package/runtime/prime/bin/lib/settings-drift.sh +8 -5
- package/runtime/prime/bin/lib/startup-restart.sh +41 -0
- package/runtime/prime/bin/lib/worktree-links.sh +92 -0
- package/runtime/prime/bin/memory-push.sh +6 -0
- package/runtime/prime/bin/merge-gate.sh +498 -0
- package/runtime/prime/bin/notify.sh +5 -2
- package/runtime/prime/bin/page-access.sh +103 -0
- package/runtime/prime/bin/person.sh +13 -2
- package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
- package/runtime/prime/bin/pi-observe-context.mjs +97 -4
- package/runtime/prime/bin/pi-render-spec.mjs +0 -1
- package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/prime/bin/post-update.sh +181 -0
- package/runtime/prime/bin/prime-render-spec.mjs +0 -1
- package/runtime/prime/bin/prime-session-start.sh +3 -1
- package/runtime/prime/bin/publish-artifact.sh +89 -42
- package/runtime/prime/bin/qa-inventory.sh +2 -2
- package/runtime/prime/bin/quest.sh +1 -1
- package/runtime/prime/bin/records.sh +11 -0
- package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/prime/bin/reflexes/30-cost.py +116 -0
- package/runtime/prime/bin/reflexes/40-shells.py +51 -0
- package/runtime/prime/bin/reflexes/README.md +61 -0
- package/runtime/prime/bin/reflexes.sh +7 -0
- package/runtime/prime/bin/runtime-identity.sh +140 -42
- package/runtime/prime/bin/screenshot.sh +77 -0
- package/runtime/prime/bin/scroll.sh +1 -1
- package/runtime/prime/bin/search.sh +31 -5
- package/runtime/prime/bin/session-autosave.sh +6 -3
- package/runtime/prime/bin/session-id.sh +147 -0
- package/runtime/prime/bin/session-log.sh +19 -0
- package/runtime/prime/bin/session-record.sh +26 -0
- package/runtime/prime/bin/settings.sh +13 -8
- package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/prime/bin/spirit-discovery.sh +4 -0
- package/runtime/prime/bin/startup-check.sh +1 -1
- package/runtime/prime/bin/telemetry.sh +11 -4
- package/runtime/prime/bin/test-baseline.sh +33 -5
- package/runtime/prime/bin/todo.sh +1 -1
- package/runtime/prime/bin/worktree-create.sh +15 -45
- package/runtime/prime/bin/worktree.sh +6 -0
- package/runtime/prime/docs/spirit-discovery.md +96 -0
- package/runtime/prime/egregore_runtime/__init__.py +18 -12
- package/runtime/prime/egregore_runtime/access.py +608 -0
- package/runtime/prime/egregore_runtime/access_cli.py +190 -0
- package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
- package/runtime/prime/egregore_runtime/artifacts.py +222 -16
- package/runtime/prime/egregore_runtime/contracts.py +27 -0
- package/runtime/prime/egregore_runtime/eligibility.py +5 -12
- package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
- package/runtime/prime/egregore_runtime/github_issues.py +2 -2
- package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
- package/runtime/prime/egregore_runtime/held.py +2436 -0
- package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
- package/runtime/prime/egregore_runtime/identity.py +196 -65
- package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/prime/egregore_runtime/installation.py +119 -0
- package/runtime/prime/egregore_runtime/investigation.py +64 -41
- package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
- package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/prime/egregore_runtime/issues.py +12 -6
- package/runtime/prime/egregore_runtime/knowledge.py +331 -5
- package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
- package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/prime/egregore_runtime/line_ids.py +10 -0
- package/runtime/prime/egregore_runtime/memory_push.py +349 -0
- package/runtime/prime/egregore_runtime/observe.py +64 -25
- package/runtime/prime/egregore_runtime/observers.py +19 -0
- package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
- package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/prime/egregore_runtime/policy.py +651 -24
- package/runtime/prime/egregore_runtime/private_result.py +2 -1
- package/runtime/prime/egregore_runtime/projection.py +946 -0
- package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
- package/runtime/prime/egregore_runtime/question_cli.py +15 -11
- package/runtime/prime/egregore_runtime/questions.py +10 -10
- package/runtime/prime/egregore_runtime/quests.py +10 -10
- package/runtime/prime/egregore_runtime/record_publish.py +548 -0
- package/runtime/prime/egregore_runtime/record_subject.py +564 -0
- package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
- package/runtime/prime/egregore_runtime/runtime.py +169 -60
- package/runtime/prime/egregore_runtime/sandbox.py +194 -0
- package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/prime/egregore_runtime/scrolls.py +23 -15
- package/runtime/prime/egregore_runtime/shadow.py +262 -0
- package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/prime/egregore_runtime/status.py +101 -44
- package/runtime/prime/egregore_runtime/status_cli.py +13 -5
- package/runtime/prime/egregore_runtime/sync.py +39 -21
- package/runtime/prime/egregore_runtime/taste.py +934 -0
- package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
- package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
- package/runtime/prime/egregore_runtime/telemetry.py +26 -3
- package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
- package/runtime/prime/egregore_runtime/todos.py +3 -2
- package/runtime/prime/egregore_runtime/upgrade.py +85 -58
- package/runtime/prime/egregore_runtime/writeback.py +330 -24
- package/runtime/prime/manifest.json +66 -5
- package/runtime/claude/bin/thread.sh +0 -9
- package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
- package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
- package/runtime/claude/egregore_runtime/threads.py +0 -521
- package/runtime/codex/bin/thread.sh +0 -9
- package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
- package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
- package/runtime/codex/egregore_runtime/threads.py +0 -521
- package/runtime/pi/bin/thread.sh +0 -9
- package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
- package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
- package/runtime/pi/egregore_runtime/threads.py +0 -521
- package/runtime/prime/bin/thread.sh +0 -9
- package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
- package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
- package/runtime/prime/egregore_runtime/threads.py +0 -521
|
@@ -7,7 +7,7 @@ implementation details. Infrastructure is composed behind ``local_runtime``.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from .investigation_guidance import INVESTIGATION_GUIDANCE, RECALL_WORKFLOW_GUIDANCE
|
|
10
|
+
from .investigation_guidance import INVESTIGATION_GUIDANCE, RECALL_WORKFLOW_GUIDANCE, investigation_guidance
|
|
11
11
|
|
|
12
12
|
import argparse
|
|
13
13
|
import fcntl
|
|
@@ -16,12 +16,14 @@ from io import StringIO
|
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
18
|
import re
|
|
19
|
+
import stat
|
|
19
20
|
import subprocess
|
|
20
21
|
import sys
|
|
22
|
+
import threading
|
|
21
23
|
import time
|
|
22
24
|
import unicodedata
|
|
23
25
|
import uuid
|
|
24
|
-
from contextlib import contextmanager, redirect_stdout
|
|
26
|
+
from contextlib import contextmanager, nullcontext, redirect_stdout
|
|
25
27
|
from contextvars import ContextVar
|
|
26
28
|
from dataclasses import asdict
|
|
27
29
|
from datetime import UTC, datetime, timedelta
|
|
@@ -31,6 +33,7 @@ from .contracts import (
|
|
|
31
33
|
EvidenceItem,
|
|
32
34
|
OrgContext,
|
|
33
35
|
Permission,
|
|
36
|
+
PolicyDecision,
|
|
34
37
|
RetrievalMode,
|
|
35
38
|
RetrievalRequest,
|
|
36
39
|
TemporalScope,
|
|
@@ -40,7 +43,19 @@ from .identity import actor_lexical_formulations, organization_member_presentati
|
|
|
40
43
|
from .investigation_contracts import InvestigationRequest, decode_request
|
|
41
44
|
from . import episode_binding
|
|
42
45
|
from .runtime import EgregoreRuntime, local_runtime
|
|
46
|
+
from .policy import is_admin
|
|
47
|
+
from .installation import installation_key, installation_root
|
|
48
|
+
from .optional_modules import optional_module
|
|
43
49
|
|
|
50
|
+
_record = optional_module(".record")
|
|
51
|
+
record_notice = _record.notice if _record is not None else lambda *args, **kwargs: None
|
|
52
|
+
member_cli = optional_module(".member_cli")
|
|
53
|
+
from .member_authority import MemberAuthorityError
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# A later package adds a command as a new module with register(commands)
|
|
57
|
+
# and one line here; relative names resolve inside each Runtime bundle.
|
|
58
|
+
_COMMAND_MODULES: tuple[str, ...] = (".access_cli", ".mind_cli", ".outputs_cli")
|
|
44
59
|
|
|
45
60
|
_HISTORICAL_SCOPE = re.compile(
|
|
46
61
|
r"\b(history|historical|previous|older|old decision|superseded|what changed|how .* evolv(?:e|ed))\b",
|
|
@@ -98,10 +113,31 @@ _episode_session: ContextVar[str | None] = ContextVar("observe_episode_session",
|
|
|
98
113
|
_prompt_binding: ContextVar[episode_binding.EpisodeBinding | None] = ContextVar('prompt_investigation_binding',default=None)
|
|
99
114
|
_invocation_root: ContextVar[Path | None] = ContextVar('investigation_root',default=None)
|
|
100
115
|
|
|
116
|
+
def _runtime(root, *, push_remote=False):
|
|
117
|
+
member = (member_cli.current() if member_cli is not None else None)
|
|
118
|
+
return (member.runtime(root, push_remote=push_remote) if member
|
|
119
|
+
else local_runtime(root, push_remote=push_remote))
|
|
120
|
+
|
|
101
121
|
|
|
102
122
|
@contextmanager
|
|
103
123
|
def _invocation(root, reference=None):
|
|
104
124
|
"""A native prompt's explicit reference is stronger than shared shell state."""
|
|
125
|
+
member = (member_cli.current() if member_cli is not None else None)
|
|
126
|
+
if member:
|
|
127
|
+
binding = (episode_binding.resolve(root, reference) if reference
|
|
128
|
+
else episode_binding.resolve_native(root, harness="durable", session_id=member.session_id))
|
|
129
|
+
if binding is None or binding.session_id != member.session_id:
|
|
130
|
+
raise MemberAuthorityError()
|
|
131
|
+
session_token = _episode_session.set(member.session_id)
|
|
132
|
+
binding_token = _prompt_binding.set(binding)
|
|
133
|
+
root_token = _invocation_root.set(root)
|
|
134
|
+
try:
|
|
135
|
+
yield binding
|
|
136
|
+
finally:
|
|
137
|
+
_invocation_root.reset(root_token)
|
|
138
|
+
_prompt_binding.reset(binding_token)
|
|
139
|
+
_episode_session.reset(session_token)
|
|
140
|
+
return
|
|
105
141
|
binding = _prompt_binding.get()
|
|
106
142
|
if binding is None and (reference or os.environ.get('EGREGORE_EPISODE_ID')):
|
|
107
143
|
binding = episode_binding.resolve(root,reference or os.environ['EGREGORE_EPISODE_ID'])
|
|
@@ -135,11 +171,11 @@ def _invocation(root, reference=None):
|
|
|
135
171
|
|
|
136
172
|
def _execute_investigation(root, harness, request, *, runtime=None, actor=None, reference=None, request_id=None, origin='investigate'):
|
|
137
173
|
with _invocation(root,reference) as binding:
|
|
138
|
-
runtime = runtime or
|
|
174
|
+
runtime = runtime or _runtime(root,push_remote=False)
|
|
139
175
|
actor = actor or _actor(runtime,root,harness)
|
|
140
176
|
episode_id = binding.episode_id if binding else _session_id(root)
|
|
141
177
|
# Old receipts are read only as a migration bridge; new calls never write them.
|
|
142
|
-
receipt = None if binding else _active_observe_receipt(root,harness)
|
|
178
|
+
receipt = None if binding or (member_cli.current() if member_cli is not None else None) else _active_observe_receipt(root,harness)
|
|
143
179
|
contexts,initial_searches,bounds=[],0,{}
|
|
144
180
|
if receipt:
|
|
145
181
|
contexts=[entry.get('context',{}) for entry in _receipt_requests(receipt).values()]
|
|
@@ -150,12 +186,20 @@ def _execute_investigation(root, harness, request, *, runtime=None, actor=None,
|
|
|
150
186
|
bounds['_not_before']=receipt['not_before']
|
|
151
187
|
if receipt.get('terminal_gap'):
|
|
152
188
|
bounds['_legacy_terminal_gap']=True
|
|
153
|
-
|
|
189
|
+
response = runtime.investigate(actor,request,episode_id=episode_id,seeds=contexts,
|
|
154
190
|
initial_searches=initial_searches,initial_bounds=bounds,request_id=request_id,
|
|
155
191
|
binding=binding,origin=origin)
|
|
192
|
+
_mind_call.set((response, origin, request))
|
|
193
|
+
return response
|
|
156
194
|
|
|
157
195
|
|
|
158
196
|
def _publish_result(root, response, rendered, *, private=False, harness='shell'):
|
|
197
|
+
if (member_cli.current() if member_cli is not None else None):
|
|
198
|
+
# Member content stays on the authenticated host's private stdout.
|
|
199
|
+
# Local attachment, record and mind surfaces have their own operator
|
|
200
|
+
# lifecycle and are deliberately absent from this composition.
|
|
201
|
+
sys.stdout.write(rendered)
|
|
202
|
+
return False
|
|
159
203
|
from .private_result import supports_attachment
|
|
160
204
|
# An invalidated prompt cannot consume an attachment. Keep its error visible;
|
|
161
205
|
# never use this fallback for successful evidence from a failed binding.
|
|
@@ -166,11 +210,13 @@ def _publish_result(root, response, rendered, *, private=False, harness='shell')
|
|
|
166
210
|
if response['ok']:
|
|
167
211
|
raise
|
|
168
212
|
private = False
|
|
213
|
+
receipt = None
|
|
169
214
|
if private:
|
|
170
215
|
from .private_result import prepare
|
|
171
216
|
reference=response['episode_id']
|
|
172
217
|
session=episode_binding.resolve(root,reference).session_id if reference.startswith('ep_') else _session_id(root)
|
|
173
|
-
|
|
218
|
+
receipt = prepare(root,response,rendered,session)
|
|
219
|
+
print(receipt)
|
|
174
220
|
else:
|
|
175
221
|
from .private_result import record_stdout
|
|
176
222
|
try:
|
|
@@ -180,12 +226,347 @@ def _publish_result(root, response, rendered, *, private=False, harness='shell')
|
|
|
180
226
|
raise
|
|
181
227
|
# A missing/corrupt receipt must not replace the operation's error.
|
|
182
228
|
sys.stdout.write(rendered)
|
|
229
|
+
_mind_notice(root, response, rendered, harness, receipt)
|
|
183
230
|
return private
|
|
184
231
|
|
|
185
232
|
|
|
233
|
+
# Notices for an open memory map (experiments/egregore-mind/AGENT-SPEC.md §3.6).
|
|
234
|
+
# Inert unless a map is open for this workspace. A notice goes to this
|
|
235
|
+
# checkout's door ($MIND_HOME, else experiments/egregore-mind/.mind) when its
|
|
236
|
+
# door.json names this checkout as the one it serves; only if that door took
|
|
237
|
+
# nothing, to the instance door (.egregore/runtime/mind under the installation
|
|
238
|
+
# this checkout belongs to) when its door.json names that installation. Either
|
|
239
|
+
# door needs a heartbeat under 3 s old and must be the user's alone, and no
|
|
240
|
+
# folder below the checkout or installation may be a link. Notices may cross
|
|
241
|
+
# the checkouts of one installation, never into another instance; `mind`
|
|
242
|
+
# requests stay per checkout (§3.3).
|
|
243
|
+
# Each notice is one small file of paths, counts and ids, never prompt, query or
|
|
244
|
+
# document text, and writing it never changes, fails or holds up the call it
|
|
245
|
+
# describes: every filesystem step runs on a writer thread the call waits for
|
|
246
|
+
# at most 0.1 s.
|
|
247
|
+
_mind_call: ContextVar[tuple | None] = ContextVar('mind_call', default=None)
|
|
248
|
+
_MIND_KINDS = ('seen-', 'asked-', 'delivered-')
|
|
249
|
+
_MIND_WAITING = 1000 # files a door may hold before notices are dropped (soft: writers do not lock)
|
|
250
|
+
_MIND_LARGEST = 65536 # bytes; a larger record is not written
|
|
251
|
+
_MIND_WRITERS = threading.BoundedSemaphore(4) # stuck writers a process may hold; then notices drop
|
|
252
|
+
_MIND_WORKSPACE = re.compile(r'[A-Za-z0-9][A-Za-z0-9_.-]{0,63}')
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _mind_send(root, kind, harness, session, note):
|
|
256
|
+
if _record is None: # the Mind runtime is not installed
|
|
257
|
+
return
|
|
258
|
+
configured = os.environ.get('MIND_HOME')
|
|
259
|
+
native, codex = os.environ.get('EGREGORE_NATIVE_SESSION_ID'), os.environ.get('CODEX_THREAD_ID')
|
|
260
|
+
# Codex first: a Codex worker started from a Claude or Pi shell inherits that session's
|
|
261
|
+
# EGREGORE_NATIVE_* variables, and its prompt hook names it codex/<thread>, so its searches must
|
|
262
|
+
# too. The reverse nesting (Claude or Pi run inside a Codex shell) is split across two agents.
|
|
263
|
+
environment = (_mind_actor('codex', codex) if codex else
|
|
264
|
+
_mind_actor(os.environ.get('EGREGORE_NATIVE_HARNESS') or 'agent', native) if native else '')
|
|
265
|
+
if kind == 'asked' and harness == 'codex' and codex:
|
|
266
|
+
actor = _mind_actor('codex', codex) # one Codex session is one agent, whatever its hook calls itself
|
|
267
|
+
elif kind == 'delivered':
|
|
268
|
+
actor = _mind_actor(harness, session) if session else '' # only the hook that attached the result
|
|
269
|
+
else:
|
|
270
|
+
actor = _mind_actor(harness, session) if session else environment # the hook's own session first
|
|
271
|
+
if not _MIND_WRITERS.acquire(blocking=False):
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
def write():
|
|
275
|
+
try:
|
|
276
|
+
_mind_write(root, configured, kind, harness, actor, note)
|
|
277
|
+
except Exception:
|
|
278
|
+
pass # no map, a stopped map, or a door it must not use
|
|
279
|
+
finally:
|
|
280
|
+
_MIND_WRITERS.release()
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
writer = threading.Thread(target=write, name='mind-notice', daemon=True)
|
|
284
|
+
writer.start()
|
|
285
|
+
except BaseException:
|
|
286
|
+
_MIND_WRITERS.release()
|
|
287
|
+
raise
|
|
288
|
+
writer.join(0.1)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _mind_actor(harness, session):
|
|
292
|
+
"""`<harness>/<session>` exactly as the map's bridge accepts an actor, else ''."""
|
|
293
|
+
harness = 'claude' if harness == 'claude-code' else str(harness or '')
|
|
294
|
+
if re.fullmatch(r'[a-z][a-z0-9-]{0,19}', harness) and re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}', str(session)):
|
|
295
|
+
return f'{harness}/{session}'
|
|
296
|
+
return ''
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _mind_write(root, configured, kind, harness, actor, note):
|
|
300
|
+
"""One notice: this checkout's door, else the instance door. True once one is renamed into place."""
|
|
301
|
+
here = os.path.realpath(root)
|
|
302
|
+
instance = os.path.realpath(installation_root(here))
|
|
303
|
+
folder = os.path.basename(here)
|
|
304
|
+
fields = {'actor': actor, 'harness': str(harness)[:40],
|
|
305
|
+
'workspace': 'main' if here == instance else folder if _MIND_WORKSPACE.fullmatch(folder) else 'other',
|
|
306
|
+
**note}
|
|
307
|
+
checkout = [configured, 'door'] if configured else [str(root), 'experiments', 'egregore-mind', '.mind', 'door']
|
|
308
|
+
return (_mind_door(checkout, 'root', here, kind, fields) or # MIND_HOME never reaches the instance door
|
|
309
|
+
_mind_door([instance, '.egregore', 'runtime', 'mind', 'door'], 'instance', instance, kind, fields))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _mind_door(walk, field, expected, kind, fields):
|
|
313
|
+
"""Write one notice into the live door at ``walk`` whose door.json names ``expected`` as its ``field``.
|
|
314
|
+
|
|
315
|
+
Below the first folder none may be a link, so the door checked is the door written. True once the
|
|
316
|
+
notice is renamed into place; False when the door is absent, stopped, someone else's, full, names
|
|
317
|
+
another checkout or instance, or refuses the write.
|
|
318
|
+
"""
|
|
319
|
+
held = []
|
|
320
|
+
try:
|
|
321
|
+
for part in walk:
|
|
322
|
+
held.append(os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NONBLOCK | (os.O_NOFOLLOW if held else 0),
|
|
323
|
+
dir_fd=held[-1] if held else None))
|
|
324
|
+
home, door = held[-2], held[-1]
|
|
325
|
+
beat, info = os.stat('heartbeat', dir_fd=door, follow_symlinks=False), os.fstat(door)
|
|
326
|
+
if (not stat.S_ISREG(beat.st_mode) or not -1 <= time.time() - beat.st_mtime <= 3
|
|
327
|
+
or info.st_uid != os.getuid() or info.st_mode & 0o077):
|
|
328
|
+
return False # a stopped map, or a door someone else could open
|
|
329
|
+
with os.fdopen(os.open('door.json', os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=home), 'rb') as handle:
|
|
330
|
+
pointer = json.loads(handle.read(4096))
|
|
331
|
+
if not isinstance(pointer, dict) or pointer.get(field) != expected:
|
|
332
|
+
return False # a map serving another checkout or instance, or one that does not say which
|
|
333
|
+
run = pointer.get('run')
|
|
334
|
+
record = json.dumps({'v': 2, 'k': kind, 't': datetime.now(UTC).isoformat(timespec='milliseconds')
|
|
335
|
+
.replace('+00:00', 'Z'), 'run': str(run)[:40] if run else None, **fields},
|
|
336
|
+
ensure_ascii=False) + '\n'
|
|
337
|
+
if len(record.encode('utf-8')) > _MIND_LARGEST:
|
|
338
|
+
return False
|
|
339
|
+
waiting, leftovers = 0, tuple('.' + k for k in _MIND_KINDS)
|
|
340
|
+
with os.scandir(door) as entries:
|
|
341
|
+
for entry in entries:
|
|
342
|
+
try:
|
|
343
|
+
if entry.name.startswith(leftovers) and time.time() - entry.stat(
|
|
344
|
+
follow_symlinks=False).st_mtime > 60:
|
|
345
|
+
os.unlink(entry.name, dir_fd=door) # a writer that died between create and rename
|
|
346
|
+
continue
|
|
347
|
+
except FileNotFoundError:
|
|
348
|
+
continue # its writer renamed it, or another writer reclaimed it, after the listing
|
|
349
|
+
waiting += entry.name.lstrip('.').startswith(_MIND_KINDS)
|
|
350
|
+
if waiting >= _MIND_WAITING:
|
|
351
|
+
return False
|
|
352
|
+
name = f'{kind}-{time.time_ns()}-{os.getpid()}-{uuid.uuid4().hex[:8]}'
|
|
353
|
+
descriptor = os.open('.' + name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600, dir_fd=door)
|
|
354
|
+
try:
|
|
355
|
+
with os.fdopen(descriptor, 'w', encoding='utf-8') as handle:
|
|
356
|
+
handle.write(record)
|
|
357
|
+
os.rename('.' + name, name, src_dir_fd=door, dst_dir_fd=door)
|
|
358
|
+
except BaseException:
|
|
359
|
+
os.unlink('.' + name, dir_fd=door)
|
|
360
|
+
raise
|
|
361
|
+
return True
|
|
362
|
+
except (OSError, ValueError):
|
|
363
|
+
return False # no door here, or one it must not use: the next door may still take it
|
|
364
|
+
finally:
|
|
365
|
+
for descriptor in reversed(held):
|
|
366
|
+
os.close(descriptor)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _mind_asked(root, harness, payload):
|
|
370
|
+
"""The person asked something in this session: when and where, never what."""
|
|
371
|
+
try:
|
|
372
|
+
_mind_send(root, 'asked', harness, str(payload.get('session_id') or ''), {})
|
|
373
|
+
except Exception:
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _mind_digest(token):
|
|
378
|
+
"""A short name two notices share for one attachment or cursor; never the token itself."""
|
|
379
|
+
return hashlib.sha256(str(token).encode()).hexdigest()[:16]
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _mind_delivered(root, harness, payload, receipt):
|
|
383
|
+
"""The agent's own hook attached a prepared result: the seen notice with this packet reached the agent."""
|
|
384
|
+
try:
|
|
385
|
+
_mind_send(root, 'delivered', harness, str(payload.get('session_id') or ''), {'packet': _mind_digest(receipt)})
|
|
386
|
+
except Exception:
|
|
387
|
+
return
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _mind_notice(root, response, rendered, harness, receipt=None):
|
|
391
|
+
"""What a find, open, query or investigate call showed the agent, or why it showed nothing.
|
|
392
|
+
|
|
393
|
+
With ``receipt`` (Claude's attachment line) the result was only prepared: it counts as seen
|
|
394
|
+
once a ``delivered`` notice with the same ``packet`` follows from the consuming hook.
|
|
395
|
+
"""
|
|
396
|
+
if (member_cli.current() if member_cli is not None else None):
|
|
397
|
+
return
|
|
398
|
+
try:
|
|
399
|
+
call = _mind_call.get()
|
|
400
|
+
if call is None or call[0] is not response:
|
|
401
|
+
return
|
|
402
|
+
request = call[2].to_dict() if hasattr(call[2], 'to_dict') else dict(call[2])
|
|
403
|
+
operation = request.get('operation')
|
|
404
|
+
if operation not in {'discover', 'open', 'open_many'}:
|
|
405
|
+
return
|
|
406
|
+
projection = request.get('context_projection') or {}
|
|
407
|
+
retrieval = projection.get('retrieval') or {}
|
|
408
|
+
result = (response.get('result') or {}) if response.get('ok') else {}
|
|
409
|
+
context, details = result.get('context') or {}, result.get('coverage_details') or {}
|
|
410
|
+
def paths(values):
|
|
411
|
+
return list(dict.fromkeys(p for p in values if isinstance(p, str) and p.startswith('memory/')
|
|
412
|
+
and len(p) <= 300))[:100]
|
|
413
|
+
# A result path counts only when the published text names it, plain or as JSON either way:
|
|
414
|
+
# a query that missed its minimum evidence prints the gap, not the paths.
|
|
415
|
+
shown = [p for p in paths([row.get('path') for row in result.get('results', ())] +
|
|
416
|
+
[item.get('canonical_path') for item in context.get('evidence', ())])
|
|
417
|
+
if any(form in rendered for form in
|
|
418
|
+
(p, json.dumps(p)[1:-1], json.dumps(p, ensure_ascii=False)[1:-1]))]
|
|
419
|
+
opened = paths([s.get('path') for s in result.get('sources', [result] if 'content' in result else ())] +
|
|
420
|
+
(shown if retrieval.get('open_sources') else []))
|
|
421
|
+
kind = ({'lex': 'keyword', 'vec': 'semantic', 'lex+vec': 'hybrid'}.get(str(retrieval.get('mode')))
|
|
422
|
+
or result.get('operation') or request.get('kind') or 'keyword')
|
|
423
|
+
def day(value):
|
|
424
|
+
return value if isinstance(value, str) and re.fullmatch(r'\d{4}-\d{2}-\d{2}', value) else None
|
|
425
|
+
days = request.get('recent_days') or projection.get('recent_days')
|
|
426
|
+
def count(value):
|
|
427
|
+
return value if type(value) is int and 0 <= value <= 10**6 else None
|
|
428
|
+
if receipt:
|
|
429
|
+
from .private_result import MARKER
|
|
430
|
+
receipt = json.loads(receipt.removeprefix(MARKER))['id']
|
|
431
|
+
_mind_send(root, 'seen', harness, None, {
|
|
432
|
+
'episode': str(response.get('episode_id') or '')[:80] or None,
|
|
433
|
+
'invocation': str(response.get('invocation_id') or '')[:80] or None,
|
|
434
|
+
'call': call[1], 'op': operation, 'delivery': 'attachment' if receipt else 'stdout',
|
|
435
|
+
'packet': _mind_digest(receipt) if receipt else None,
|
|
436
|
+
'kind': kind if operation == 'discover' and kind in
|
|
437
|
+
{'filename', 'literal', 'dates', 'keyword', 'semantic', 'hybrid'} else None,
|
|
438
|
+
# No query words: the kind, dates and paths say where the agent looked, never what it typed.
|
|
439
|
+
# A next page carries only its cursor: `continues` names the notice whose `next` handed
|
|
440
|
+
# that cursor out, and that notice holds the kind and dates this page continues.
|
|
441
|
+
'continues': _mind_digest(request['cursor']) if request.get('cursor') else None,
|
|
442
|
+
'next': _mind_digest(result['next_cursor']) if result.get('next_cursor') else None,
|
|
443
|
+
# This call's own date arguments. Bounds the Runtime kept from earlier calls in the
|
|
444
|
+
# prompt are not in its response, so these are never the whole scope searched.
|
|
445
|
+
'requested': {'after': day(request.get('after')), 'before': day(request.get('before')),
|
|
446
|
+
'recent_days': days if type(days) is int and 1 <= days <= 36500 else None},
|
|
447
|
+
'paths': shown, 'opened': opened,
|
|
448
|
+
'searches_left': count(result.get('searches_remaining')), 'reads_left': count(result.get('reads_remaining')),
|
|
449
|
+
'coverage': {'degraded': bool(result.get('degraded') or context.get('degraded')),
|
|
450
|
+
'absent_or_stale': count(details.get('stale_or_missing_count', 0)),
|
|
451
|
+
'vectors_missing': count(details.get('vector_missing_count', 0)),
|
|
452
|
+
'omitted': len(context.get('omissions') or ()),
|
|
453
|
+
'more': count(result.get('remaining_in_result_set', 0)),
|
|
454
|
+
'reach': str(result.get('coverage') or '')[:120]},
|
|
455
|
+
'ok': bool(response.get('ok')), 'error': str(response.get('error_code') or '')[:40] or None})
|
|
456
|
+
except Exception:
|
|
457
|
+
return
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
# A worktree's parser and held rules can differ even when this hook's own
|
|
461
|
+
# source is identical. Compose and validate with that checkout's imported code,
|
|
462
|
+
# not just its data root. The bootstrap uses APIs present in older checkouts too.
|
|
463
|
+
_RESULT_CONSUMER = '''
|
|
464
|
+
import json, sys
|
|
465
|
+
from pathlib import Path
|
|
466
|
+
root = Path(sys.argv[1])
|
|
467
|
+
sys.path.insert(0, str(root))
|
|
468
|
+
try:
|
|
469
|
+
from egregore_runtime import private_result
|
|
470
|
+
from egregore_runtime.runtime import local_runtime
|
|
471
|
+
loaded = Path(private_result.__file__).resolve()
|
|
472
|
+
if not loaded.is_relative_to(root.resolve()) or loaded.parent != (root / 'egregore_runtime').resolve():
|
|
473
|
+
raise ValueError('packet Runtime could not be loaded from its checkout')
|
|
474
|
+
for name, module in tuple(sys.modules.items()):
|
|
475
|
+
if name == 'egregore_runtime' or name.startswith('egregore_runtime.'):
|
|
476
|
+
filename = getattr(module, '__file__', None)
|
|
477
|
+
if not filename or not Path(filename).resolve().is_relative_to(root.resolve()):
|
|
478
|
+
raise ValueError('packet Runtime could not be loaded from its checkout')
|
|
479
|
+
request = json.load(sys.stdin)
|
|
480
|
+
runtime = local_runtime(root, push_remote=False)
|
|
481
|
+
actor = runtime.resolve_actor(session_id=request['session_id'], harness=request['harness'])
|
|
482
|
+
rendered = private_result.consume(root, request['id'], runtime, actor,
|
|
483
|
+
session_id=request['session_id'], native_turn_id=request['native_turn_id'])
|
|
484
|
+
result = {'rendered': rendered}
|
|
485
|
+
except Exception as error:
|
|
486
|
+
result = {'error': str(error)}
|
|
487
|
+
print(json.dumps(result))
|
|
488
|
+
'''
|
|
489
|
+
|
|
490
|
+
# Context-projection packets recheck the same collection and index revision as
|
|
491
|
+
# the search. Keep those data-location overrides, not the harness environment.
|
|
492
|
+
_RESULT_CONSUMER_ENV = (
|
|
493
|
+
'PATH', 'HOME', 'TMPDIR', 'LANG', 'LC_ALL', 'LC_CTYPE',
|
|
494
|
+
'EGREGORE_QMD_COLLECTION', 'EGREGORE_QMD_INDEX_PATH', 'EGREGORE_QMD_RUNTIME_DIR',
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def _validate_packet_runtime(root: Path) -> None:
|
|
499
|
+
"""Reject unchecked import paths anywhere in the packet's Runtime package."""
|
|
500
|
+
package = root / 'egregore_runtime'
|
|
501
|
+
|
|
502
|
+
def check(entry, regular_type):
|
|
503
|
+
# lstat rejects links, including directory links that os.walk won't follow.
|
|
504
|
+
if (not regular_type(entry.lstat().st_mode)
|
|
505
|
+
or not entry.resolve(strict=True).is_relative_to(checkout)):
|
|
506
|
+
raise ValueError('uncontained Runtime entry')
|
|
507
|
+
|
|
508
|
+
def walk_error(error):
|
|
509
|
+
raise error
|
|
510
|
+
|
|
511
|
+
try:
|
|
512
|
+
checkout = root.resolve(strict=True)
|
|
513
|
+
check(package, stat.S_ISDIR)
|
|
514
|
+
for directory, directories, files in os.walk(package, followlinks=False, onerror=walk_error):
|
|
515
|
+
for name in directories:
|
|
516
|
+
check(Path(directory) / name, stat.S_ISDIR)
|
|
517
|
+
for name in files:
|
|
518
|
+
check(Path(directory) / name, stat.S_ISREG)
|
|
519
|
+
# Preserve the required package entry points as well as tree containment.
|
|
520
|
+
for name in ('__init__.py', 'private_result.py'):
|
|
521
|
+
check(package / name, stat.S_ISREG)
|
|
522
|
+
except (OSError, RuntimeError, ValueError) as error:
|
|
523
|
+
raise EgregoreRuntimeError('packet Runtime is not contained in its checkout') from error
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _consume_private_result(root, identifier, *, session_id, harness, native_turn_id):
|
|
527
|
+
"""Use the admitted packet checkout's Runtime without mixing module versions."""
|
|
528
|
+
from .private_result import consume
|
|
529
|
+
package = root / 'egregore_runtime'
|
|
530
|
+
# Data-only Runtime roots use the installed package. A checkout carrying
|
|
531
|
+
# its own package must validate with that package, with no fallback on error.
|
|
532
|
+
checkout_package = package.exists() or package.is_symlink()
|
|
533
|
+
if checkout_package:
|
|
534
|
+
_validate_packet_runtime(root)
|
|
535
|
+
if checkout_package and package.resolve() != Path(__file__).resolve().parent:
|
|
536
|
+
environment = {name: os.environ[name] for name in _RESULT_CONSUMER_ENV if name in os.environ}
|
|
537
|
+
environment['EGREGORE_ROOT'] = str(root)
|
|
538
|
+
try:
|
|
539
|
+
worker = subprocess.run(
|
|
540
|
+
[sys.executable, '-I', '-B', '-c', _RESULT_CONSUMER, str(root)],
|
|
541
|
+
input=json.dumps({'id': identifier, 'session_id': session_id,
|
|
542
|
+
'harness': harness, 'native_turn_id': native_turn_id}),
|
|
543
|
+
cwd=root, env=environment,
|
|
544
|
+
capture_output=True, text=True, timeout=30)
|
|
545
|
+
except subprocess.TimeoutExpired as error:
|
|
546
|
+
raise EgregoreRuntimeError('packet Runtime validation timed out') from error
|
|
547
|
+
if worker.returncode:
|
|
548
|
+
raise EgregoreRuntimeError('packet Runtime validation failed')
|
|
549
|
+
try:
|
|
550
|
+
result = json.loads(worker.stdout)
|
|
551
|
+
if not isinstance(result, dict):
|
|
552
|
+
raise ValueError('expected a result object')
|
|
553
|
+
if 'error' in result:
|
|
554
|
+
raise EgregoreRuntimeError(str(result['error']))
|
|
555
|
+
if not isinstance(result.get('rendered'), str):
|
|
556
|
+
raise ValueError('expected rendered evidence')
|
|
557
|
+
return result['rendered']
|
|
558
|
+
except (ValueError, TypeError) as error:
|
|
559
|
+
# Never include worker stdout in a failure: it may contain evidence.
|
|
560
|
+
raise EgregoreRuntimeError('packet Runtime returned an invalid delivery') from error
|
|
561
|
+
runtime = local_runtime(root, push_remote=False)
|
|
562
|
+
actor = runtime.resolve_actor(session_id=session_id, harness=harness)
|
|
563
|
+
return consume(root, identifier, runtime, actor, session_id=session_id, native_turn_id=native_turn_id)
|
|
564
|
+
|
|
565
|
+
|
|
186
566
|
def _result_hook(args):
|
|
187
567
|
"""Native PostToolUse adapter; stdin and stdout use the host hook contract."""
|
|
188
|
-
from .private_result import allowed_root,
|
|
568
|
+
from .private_result import allowed_root, receipts_from_tool
|
|
569
|
+
delivered=[]
|
|
189
570
|
try:
|
|
190
571
|
payload=json.load(sys.stdin)
|
|
191
572
|
if payload.get('tool_name')!='Bash':
|
|
@@ -197,20 +578,25 @@ def _result_hook(args):
|
|
|
197
578
|
for receipt in receipts_from_tool(payload):
|
|
198
579
|
root=allowed_root(_root(),receipt['root'])
|
|
199
580
|
session=str(payload.get('session_id') or _session_id(root))
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
native_turn_id=payload.get('turn_id') or payload.get('prompt_id')))
|
|
581
|
+
results.append(_consume_private_result(root,receipt['id'],session_id=session,harness=args.harness,
|
|
582
|
+
native_turn_id=payload.get('turn_id') or payload.get('prompt_id')))
|
|
583
|
+
delivered.append((root,receipt['id']))
|
|
204
584
|
if not results:
|
|
205
585
|
return 0
|
|
206
586
|
context='EGREGORE_RETRIEVAL_CONTEXT_V1\nAuthorized Runtime evidence for this invocation. Do not reproduce ranking traces.\n'+'\n'.join(results)+'\nEND_EGREGORE_RETRIEVAL_CONTEXT_V1'
|
|
207
587
|
except (OSError,ValueError,TypeError,KeyError,EgregoreRuntimeError) as error:
|
|
208
588
|
context=f'Runtime private result could not be attached: {error}. Do not treat it as received evidence.'
|
|
589
|
+
delivered=[] # nothing in this tool call reached the agent
|
|
209
590
|
print(json.dumps({'hookSpecificOutput':{'hookEventName':'PostToolUse','additionalContext':context}}))
|
|
591
|
+
for root,receipt in delivered:
|
|
592
|
+
record_notice(root,'delivered',args.harness,payload.get('session_id'))
|
|
593
|
+
_mind_delivered(root,args.harness,payload,receipt)
|
|
210
594
|
return 0
|
|
211
595
|
|
|
212
596
|
|
|
213
597
|
def _session_id(root: Path) -> str:
|
|
598
|
+
if member := (member_cli.current() if member_cli is not None else None):
|
|
599
|
+
return member.session_id
|
|
214
600
|
if pinned := _episode_session.get():
|
|
215
601
|
return pinned
|
|
216
602
|
configured = os.environ.get("EGREGORE_SESSION_ID", "").strip()
|
|
@@ -293,11 +679,83 @@ def _actor_contract_marker_path(
|
|
|
293
679
|
session there and rewritten by each new one, so keying on it alone made
|
|
294
680
|
parallel sessions re-attach the full contract on every prompt."""
|
|
295
681
|
|
|
682
|
+
return _session_state_path(root, "actor-contract", harness, session, ".sent")
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def _session_state_path(root: Path, kind: str, harness: str, session: str | None, suffix: str) -> Path:
|
|
296
686
|
session = re.sub(r"[^A-Za-z0-9_.-]", "-", session or _session_id(root))[:120]
|
|
297
|
-
return
|
|
298
|
-
|
|
299
|
-
|
|
687
|
+
return root / ".egregore" / "runtime" / "observe" / f"{kind}-{harness}-{session}{suffix}"
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def _situated_hints_enabled(root: Path) -> bool:
|
|
691
|
+
"""Off switch: ``egregore.json`` → ``knowledge.situated_hints: false``."""
|
|
692
|
+
try:
|
|
693
|
+
config = json.loads((root / "egregore.json").read_text(encoding="utf-8"))
|
|
694
|
+
except (OSError, ValueError):
|
|
695
|
+
return True
|
|
696
|
+
knowledge = config.get("knowledge") if isinstance(config, dict) else None
|
|
697
|
+
return not (isinstance(knowledge, dict) and knowledge.get("situated_hints") is False)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def _working_paths(root: Path) -> list[str]:
|
|
701
|
+
"""Paths changed in this workspace: tracked edits plus untracked files."""
|
|
702
|
+
paths: list[str] = []
|
|
703
|
+
# NUL-delimited so Git does not quote non-ASCII names ("caf\303\251.md").
|
|
704
|
+
for args in (("diff", "--name-only", "-z", "HEAD"), ("ls-files", "-z", "--others", "--exclude-standard")):
|
|
705
|
+
try:
|
|
706
|
+
result = subprocess.run(["git", "-C", str(root), *args], capture_output=True, text=True, timeout=2)
|
|
707
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
708
|
+
continue
|
|
709
|
+
if result.returncode == 0:
|
|
710
|
+
paths.extend(name for name in result.stdout.split("\0") if name)
|
|
711
|
+
return list(dict.fromkeys(paths))[:500]
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def _sweep_situated_state(directory: Path, max_age_days: int = 7) -> None:
|
|
715
|
+
"""Drop per-session offer records older than a week; a new session starts one."""
|
|
716
|
+
cutoff = time.time() - max_age_days * 86400
|
|
717
|
+
try:
|
|
718
|
+
for stale in directory.glob("situated-*.json"):
|
|
719
|
+
try:
|
|
720
|
+
if stale.stat().st_mtime < cutoff:
|
|
721
|
+
stale.unlink()
|
|
722
|
+
except OSError:
|
|
723
|
+
continue
|
|
724
|
+
except OSError:
|
|
725
|
+
return
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _situated_hint(runtime, root: Path, harness: str, session: str | None, prompt: str) -> str:
|
|
729
|
+
"""At most one offer line per prompt, each pattern at most once per session.
|
|
730
|
+
|
|
731
|
+
An offer, never a gate: the working changes and the prompt are matched
|
|
732
|
+
against pattern situations; nothing is retrieved for the model.
|
|
733
|
+
"""
|
|
734
|
+
memory = root / "memory"
|
|
735
|
+
if not (memory / "knowledge" / "patterns").is_dir() or not _situated_hints_enabled(root):
|
|
736
|
+
return ""
|
|
737
|
+
from .knowledge import CanonicalKnowledgeService, situated_hint_line
|
|
738
|
+
state = _session_state_path(root, "situated", harness, session, ".json")
|
|
739
|
+
try:
|
|
740
|
+
offered = set(json.loads(state.read_text(encoding="utf-8")))
|
|
741
|
+
except (OSError, ValueError, TypeError):
|
|
742
|
+
offered = set()
|
|
743
|
+
_sweep_situated_state(state.parent)
|
|
744
|
+
found = CanonicalKnowledgeService(runtime, memory).situated(
|
|
745
|
+
_actor(runtime, root, harness), paths=_working_paths(root), text=prompt,
|
|
746
|
+
limit=len(offered) + 3, count_instances=False,
|
|
300
747
|
)
|
|
748
|
+
for hit in found["patterns"]:
|
|
749
|
+
line = situated_hint_line(hit["title"], hit["canonical_path"])
|
|
750
|
+
if hit["id"] in offered or not line:
|
|
751
|
+
continue
|
|
752
|
+
try:
|
|
753
|
+
state.parent.mkdir(parents=True, exist_ok=True)
|
|
754
|
+
state.write_text(json.dumps(sorted(offered | {hit["id"]})), encoding="utf-8")
|
|
755
|
+
except OSError:
|
|
756
|
+
return "" # without the record the same offer would repeat every prompt
|
|
757
|
+
return line
|
|
758
|
+
return ""
|
|
301
759
|
|
|
302
760
|
|
|
303
761
|
def _receipt_requests(receipt) -> dict:
|
|
@@ -511,11 +969,12 @@ def _query(args: argparse.Namespace) -> int:
|
|
|
511
969
|
|
|
512
970
|
|
|
513
971
|
def _query_in_episode(args: argparse.Namespace, root: Path) -> int:
|
|
514
|
-
runtime =
|
|
972
|
+
runtime = _runtime(root, push_remote=False)
|
|
515
973
|
actor = _actor(runtime, root, args.harness)
|
|
516
974
|
request = _request(
|
|
517
975
|
task=args.task, org_id=actor.profile.org_id, actor_id=actor.actor.actor_id,
|
|
518
|
-
lex=identity_aware_lexical_formulations(root, actor, args.task, tuple(args.lex)
|
|
976
|
+
lex=identity_aware_lexical_formulations(root, actor, args.task, tuple(args.lex),
|
|
977
|
+
decision=runtime.authorize(actor, Permission.READ)),
|
|
519
978
|
vec=tuple(args.vec), mode=args.mode, limit=args.limit,
|
|
520
979
|
temporal_scope=getattr(args, 'temporal_scope', None),
|
|
521
980
|
recent_days=getattr(args, 'recent_days', None),
|
|
@@ -535,6 +994,7 @@ def _query_in_episode(args: argparse.Namespace, root: Path) -> int:
|
|
|
535
994
|
private=True,harness=args.harness)
|
|
536
995
|
return 0 if attached else 2 # Claude's attachment carries its operation error.
|
|
537
996
|
print(response['error'], file=sys.stderr)
|
|
997
|
+
_mind_notice(root, response, '', args.harness)
|
|
538
998
|
return 2
|
|
539
999
|
context = _restore_context(response['result']['context'])
|
|
540
1000
|
entry = _observe_entry(context)
|
|
@@ -585,35 +1045,41 @@ def _actor_context_block(
|
|
|
585
1045
|
if value and value.strip() and value.strip().casefold() != actor.display_name.casefold()
|
|
586
1046
|
)
|
|
587
1047
|
) or "none"
|
|
1048
|
+
# Prompt context is a read surface: only profiles this actor may read name anyone.
|
|
1049
|
+
readable = runtime.authorize(actor_context, Permission.READ)
|
|
588
1050
|
member_names = "; ".join(
|
|
589
1051
|
f"{', '.join(member.aliases)} → {member.display_name}"
|
|
590
1052
|
if member.aliases
|
|
591
1053
|
else member.display_name
|
|
592
|
-
for member in organization_member_presentations(root, actor_context)
|
|
1054
|
+
for member in organization_member_presentations(root, actor_context, decision=readable)
|
|
593
1055
|
) or "unavailable"
|
|
594
1056
|
search_command = 'bash bin/search.sh find "<query>" --kind <kind>'
|
|
595
1057
|
if harness == "claude":
|
|
596
1058
|
search_command += " --context-packet"
|
|
597
1059
|
if not full:
|
|
1060
|
+
# Rides on every later prompt, so every line here costs once per turn.
|
|
598
1061
|
return "\n".join(
|
|
599
1062
|
(
|
|
600
1063
|
"EGREGORE_ACTOR_CONTEXT_V1",
|
|
601
|
-
"Reminder
|
|
602
|
-
|
|
1064
|
+
"Reminder: the full contract was attached earlier in this session; if it is no longer "
|
|
1065
|
+
"in your context, read `.claude/context/retrieval-investigation.md` once.",
|
|
603
1066
|
f"Actor: {actor.display_name} · Actor ID: {actor.actor_id} · "
|
|
604
1067
|
f"Organization ID: {actor_context.profile.org_id} · Runtime root: {root}",
|
|
1068
|
+
"`me` and `myself` mean this actor; never infer identity from OS, Git, or GitHub names.",
|
|
605
1069
|
_RECALL_ONLY_SCOPE,
|
|
1070
|
+
RECALL_WORKFLOW_GUIDANCE,
|
|
606
1071
|
"When a prompt needs that recall and no EGREGORE_ORG_CONTEXT_V1 block is "
|
|
607
1072
|
f"attached, choose the operation kind and use `{search_command}`; exact latest/oldest/open "
|
|
608
1073
|
"handoff questions use `bash bin/search.sh handoffs …` instead.",
|
|
609
|
-
'Reuse evidence; batch source reads with search.sh open path-a path-b. '
|
|
610
|
-
'Runtime retains evidence and enforces the prompt budget and date boundary.',
|
|
611
|
-
"`me` and `myself` mean this Actor ID and display name; never infer identity "
|
|
612
|
-
"from the OS user, Git, or GitHub names.",
|
|
613
1074
|
"END_EGREGORE_ACTOR_CONTEXT_V1",
|
|
614
1075
|
)
|
|
615
1076
|
)
|
|
616
|
-
|
|
1077
|
+
packet_note = (
|
|
1078
|
+
"On Claude, `--context-packet` keeps the internal evidence trace out of the user "
|
|
1079
|
+
"transcript and attaches it through PostToolUse. Keep that flag on model-led "
|
|
1080
|
+
"searches; never repeat the attached ranking trace in the answer."
|
|
1081
|
+
) if harness == "claude" else None
|
|
1082
|
+
return "\n".join(line for line in
|
|
617
1083
|
(
|
|
618
1084
|
"EGREGORE_ACTOR_CONTEXT_V1",
|
|
619
1085
|
"Resolved automatically by EgregoreRuntime before the ritual.",
|
|
@@ -638,25 +1104,18 @@ def _actor_context_block(
|
|
|
638
1104
|
"work, a prior handoff, or continuation of earlier work and no "
|
|
639
1105
|
"EGREGORE_ORG_CONTEXT_V1 block is attached, choose the operation kind and use "
|
|
640
1106
|
f"`{search_command}`. ",
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
"
|
|
644
|
-
"
|
|
645
|
-
"
|
|
646
|
-
"
|
|
647
|
-
"
|
|
648
|
-
"run the typed deterministic lookup `bash bin/search.sh handoffs "
|
|
649
|
-
"[--mine|--sent|--addressed-to X|--sent-by X] [--status open] "
|
|
650
|
-
"[--order newest|oldest] [--limit N] [--open]` instead of a semantic query — one "
|
|
651
|
-
"memory-form retrieval beat, then answer from its result. Topical or synthesis "
|
|
652
|
-
"questions about handoffs (`the handoff about privacy`) remain semantic "
|
|
653
|
-
"`search.sh find` recall.",
|
|
1107
|
+
investigation_guidance(harness),
|
|
1108
|
+
packet_note,
|
|
1109
|
+
"Recognize exact lifecycle questions from meaning: the latest/newest/oldest handoff "
|
|
1110
|
+
"addressed to or sent by someone (including `me`), or open/unresolved handoffs, use "
|
|
1111
|
+
"`bash bin/search.sh handoffs` with [--mine|--sent|--addressed-to X|--sent-by X] "
|
|
1112
|
+
"[--status open] [--order newest|oldest] [--limit N] [--open], never a semantic "
|
|
1113
|
+
"query. Topical handoff questions (`the handoff about privacy`) stay `search.sh find` recall.",
|
|
654
1114
|
"Do not substitute Activity, Dashboard, Project, Git inspection, optional hosted indexes, or raw "
|
|
655
1115
|
"memory scans for that recall. A named request to display a status card is an "
|
|
656
1116
|
"explicit status workflow; a question asking for an explained synthesis of "
|
|
657
1117
|
"organizational work is Runtime/QMD recall. Do not query optional hosted indexes unless the user "
|
|
658
|
-
"explicitly requests relationship "
|
|
659
|
-
"or optional hosted index status.",
|
|
1118
|
+
"explicitly requests relationship or optional hosted index status.",
|
|
660
1119
|
"For every identity-bearing workflow, `me`, `myself`, `from me`, "
|
|
661
1120
|
"and `to myself` mean this Actor ID and display name. Use the "
|
|
662
1121
|
"display name for presentation and the Actor ID for ownership, "
|
|
@@ -664,7 +1123,7 @@ def _actor_context_block(
|
|
|
664
1123
|
"respondent fields. Never infer identity from the OS user, Git, "
|
|
665
1124
|
"GitHub, a branch name, or prose aliases.",
|
|
666
1125
|
"END_EGREGORE_ACTOR_CONTEXT_V1",
|
|
667
|
-
)
|
|
1126
|
+
) if line is not None
|
|
668
1127
|
)
|
|
669
1128
|
|
|
670
1129
|
|
|
@@ -691,13 +1150,16 @@ def identity_aware_lexical_formulations(
|
|
|
691
1150
|
actor: object,
|
|
692
1151
|
task: str,
|
|
693
1152
|
formulations: tuple[str, ...],
|
|
1153
|
+
*,
|
|
1154
|
+
decision: PolicyDecision,
|
|
694
1155
|
) -> tuple[str, ...]:
|
|
695
1156
|
"""Bind the active actor and expand explicitly named organization members.
|
|
696
1157
|
|
|
697
1158
|
This keeps cold BM25 retrieval equivalent to stable identity resolution:
|
|
698
1159
|
a chosen display name, provider handle, and legacy local alias all refer to
|
|
699
1160
|
one member. First-person references use the active actor; the directory is
|
|
700
|
-
never added wholesale
|
|
1161
|
+
never added wholesale, and only profiles ``decision`` lets the reader see
|
|
1162
|
+
add terms. Semantic identity binding happens at request construction.
|
|
701
1163
|
"""
|
|
702
1164
|
|
|
703
1165
|
def normalized(value: str) -> str:
|
|
@@ -710,7 +1172,7 @@ def identity_aware_lexical_formulations(
|
|
|
710
1172
|
task_tokens = f" {normalized(task)} "
|
|
711
1173
|
expanded = list(actor_lexical_formulations(actor, task, formulations))
|
|
712
1174
|
seen = {value.strip().casefold() for value in expanded if value.strip()}
|
|
713
|
-
for member in organization_member_presentations(root, actor):
|
|
1175
|
+
for member in organization_member_presentations(root, actor, decision=decision):
|
|
714
1176
|
references = (member.display_name, *member.aliases)
|
|
715
1177
|
if not any(
|
|
716
1178
|
reference_normalized
|
|
@@ -727,7 +1189,95 @@ def identity_aware_lexical_formulations(
|
|
|
727
1189
|
return tuple(expanded)
|
|
728
1190
|
|
|
729
1191
|
|
|
1192
|
+
def _member_prompt_hook(args):
|
|
1193
|
+
"""Trusted Durable hook: member identity only, no operator startup surfaces."""
|
|
1194
|
+
member = (member_cli.current() if member_cli is not None else None)
|
|
1195
|
+
raw = sys.stdin.read(262145)
|
|
1196
|
+
if len(raw) > 262144:
|
|
1197
|
+
raise MemberAuthorityError()
|
|
1198
|
+
try:
|
|
1199
|
+
payload = json.loads(raw)
|
|
1200
|
+
except (ValueError, TypeError):
|
|
1201
|
+
raise MemberAuthorityError() from None
|
|
1202
|
+
if (not isinstance(payload, dict) or payload.get("session_id") != member.native_session
|
|
1203
|
+
or not isinstance(payload.get("prompt", payload.get("user_prompt", "")), str)):
|
|
1204
|
+
raise MemberAuthorityError()
|
|
1205
|
+
prompt = payload.get("prompt", payload.get("user_prompt", "")).strip()
|
|
1206
|
+
runtime = member.runtime(member.root)
|
|
1207
|
+
actor = runtime.resolve_actor(session_id=member.session_id, harness="durable")
|
|
1208
|
+
if not runtime.authorize(actor, Permission.READ).allowed:
|
|
1209
|
+
raise MemberAuthorityError()
|
|
1210
|
+
binding = episode_binding.begin(
|
|
1211
|
+
member.root, actor, harness="durable", session_id=member.session_id,
|
|
1212
|
+
native_turn_id=payload.get("turn_id") or payload.get("prompt_id"), question=prompt,
|
|
1213
|
+
)
|
|
1214
|
+
context = "\n".join((
|
|
1215
|
+
episode_binding.context_line(binding),
|
|
1216
|
+
"EGREGORE_ACTOR_CONTEXT_V1",
|
|
1217
|
+
f"Actor ID: {actor.actor.actor_id}",
|
|
1218
|
+
f"Actor display name: {actor.actor.display_name}",
|
|
1219
|
+
f"Organization ID: {actor.profile.org_id}",
|
|
1220
|
+
"`me` and `myself` mean this verified member, never the host operator.",
|
|
1221
|
+
"Organizational retrieval status: NOT_PERFORMED.",
|
|
1222
|
+
_RECALL_ONLY_SCOPE,
|
|
1223
|
+
'Use the supplied memory tools: `bash bin/search.sh find "<query>" --kind <kind>`, '
|
|
1224
|
+
'`bash bin/search.sh open memory/path.md`, or `bash bin/search.sh handoffs --mine`.',
|
|
1225
|
+
"The host supplies member authority to those tools. Status and identity context do not grant access.",
|
|
1226
|
+
INVESTIGATION_GUIDANCE,
|
|
1227
|
+
"END_EGREGORE_ACTOR_CONTEXT_V1",
|
|
1228
|
+
))
|
|
1229
|
+
# Verification is current at delivery, not only before writing the binding.
|
|
1230
|
+
if not runtime.authorize(actor, Permission.READ).allowed:
|
|
1231
|
+
raise MemberAuthorityError()
|
|
1232
|
+
print(json.dumps({"hookSpecificOutput": {
|
|
1233
|
+
"hookEventName": "UserPromptSubmit", "additionalContext": context,
|
|
1234
|
+
}}, separators=(",", ":")))
|
|
1235
|
+
return 0
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def _member_ready(args):
|
|
1239
|
+
member = (member_cli.current() if member_cli is not None else None)
|
|
1240
|
+
if member is None:
|
|
1241
|
+
raise MemberAuthorityError()
|
|
1242
|
+
runtime = member.runtime(member.root)
|
|
1243
|
+
actor = runtime.resolve_actor(session_id=member.session_id, harness="durable")
|
|
1244
|
+
if not runtime.retrieval_health().available:
|
|
1245
|
+
raise MemberAuthorityError()
|
|
1246
|
+
if not all(runtime.authorize(actor, operation).allowed
|
|
1247
|
+
for operation in (Permission.DISCOVER, Permission.READ)):
|
|
1248
|
+
raise MemberAuthorityError()
|
|
1249
|
+
print(json.dumps({
|
|
1250
|
+
"schema": "egregore.member-ready/v1",
|
|
1251
|
+
"accountId": actor.account.account_id,
|
|
1252
|
+
"orgId": actor.profile.org_id,
|
|
1253
|
+
"actorId": actor.actor.actor_id,
|
|
1254
|
+
"membershipId": actor.membership.membership_id,
|
|
1255
|
+
"verifiedAt": int(time.time() * 1000),
|
|
1256
|
+
}, separators=(",", ":")))
|
|
1257
|
+
return 0
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def _bind_native_session(root: Path, harness: str, session) -> None:
|
|
1261
|
+
"""Codex starts before its thread exists, so its first prompt maps it.
|
|
1262
|
+
|
|
1263
|
+
bin/session-id.sh binds the thread to the Egregore session id it resolves
|
|
1264
|
+
now and keeps an existing map, so commits made in this thread keep their
|
|
1265
|
+
own Egregore-Session trailer after a later session start elsewhere.
|
|
1266
|
+
"""
|
|
1267
|
+
script = Path(root) / 'bin' / 'session-id.sh'
|
|
1268
|
+
if not isinstance(session, str) or not session or not script.is_file():
|
|
1269
|
+
return
|
|
1270
|
+
try:
|
|
1271
|
+
subprocess.run(['bash', str(script), 'bind', harness, session, '--root', str(root)],
|
|
1272
|
+
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
|
1273
|
+
stderr=subprocess.DEVNULL, timeout=2, check=False)
|
|
1274
|
+
except (OSError, subprocess.SubprocessError):
|
|
1275
|
+
pass # a missing map falls back to the shared file; never cost the prompt
|
|
1276
|
+
|
|
1277
|
+
|
|
730
1278
|
def _prompt_hook(args: argparse.Namespace) -> int:
|
|
1279
|
+
if (member_cli.current() if member_cli is not None else None):
|
|
1280
|
+
return _member_prompt_hook(args)
|
|
731
1281
|
try:
|
|
732
1282
|
payload = json.load(sys.stdin)
|
|
733
1283
|
except (json.JSONDecodeError, OSError):
|
|
@@ -735,7 +1285,12 @@ def _prompt_hook(args: argparse.Namespace) -> int:
|
|
|
735
1285
|
if not isinstance(payload, dict):
|
|
736
1286
|
return 0
|
|
737
1287
|
prompt = str(payload.get("prompt") or payload.get("user_prompt") or "").strip()
|
|
738
|
-
|
|
1288
|
+
launch = _root()
|
|
1289
|
+
root = _prompt_root(payload, launch)
|
|
1290
|
+
if args.harness == 'codex':
|
|
1291
|
+
_bind_native_session(launch, 'codex', payload.get('session_id'))
|
|
1292
|
+
record_notice(launch, 'asked', args.harness, payload.get('session_id'))
|
|
1293
|
+
_mind_asked(launch, args.harness, payload) # the root the hook runs in (§3.3), never one the prompt names
|
|
739
1294
|
try:
|
|
740
1295
|
native_session = str(payload.get('session_id') or '').strip()
|
|
741
1296
|
if native_session:
|
|
@@ -783,11 +1338,17 @@ def _prompt_hook_in_episode(args, payload, prompt, root):
|
|
|
783
1338
|
marker.write_text(datetime.now(UTC).isoformat(), encoding="utf-8")
|
|
784
1339
|
except OSError:
|
|
785
1340
|
pass
|
|
1341
|
+
try:
|
|
1342
|
+
hint = _situated_hint(runtime, root, args.harness, hook_session, prompt)
|
|
1343
|
+
except Exception: # an offer only; a failure never costs the prompt its context
|
|
1344
|
+
hint = ""
|
|
1345
|
+
if hint:
|
|
1346
|
+
additional_context += "\n" + hint
|
|
786
1347
|
except Exception as exc: # Hooks are advisory and must never block a prompt.
|
|
787
1348
|
additional_context = (
|
|
788
1349
|
f"Actor context is unavailable ({type(exc).__name__}). "
|
|
789
1350
|
"Do not guess actor identity. Organizational retrieval has not run.\n"
|
|
790
|
-
+
|
|
1351
|
+
+ investigation_guidance(args.harness)
|
|
791
1352
|
)
|
|
792
1353
|
if _prompt_binding.get():
|
|
793
1354
|
additional_context = episode_binding.context_line(_prompt_binding.get()) + '\n' + additional_context
|
|
@@ -812,61 +1373,13 @@ def _admin_status(args: argparse.Namespace) -> int:
|
|
|
812
1373
|
actor = _actor(runtime, root, args.harness)
|
|
813
1374
|
result["display_name"] = actor.actor.display_name
|
|
814
1375
|
result["actor_id"] = actor.actor.actor_id
|
|
815
|
-
|
|
816
|
-
result["admin"] = bool(gate is not None and gate.actor_is_admin(actor))
|
|
1376
|
+
result["admin"] = is_admin(runtime.authorize(actor, Permission.READ))
|
|
817
1377
|
except Exception:
|
|
818
1378
|
result = {"admin": False, "display_name": None, "actor_id": None}
|
|
819
1379
|
print(json.dumps(result, sort_keys=True))
|
|
820
1380
|
return 0
|
|
821
1381
|
|
|
822
1382
|
|
|
823
|
-
def _access_status(args: argparse.Namespace) -> int:
|
|
824
|
-
"""One authorized snapshot of identity, membership, and capabilities.
|
|
825
|
-
|
|
826
|
-
The Runtime authorization backend is the source of truth: every value is
|
|
827
|
-
a real authorize() decision, never derived independently by a UI. Any
|
|
828
|
-
failure reports status "attention" and nothing else — no partial claims.
|
|
829
|
-
"""
|
|
830
|
-
|
|
831
|
-
root = _root()
|
|
832
|
-
try:
|
|
833
|
-
runtime = local_runtime(root, push_remote=False)
|
|
834
|
-
actor = _actor(runtime, root, args.harness)
|
|
835
|
-
gate = runtime.admin_gate
|
|
836
|
-
|
|
837
|
-
def allowed(*permissions: Permission) -> bool:
|
|
838
|
-
return all(
|
|
839
|
-
runtime.authorize(actor, permission).allowed
|
|
840
|
-
for permission in permissions
|
|
841
|
-
)
|
|
842
|
-
|
|
843
|
-
read_decision = runtime.authorize(actor, Permission.READ)
|
|
844
|
-
membership = actor.membership
|
|
845
|
-
out = {
|
|
846
|
-
"status": "ok",
|
|
847
|
-
"display_name": actor.actor.display_name,
|
|
848
|
-
"actor_id": actor.actor.actor_id,
|
|
849
|
-
"org_name": actor.profile.name,
|
|
850
|
-
"org_id": actor.profile.org_id,
|
|
851
|
-
"membership_status": membership.status if membership else "unknown",
|
|
852
|
-
"roles": sorted(membership.roles) if membership else [],
|
|
853
|
-
"admin": bool(gate is not None and gate.actor_is_admin(actor)),
|
|
854
|
-
"capabilities": {
|
|
855
|
-
"read": allowed(Permission.READ),
|
|
856
|
-
"create": allowed(Permission.WRITE),
|
|
857
|
-
"update": allowed(Permission.WRITE),
|
|
858
|
-
"publish": allowed(Permission.SHARE),
|
|
859
|
-
"notify": allowed(Permission.SHARE, Permission.EXECUTE),
|
|
860
|
-
"org_settings": allowed(Permission.ADMINISTER),
|
|
861
|
-
},
|
|
862
|
-
"shared_memory_scope": sorted(read_decision.scopes),
|
|
863
|
-
}
|
|
864
|
-
except Exception:
|
|
865
|
-
out = {"status": "attention"}
|
|
866
|
-
print(json.dumps(out, sort_keys=True))
|
|
867
|
-
return 0
|
|
868
|
-
|
|
869
|
-
|
|
870
1383
|
def _handoffs(args: argparse.Namespace) -> int:
|
|
871
1384
|
"""Typed lifecycle lookup — exact handoff questions, zero semantic search.
|
|
872
1385
|
|
|
@@ -878,7 +1391,7 @@ def _handoffs(args: argparse.Namespace) -> int:
|
|
|
878
1391
|
from .handoff_lookup import HandoffLookupService
|
|
879
1392
|
|
|
880
1393
|
root = _root()
|
|
881
|
-
runtime =
|
|
1394
|
+
runtime = _runtime(root, push_remote=False)
|
|
882
1395
|
actor = _actor(runtime, root, args.harness)
|
|
883
1396
|
service = HandoffLookupService(runtime, root)
|
|
884
1397
|
started = time.monotonic()
|
|
@@ -893,57 +1406,62 @@ def _handoffs(args: argparse.Namespace) -> int:
|
|
|
893
1406
|
lookup_ms = round((time.monotonic() - started) * 1000)
|
|
894
1407
|
if args.timing:
|
|
895
1408
|
print(f"lookup: {lookup_ms}ms", file=sys.stderr)
|
|
1409
|
+
from . import held
|
|
1410
|
+
from .policy import allows
|
|
1411
|
+
decision, memory = runtime.authorize(actor, Permission.READ), service.memory_root
|
|
1412
|
+
records = [record for record in records # the lookup's rows, decided again where they are printed
|
|
1413
|
+
if allows(decision, "memory/" + record.canonical_path.removeprefix("memory/"), root=memory)]
|
|
896
1414
|
if args.json:
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
"records": [asdict(record) for record in records],
|
|
901
|
-
"lookup_ms": lookup_ms,
|
|
902
|
-
},
|
|
903
|
-
sort_keys=True,
|
|
904
|
-
)
|
|
905
|
-
)
|
|
906
|
-
return 0
|
|
907
|
-
if not records:
|
|
908
|
-
print("no matching handoff exists for that filter")
|
|
909
|
-
return 0
|
|
1415
|
+
return held.emit(decision, {"records": [asdict(record) for record in records], "lookup_ms": lookup_ms},
|
|
1416
|
+
root=memory, render=lambda shown: json.dumps(shown, sort_keys=True))
|
|
1417
|
+
lines = []
|
|
910
1418
|
for index, record in enumerate(records, start=1):
|
|
911
1419
|
day = record.occurred_at.split("T")[0]
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
if record.ask:
|
|
916
|
-
print(f" ask: {record.ask}")
|
|
917
|
-
print(f" source: {record.canonical_path}")
|
|
1420
|
+
lines.append(f"{index}. {day} · from {record.sender} · to {record.recipient} · {record.status} · {record.topic}")
|
|
1421
|
+
lines += [f" claim: {record.claim}"] * bool(record.claim) + [f" ask: {record.ask}"] * bool(record.ask)
|
|
1422
|
+
lines.append(f" source: {record.canonical_path}")
|
|
918
1423
|
if args.open and records:
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
print(content)
|
|
922
|
-
return 0
|
|
1424
|
+
lines += ["", runtime.open_source(actor, records[0].canonical_path)]
|
|
1425
|
+
return held.emit(decision, "\n".join(lines) or "no matching handoff exists for that filter", root=memory, render=str)
|
|
923
1426
|
|
|
924
1427
|
|
|
925
1428
|
def _status(args: argparse.Namespace) -> int:
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1429
|
+
from dataclasses import replace
|
|
1430
|
+
from . import held
|
|
1431
|
+
root = _root()
|
|
1432
|
+
runtime = local_runtime(root, push_remote=False)
|
|
1433
|
+
health = runtime.retrieval_health()
|
|
1434
|
+
try:
|
|
1435
|
+
actor = _actor(runtime, root, "shell")
|
|
1436
|
+
except Exception: # noqa: BLE001 - no identity: health still prints, its warnings (which can name memory) counted
|
|
1437
|
+
actor = None
|
|
1438
|
+
health = replace(health, warnings=(f"{len(health.warnings)} warnings withheld: identity is unavailable",)
|
|
1439
|
+
if health.warnings else ())
|
|
1440
|
+
value = health.to_dict() if args.json else "\n".join((
|
|
1441
|
+
f"adapter: {health.adapter} {health.adapter_version}",
|
|
1442
|
+
f"index spec: {health.index_spec_version}",
|
|
1443
|
+
f"index revision: {health.index_revision}",
|
|
1444
|
+
f"source revision: {health.source_revision}",
|
|
1445
|
+
f"embedding: {health.embedding_state}",
|
|
1446
|
+
f"readiness: {health.runtime_state}",
|
|
1447
|
+
f"bm25 ready: {'yes' if health.bm25_ready else 'no'}",
|
|
1448
|
+
f"semantic ready: {'yes' if health.semantic_ready else 'no'}",
|
|
1449
|
+
f"semantic build: {health.semantic_source_revision or 'none'}",
|
|
1450
|
+
f"worker pid: {health.runtime_pid or 'none'}",
|
|
1451
|
+
f"endpoint: {health.runtime_endpoint or 'none'}",
|
|
1452
|
+
f"index: {health.index_path or 'unknown'}",
|
|
1453
|
+
f"collection: {health.collection or 'unknown'}",
|
|
1454
|
+
f"available: {'yes' if health.available else 'no'}",
|
|
1455
|
+
*(f"warning: {warning}" for warning in health.warnings)))
|
|
1456
|
+
render = (lambda shown: json.dumps(shown, indent=2)) if args.json else str # JSON packaged field by field
|
|
1457
|
+
shown = held.emit_for(runtime, actor, value, render=render) if actor else _content_free(render(value))
|
|
1458
|
+
return shown or (0 if health.available else 1)
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
def _content_free(text: str) -> int:
|
|
1462
|
+
"""Health with every warning replaced by a count: no memory text to package."""
|
|
1463
|
+
print(text)
|
|
1464
|
+
return 0
|
|
947
1465
|
|
|
948
1466
|
|
|
949
1467
|
_RUNTIME_CANDIDATE_VERSION = re.compile(
|
|
@@ -1052,7 +1570,7 @@ def _upgrade(args: argparse.Namespace) -> int:
|
|
|
1052
1570
|
if action == "status":
|
|
1053
1571
|
state = upgrade_module.describe(store.load())
|
|
1054
1572
|
state["active_version"] = _active_runtime_version(root, store)
|
|
1055
|
-
state["instance_hash"] =
|
|
1573
|
+
state["instance_hash"] = installation_key(root)
|
|
1056
1574
|
# A live apply journal means a previous candidate application died
|
|
1057
1575
|
# mid-transaction; the next activate recovers deterministically.
|
|
1058
1576
|
state["interrupted_apply"] = store.load_apply_journal() is not None
|
|
@@ -1101,7 +1619,7 @@ def _upgrade(args: argparse.Namespace) -> int:
|
|
|
1101
1619
|
state = upgrade_module.new_state(
|
|
1102
1620
|
instance_path=root,
|
|
1103
1621
|
org_id=org_id,
|
|
1104
|
-
instance_hash=
|
|
1622
|
+
instance_hash=installation_key(root),
|
|
1105
1623
|
candidate_version=version,
|
|
1106
1624
|
active_version=_active_runtime_version(root, store),
|
|
1107
1625
|
candidate_source=str(tarball),
|
|
@@ -1279,6 +1797,7 @@ def _open(args: argparse.Namespace) -> int:
|
|
|
1279
1797
|
private=True,harness=args.harness)
|
|
1280
1798
|
return 0 if attached else 2 # Claude's attachment carries its operation error.
|
|
1281
1799
|
print(response['error'],file=sys.stderr)
|
|
1800
|
+
_mind_notice(root, response, '', args.harness)
|
|
1282
1801
|
return 2
|
|
1283
1802
|
sources = response['result'].get('sources', [response['result']])
|
|
1284
1803
|
parts = []
|
|
@@ -1384,7 +1903,12 @@ def _session_env(args):
|
|
|
1384
1903
|
|
|
1385
1904
|
def _parser() -> argparse.ArgumentParser:
|
|
1386
1905
|
parser = argparse.ArgumentParser(prog="egregore-runtime")
|
|
1906
|
+
if member_cli is not None:
|
|
1907
|
+
member_cli.add_arguments(parser)
|
|
1387
1908
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
1909
|
+
if member_cli is not None:
|
|
1910
|
+
member_ready = commands.add_parser("member-ready", help=argparse.SUPPRESS)
|
|
1911
|
+
member_ready.set_defaults(handler=_member_ready)
|
|
1388
1912
|
|
|
1389
1913
|
session_env = commands.add_parser('session-env', help=argparse.SUPPRESS)
|
|
1390
1914
|
session_env.add_argument('--harness', choices=['claude'], required=True)
|
|
@@ -1469,12 +1993,6 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
1469
1993
|
)
|
|
1470
1994
|
handoffs.set_defaults(handler=_handoffs)
|
|
1471
1995
|
|
|
1472
|
-
access_status = commands.add_parser("access-status")
|
|
1473
|
-
access_status.add_argument(
|
|
1474
|
-
"--harness", default=os.environ.get("EGREGORE_RUNTIME", "shell")
|
|
1475
|
-
)
|
|
1476
|
-
access_status.set_defaults(handler=_access_status)
|
|
1477
|
-
|
|
1478
1996
|
readiness = commands.add_parser("readiness")
|
|
1479
1997
|
readiness.add_argument("--json", action="store_true")
|
|
1480
1998
|
readiness.add_argument("--plain", action="store_true")
|
|
@@ -1518,14 +2036,21 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
1518
2036
|
source.add_argument('--request-id',help='stable id for retrying this exact invocation')
|
|
1519
2037
|
source.add_argument('--private-result',action='store_true',help=argparse.SUPPRESS)
|
|
1520
2038
|
source.set_defaults(handler=_open)
|
|
2039
|
+
for name in _COMMAND_MODULES:
|
|
2040
|
+
module = optional_module(name)
|
|
2041
|
+
if module is not None:
|
|
2042
|
+
module.register(commands)
|
|
1521
2043
|
return parser
|
|
1522
2044
|
|
|
1523
2045
|
|
|
1524
|
-
def main() -> int:
|
|
1525
|
-
args = _parser().parse_args()
|
|
2046
|
+
def main(argv=None) -> int:
|
|
2047
|
+
args = _parser().parse_args(argv)
|
|
1526
2048
|
if getattr(args, "limit", None) is not None and args.limit < 1:
|
|
1527
2049
|
raise ValueError("retrieval limit must be positive")
|
|
1528
|
-
|
|
2050
|
+
with (member_cli.invocation(args, _root(), allowed_commands={
|
|
2051
|
+
"prompt-hook", "find", "open", "handoffs", "member-ready",
|
|
2052
|
+
}) if member_cli is not None else nullcontext()):
|
|
2053
|
+
return args.handler(args)
|
|
1529
2054
|
|
|
1530
2055
|
|
|
1531
2056
|
if __name__ == "__main__":
|