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,16 +7,18 @@ composed here and are not part of the harness contract.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from dataclasses import dataclass
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
11
|
from datetime import UTC, datetime
|
|
12
12
|
import hashlib
|
|
13
|
+
import json
|
|
13
14
|
import os
|
|
14
15
|
from pathlib import Path
|
|
15
|
-
from typing import Callable, Sequence
|
|
16
|
+
from typing import TYPE_CHECKING, Callable, Sequence
|
|
16
17
|
from uuid import uuid4
|
|
17
18
|
|
|
18
19
|
from .adapters.qmd import QmdLocalRetriever
|
|
19
|
-
from .
|
|
20
|
+
from .held import document_is_admin, redact
|
|
21
|
+
from .access import authorization_inputs
|
|
20
22
|
from .artifacts import (
|
|
21
23
|
CanonicalArtifactStore,
|
|
22
24
|
CanonicalDocument,
|
|
@@ -59,12 +61,21 @@ from .lifecycle import (
|
|
|
59
61
|
LifecycleRecord,
|
|
60
62
|
)
|
|
61
63
|
from .observe import DefaultOrgContextCompiler
|
|
64
|
+
from .observers import MAX_EVENT_PATHS, Observer, RuntimeEvent
|
|
62
65
|
from .sync_health import grade_retrieval, sync_remedy
|
|
63
|
-
from .policy import
|
|
66
|
+
from .policy import check, is_admin, policy_for
|
|
67
|
+
from .projection import observer as projection_observer
|
|
68
|
+
from .optional_modules import optional_module
|
|
69
|
+
|
|
70
|
+
_record = optional_module(".record")
|
|
71
|
+
record_observer = _record.observer if _record is not None else None
|
|
64
72
|
from .sync import LocalGitSyncTransport
|
|
65
73
|
from .telemetry import LocalTelemetrySink, TELEMETRY_SCHEMA_VERSION
|
|
66
74
|
from .writeback import CanonicalArtifactWriteback
|
|
67
75
|
|
|
76
|
+
if TYPE_CHECKING:
|
|
77
|
+
from .member_authority import AuthenticatedMemberResolver
|
|
78
|
+
|
|
68
79
|
|
|
69
80
|
PolicyFactory = Callable[[ActorContext], ActionAuthorizer]
|
|
70
81
|
|
|
@@ -82,8 +93,20 @@ class EgregoreRuntime:
|
|
|
82
93
|
policy_factory: PolicyFactory
|
|
83
94
|
lifecycle: CanonicalLifecycleService | None = None
|
|
84
95
|
canonical_root: Path | None = None
|
|
85
|
-
admin_gate: AdminGate | None = None
|
|
86
96
|
instance_root: Path | None = None
|
|
97
|
+
observers: list[Observer] = field(default_factory=list)
|
|
98
|
+
|
|
99
|
+
def _notify(self, kind, actor, paths):
|
|
100
|
+
paths = tuple(dict.fromkeys(path for path in paths if path))[:MAX_EVENT_PATHS]
|
|
101
|
+
if not paths:
|
|
102
|
+
return
|
|
103
|
+
event = RuntimeEvent(kind, actor.actor.actor_id, actor.session_id, actor.harness, paths,
|
|
104
|
+
execution=actor.execution)
|
|
105
|
+
for observer in self.observers:
|
|
106
|
+
try:
|
|
107
|
+
observer(event)
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
87
110
|
|
|
88
111
|
def resolve_actor(self, *, session_id: str, harness: str) -> ActorContext:
|
|
89
112
|
return self.identity.resolve(session_id=session_id, harness=harness)
|
|
@@ -108,13 +131,15 @@ class EgregoreRuntime:
|
|
|
108
131
|
retriever=self.retriever,
|
|
109
132
|
telemetry=self.telemetry,
|
|
110
133
|
lifecycle=self.lifecycle,
|
|
111
|
-
|
|
134
|
+
memory_root=self.canonical_root,
|
|
112
135
|
)
|
|
113
|
-
|
|
136
|
+
context = compiler.observe(
|
|
114
137
|
actor,
|
|
115
138
|
request,
|
|
116
139
|
token_budget=token_budget or actor.profile.default_context_budget,
|
|
117
140
|
)
|
|
141
|
+
self._notify("observed", actor, (item.canonical_path for item in context.evidence))
|
|
142
|
+
return context
|
|
118
143
|
|
|
119
144
|
def retrieval_health(self) -> RetrieverHealth:
|
|
120
145
|
return self.retriever.health()
|
|
@@ -236,6 +261,54 @@ class EgregoreRuntime:
|
|
|
236
261
|
def open_source(self, actor: ActorContext, canonical_path: str) -> str:
|
|
237
262
|
"""Authorize, open, and record one canonical source through Runtime."""
|
|
238
263
|
|
|
264
|
+
content, artifact_id, canonical = self._served(actor, canonical_path)
|
|
265
|
+
try:
|
|
266
|
+
self.telemetry.emit(
|
|
267
|
+
TelemetryEvent(
|
|
268
|
+
schema_version=TELEMETRY_SCHEMA_VERSION,
|
|
269
|
+
event_id=f"evt_{uuid4().hex}",
|
|
270
|
+
event_type="source.opened",
|
|
271
|
+
occurred_at=datetime.now(UTC),
|
|
272
|
+
org_id=actor.profile.org_id,
|
|
273
|
+
actor_id=actor.actor.actor_id,
|
|
274
|
+
session_id=actor.session_id,
|
|
275
|
+
org_revision=actor.profile.revision,
|
|
276
|
+
index_spec_version=self._index_spec_version(),
|
|
277
|
+
metrics={
|
|
278
|
+
"opened_count": 1,
|
|
279
|
+
"result": "authorized",
|
|
280
|
+
"success": True,
|
|
281
|
+
},
|
|
282
|
+
artifact_ids=(artifact_id,),
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
except Exception:
|
|
286
|
+
# Source access is canonical behavior; telemetry is disposable.
|
|
287
|
+
pass
|
|
288
|
+
self._notify("opened", actor, (canonical,))
|
|
289
|
+
return content
|
|
290
|
+
|
|
291
|
+
def serve_source(self, actor: ActorContext, canonical_path: str) -> str:
|
|
292
|
+
"""One canonical source as ``open_source`` serves it (authorized, the admin guard, redacted), without
|
|
293
|
+
recording it as opened: for a listing that reads many files to show a few (``shown`` records those)."""
|
|
294
|
+
|
|
295
|
+
return self._served(actor, canonical_path)[0]
|
|
296
|
+
|
|
297
|
+
def shown(self, actor: ActorContext, canonical_paths: Sequence[str]) -> None:
|
|
298
|
+
"""Record that a listing showed the actor what these canonical sources hold, as an observe does."""
|
|
299
|
+
|
|
300
|
+
self._notify("observed", actor, canonical_paths)
|
|
301
|
+
|
|
302
|
+
def _index_spec_version(self) -> str:
|
|
303
|
+
"""The retriever's index spec: its own constant when it states one, since its health check can be a
|
|
304
|
+
subprocess (QMD's runs ``qmd status``) that every source open would pay for."""
|
|
305
|
+
|
|
306
|
+
known = getattr(self.retriever, "index_spec_version", None)
|
|
307
|
+
return known if isinstance(known, str) and known else self.retriever.health().index_spec_version
|
|
308
|
+
|
|
309
|
+
def _served(self, actor: ActorContext, canonical_path: str) -> tuple[str, str, str]:
|
|
310
|
+
"""(content, stable artifact id, canonical path) of one authorized, redacted canonical source."""
|
|
311
|
+
|
|
239
312
|
path = canonical_path.strip().replace("\\", "/")
|
|
240
313
|
relative = path.removeprefix("memory/")
|
|
241
314
|
artifact_id = artifact_id_from_path(relative)
|
|
@@ -243,13 +316,14 @@ class EgregoreRuntime:
|
|
|
243
316
|
# stable declared id is resolved from the authorized source and then
|
|
244
317
|
# checked separately before content is returned to the harness.
|
|
245
318
|
decision = self.authorize(actor, Permission.READ)
|
|
246
|
-
|
|
319
|
+
verdict = check(decision, path, "read", root=self.canonical_root)
|
|
320
|
+
if not verdict.allowed:
|
|
247
321
|
raise AuthorizationDenied("canonical source is outside the actor read scope")
|
|
248
322
|
|
|
249
323
|
content = self.retriever.open_source(
|
|
250
324
|
RetrievalHit(
|
|
251
325
|
artifact_id=artifact_id,
|
|
252
|
-
canonical_path=
|
|
326
|
+
canonical_path=verdict.canonical,
|
|
253
327
|
rank=1,
|
|
254
328
|
score=1,
|
|
255
329
|
retrieval_types=(RetrievalMode.LEX,),
|
|
@@ -259,44 +333,31 @@ class EgregoreRuntime:
|
|
|
259
333
|
# only to organization admins. Repository members can still read the
|
|
260
334
|
# raw Git file — this guards the product surface, not the filesystem.
|
|
261
335
|
if (
|
|
262
|
-
|
|
263
|
-
and document_is_admin(content)
|
|
264
|
-
and not self.admin_gate.actor_is_admin(actor)
|
|
336
|
+
not is_admin(decision) and document_is_admin(content)
|
|
265
337
|
):
|
|
266
338
|
raise AuthorizationDenied(
|
|
267
339
|
"this document is admin-marked; Egregore serves it to organization admins only"
|
|
268
340
|
)
|
|
269
341
|
stable_artifact_id = canonical_artifact_id(content, path)
|
|
270
342
|
stable_decision = self.authorize(actor, Permission.READ, (stable_artifact_id,))
|
|
271
|
-
if not stable_decision.allowed
|
|
343
|
+
if not stable_decision.allowed or (
|
|
344
|
+
stable_decision.resource_ids and stable_artifact_id not in stable_decision.resource_ids
|
|
345
|
+
):
|
|
272
346
|
raise AuthorizationDenied(
|
|
273
347
|
"canonical artifact identity is outside the actor read scope"
|
|
274
348
|
)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
TelemetryEvent(
|
|
279
|
-
schema_version=TELEMETRY_SCHEMA_VERSION,
|
|
280
|
-
event_id=f"evt_{uuid4().hex}",
|
|
281
|
-
event_type="source.opened",
|
|
282
|
-
occurred_at=datetime.now(UTC),
|
|
283
|
-
org_id=actor.profile.org_id,
|
|
284
|
-
actor_id=actor.actor.actor_id,
|
|
285
|
-
session_id=actor.session_id,
|
|
286
|
-
org_revision=actor.profile.revision,
|
|
287
|
-
index_spec_version=self.retriever.health().index_spec_version,
|
|
288
|
-
metrics={
|
|
289
|
-
"opened_count": 1,
|
|
290
|
-
"result": "authorized",
|
|
291
|
-
"success": True,
|
|
292
|
-
},
|
|
293
|
-
artifact_ids=(artifact_id,),
|
|
294
|
-
)
|
|
349
|
+
if not check(stable_decision, path, "read", root=self.canonical_root).allowed:
|
|
350
|
+
raise AuthorizationDenied(
|
|
351
|
+
"canonical source is outside the current actor read scope"
|
|
295
352
|
)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
353
|
+
if not is_admin(stable_decision) and document_is_admin(content):
|
|
354
|
+
raise AuthorizationDenied(
|
|
355
|
+
"this document is admin-marked; Egregore serves it to organization admins only"
|
|
356
|
+
)
|
|
357
|
+
artifact_id = stable_artifact_id
|
|
358
|
+
# The one packaging: names of the documents held from this reader leave the text.
|
|
359
|
+
content = redact(stable_decision, content, root=self.canonical_root)
|
|
360
|
+
return content, artifact_id, verdict.canonical
|
|
300
361
|
|
|
301
362
|
def write_artifact(
|
|
302
363
|
self,
|
|
@@ -304,21 +365,36 @@ class EgregoreRuntime:
|
|
|
304
365
|
artifact: CanonicalArtifact,
|
|
305
366
|
content: str,
|
|
306
367
|
) -> WritebackEvent:
|
|
307
|
-
|
|
368
|
+
receipt = self._writeback(actor).write(actor, artifact, content)
|
|
369
|
+
if receipt.status in (WritebackStatus.ACCEPTED, WritebackStatus.PARTIAL):
|
|
370
|
+
self._notify("wrote", actor, ("memory/" + receipt.artifact.canonical_path,))
|
|
371
|
+
return receipt
|
|
308
372
|
|
|
309
373
|
def write_document(
|
|
310
374
|
self,
|
|
311
375
|
actor: ActorContext,
|
|
312
376
|
document: CanonicalDocument,
|
|
377
|
+
*,
|
|
378
|
+
message: str | None = None,
|
|
313
379
|
) -> WritebackEvent:
|
|
314
|
-
|
|
380
|
+
"""``message``: the commit's own message (a document edit's file, why and trailers); every other
|
|
381
|
+
write keeps the writeback's."""
|
|
382
|
+
receipt = self._writeback(actor).write_document(actor, document, **({"message": message} if message else {}))
|
|
383
|
+
if receipt.status in (WritebackStatus.ACCEPTED, WritebackStatus.PARTIAL):
|
|
384
|
+
self._notify("wrote", actor, ("memory/" + receipt.artifact.canonical_path,))
|
|
385
|
+
return receipt
|
|
315
386
|
|
|
316
387
|
def write_documents(
|
|
317
388
|
self,
|
|
318
389
|
actor: ActorContext,
|
|
319
390
|
documents: tuple[CanonicalDocument, ...],
|
|
320
391
|
) -> tuple[WritebackEvent, ...]:
|
|
321
|
-
|
|
392
|
+
receipts = self._writeback(actor).write_documents(actor, documents)
|
|
393
|
+
self._notify("wrote", actor, (
|
|
394
|
+
"memory/" + receipt.artifact.canonical_path for receipt in receipts
|
|
395
|
+
if receipt.status in (WritebackStatus.ACCEPTED, WritebackStatus.PARTIAL)
|
|
396
|
+
))
|
|
397
|
+
return receipts
|
|
322
398
|
|
|
323
399
|
def commit_canonical_files(
|
|
324
400
|
self,
|
|
@@ -331,9 +407,12 @@ class EgregoreRuntime:
|
|
|
331
407
|
|
|
332
408
|
if self.canonical_root is None:
|
|
333
409
|
raise RuntimeError("canonical file writeback is unavailable")
|
|
334
|
-
|
|
410
|
+
receipt = self._writeback(actor).commit_files(
|
|
335
411
|
actor, paths, memory_root=self.canonical_root, message=message,
|
|
336
412
|
)
|
|
413
|
+
if receipt.status in (WritebackStatus.ACCEPTED, WritebackStatus.PARTIAL):
|
|
414
|
+
self._notify("wrote", actor, ("memory/" + path for path in receipt.canonical_paths))
|
|
415
|
+
return receipt
|
|
337
416
|
|
|
338
417
|
def register_ingest_manifest(
|
|
339
418
|
self,
|
|
@@ -385,7 +464,7 @@ class EgregoreRuntime:
|
|
|
385
464
|
aliases.update(str(value) for value in actor.account.provider_aliases.values())
|
|
386
465
|
recipient_aliases = tuple(sorted(value for value in aliases if value.strip()))
|
|
387
466
|
return self.lifecycle.scan(
|
|
388
|
-
|
|
467
|
+
decision=decision,
|
|
389
468
|
artifact_types=artifact_types,
|
|
390
469
|
user=recipient_aliases,
|
|
391
470
|
)
|
|
@@ -413,12 +492,8 @@ class EgregoreRuntime:
|
|
|
413
492
|
matches = [record for record in plan.records if record.artifact_id == target_artifact_id]
|
|
414
493
|
if len(matches) != 1:
|
|
415
494
|
raise LifecycleError("lifecycle target is missing or ambiguous")
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
and {role.casefold() for role in actor.membership.roles}
|
|
419
|
-
& {"admin", "administrator", "owner", "founder"}
|
|
420
|
-
and self.authorize(actor, Permission.ADMINISTER, (target_artifact_id,)).allowed
|
|
421
|
-
)
|
|
495
|
+
admin = self.authorize(actor, Permission.ADMINISTER, (target_artifact_id,))
|
|
496
|
+
administer = is_admin(admin)
|
|
422
497
|
if self.lifecycle is None:
|
|
423
498
|
raise LifecycleError("canonical lifecycle service is unavailable")
|
|
424
499
|
transition = self.lifecycle.prepare_transition(
|
|
@@ -477,6 +552,8 @@ def local_runtime(
|
|
|
477
552
|
*,
|
|
478
553
|
push_remote: bool = True,
|
|
479
554
|
policy_factory: PolicyFactory | None = None,
|
|
555
|
+
identity: IdentityResolver | None = None,
|
|
556
|
+
observers: Sequence[Observer] | None = None,
|
|
480
557
|
) -> EgregoreRuntime:
|
|
481
558
|
"""Compose the Local architecture; all projections remain disposable."""
|
|
482
559
|
|
|
@@ -488,14 +565,12 @@ def local_runtime(
|
|
|
488
565
|
state_dir=Path.home() / ".egregore" / "runtime" / "lifecycle" / instance_id,
|
|
489
566
|
)
|
|
490
567
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
)
|
|
496
|
-
|
|
568
|
+
active_observers = list(observers) if observers is not None else [
|
|
569
|
+
*([record_observer(repository_root)] if record_observer is not None else []),
|
|
570
|
+
projection_observer(repository_root),
|
|
571
|
+
]
|
|
497
572
|
return EgregoreRuntime(
|
|
498
|
-
identity=LocalIdentityResolver(repository_root),
|
|
573
|
+
identity=identity if identity is not None else LocalIdentityResolver(repository_root),
|
|
499
574
|
retriever=QmdLocalRetriever(
|
|
500
575
|
repository_root=repository_root,
|
|
501
576
|
memory_root=memory_root,
|
|
@@ -511,9 +586,43 @@ def local_runtime(
|
|
|
511
586
|
lifecycle=lifecycle,
|
|
512
587
|
canonical_root=memory_root,
|
|
513
588
|
instance_root=repository_root,
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
589
|
+
observers=active_observers,
|
|
590
|
+
policy_factory=policy_factory or (lambda actor: policy_for(authorization_inputs(repository_root))),
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def authenticated_member_runtime(
|
|
595
|
+
root: Path,
|
|
596
|
+
*,
|
|
597
|
+
identity: AuthenticatedMemberResolver,
|
|
598
|
+
push_remote: bool = True,
|
|
599
|
+
policy_factory: PolicyFactory | None = None,
|
|
600
|
+
observers: Sequence[Observer] = (),
|
|
601
|
+
) -> EgregoreRuntime:
|
|
602
|
+
"""Compose current member authority over Local policy, without Local login.
|
|
603
|
+
|
|
604
|
+
The trusted host supplies an authenticated resolver. Missing/unavailable
|
|
605
|
+
verification never falls back to the machine owner's identity. This factory
|
|
606
|
+
does not issue grants, connect transports, or grant paid capabilities.
|
|
607
|
+
"""
|
|
608
|
+
from .member_authority import AuthenticatedMemberResolver, MemberAuthorityPolicy
|
|
609
|
+
|
|
610
|
+
if not isinstance(identity, AuthenticatedMemberResolver):
|
|
611
|
+
raise ValueError("authenticated member identity is required")
|
|
612
|
+
repository_root = runtime_root(root)
|
|
613
|
+
# Verify the configured workspace independently of the server response.
|
|
614
|
+
# Merely presenting a valid grant for another organization cannot select it.
|
|
615
|
+
try:
|
|
616
|
+
config = json.loads((repository_root / "egregore.json").read_text())
|
|
617
|
+
if config.get("org_id") != identity.expected.org_id:
|
|
618
|
+
raise ValueError("different organization")
|
|
619
|
+
except (OSError, ValueError, AttributeError):
|
|
620
|
+
raise ValueError("member authority does not match this workspace") from None
|
|
621
|
+
base = policy_factory or (lambda actor: policy_for(authorization_inputs(repository_root)))
|
|
622
|
+
return local_runtime(
|
|
623
|
+
repository_root, push_remote=push_remote, identity=identity,
|
|
624
|
+
policy_factory=lambda actor: MemberAuthorityPolicy(identity, base),
|
|
625
|
+
# Legacy projection refresh resolves Local identity during composition.
|
|
626
|
+
# Hosts opt into observers only after adapting their authority boundary.
|
|
627
|
+
observers=observers,
|
|
519
628
|
)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Sandbox markers, and where per-user state goes when it refuses writes.
|
|
2
|
+
|
|
3
|
+
A session started outside the ``egregore`` launcher can run inside Codex's
|
|
4
|
+
sandbox or a macOS app sandbox, where the per-user state under ``~/.egregore``
|
|
5
|
+
refuses writes. A sandbox is named only from positive markers, the predicate
|
|
6
|
+
``bin/lib/secret_entry.py`` and ``bin/reflexes/20-sandbox.sh`` also use; a
|
|
7
|
+
failing write alone never names one.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from contextlib import suppress
|
|
13
|
+
import errno
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
import stat
|
|
17
|
+
import tempfile
|
|
18
|
+
from typing import Mapping
|
|
19
|
+
|
|
20
|
+
# The errors that mean a location refuses writes. Anything else (a file where
|
|
21
|
+
# a directory belongs, a full disk) keeps its own failure: a fallback must not
|
|
22
|
+
# hide an unrelated fault.
|
|
23
|
+
REFUSED = frozenset({errno.EACCES, errno.EPERM, errno.EROFS})
|
|
24
|
+
REMEDY = "start through `egregore` to lift it"
|
|
25
|
+
# The workspace copy ignores itself, so it never reaches a commit in a
|
|
26
|
+
# checkout whose .gitignore predates it.
|
|
27
|
+
IGNORE_EVERYTHING = "# Egregore state for a sandboxed session\n*\n"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class FallbackRefused(Exception):
|
|
31
|
+
"""The workspace copy cannot be used; the message says why."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def sandboxed(environment: Mapping[str, str] | None = None) -> bool:
|
|
35
|
+
"""True when CODEX_SANDBOX or APP_SANDBOX_CONTAINER_ID says so."""
|
|
36
|
+
env = os.environ if environment is None else environment
|
|
37
|
+
return (str(env.get("CODEX_SANDBOX") or "").lower() not in {"", "none", "false", "0"}
|
|
38
|
+
or bool(env.get("APP_SANDBOX_CONTAINER_ID")))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def reason(error: OSError) -> str:
|
|
42
|
+
return (error.strerror or type(error).__name__).lower()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def write_probe(directory: Path) -> OSError | None:
|
|
46
|
+
"""Create and remove one file where ``directory`` is, or would be created.
|
|
47
|
+
|
|
48
|
+
The file goes in the nearest existing ancestor, so the probe creates no
|
|
49
|
+
directory. A file that cannot be removed fails the probe as surely as one
|
|
50
|
+
that cannot be created. Returns the error, or None when both succeeded.
|
|
51
|
+
"""
|
|
52
|
+
anchor = Path(directory)
|
|
53
|
+
while not os.path.exists(anchor) and anchor.parent != anchor:
|
|
54
|
+
anchor = anchor.parent
|
|
55
|
+
try:
|
|
56
|
+
descriptor, probe = tempfile.mkstemp(prefix=".egregore-write-probe-", dir=anchor)
|
|
57
|
+
except OSError as error:
|
|
58
|
+
return error
|
|
59
|
+
removal: OSError | None = None
|
|
60
|
+
try:
|
|
61
|
+
os.close(descriptor)
|
|
62
|
+
except OSError:
|
|
63
|
+
pass
|
|
64
|
+
finally:
|
|
65
|
+
try:
|
|
66
|
+
os.unlink(probe)
|
|
67
|
+
except OSError as error:
|
|
68
|
+
removal = error
|
|
69
|
+
return removal
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def display(path: Path, home: Path | None = None) -> str:
|
|
73
|
+
"""``path`` with the home directory written as ``~``.
|
|
74
|
+
|
|
75
|
+
Both the home as given and its resolved form count, so a resolved path
|
|
76
|
+
under a linked home does not show the physical directory.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
base = Path(home) if home is not None else Path.home()
|
|
80
|
+
except (KeyError, OSError, RuntimeError):
|
|
81
|
+
return str(path)
|
|
82
|
+
bases = [base]
|
|
83
|
+
with suppress(OSError, RuntimeError):
|
|
84
|
+
if base.resolve() != base:
|
|
85
|
+
bases.append(base.resolve())
|
|
86
|
+
for candidate in bases:
|
|
87
|
+
if candidate == candidate.parent:
|
|
88
|
+
continue
|
|
89
|
+
try:
|
|
90
|
+
relative = Path(path).relative_to(candidate)
|
|
91
|
+
except ValueError:
|
|
92
|
+
continue
|
|
93
|
+
return "~" if relative == Path(".") else f"~/{relative.as_posix()}"
|
|
94
|
+
return str(path)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _ignore_everything(directory: Path, home: Path | None) -> None:
|
|
98
|
+
"""Make ``directory/.gitignore`` a regular file that ignores everything.
|
|
99
|
+
|
|
100
|
+
A file that already says so is kept. Anything else (missing, empty,
|
|
101
|
+
edited, a link, not a file) is replaced through a temporary file and a
|
|
102
|
+
rename, which replaces a link itself and never writes through it.
|
|
103
|
+
"""
|
|
104
|
+
ignore = directory / ".gitignore"
|
|
105
|
+
current = None
|
|
106
|
+
flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0)
|
|
107
|
+
with suppress(OSError):
|
|
108
|
+
descriptor = os.open(ignore, flags)
|
|
109
|
+
with os.fdopen(descriptor, "rb") as handle:
|
|
110
|
+
if stat.S_ISREG(os.fstat(handle.fileno()).st_mode):
|
|
111
|
+
current = handle.read(len(IGNORE_EVERYTHING) + 1)
|
|
112
|
+
if current == IGNORE_EVERYTHING.encode():
|
|
113
|
+
return
|
|
114
|
+
try:
|
|
115
|
+
descriptor, temporary = tempfile.mkstemp(prefix=".gitignore.", dir=directory)
|
|
116
|
+
except OSError as error:
|
|
117
|
+
raise FallbackRefused(f"{display(ignore, home)} cannot be written ({reason(error)})") from error
|
|
118
|
+
try:
|
|
119
|
+
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
|
120
|
+
handle.write(IGNORE_EVERYTHING)
|
|
121
|
+
os.replace(temporary, ignore)
|
|
122
|
+
except OSError as error:
|
|
123
|
+
with suppress(OSError):
|
|
124
|
+
os.unlink(temporary)
|
|
125
|
+
raise FallbackRefused(f"{display(ignore, home)} cannot be written ({reason(error)})") from error
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _prepare(fallback: Path, workspace: Path, home: Path | None) -> None:
|
|
129
|
+
"""Create ``fallback`` inside ``workspace`` and make it ignore itself.
|
|
130
|
+
|
|
131
|
+
Raises FallbackRefused, naming why, when the copy would leave the
|
|
132
|
+
workspace (through a link under it or outright), or when it cannot be
|
|
133
|
+
created, marked ignored, or written.
|
|
134
|
+
"""
|
|
135
|
+
root = Path(workspace)
|
|
136
|
+
try:
|
|
137
|
+
relative = Path(fallback).relative_to(root)
|
|
138
|
+
except ValueError:
|
|
139
|
+
relative = None
|
|
140
|
+
if relative is None or not relative.parts:
|
|
141
|
+
# Never the workspace root itself: its .gitignore would hide the checkout.
|
|
142
|
+
raise FallbackRefused(f"{display(fallback, home)} is not inside the workspace")
|
|
143
|
+
current = root
|
|
144
|
+
for part in relative.parts:
|
|
145
|
+
current = current / part
|
|
146
|
+
if os.path.islink(current):
|
|
147
|
+
raise FallbackRefused(f"{display(current, home)} is a link")
|
|
148
|
+
try:
|
|
149
|
+
os.mkdir(current, 0o700)
|
|
150
|
+
except FileExistsError:
|
|
151
|
+
if os.path.islink(current) or not os.path.isdir(current):
|
|
152
|
+
raise FallbackRefused(f"{display(current, home)} is not a directory") from None
|
|
153
|
+
except OSError as error:
|
|
154
|
+
raise FallbackRefused(f"{display(current, home)} cannot be created ({reason(error)})") from error
|
|
155
|
+
try:
|
|
156
|
+
inside = Path(os.path.realpath(fallback)).is_relative_to(os.path.realpath(root))
|
|
157
|
+
except (OSError, ValueError):
|
|
158
|
+
inside = False
|
|
159
|
+
if not inside:
|
|
160
|
+
raise FallbackRefused(f"{display(fallback, home)} resolves outside the workspace")
|
|
161
|
+
_ignore_everything(Path(fallback), home)
|
|
162
|
+
# An ignore file that already matched proves nothing about new files: a
|
|
163
|
+
# folder may take subfolders yet refuse files (a macOS deny add_file ACL).
|
|
164
|
+
error = write_probe(Path(fallback))
|
|
165
|
+
if error is not None:
|
|
166
|
+
raise FallbackRefused(f"{display(fallback, home)} is not writable ({reason(error)})")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def state_directory(
|
|
170
|
+
preferred: Path,
|
|
171
|
+
fallback: Path,
|
|
172
|
+
*,
|
|
173
|
+
workspace: Path,
|
|
174
|
+
environment: Mapping[str, str] | None = None,
|
|
175
|
+
home: Path | None = None,
|
|
176
|
+
) -> tuple[Path, str | None]:
|
|
177
|
+
"""``preferred``, or ``fallback`` when the first refuses writes; and a warning.
|
|
178
|
+
|
|
179
|
+
The fallback is taken only when ``preferred`` refuses writes and the
|
|
180
|
+
workspace copy can be created inside ``workspace`` and marked ignored.
|
|
181
|
+
The warning names the cause, the sandbox when a marker is set and the
|
|
182
|
+
operating system's reason otherwise. When the copy is refused too, the
|
|
183
|
+
preferred directory stays, and the warning names both failures.
|
|
184
|
+
"""
|
|
185
|
+
error = write_probe(preferred)
|
|
186
|
+
if error is None or error.errno not in REFUSED:
|
|
187
|
+
return preferred, None
|
|
188
|
+
cause = "sandbox" if sandboxed(environment) else reason(error)
|
|
189
|
+
refused = f"per-user state at {display(preferred, home)} is not writable ({cause})"
|
|
190
|
+
try:
|
|
191
|
+
_prepare(fallback, workspace, home)
|
|
192
|
+
except FallbackRefused as why:
|
|
193
|
+
return preferred, f"{refused}; no workspace copy either: {why}"
|
|
194
|
+
return fallback, f"{refused}; using {display(fallback, home)}"
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
import os
|
|
8
8
|
from uuid import uuid4
|
|
9
9
|
|
|
10
|
+
from . import held
|
|
10
11
|
from .runtime import local_runtime, runtime_root
|
|
11
12
|
from .scratch import read_and_consume
|
|
12
13
|
from .scrolls import CanonicalScrollService, EXACT_SHARE_CONFIRMATION, ScrollError
|
|
@@ -45,8 +46,8 @@ def _receipts(rows):
|
|
|
45
46
|
|
|
46
47
|
def _show(args):
|
|
47
48
|
actor, service = _context()
|
|
48
|
-
|
|
49
|
-
return
|
|
49
|
+
row = _row(service.read(actor, args.slug))
|
|
50
|
+
return held.emit_for(service.runtime, actor, {"scroll": row}, paths=(row["canonical_path"],))
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def _create(args):
|
|
@@ -65,8 +66,9 @@ def _create(args):
|
|
|
65
66
|
attention_ttl_days=args.ttl_days,
|
|
66
67
|
supersedes=args.supersedes,
|
|
67
68
|
)
|
|
68
|
-
|
|
69
|
-
return
|
|
69
|
+
ok = all(row.status.value in {"accepted", "partial"} for row in receipts)
|
|
70
|
+
return held.emit_for(service.runtime, actor, {"scroll": _row(snapshot), "writeback": _receipts(receipts)}) or (
|
|
71
|
+
0 if ok else 1)
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def _event(args):
|
|
@@ -79,24 +81,18 @@ def _event(args):
|
|
|
79
81
|
face_markdown=read_and_consume(args.face, runtime_root()).decode("utf-8") if args.face else None,
|
|
80
82
|
source_html=args.html,
|
|
81
83
|
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
90
|
-
ensure_ascii=False,
|
|
91
|
-
)
|
|
92
|
-
)
|
|
93
|
-
return 0 if all(row.status.value in {"accepted", "partial"} for row in receipt.receipts) else 1
|
|
84
|
+
ok = all(row.status.value in {"accepted", "partial"} for row in receipt.receipts)
|
|
85
|
+
return held.emit_for(service.runtime, actor, {
|
|
86
|
+
"scroll": _row(receipt.scroll),
|
|
87
|
+
"event_id": receipt.event_id,
|
|
88
|
+
"disposition": receipt.disposition,
|
|
89
|
+
"writeback": _receipts(receipt.receipts),
|
|
90
|
+
}) or (0 if ok else 1)
|
|
94
91
|
|
|
95
92
|
|
|
96
93
|
def _ledger(args):
|
|
97
94
|
actor, service = _context()
|
|
98
|
-
|
|
99
|
-
return 0
|
|
95
|
+
return held.emit_for(service.runtime, actor, service.project_ledger(actor, args.slug))
|
|
100
96
|
|
|
101
97
|
|
|
102
98
|
def _plan_share(args):
|
|
@@ -104,8 +100,7 @@ def _plan_share(args):
|
|
|
104
100
|
plan = service.plan_share(
|
|
105
101
|
actor, slug=args.slug, html_path=args.html, description=args.description
|
|
106
102
|
)
|
|
107
|
-
|
|
108
|
-
return 0
|
|
103
|
+
return held.emit_for(service.runtime, actor, plan.to_dict())
|
|
109
104
|
|
|
110
105
|
|
|
111
106
|
def _share(args):
|
|
@@ -116,18 +111,13 @@ def _share(args):
|
|
|
116
111
|
digest=args.digest,
|
|
117
112
|
confirmation=args.confirm,
|
|
118
113
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
},
|
|
127
|
-
ensure_ascii=False,
|
|
128
|
-
)
|
|
129
|
-
)
|
|
130
|
-
return 0 if all(row.status.value in {"accepted", "partial"} for row in receipt.writeback) else 1
|
|
114
|
+
ok = all(row.status.value in {"accepted", "partial"} for row in receipt.writeback)
|
|
115
|
+
return held.emit_for(service.runtime, actor, {
|
|
116
|
+
"plan_id": receipt.plan_id,
|
|
117
|
+
"slug": receipt.slug,
|
|
118
|
+
"url": receipt.url,
|
|
119
|
+
"writeback": _receipts(receipt.writeback),
|
|
120
|
+
}) or (0 if ok else 1)
|
|
131
121
|
|
|
132
122
|
|
|
133
123
|
def _parser():
|