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
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""The address every hosted request goes to.
|
|
2
|
+
|
|
3
|
+
The hosted API moved to api.egregore.xyz after the October 2026 security
|
|
4
|
+
reset. Its old Railway origin was retired and is no longer ours to trust: a
|
|
5
|
+
credential sent there could reach whoever holds that address next. Every
|
|
6
|
+
request that carries a token or key resolves its address here, so a
|
|
7
|
+
configuration or environment saved before the move never reaches it.
|
|
8
|
+
|
|
9
|
+
The rule is an allowlist, not a normalizer. Only a plain web address is used:
|
|
10
|
+
http or https, a host of ASCII letters, digits, dots, hyphens and underscores,
|
|
11
|
+
an optional port and a plain path. Every transport reads such an address the
|
|
12
|
+
same way, so its host is the one the request reaches. A plain address on the
|
|
13
|
+
retired host becomes the hosted API; any other plain address is kept.
|
|
14
|
+
Anything else is refused, because curl, urllib and WHATWG parsers disagree
|
|
15
|
+
about where it goes: userinfo, backslashes, percent-encoding, curl glob
|
|
16
|
+
characters, a query or fragment, whitespace or invisible characters inside,
|
|
17
|
+
a ``URL:`` prefix, non-ASCII. A refused address raises
|
|
18
|
+
:class:`RefusedApiAddress` and nothing is sent.
|
|
19
|
+
|
|
20
|
+
The shell (bin/lib/config.sh), Node (packages/create-egregore/lib/
|
|
21
|
+
hosted-api.js) and connector resolvers follow the same table:
|
|
22
|
+
bin/tests/fixtures/hosted-api-url-vectors.json.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
from typing import Mapping
|
|
30
|
+
from urllib.request import Request
|
|
31
|
+
|
|
32
|
+
HOSTED_API_ORIGIN = "https://api.egregore.xyz"
|
|
33
|
+
RETIRED_API_ORIGIN = "https://egregore-production-55f2.up.railway.app"
|
|
34
|
+
REFUSED_API_ADDRESS_NOTICE = (
|
|
35
|
+
"The hosted services address isn't a plain web address, so nothing was sent. "
|
|
36
|
+
f"Correct api_url in egregore.json; the hosted address is {HOSTED_API_ORIGIN}."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
_RETIRED_HOST = RETIRED_API_ORIGIN.split("://", 1)[1]
|
|
40
|
+
_WHITESPACE = " \t\n\r\f\v"
|
|
41
|
+
_PLAIN = re.compile(r"https?://([A-Za-z0-9._-]+)(:[0-9]{1,5})?(/[A-Za-z0-9._~/-]*)?")
|
|
42
|
+
# The start of a request URL: a plain origin, then a path, query or nothing.
|
|
43
|
+
_PLAIN_ORIGIN = re.compile(r"https?://([A-Za-z0-9._-]+)(:[0-9]{1,5})?(?=[/?#]|\Z)")
|
|
44
|
+
_notice_shown = False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RefusedApiAddress(ValueError):
|
|
48
|
+
"""The hosted address is not a plain web address, so nothing may be sent to it."""
|
|
49
|
+
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
super().__init__(REFUSED_API_ADDRESS_NOTICE)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _refuse() -> RefusedApiAddress:
|
|
55
|
+
"""Say why once per process on stderr, and return the error to raise."""
|
|
56
|
+
global _notice_shown
|
|
57
|
+
if not _notice_shown:
|
|
58
|
+
_notice_shown = True
|
|
59
|
+
try:
|
|
60
|
+
print(REFUSED_API_ADDRESS_NOTICE, file=sys.stderr)
|
|
61
|
+
except (OSError, ValueError):
|
|
62
|
+
pass
|
|
63
|
+
return RefusedApiAddress()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _retired_host(host: str) -> bool:
|
|
67
|
+
return host.lower().rstrip(".") == _RETIRED_HOST
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def retired_api_url(value: object) -> bool:
|
|
71
|
+
"""Whether ``value`` is a plain address on the retired hosted origin."""
|
|
72
|
+
text = ("" if value is None else str(value)).strip(_WHITESPACE)
|
|
73
|
+
plain = _PLAIN.fullmatch(text)
|
|
74
|
+
return bool(plain) and _retired_host(plain.group(1))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def hosted_api_url(value: object, exact: bool = False) -> str:
|
|
78
|
+
"""The address to send a request to.
|
|
79
|
+
|
|
80
|
+
A plain address on the retired origin becomes the hosted API, keeping its
|
|
81
|
+
path, with trailing slashes trimmed. Any other plain address keeps its text
|
|
82
|
+
apart from surrounding whitespace and trailing slashes; empty stays empty.
|
|
83
|
+
Anything else raises :class:`RefusedApiAddress`, and the caller sends
|
|
84
|
+
nothing.
|
|
85
|
+
|
|
86
|
+
``exact`` is for the server's own settings, such as an OAuth redirect URI
|
|
87
|
+
that is compared exactly: only a plain retired address changes, and every
|
|
88
|
+
other value is returned byte for byte, never refused.
|
|
89
|
+
"""
|
|
90
|
+
text = "" if value is None else str(value)
|
|
91
|
+
trimmed = text.strip(_WHITESPACE)
|
|
92
|
+
plain = _PLAIN.fullmatch(trimmed)
|
|
93
|
+
if plain and _retired_host(plain.group(1)):
|
|
94
|
+
return (HOSTED_API_ORIGIN + (plain.group(3) or "")).rstrip("/")
|
|
95
|
+
if exact:
|
|
96
|
+
return text
|
|
97
|
+
if not trimmed:
|
|
98
|
+
return ""
|
|
99
|
+
if not plain:
|
|
100
|
+
raise _refuse()
|
|
101
|
+
return trimmed.rstrip("/")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
_CREDENTIAL_HEADER = re.compile(r"authorization|cookie|token|key|secret|auth", re.IGNORECASE)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def credential_request(
|
|
108
|
+
url: str,
|
|
109
|
+
*,
|
|
110
|
+
headers: Mapping[str, str] | None = None,
|
|
111
|
+
data: bytes | None = None,
|
|
112
|
+
method: str | None = None,
|
|
113
|
+
) -> Request:
|
|
114
|
+
"""A request whose credentials reach only a plain, current hosted address.
|
|
115
|
+
|
|
116
|
+
The URL must start with a plain origin that is not the retired one;
|
|
117
|
+
otherwise :class:`RefusedApiAddress` is raised and nothing is built. The
|
|
118
|
+
resolver already guarantees that for an address it returned; this holds it
|
|
119
|
+
for every URL built from one.
|
|
120
|
+
|
|
121
|
+
urllib's redirect handler copies only ``Request.headers`` to the next
|
|
122
|
+
request. Authorization, cookies and any token, key or secret header are
|
|
123
|
+
added with ``add_unredirected_header`` instead, so they stay with the
|
|
124
|
+
first request and cannot follow a redirect to another host. A redirected
|
|
125
|
+
POST loses its body (urllib refuses 307 and 308 for it), so a credential
|
|
126
|
+
in the body stays behind too.
|
|
127
|
+
"""
|
|
128
|
+
origin = _PLAIN_ORIGIN.match(url) if isinstance(url, str) else None
|
|
129
|
+
if not origin or _retired_host(origin.group(1)):
|
|
130
|
+
raise _refuse()
|
|
131
|
+
request = Request(url, data=data, method=method)
|
|
132
|
+
for name, value in (headers or {}).items():
|
|
133
|
+
if _CREDENTIAL_HEADER.search(name):
|
|
134
|
+
request.add_unredirected_header(name, value)
|
|
135
|
+
else:
|
|
136
|
+
request.add_header(name, value)
|
|
137
|
+
return request
|
|
@@ -12,8 +12,9 @@ import tempfile
|
|
|
12
12
|
import fcntl
|
|
13
13
|
import hashlib
|
|
14
14
|
import re
|
|
15
|
+
import stat
|
|
15
16
|
from contextlib import contextmanager
|
|
16
|
-
from collections.abc import Callable, Mapping
|
|
17
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
17
18
|
from dataclasses import dataclass
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
from typing import Any
|
|
@@ -21,12 +22,16 @@ from uuid import uuid4
|
|
|
21
22
|
|
|
22
23
|
from .contracts import (
|
|
23
24
|
AccountIdentity,
|
|
25
|
+
EgregoreProfile,
|
|
24
26
|
ActorContext,
|
|
25
27
|
ActorIdentity,
|
|
26
28
|
ActorKind,
|
|
27
29
|
OrgMembership,
|
|
30
|
+
PolicyDecision,
|
|
28
31
|
)
|
|
32
|
+
from . import held
|
|
29
33
|
from .entitlements import resolve_entitlements
|
|
34
|
+
from .policy import check
|
|
30
35
|
from .profile import initialize_org_config, load_profile, render_identity_document
|
|
31
36
|
|
|
32
37
|
|
|
@@ -48,9 +53,10 @@ class OrganizationMemberPresentation:
|
|
|
48
53
|
aliases: tuple[str, ...]
|
|
49
54
|
|
|
50
55
|
|
|
51
|
-
def _profile_identity(path: Path) -> dict[str, Any]:
|
|
56
|
+
def _profile_identity(path: Path, source: Path | None = None, data: bytes | None = None) -> dict[str, Any]:
|
|
52
57
|
try:
|
|
53
|
-
text =
|
|
58
|
+
text = (data.decode("utf-8", "replace") if data is not None
|
|
59
|
+
else (source or path).read_text(encoding="utf-8", errors="replace"))[:4_000]
|
|
54
60
|
except OSError:
|
|
55
61
|
return {}
|
|
56
62
|
title = re.search(r"(?m)^#\s+(.+?)\s*$", text)
|
|
@@ -83,6 +89,10 @@ def _profile_identity(path: Path) -> dict[str, Any]:
|
|
|
83
89
|
def organization_member_presentations(
|
|
84
90
|
root: Path,
|
|
85
91
|
actor: ActorContext | None = None,
|
|
92
|
+
*,
|
|
93
|
+
decision: PolicyDecision | None,
|
|
94
|
+
sources: Mapping[str, bytes] | None = None,
|
|
95
|
+
removed: Iterable[str] | None = None,
|
|
86
96
|
) -> tuple[OrganizationMemberPresentation, ...]:
|
|
87
97
|
"""Load stable member names without treating aliases as extra people.
|
|
88
98
|
|
|
@@ -90,31 +100,50 @@ def organization_member_presentations(
|
|
|
90
100
|
reconciliation. Profiles with the same Person-ID collapse into one member;
|
|
91
101
|
an explicit Alias-Of title is the preferred presentation witness for
|
|
92
102
|
legacy canonical profiles whose title is still a provider handle.
|
|
103
|
+
|
|
104
|
+
``decision`` is required: a profile is read only when the one predicate
|
|
105
|
+
admits it for that reader, from the path it checked, so prompt context and
|
|
106
|
+
query terms name nobody the reader may not see. ``decision=None`` is an
|
|
107
|
+
explicit opt-out for a caller that applies its own holds (the exporter's
|
|
108
|
+
hand-over chain); leaving it out is an error, never an unchecked read.
|
|
109
|
+
``sources`` (canonical path → bytes a caller already read and authorized,
|
|
110
|
+
as a hand-over holds them) replaces reading the profiles from disk, and
|
|
111
|
+
``removed`` the working tree's removal ledger (another principal's
|
|
112
|
+
hand-over passes the committed one).
|
|
93
113
|
"""
|
|
94
114
|
|
|
95
115
|
people = root / "memory" / "people"
|
|
96
|
-
if not people.is_dir():
|
|
116
|
+
if sources is None and not people.is_dir():
|
|
97
117
|
return ()
|
|
98
118
|
# Durable removals: the committed ledger in egregore.json outranks file
|
|
99
119
|
# presence — a person file resurrected by a session write or sync never
|
|
100
120
|
# resurfaces a removed member in the roster.
|
|
101
|
-
removed
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
if removed is None:
|
|
122
|
+
try:
|
|
123
|
+
config = json.loads((root / "egregore.json").read_text(encoding="utf-8"))
|
|
124
|
+
removed = {
|
|
125
|
+
str(handle).strip().casefold()
|
|
126
|
+
for handle in (config.get("people_removed") or [])
|
|
127
|
+
if str(handle).strip()
|
|
128
|
+
}
|
|
129
|
+
except (OSError, ValueError):
|
|
130
|
+
removed = set()
|
|
131
|
+
removed = set(removed)
|
|
111
132
|
grouped: dict[str, list[dict[str, Any]]] = {}
|
|
112
|
-
|
|
133
|
+
profiles = sorted(people.glob("*.md")) if sources is None else sorted(
|
|
134
|
+
root / name for name in sources if name.startswith("memory/people/") and name.count("/") == 2)
|
|
135
|
+
for path in profiles:
|
|
113
136
|
if path.name == "index.md":
|
|
114
137
|
continue
|
|
115
138
|
if path.stem.casefold() in removed:
|
|
116
139
|
continue
|
|
117
|
-
|
|
140
|
+
source = path
|
|
141
|
+
if decision is not None:
|
|
142
|
+
verdict = check(decision, f"memory/people/{path.name}", "read", root=root / "memory")
|
|
143
|
+
if not verdict.allowed:
|
|
144
|
+
continue
|
|
145
|
+
source = verdict.resolved
|
|
146
|
+
profile = _profile_identity(path, source, None if sources is None else sources[f"memory/people/{path.name}"])
|
|
118
147
|
if not profile:
|
|
119
148
|
continue
|
|
120
149
|
if str(profile.get("github") or "").strip().casefold() in removed:
|
|
@@ -211,7 +240,8 @@ def organization_member_presentations(
|
|
|
211
240
|
aliases=compact_aliases,
|
|
212
241
|
)
|
|
213
242
|
)
|
|
214
|
-
|
|
243
|
+
members.sort(key=lambda item: item.display_name.casefold()) # a readable profile can name held material too
|
|
244
|
+
return tuple(members if decision is None else held.redact(decision, members, root=root / "memory"))
|
|
215
245
|
|
|
216
246
|
|
|
217
247
|
def resolve_actor_query(actor: ActorContext, query: str) -> str:
|
|
@@ -752,6 +782,102 @@ def _identity_lock(state_path: Path):
|
|
|
752
782
|
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
|
|
753
783
|
|
|
754
784
|
|
|
785
|
+
def _existing_identity_json(path: Path) -> tuple[dict[str, Any], bytes]:
|
|
786
|
+
"""Bounded, no-symlink read for the opt-in non-migrating resolver."""
|
|
787
|
+
path = Path(os.path.abspath(path))
|
|
788
|
+
parent = os.open(path.anchor, os.O_RDONLY | os.O_DIRECTORY)
|
|
789
|
+
descriptor = None
|
|
790
|
+
try:
|
|
791
|
+
for part in path.parts[1:-1]:
|
|
792
|
+
child = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=parent)
|
|
793
|
+
os.close(parent)
|
|
794
|
+
parent = child
|
|
795
|
+
descriptor = os.open(path.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=parent)
|
|
796
|
+
info = os.fstat(descriptor)
|
|
797
|
+
if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_size > 200000:
|
|
798
|
+
raise ValueError("existing identity is unavailable")
|
|
799
|
+
raw = os.read(descriptor, 200001)
|
|
800
|
+
if len(raw) > 200000:
|
|
801
|
+
raise ValueError("existing identity is unavailable")
|
|
802
|
+
finally:
|
|
803
|
+
if descriptor is not None:
|
|
804
|
+
os.close(descriptor)
|
|
805
|
+
os.close(parent)
|
|
806
|
+
def unique(pairs):
|
|
807
|
+
result = {}
|
|
808
|
+
for key, value in pairs:
|
|
809
|
+
if key in result:
|
|
810
|
+
raise ValueError("existing identity is invalid")
|
|
811
|
+
result[key] = value
|
|
812
|
+
return result
|
|
813
|
+
try:
|
|
814
|
+
value = json.loads(raw, object_pairs_hook=unique)
|
|
815
|
+
pending = [(value, 0)]
|
|
816
|
+
while pending:
|
|
817
|
+
item, depth = pending.pop()
|
|
818
|
+
if depth > 64:
|
|
819
|
+
raise ValueError("existing identity is invalid")
|
|
820
|
+
if isinstance(item, str):
|
|
821
|
+
item.encode("utf-8")
|
|
822
|
+
elif isinstance(item, dict):
|
|
823
|
+
pending.extend((part, depth + 1) for pair in item.items() for part in pair)
|
|
824
|
+
elif isinstance(item, list):
|
|
825
|
+
pending.extend((part, depth + 1) for part in item)
|
|
826
|
+
if not isinstance(value, dict):
|
|
827
|
+
raise ValueError("existing identity is invalid")
|
|
828
|
+
except (ValueError, UnicodeError, RecursionError):
|
|
829
|
+
raise ValueError("existing identity is invalid") from None
|
|
830
|
+
return value, raw
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def _existing_identifier(value: Any) -> bool:
|
|
834
|
+
return (isinstance(value, str) and bool(value) and value == value.strip()
|
|
835
|
+
and len(value.encode("utf-8")) <= 2000
|
|
836
|
+
and not any(ord(character) < 33 or ord(character) == 127 for character in value))
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def _existing_identity(root: Path):
|
|
840
|
+
"""Retained IDs and config only; deliberately never read identity documents.
|
|
841
|
+
|
|
842
|
+
The profile revision has a distinct config-only namespace and cannot stand
|
|
843
|
+
in for load_profile's revision of config plus declared identity documents.
|
|
844
|
+
"""
|
|
845
|
+
from .profile import PROFILE_SCHEMA, DEFAULT_CONTEXT_BUDGET
|
|
846
|
+
config, raw = _existing_identity_json(root / "egregore.json")
|
|
847
|
+
state, _ = _existing_identity_json(root / ".egregore-state.json")
|
|
848
|
+
org = config.get("org_id")
|
|
849
|
+
if not _existing_identifier(org) or any(not _existing_identifier(state.get(key)) for key in ("account_id", "actor_id", "membership_id")):
|
|
850
|
+
raise ValueError("existing identity is required")
|
|
851
|
+
nested = state.get("identity", {})
|
|
852
|
+
if not isinstance(nested, dict) or any(key in nested and nested[key] != value for key, value in {
|
|
853
|
+
"account_id": state["account_id"], "actor_id": state["actor_id"],
|
|
854
|
+
"membership_id": state["membership_id"], "org_id": org, "actor_kind": "person",
|
|
855
|
+
}.items()) or "org_id" in state and state["org_id"] != org:
|
|
856
|
+
raise ValueError("existing identity is inconsistent")
|
|
857
|
+
if not isinstance(state.get("provider_identities", {}), dict):
|
|
858
|
+
raise ValueError("existing identity is invalid")
|
|
859
|
+
declared = config.get("profile") or {}
|
|
860
|
+
if not isinstance(declared, dict):
|
|
861
|
+
raise ValueError("existing profile is invalid")
|
|
862
|
+
schema = declared.get("schema_version") or PROFILE_SCHEMA
|
|
863
|
+
budget = declared.get("default_context_budget", DEFAULT_CONTEXT_BUDGET)
|
|
864
|
+
slug, name = str(config.get("slug") or "").strip(), str(config.get("org_name") or config.get("name") or "").strip()
|
|
865
|
+
if not slug or not name or schema != PROFILE_SCHEMA or type(budget) is not int or budget <= 0:
|
|
866
|
+
raise ValueError("existing profile is invalid")
|
|
867
|
+
for field in ("purpose_path", "principles_path", "conventions_path"):
|
|
868
|
+
relative = declared.get(field)
|
|
869
|
+
if relative is not None and (not isinstance(relative, str) or Path(relative.replace("\\", "/")).is_absolute() or ".." in Path(relative.replace("\\", "/")).parts):
|
|
870
|
+
raise ValueError("existing profile is invalid")
|
|
871
|
+
profile = EgregoreProfile(
|
|
872
|
+
schema_version=schema, org_id=org, slug=slug, name=name,
|
|
873
|
+
revision="profile-config-only-sha256:" + hashlib.sha256(raw).hexdigest(),
|
|
874
|
+
purpose_path=declared.get("purpose_path"), principles_path=declared.get("principles_path"),
|
|
875
|
+
conventions_path=declared.get("conventions_path"),
|
|
876
|
+
default_policy=str(declared.get("default_policy") or "all-access-v1"), default_context_budget=budget,
|
|
877
|
+
)
|
|
878
|
+
return profile, state, config
|
|
879
|
+
|
|
880
|
+
|
|
755
881
|
class LocalIdentityResolver:
|
|
756
882
|
"""Resolve actor context from committed org config and local user state."""
|
|
757
883
|
|
|
@@ -761,62 +887,67 @@ class LocalIdentityResolver:
|
|
|
761
887
|
*,
|
|
762
888
|
persist_migrations: bool = True,
|
|
763
889
|
entitlement_row: Mapping[str, Any] | None = None,
|
|
890
|
+
require_existing: bool = False,
|
|
764
891
|
) -> None:
|
|
765
892
|
self.root = Path(root)
|
|
766
893
|
self.persist_migrations = persist_migrations
|
|
767
894
|
self.entitlement_row = entitlement_row
|
|
895
|
+
self.require_existing = require_existing
|
|
768
896
|
|
|
769
897
|
def resolve(self, *, session_id: str, harness: str) -> ActorContext:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
898
|
+
if self.require_existing:
|
|
899
|
+
profile, migrated, config = _existing_identity(self.root)
|
|
900
|
+
else:
|
|
901
|
+
profile = load_profile(self.root / "egregore.json")
|
|
902
|
+
state_path = self.root / ".egregore-state.json"
|
|
903
|
+
try:
|
|
904
|
+
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
905
|
+
except FileNotFoundError as exc:
|
|
906
|
+
raise ValueError(f"local identity state is missing: {state_path}") from exc
|
|
907
|
+
if not isinstance(state, dict):
|
|
908
|
+
raise ValueError(f"local identity state must be an object: {state_path}")
|
|
909
|
+
|
|
910
|
+
config = json.loads((self.root / "egregore.json").read_text(encoding="utf-8"))
|
|
911
|
+
id_factory = new_identity_id
|
|
912
|
+
if (
|
|
913
|
+
profile.org_id.startswith("legacy-org:")
|
|
914
|
+
and (str(config.get("mode") or "local") == "connected" or config.get("api_url"))
|
|
915
|
+
):
|
|
916
|
+
# A pre-migration Connected install must remain usable offline, but
|
|
917
|
+
# it must not mint a new random actor in each detached checkout.
|
|
918
|
+
# These explicitly legacy ids are deterministic compatibility
|
|
919
|
+
# placeholders and are replaced only by control-plane IDs.
|
|
920
|
+
legacy_seed = json.dumps(
|
|
921
|
+
{
|
|
922
|
+
"org": profile.org_id,
|
|
923
|
+
"person_id": state.get("person_id"),
|
|
924
|
+
"github_id": state.get("github_id"),
|
|
925
|
+
"github_username": state.get("github_username"),
|
|
926
|
+
"email": state.get("email"),
|
|
927
|
+
},
|
|
928
|
+
sort_keys=True,
|
|
929
|
+
separators=(",", ":"),
|
|
930
|
+
)
|
|
800
931
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
932
|
+
def id_factory(prefix: str) -> str:
|
|
933
|
+
digest = hashlib.sha256(f"{prefix}:{legacy_seed}".encode()).hexdigest()[:24]
|
|
934
|
+
return f"legacy-{prefix}:{digest}"
|
|
804
935
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
936
|
+
migrated, changed = migrate_local_identity_state(
|
|
937
|
+
state, org_id=profile.org_id, id_factory=id_factory
|
|
938
|
+
)
|
|
939
|
+
if changed and self.persist_migrations:
|
|
940
|
+
# First-session races must not mint competing account/actor ids.
|
|
941
|
+
# Re-read after taking the instance lock, then persist once.
|
|
942
|
+
with _identity_lock(state_path):
|
|
943
|
+
current = json.loads(state_path.read_text(encoding="utf-8"))
|
|
944
|
+
if not isinstance(current, dict):
|
|
945
|
+
raise ValueError(f"local identity state must be an object: {state_path}")
|
|
946
|
+
migrated, changed = migrate_local_identity_state(
|
|
947
|
+
current, org_id=profile.org_id, id_factory=id_factory
|
|
948
|
+
)
|
|
949
|
+
if changed:
|
|
950
|
+
_atomic_json(state_path, migrated)
|
|
820
951
|
|
|
821
952
|
provider_aliases: dict[str, str] = {}
|
|
822
953
|
for provider, record in (migrated.get("provider_identities") or {}).items():
|
|
@@ -18,7 +18,7 @@ from typing import Any, Mapping
|
|
|
18
18
|
|
|
19
19
|
from .artifacts import parse_canonical_markdown, render_canonical_markdown, split_frontmatter
|
|
20
20
|
from .contracts import ActorContext, Permission
|
|
21
|
-
from .policy import
|
|
21
|
+
from .policy import allows, action_for
|
|
22
22
|
from .runtime import local_runtime
|
|
23
23
|
|
|
24
24
|
SCHEMA = "egregore-identity-transfer/v1"
|
|
@@ -145,7 +145,7 @@ def _document(before: str, path: str, pair: Mapping[str, Any], transfer_id: str)
|
|
|
145
145
|
|
|
146
146
|
def _authorize(runtime, source: ActorContext, path: str, permission: Permission) -> None:
|
|
147
147
|
decision = runtime.authorize(source, permission, (f"memory/{path}",))
|
|
148
|
-
if not decision.allowed or not
|
|
148
|
+
if not decision.allowed or not allows(decision, f"memory/{path}", action_for(decision.permission), root=runtime.canonical_root):
|
|
149
149
|
raise IdentityTransferError("current actor cannot authorize ownership transfer for the canonical path")
|
|
150
150
|
|
|
151
151
|
|
|
@@ -192,6 +192,10 @@ class _RevisionStore:
|
|
|
192
192
|
def __init__(self, store, path: str, expected: str):
|
|
193
193
|
self.store, self.path, self.expected = store, path, expected
|
|
194
194
|
|
|
195
|
+
@property
|
|
196
|
+
def root(self):
|
|
197
|
+
return self.store.root
|
|
198
|
+
|
|
195
199
|
def check_write(self, document):
|
|
196
200
|
if document.artifact.canonical_path != self.path:
|
|
197
201
|
raise IdentityTransferError("ownership transfer attempted an unplanned write")
|
|
@@ -22,7 +22,7 @@ from .ingest import (
|
|
|
22
22
|
LocalIngestJournal,
|
|
23
23
|
LocalQuarantine,
|
|
24
24
|
)
|
|
25
|
-
from .policy import
|
|
25
|
+
from .policy import allows, action_for
|
|
26
26
|
from .runtime import local_runtime, runtime_root
|
|
27
27
|
from .scratch import read_and_consume
|
|
28
28
|
|
|
@@ -70,7 +70,7 @@ def _require(runtime, actor, permission: Permission, source_id: str):
|
|
|
70
70
|
raise PermissionError(
|
|
71
71
|
"; ".join(decision.reasons) or f"ingest {permission.value} denied"
|
|
72
72
|
)
|
|
73
|
-
if not
|
|
73
|
+
if not allows(decision, _canonical_directory(source_id), action_for(decision.permission), root=runtime.canonical_root):
|
|
74
74
|
raise PermissionError("ingest path is outside the actor's authorized scope")
|
|
75
75
|
return decision
|
|
76
76
|
|
|
@@ -8,6 +8,7 @@ legacy JSON shape consumed by the ingest boundary filter.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
+
from dataclasses import replace
|
|
11
12
|
import json
|
|
12
13
|
import os
|
|
13
14
|
import sys
|
|
@@ -17,9 +18,7 @@ from pathlib import Path, PurePosixPath
|
|
|
17
18
|
from egregore_runtime.adapters.qmd import QmdAdapterError, QmdLocalRetriever
|
|
18
19
|
from egregore_runtime.contracts import RetrievalMode, RetrievalRequest
|
|
19
20
|
from egregore_runtime.identity import LocalIdentityResolver
|
|
20
|
-
from egregore_runtime.
|
|
21
|
-
from egregore_runtime.policy import all_access_dogfood_policy
|
|
22
|
-
from egregore_runtime.telemetry import LocalTelemetrySink
|
|
21
|
+
from egregore_runtime.runtime import local_runtime
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def _root() -> Path:
|
|
@@ -76,19 +75,8 @@ def _query(args: argparse.Namespace) -> int:
|
|
|
76
75
|
mode=RetrievalMode.HYBRID,
|
|
77
76
|
open_sources=False,
|
|
78
77
|
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
actor_id=actor.actor.actor_id,
|
|
82
|
-
)
|
|
83
|
-
compiler = DefaultOrgContextCompiler(
|
|
84
|
-
authorizer=policy,
|
|
85
|
-
retriever=retriever,
|
|
86
|
-
telemetry=LocalTelemetrySink(
|
|
87
|
-
state_file=root / ".egregore-state.json",
|
|
88
|
-
instance_root=(root / "memory").resolve(),
|
|
89
|
-
),
|
|
90
|
-
)
|
|
91
|
-
context = compiler.observe(
|
|
78
|
+
runtime = replace(local_runtime(root, push_remote=False), retriever=retriever)
|
|
79
|
+
context = runtime.observe(
|
|
92
80
|
actor,
|
|
93
81
|
request,
|
|
94
82
|
token_budget=actor.profile.default_context_budget,
|