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
|
@@ -10,13 +10,16 @@ from __future__ import annotations
|
|
|
10
10
|
import hashlib
|
|
11
11
|
import hmac
|
|
12
12
|
import json
|
|
13
|
+
import math
|
|
13
14
|
import os
|
|
14
15
|
import re
|
|
15
16
|
import shutil
|
|
16
17
|
import signal
|
|
17
18
|
import secrets
|
|
18
19
|
import socket
|
|
20
|
+
import sqlite3
|
|
19
21
|
import stat
|
|
22
|
+
import struct
|
|
20
23
|
import subprocess
|
|
21
24
|
import sys
|
|
22
25
|
import time
|
|
@@ -24,7 +27,7 @@ import urllib.error
|
|
|
24
27
|
import urllib.parse
|
|
25
28
|
import urllib.request
|
|
26
29
|
import uuid
|
|
27
|
-
from contextlib import contextmanager
|
|
30
|
+
from contextlib import closing, contextmanager
|
|
28
31
|
from dataclasses import dataclass, replace
|
|
29
32
|
from datetime import UTC, datetime
|
|
30
33
|
from pathlib import Path
|
|
@@ -32,6 +35,7 @@ from typing import Any, Mapping, Sequence
|
|
|
32
35
|
|
|
33
36
|
import fcntl
|
|
34
37
|
|
|
38
|
+
from egregore_runtime.policy import within
|
|
35
39
|
from egregore_runtime.contracts import (
|
|
36
40
|
RetrievalHit,
|
|
37
41
|
RetrievalMode,
|
|
@@ -44,6 +48,7 @@ from egregore_runtime.artifacts import (
|
|
|
44
48
|
canonical_artifact_id,
|
|
45
49
|
split_frontmatter,
|
|
46
50
|
)
|
|
51
|
+
from egregore_runtime.sandbox import REFUSED, REMEDY, display, sandboxed, state_directory
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
QMD_VERSION = "2.8.3"
|
|
@@ -52,6 +57,10 @@ RUNTIME_SCHEMA_VERSION = "egregore-qmd-runtime/v1"
|
|
|
52
57
|
EMBEDDING_MODEL = "EmbeddingGemma"
|
|
53
58
|
MAX_AUTHORIZED_CANDIDATES = 2_000
|
|
54
59
|
MAX_SDK_PASSAGE_CHARACTERS = 4_000
|
|
60
|
+
# `qmd status` counts are reused only while the files they count are
|
|
61
|
+
# unchanged, and never for longer than this.
|
|
62
|
+
STATUS_COUNTS_SCHEMA = "egregore-qmd-status-counts/v1"
|
|
63
|
+
STATUS_COUNTS_MAX_AGE_SECONDS = 600
|
|
55
64
|
# Every query this adapter issues — daemon and CLI — disables model
|
|
56
65
|
# reranking; results come from typed lex/vec rank fusion only. Readiness
|
|
57
66
|
# surfaces report this posture from here rather than restating it.
|
|
@@ -149,6 +158,9 @@ class QmdLocalRetriever:
|
|
|
149
158
|
used by the pinned CLI fallback.
|
|
150
159
|
"""
|
|
151
160
|
|
|
161
|
+
# Known without a health check (which runs ``qmd status``): what telemetry names on every source open.
|
|
162
|
+
index_spec_version = INDEX_SPEC_VERSION
|
|
163
|
+
|
|
152
164
|
def __init__(
|
|
153
165
|
self,
|
|
154
166
|
*,
|
|
@@ -163,6 +175,12 @@ class QmdLocalRetriever:
|
|
|
163
175
|
configured_memory = memory_root or self.repository_root / "memory"
|
|
164
176
|
self.memory_root = configured_memory.resolve()
|
|
165
177
|
self.environment = dict(os.environ if environment is None else environment)
|
|
178
|
+
# Resolved once by _runtime_root(). The warning is set when the
|
|
179
|
+
# per-user state refuses writes: with the workspace copy in use, or
|
|
180
|
+
# naming why that copy was refused too.
|
|
181
|
+
self._state_root: Path | None = None
|
|
182
|
+
self._state_fallback = False
|
|
183
|
+
self._state_warning: str | None = None
|
|
166
184
|
self.collection_purpose = re.sub(
|
|
167
185
|
r"[^A-Za-z0-9_.-]", "-", collection_purpose.strip()
|
|
168
186
|
) or "memory"
|
|
@@ -180,7 +198,18 @@ class QmdLocalRetriever:
|
|
|
180
198
|
# retrieval is never silent.
|
|
181
199
|
self._last_query_used_daemon: bool | None = None
|
|
182
200
|
self._source_revision_cache: str | None = None
|
|
201
|
+
# The memory checkout's Git top level: fixed for the adapter's life.
|
|
202
|
+
self._git_top_level_cache: Path | None = None
|
|
203
|
+
# (source revision, Markdown revision): valid for that source revision only.
|
|
204
|
+
self._markdown_revision_cache: tuple[str, str] | None = None
|
|
183
205
|
self._configure_isolated_environment()
|
|
206
|
+
if self._state_fallback:
|
|
207
|
+
# The workspace copy belongs to one checkout: keep it lexical and
|
|
208
|
+
# cold, with no query worker or background embedding that would
|
|
209
|
+
# outlive the session or repeat in every worktree. Explicit
|
|
210
|
+
# settings still win.
|
|
211
|
+
self.environment.setdefault("EGREGORE_QMD_PERSISTENT", "0")
|
|
212
|
+
self.environment.setdefault("EGREGORE_SEARCH_NO_WARM", "1")
|
|
184
213
|
self._prefer_usable_node_runtime()
|
|
185
214
|
|
|
186
215
|
def _configure_isolated_environment(self) -> None:
|
|
@@ -327,8 +356,41 @@ class QmdLocalRetriever:
|
|
|
327
356
|
override = self.environment.get("EGREGORE_QMD_RUNTIME_DIR")
|
|
328
357
|
if override:
|
|
329
358
|
return Path(override).expanduser().resolve()
|
|
330
|
-
|
|
331
|
-
|
|
359
|
+
root = getattr(self, "_state_root", None)
|
|
360
|
+
if root is None:
|
|
361
|
+
# Resolved on first use, so every construction path works: per-user
|
|
362
|
+
# state unless it refuses writes (a sandboxed session), then this
|
|
363
|
+
# checkout's own .egregore/runtime, and a warning. An adapter built
|
|
364
|
+
# for its path rules alone (vectors_receipt) has no workspace, so it
|
|
365
|
+
# neither probes nor falls back.
|
|
366
|
+
preferred = self._user_runtime_root()
|
|
367
|
+
workspace = getattr(self, "repository_root", None)
|
|
368
|
+
warning = None
|
|
369
|
+
if workspace is None:
|
|
370
|
+
root = preferred
|
|
371
|
+
else:
|
|
372
|
+
root, warning = state_directory(
|
|
373
|
+
preferred,
|
|
374
|
+
workspace / ".egregore" / "runtime" / "qmd" / self._instance_hash(),
|
|
375
|
+
workspace=workspace,
|
|
376
|
+
environment=self.environment,
|
|
377
|
+
home=self._home(),
|
|
378
|
+
)
|
|
379
|
+
self._state_root, self._state_warning = root, warning
|
|
380
|
+
self._state_fallback = root != preferred
|
|
381
|
+
return root
|
|
382
|
+
|
|
383
|
+
def _home(self) -> Path:
|
|
384
|
+
return Path(self.environment.get("HOME", str(Path.home()))).expanduser()
|
|
385
|
+
|
|
386
|
+
def _user_runtime_root(self) -> Path:
|
|
387
|
+
return self._home() / ".egregore" / "runtime" / "qmd" / self._instance_hash()
|
|
388
|
+
|
|
389
|
+
def _first_warnings(self) -> list[str]:
|
|
390
|
+
"""The state warning, which every health and result names first."""
|
|
391
|
+
self._runtime_root()
|
|
392
|
+
warning = getattr(self, "_state_warning", None)
|
|
393
|
+
return [warning] if warning else []
|
|
332
394
|
|
|
333
395
|
def _runtime_state_path(self) -> Path:
|
|
334
396
|
return self._runtime_root() / "runtime.json"
|
|
@@ -364,11 +426,19 @@ class QmdLocalRetriever:
|
|
|
364
426
|
index_spec_version=INDEX_SPEC_VERSION,
|
|
365
427
|
index_revision="unavailable", source_revision="unavailable",
|
|
366
428
|
embedding_state="unavailable", runtime_state="runtime_unavailable",
|
|
367
|
-
warnings=(self._filesystem_recovery_message(exc)
|
|
429
|
+
warnings=(*self._first_warnings(), self._filesystem_recovery_message(exc)),
|
|
368
430
|
)
|
|
369
431
|
|
|
370
|
-
|
|
371
|
-
|
|
432
|
+
def _filesystem_recovery_message(self, exc: OSError) -> str:
|
|
433
|
+
if getattr(exc, "errno", None) in REFUSED and sandboxed(self.environment):
|
|
434
|
+
# The cause and the remedy, instead of a lock path.
|
|
435
|
+
if exc.filename:
|
|
436
|
+
return (
|
|
437
|
+
f"Local retrieval storage at {display(self._runtime_root(), self._home())} "
|
|
438
|
+
f"is not writable (sandbox); {REMEDY}."
|
|
439
|
+
)
|
|
440
|
+
return (f"Local retrieval was refused by the sandbox "
|
|
441
|
+
f"({exc.strerror or type(exc).__name__}); {REMEDY}.")
|
|
372
442
|
return (
|
|
373
443
|
f"Local retrieval storage is unavailable ({type(exc).__name__}: {exc}). "
|
|
374
444
|
"Provision the instance cache with private permissions and the host's "
|
|
@@ -376,7 +446,7 @@ class QmdLocalRetriever:
|
|
|
376
446
|
)
|
|
377
447
|
|
|
378
448
|
def _health(self) -> RetrieverHealth:
|
|
379
|
-
warnings
|
|
449
|
+
warnings = self._first_warnings()
|
|
380
450
|
canonical_ready = self.memory_root.is_dir()
|
|
381
451
|
qmd_available = True
|
|
382
452
|
if not canonical_ready:
|
|
@@ -389,17 +459,22 @@ class QmdLocalRetriever:
|
|
|
389
459
|
qmd_available = False
|
|
390
460
|
warnings.append(str(exc))
|
|
391
461
|
|
|
392
|
-
|
|
462
|
+
counts = self._status_counts("")
|
|
393
463
|
status_ok = False
|
|
394
464
|
if canonical_ready and qmd_available:
|
|
395
|
-
|
|
396
|
-
if
|
|
397
|
-
|
|
398
|
-
status_ok = True
|
|
465
|
+
remembered = self._remembered_status_counts()
|
|
466
|
+
if remembered is not None:
|
|
467
|
+
counts, status_ok = remembered, True
|
|
399
468
|
else:
|
|
400
|
-
|
|
469
|
+
observed = self._status_identity()
|
|
470
|
+
completed = self._run(("status",), timeout=20)
|
|
471
|
+
if completed.returncode == 0:
|
|
472
|
+
counts = self._status_counts(completed.stdout)
|
|
473
|
+
status_ok = True
|
|
474
|
+
self._remember_status_counts(observed, counts)
|
|
475
|
+
else:
|
|
476
|
+
warnings.append(self._failure_message("qmd status", completed))
|
|
401
477
|
|
|
402
|
-
counts = self._status_counts(status)
|
|
403
478
|
# BM25 readiness is usability, mirroring the semantic split: a
|
|
404
479
|
# canonical write leaves the index trailing, not unusable — queries
|
|
405
480
|
# refresh it inline. Only an identity mismatch makes it stale.
|
|
@@ -420,7 +495,7 @@ class QmdLocalRetriever:
|
|
|
420
495
|
and self._semantic_state_current()
|
|
421
496
|
and not self._live_lock(self._embed_lock_path())
|
|
422
497
|
)
|
|
423
|
-
embedding_state = self._embedding_state(
|
|
498
|
+
embedding_state = self._embedding_state(counts, semantic_ready=semantic_ready)
|
|
424
499
|
if embedding_state == "missing_model":
|
|
425
500
|
if counts["vectors"] > 0:
|
|
426
501
|
warnings.append(
|
|
@@ -533,12 +608,12 @@ class QmdLocalRetriever:
|
|
|
533
608
|
"""Canonical metadata for a path already authorized by Runtime."""
|
|
534
609
|
return self._metadata(canonical_path)
|
|
535
610
|
|
|
536
|
-
def retrieve_authorized(self, request, *, actor,
|
|
611
|
+
def retrieve_authorized(self, request, *, actor, decision, lifecycle=None,
|
|
537
612
|
path_filter=None, metadata_filter=None):
|
|
538
613
|
from ..eligibility import build_eligibility
|
|
539
614
|
try:
|
|
540
615
|
return self._retrieve(request, eligibility_builder=lambda: build_eligibility(
|
|
541
|
-
self, request, actor=actor,
|
|
616
|
+
self, request, actor=actor, decision=decision, lifecycle=lifecycle,
|
|
542
617
|
path_filter=path_filter, metadata_filter=metadata_filter))
|
|
543
618
|
except OSError as exc:
|
|
544
619
|
raise QmdAdapterError(self._filesystem_recovery_message(exc)) from exc
|
|
@@ -560,7 +635,7 @@ class QmdLocalRetriever:
|
|
|
560
635
|
|
|
561
636
|
def _retrieve(self, request: RetrievalRequest, *, eligibility_builder=None) -> RetrievalResult:
|
|
562
637
|
started = time.monotonic()
|
|
563
|
-
warnings
|
|
638
|
+
warnings = self._first_warnings()
|
|
564
639
|
# A long-lived runtime may observe a new canonical Git revision between
|
|
565
640
|
# requests. Cache only within one retrieval operation.
|
|
566
641
|
self._source_revision_cache = None
|
|
@@ -690,6 +765,11 @@ class QmdLocalRetriever:
|
|
|
690
765
|
# spawns for an unchanged corpus; any revision change (including
|
|
691
766
|
# a dirty-tree hash change) still takes the full update path.
|
|
692
767
|
return self.health()
|
|
768
|
+
if not force and self._index_markdown_current():
|
|
769
|
+
# Only files the index never reads changed (a taste event, a
|
|
770
|
+
# YAML registry): record the new revision without a rescan.
|
|
771
|
+
self._write_index_state()
|
|
772
|
+
return self.health()
|
|
693
773
|
# A canonical write makes the semantic build stale, never unusable:
|
|
694
774
|
# staleness is expressed by revision inequality alone, and the last
|
|
695
775
|
# complete vector receipt persists so hybrid keeps serving while the
|
|
@@ -941,7 +1021,7 @@ class QmdLocalRetriever:
|
|
|
941
1021
|
accepted: list[tuple[Mapping[str, Any], str, _DocumentMetadata]] = []
|
|
942
1022
|
for raw in raw_hits:
|
|
943
1023
|
canonical_path = self._canonical_path(str(raw.get("file", "")))
|
|
944
|
-
if canonical_path is None or not
|
|
1024
|
+
if canonical_path is None or not within(canonical_path, request.authorized_scopes, root=self.memory_root):
|
|
945
1025
|
continue
|
|
946
1026
|
if metadata_by_path is not None and canonical_path not in metadata_by_path:
|
|
947
1027
|
raise QmdAdapterError('scoped QMD returned a document outside its eligible set')
|
|
@@ -999,6 +1079,9 @@ class QmdLocalRetriever:
|
|
|
999
1079
|
self._ensure_version()
|
|
1000
1080
|
if self._index_state_current():
|
|
1001
1081
|
return
|
|
1082
|
+
if self._index_markdown_current():
|
|
1083
|
+
self._write_index_state()
|
|
1084
|
+
return
|
|
1002
1085
|
self._ensure_collection(force=True)
|
|
1003
1086
|
completed = self._run(("update",), timeout=300)
|
|
1004
1087
|
if completed.returncode != 0:
|
|
@@ -1133,6 +1216,14 @@ class QmdLocalRetriever:
|
|
|
1133
1216
|
instance = self._managed_prefix() / "node_modules" / ".bin" / "qmd"
|
|
1134
1217
|
if instance.is_file():
|
|
1135
1218
|
return instance
|
|
1219
|
+
if getattr(self, "_state_fallback", False):
|
|
1220
|
+
# The workspace copy runs the per-user install read-only: a
|
|
1221
|
+
# sandbox that refuses writes there also refuses the network an
|
|
1222
|
+
# install needs.
|
|
1223
|
+
provisioned = (self._user_runtime_root() / f"qmd-{QMD_VERSION}"
|
|
1224
|
+
/ "node_modules" / ".bin" / "qmd")
|
|
1225
|
+
if provisioned.is_file():
|
|
1226
|
+
return provisioned
|
|
1136
1227
|
# Existing installs provisioned the shared, version-addressed prefix;
|
|
1137
1228
|
# keep resolving it so an upgrade is adoption, never breakage.
|
|
1138
1229
|
legacy = Path.home() / ".egregore" / "runtime" / f"qmd-{QMD_VERSION}"
|
|
@@ -1490,7 +1581,7 @@ class QmdLocalRetriever:
|
|
|
1490
1581
|
relative = str(candidate.relative_to(self.memory_root))
|
|
1491
1582
|
except ValueError:
|
|
1492
1583
|
return None
|
|
1493
|
-
relative = relative.
|
|
1584
|
+
relative = relative.lstrip("/")
|
|
1494
1585
|
if not relative or ".." in Path(relative).parts:
|
|
1495
1586
|
return None
|
|
1496
1587
|
return f"memory/{relative}"
|
|
@@ -1605,23 +1696,17 @@ class QmdLocalRetriever:
|
|
|
1605
1696
|
title = normalized(metadata.title)
|
|
1606
1697
|
return len(title) >= 8 and title in normalized(task)
|
|
1607
1698
|
|
|
1608
|
-
def _scope_allows(self, canonical_path: str, request: RetrievalRequest) -> bool:
|
|
1609
|
-
# Fail closed: a request that carries no authorized scope gets no
|
|
1610
|
-
# hits. Observe always sets scopes; a direct caller without them is
|
|
1611
|
-
# unauthorized by construction, not universally entitled.
|
|
1612
|
-
if not request.authorized_scopes:
|
|
1613
|
-
return False
|
|
1614
|
-
for raw_scope in request.authorized_scopes:
|
|
1615
|
-
scope = raw_scope.strip().replace("\\", "/").rstrip("/")
|
|
1616
|
-
if scope == "memory":
|
|
1617
|
-
return True
|
|
1618
|
-
if not scope.startswith("memory/"):
|
|
1619
|
-
scope = f"memory/{scope.lstrip('/')}"
|
|
1620
|
-
if canonical_path == scope or canonical_path.startswith(f"{scope}/"):
|
|
1621
|
-
return True
|
|
1622
|
-
return False
|
|
1623
|
-
|
|
1624
1699
|
def _source_revision(self) -> str:
|
|
1700
|
+
"""The complete canonical revision: every cache and receipt keys on it.
|
|
1701
|
+
|
|
1702
|
+
A clean tree is its commit. A dirty tree is the commit plus a digest
|
|
1703
|
+
of the Markdown paths Git reports modified, deleted, renamed,
|
|
1704
|
+
untracked or ignored, with their current contents: the commit already
|
|
1705
|
+
pins every other file, so the digest need not read the whole corpus.
|
|
1706
|
+
Where Git's report cannot see what the index reads (a symbolic link,
|
|
1707
|
+
a submodule) or Git cannot report at all, every Markdown file is
|
|
1708
|
+
hashed instead.
|
|
1709
|
+
"""
|
|
1625
1710
|
if self._source_revision_cache is not None:
|
|
1626
1711
|
return self._source_revision_cache
|
|
1627
1712
|
completed = self._run_raw(
|
|
@@ -1629,22 +1714,174 @@ class QmdLocalRetriever:
|
|
|
1629
1714
|
)
|
|
1630
1715
|
if completed.returncode == 0 and completed.stdout.strip():
|
|
1631
1716
|
head = completed.stdout.strip()
|
|
1632
|
-
changes = self.
|
|
1633
|
-
|
|
1634
|
-
"git", "-C", str(self.memory_root), "status", "--porcelain=v1",
|
|
1635
|
-
"--untracked-files=all", "--ignored=matching", "--", "*.md",
|
|
1636
|
-
),
|
|
1637
|
-
timeout=30,
|
|
1638
|
-
)
|
|
1639
|
-
if changes.returncode == 0 and not changes.stdout.strip():
|
|
1717
|
+
changes = self._markdown_changes()
|
|
1718
|
+
if changes is not None and not changes:
|
|
1640
1719
|
revision = f"git:{head}"
|
|
1641
|
-
|
|
1720
|
+
elif changes is None or not self._git_report_covers_markdown():
|
|
1642
1721
|
revision = f"git:{head}+dirty:{self._markdown_manifest_hash()}"
|
|
1722
|
+
else:
|
|
1723
|
+
revision = f"git:{head}+dirty:{self._changed_markdown_hash(changes)}"
|
|
1643
1724
|
else:
|
|
1644
1725
|
revision = f"filesystem:{self._markdown_manifest_hash()}"
|
|
1645
1726
|
self._source_revision_cache = revision
|
|
1646
1727
|
return revision
|
|
1647
1728
|
|
|
1729
|
+
def _markdown_changes(self) -> tuple[str, ...] | None:
|
|
1730
|
+
"""The Markdown paths Git reports changed, untracked or ignored.
|
|
1731
|
+
|
|
1732
|
+
Both sides of a rename or copy are named. A directory Git does not
|
|
1733
|
+
descend into (an ignored folder, a nested repository) keeps its
|
|
1734
|
+
trailing slash. Paths are relative to the repository's top level.
|
|
1735
|
+
None when Git cannot report.
|
|
1736
|
+
"""
|
|
1737
|
+
command = (
|
|
1738
|
+
"git", "-C", str(self.memory_root), "status", "--porcelain=v1", "-z",
|
|
1739
|
+
"--untracked-files=all", "--ignored=matching", "--", "*.md",
|
|
1740
|
+
)
|
|
1741
|
+
try:
|
|
1742
|
+
completed = subprocess.run(
|
|
1743
|
+
command,
|
|
1744
|
+
cwd=self.repository_root,
|
|
1745
|
+
env=self.environment,
|
|
1746
|
+
capture_output=True,
|
|
1747
|
+
timeout=30,
|
|
1748
|
+
check=False,
|
|
1749
|
+
umask=0o077,
|
|
1750
|
+
)
|
|
1751
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
1752
|
+
return None
|
|
1753
|
+
if completed.returncode != 0:
|
|
1754
|
+
return None
|
|
1755
|
+
fields = completed.stdout.split(b"\0")
|
|
1756
|
+
paths: list[str] = []
|
|
1757
|
+
position = 0
|
|
1758
|
+
while position < len(fields):
|
|
1759
|
+
entry = fields[position]
|
|
1760
|
+
position += 1
|
|
1761
|
+
if not entry:
|
|
1762
|
+
continue
|
|
1763
|
+
if len(entry) < 4 or entry[2:3] != b" ":
|
|
1764
|
+
return None
|
|
1765
|
+
paths.append(os.fsdecode(entry[3:]))
|
|
1766
|
+
if b"R" in entry[:2] or b"C" in entry[:2]:
|
|
1767
|
+
if position >= len(fields) or not fields[position]:
|
|
1768
|
+
return None
|
|
1769
|
+
paths.append(os.fsdecode(fields[position]))
|
|
1770
|
+
position += 1
|
|
1771
|
+
return tuple(paths)
|
|
1772
|
+
|
|
1773
|
+
def _git_report_covers_markdown(self) -> bool:
|
|
1774
|
+
"""Git's change report sees every change to what the index reads:
|
|
1775
|
+
no tracked link and no submodule can hide one."""
|
|
1776
|
+
top = self._git_top_level()
|
|
1777
|
+
if top is None or (top / ".gitmodules").exists():
|
|
1778
|
+
return False
|
|
1779
|
+
return not self._markdown_reads_through_a_link()
|
|
1780
|
+
|
|
1781
|
+
def _git_top_level(self) -> Path | None:
|
|
1782
|
+
cached = getattr(self, "_git_top_level_cache", None)
|
|
1783
|
+
if cached is not None:
|
|
1784
|
+
return cached
|
|
1785
|
+
completed = self._run_raw(
|
|
1786
|
+
("git", "-C", str(self.memory_root), "rev-parse", "--show-toplevel"), timeout=10
|
|
1787
|
+
)
|
|
1788
|
+
if completed.returncode != 0 or not completed.stdout.strip():
|
|
1789
|
+
return None
|
|
1790
|
+
self._git_top_level_cache = Path(completed.stdout.strip()).resolve()
|
|
1791
|
+
return self._git_top_level_cache
|
|
1792
|
+
|
|
1793
|
+
def _changed_markdown_hash(self, changes: Sequence[str]) -> str:
|
|
1794
|
+
top = self._git_top_level()
|
|
1795
|
+
if top is None:
|
|
1796
|
+
return self._markdown_manifest_hash()
|
|
1797
|
+
reported: dict[str, Path | None] = {}
|
|
1798
|
+
for change in changes:
|
|
1799
|
+
path = top / change
|
|
1800
|
+
if not change.endswith("/"):
|
|
1801
|
+
reported[change] = path
|
|
1802
|
+
continue
|
|
1803
|
+
# Git names the directory, not the Markdown inside it.
|
|
1804
|
+
reported[change] = None
|
|
1805
|
+
if self.memory_root.is_relative_to(path):
|
|
1806
|
+
path = self.memory_root
|
|
1807
|
+
elif not path.is_relative_to(self.memory_root):
|
|
1808
|
+
continue
|
|
1809
|
+
for source in path.rglob("*.md"):
|
|
1810
|
+
reported.setdefault(source.relative_to(top).as_posix(), source)
|
|
1811
|
+
digest = hashlib.sha256()
|
|
1812
|
+
|
|
1813
|
+
def framed(kind: bytes, payload: bytes = b"") -> None:
|
|
1814
|
+
# A kind byte and a length frame each field, so no file content
|
|
1815
|
+
# can pass for a marker (a deleted file) or for the next path.
|
|
1816
|
+
digest.update(kind + len(payload).to_bytes(8, "big") + payload)
|
|
1817
|
+
|
|
1818
|
+
for relative in sorted(reported):
|
|
1819
|
+
source = reported[relative]
|
|
1820
|
+
framed(b"P", relative.encode("utf-8", "surrogateescape"))
|
|
1821
|
+
if source is None:
|
|
1822
|
+
framed(b"D")
|
|
1823
|
+
elif not os.path.lexists(source):
|
|
1824
|
+
framed(b"A")
|
|
1825
|
+
elif not source.is_file() or not source.resolve().is_relative_to(self.memory_root):
|
|
1826
|
+
framed(b"N")
|
|
1827
|
+
else:
|
|
1828
|
+
try:
|
|
1829
|
+
framed(b"F", source.read_bytes())
|
|
1830
|
+
except OSError:
|
|
1831
|
+
framed(b"U")
|
|
1832
|
+
return digest.hexdigest()[:20]
|
|
1833
|
+
|
|
1834
|
+
def _markdown_revision(self) -> str:
|
|
1835
|
+
"""What the index reads, as a revision: used only to decide whether
|
|
1836
|
+
QMD must rescan.
|
|
1837
|
+
|
|
1838
|
+
It names the newest commit that changed a Markdown file, so a commit
|
|
1839
|
+
of other canonical files (a taste event, a YAML registry) does not
|
|
1840
|
+
send QMD over an unchanged corpus. When the index could read through
|
|
1841
|
+
a symbolic link, any commit may change what it reads, so the complete
|
|
1842
|
+
revision decides. Caches and receipts never key on this one.
|
|
1843
|
+
"""
|
|
1844
|
+
source = self._source_revision()
|
|
1845
|
+
cached = self._markdown_revision_cache
|
|
1846
|
+
if cached is not None and cached[0] == source:
|
|
1847
|
+
return cached[1]
|
|
1848
|
+
revision = source
|
|
1849
|
+
if source.startswith("git:") and not self._markdown_reads_through_a_link():
|
|
1850
|
+
head, plus, dirty = source[len("git:"):].partition("+")
|
|
1851
|
+
completed = self._run_raw(
|
|
1852
|
+
("git", "-C", str(self.memory_root), "rev-list", "-1", head, "--", "*.md"),
|
|
1853
|
+
timeout=10,
|
|
1854
|
+
)
|
|
1855
|
+
if completed.returncode == 0 and completed.stdout.strip():
|
|
1856
|
+
revision = f"git:{completed.stdout.strip()}{plus}{dirty}"
|
|
1857
|
+
self._markdown_revision_cache = (source, revision)
|
|
1858
|
+
return revision
|
|
1859
|
+
|
|
1860
|
+
def _markdown_reads_through_a_link(self) -> bool:
|
|
1861
|
+
"""The repository tracks a symbolic link, or a tracked Markdown path
|
|
1862
|
+
lies below a directory that is one on disk; unknown counts as yes."""
|
|
1863
|
+
listed = self._run_raw(
|
|
1864
|
+
("git", "-C", str(self.memory_root), "ls-files", "-s", "-z"), timeout=30,
|
|
1865
|
+
)
|
|
1866
|
+
if listed.returncode != 0:
|
|
1867
|
+
return True
|
|
1868
|
+
# Plain strings, not Path objects: this runs on every dirty revision.
|
|
1869
|
+
root = str(self.memory_root)
|
|
1870
|
+
checked: set[str] = set()
|
|
1871
|
+
for entry in listed.stdout.split("\0"):
|
|
1872
|
+
mode, _, name = entry.partition("\t")
|
|
1873
|
+
if mode.startswith("120000 "):
|
|
1874
|
+
return True
|
|
1875
|
+
if not name.endswith(".md"):
|
|
1876
|
+
continue
|
|
1877
|
+
parent = name.rpartition("/")[0]
|
|
1878
|
+
while parent and parent not in checked:
|
|
1879
|
+
if os.path.islink(os.path.join(root, parent)):
|
|
1880
|
+
return True
|
|
1881
|
+
checked.add(parent)
|
|
1882
|
+
parent = parent.rpartition("/")[0]
|
|
1883
|
+
return False
|
|
1884
|
+
|
|
1648
1885
|
def _markdown_manifest_hash(self) -> str:
|
|
1649
1886
|
digest = hashlib.sha256()
|
|
1650
1887
|
for source in sorted(self.memory_root.rglob("*.md")):
|
|
@@ -1694,6 +1931,9 @@ class QmdLocalRetriever:
|
|
|
1694
1931
|
state = json.loads(self._index_state_path().read_text(encoding="utf-8"))
|
|
1695
1932
|
except (OSError, ValueError, TypeError):
|
|
1696
1933
|
return False
|
|
1934
|
+
if not isinstance(state, dict):
|
|
1935
|
+
return False
|
|
1936
|
+
state.pop("markdown_revision", None)
|
|
1697
1937
|
return state == {
|
|
1698
1938
|
"adapter_version": QMD_VERSION,
|
|
1699
1939
|
"collection": self.collection,
|
|
@@ -1705,6 +1945,18 @@ class QmdLocalRetriever:
|
|
|
1705
1945
|
"source_revision": self._source_revision(),
|
|
1706
1946
|
}
|
|
1707
1947
|
|
|
1948
|
+
def _index_markdown_current(self) -> bool:
|
|
1949
|
+
"""The index already holds what it reads at the current revision:
|
|
1950
|
+
only files it never reads changed since it was built."""
|
|
1951
|
+
if not self._index_state_usable():
|
|
1952
|
+
return False
|
|
1953
|
+
try:
|
|
1954
|
+
state = json.loads(self._index_state_path().read_text(encoding="utf-8"))
|
|
1955
|
+
except (OSError, ValueError, TypeError):
|
|
1956
|
+
return False
|
|
1957
|
+
recorded = state.get("markdown_revision") if isinstance(state, dict) else None
|
|
1958
|
+
return bool(recorded) and recorded == self._markdown_revision()
|
|
1959
|
+
|
|
1708
1960
|
def _index_state_usable(self) -> bool:
|
|
1709
1961
|
"""The BM25 index exists for this exact identity, revision aside.
|
|
1710
1962
|
|
|
@@ -1751,6 +2003,7 @@ class QmdLocalRetriever:
|
|
|
1751
2003
|
"index_spec_version": INDEX_SPEC_VERSION,
|
|
1752
2004
|
"memory_root": str(self.memory_root),
|
|
1753
2005
|
"source_revision": self._source_revision(),
|
|
2006
|
+
"markdown_revision": self._markdown_revision(),
|
|
1754
2007
|
}
|
|
1755
2008
|
self._atomic_json(state_path, state)
|
|
1756
2009
|
|
|
@@ -1770,6 +2023,78 @@ class QmdLocalRetriever:
|
|
|
1770
2023
|
"pending": count(r"Pending:\s*(\d+)"),
|
|
1771
2024
|
}
|
|
1772
2025
|
|
|
2026
|
+
# -- Remembered status counts --------------------------------------------
|
|
2027
|
+
#
|
|
2028
|
+
# `qmd status` starts Node to count what the index database holds, and a
|
|
2029
|
+
# startup pays for it more than once (start, the embedding snapshot, the
|
|
2030
|
+
# launcher's follow-up status). The counts are a function of the database
|
|
2031
|
+
# files, so they are kept with the identity of everything they depend on
|
|
2032
|
+
# and reused only while that identity is unchanged and recent.
|
|
2033
|
+
|
|
2034
|
+
def _status_counts_path(self) -> Path:
|
|
2035
|
+
return self._runtime_root() / "status-counts.json"
|
|
2036
|
+
|
|
2037
|
+
def _status_identity(self) -> dict[str, Any]:
|
|
2038
|
+
"""The index database and its write-ahead log, QMD's index
|
|
2039
|
+
configuration, this adapter's index and vector receipts, and the
|
|
2040
|
+
executables that would answer `qmd status`."""
|
|
2041
|
+
|
|
2042
|
+
def version(path: str | Path | None) -> list[int] | None:
|
|
2043
|
+
if not path:
|
|
2044
|
+
return None
|
|
2045
|
+
try:
|
|
2046
|
+
found = os.stat(path)
|
|
2047
|
+
except OSError:
|
|
2048
|
+
return None
|
|
2049
|
+
return [found.st_ino, found.st_size, found.st_mtime_ns]
|
|
2050
|
+
|
|
2051
|
+
index = self._index_path()
|
|
2052
|
+
command = self._command()
|
|
2053
|
+
executable = shutil.which(command[0], path=self.environment.get("PATH")) or command[0]
|
|
2054
|
+
node = self.environment.get("EGREGORE_QMD_NODE_BIN", "")
|
|
2055
|
+
config_dir = self.environment.get("QMD_CONFIG_DIR")
|
|
2056
|
+
config = Path(config_dir) / f"{self.index_name}.yml" if config_dir else None
|
|
2057
|
+
return {
|
|
2058
|
+
"schema": STATUS_COUNTS_SCHEMA,
|
|
2059
|
+
"qmd": [QMD_VERSION, INDEX_SPEC_VERSION, self.index_name, self.collection],
|
|
2060
|
+
# Every file argument too: `node /path/qmd.js` runs the script.
|
|
2061
|
+
"command": [*command, version(executable), [version(part) for part in command[1:]],
|
|
2062
|
+
node, version(node)],
|
|
2063
|
+
"database": version(index),
|
|
2064
|
+
"wal": version(f"{index}-wal"),
|
|
2065
|
+
"config": version(config),
|
|
2066
|
+
"receipts": [version(self._index_state_path()), version(self._semantic_state_path())],
|
|
2067
|
+
"index_path": str(index),
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
def _remembered_status_counts(self) -> dict[str, int] | None:
|
|
2071
|
+
try:
|
|
2072
|
+
stored = json.loads(self._status_counts_path().read_text(encoding="utf-8"))
|
|
2073
|
+
if not 0 <= time.time() - float(stored["observed_at"]) <= STATUS_COUNTS_MAX_AGE_SECONDS:
|
|
2074
|
+
return None
|
|
2075
|
+
if stored["identity"] != self._status_identity():
|
|
2076
|
+
return None
|
|
2077
|
+
return {key: int(stored["counts"][key]) for key in ("documents", "vectors", "pending")}
|
|
2078
|
+
except (OSError, ValueError, TypeError, KeyError):
|
|
2079
|
+
return None
|
|
2080
|
+
|
|
2081
|
+
def _remember_status_counts(self, observed: dict[str, Any], counts: Mapping[str, int]) -> None:
|
|
2082
|
+
try:
|
|
2083
|
+
if observed != self._status_identity():
|
|
2084
|
+
return # the database moved while QMD counted it
|
|
2085
|
+
# Whole-second timestamps cannot tell two writes in one second
|
|
2086
|
+
# apart: wait until the newest is safely in the past.
|
|
2087
|
+
stamps = [observed[name][2] for name in ("database", "wal") if observed[name]]
|
|
2088
|
+
if stamps and all(stamp % 1_000_000_000 == 0 for stamp in stamps) \
|
|
2089
|
+
and time.time_ns() - max(stamps) < 2_000_000_000:
|
|
2090
|
+
return
|
|
2091
|
+
self._atomic_json(
|
|
2092
|
+
self._status_counts_path(),
|
|
2093
|
+
{"identity": observed, "counts": dict(counts), "observed_at": time.time()},
|
|
2094
|
+
)
|
|
2095
|
+
except OSError:
|
|
2096
|
+
pass # remembering is an optimization; health never fails on it
|
|
2097
|
+
|
|
1773
2098
|
def _semantic_state_path(self) -> Path:
|
|
1774
2099
|
return self._runtime_root() / "semantic-state.json"
|
|
1775
2100
|
|
|
@@ -1841,7 +2166,7 @@ class QmdLocalRetriever:
|
|
|
1841
2166
|
},
|
|
1842
2167
|
)
|
|
1843
2168
|
|
|
1844
|
-
def _embedding_state(self,
|
|
2169
|
+
def _embedding_state(self, counts: Mapping[str, int], *, semantic_ready: bool = False) -> str:
|
|
1845
2170
|
if self._live_lock(self._embed_lock_path()):
|
|
1846
2171
|
return "running"
|
|
1847
2172
|
# Model presence outranks stored-vector completeness: query-time
|
|
@@ -1851,7 +2176,6 @@ class QmdLocalRetriever:
|
|
|
1851
2176
|
return "missing_model"
|
|
1852
2177
|
if semantic_ready:
|
|
1853
2178
|
return "ready"
|
|
1854
|
-
counts = self._status_counts(status)
|
|
1855
2179
|
if counts["pending"] > 0 or counts["documents"] > 0:
|
|
1856
2180
|
return "pending"
|
|
1857
2181
|
return "not_built"
|
|
@@ -1998,6 +2322,154 @@ class QmdLocalRetriever:
|
|
|
1998
2322
|
return f"{operation} failed ({completed.returncode}): {detail[:400]}"
|
|
1999
2323
|
|
|
2000
2324
|
|
|
2325
|
+
def read_document_vectors(
|
|
2326
|
+
repository_root: Path,
|
|
2327
|
+
paths: Sequence[str],
|
|
2328
|
+
dimension_prefix: int = 256,
|
|
2329
|
+
*,
|
|
2330
|
+
memory_root: Path | None = None,
|
|
2331
|
+
environment: Mapping[str, str] | None = None,
|
|
2332
|
+
hashes: Mapping[str, str] | None = None,
|
|
2333
|
+
) -> dict[str, Any]:
|
|
2334
|
+
"""Read fresh, mean-pooled document embeddings without starting QMD.
|
|
2335
|
+
|
|
2336
|
+
Paths are relative to memory, optionally prefixed with ``memory/``. Each
|
|
2337
|
+
chunk's dimension prefix is L2-normalised, then the document mean is
|
|
2338
|
+
L2-normalised. Missing, stale, unsafe, or incomplete documents are absent.
|
|
2339
|
+
The fingerprint describes the coordinate system, not corpus membership.
|
|
2340
|
+
sqlite-vec's ordinary shadow tables need no extension; a read transaction
|
|
2341
|
+
includes WAL updates while keeping rows and vector blocks consistent.
|
|
2342
|
+
``hashes`` (path → sha256 of the bytes the caller read) replaces reading
|
|
2343
|
+
the files, so a vector always belongs to the bytes the caller holds.
|
|
2344
|
+
"""
|
|
2345
|
+
if not isinstance(dimension_prefix, int) or dimension_prefix < 1:
|
|
2346
|
+
raise ValueError("dimension_prefix must be a positive integer")
|
|
2347
|
+
adapter = QmdLocalRetriever(
|
|
2348
|
+
repository_root=repository_root, memory_root=memory_root, environment=environment,
|
|
2349
|
+
)
|
|
2350
|
+
result: dict[str, Any] = {"vectors": {}, "fingerprint": "", "dimension": dimension_prefix}
|
|
2351
|
+
index = adapter._index_path()
|
|
2352
|
+
if not index.is_file() or not paths:
|
|
2353
|
+
return result
|
|
2354
|
+
requested: dict[str, list[str]] = {}
|
|
2355
|
+
read: dict[str, str] = {}
|
|
2356
|
+
for original in sorted(set(paths)):
|
|
2357
|
+
relative = original.removeprefix("memory/")
|
|
2358
|
+
candidate = adapter.memory_root / relative
|
|
2359
|
+
if Path(relative).is_absolute() or ".." in Path(relative).parts:
|
|
2360
|
+
continue
|
|
2361
|
+
try:
|
|
2362
|
+
if hashes is not None:
|
|
2363
|
+
if original not in hashes:
|
|
2364
|
+
continue
|
|
2365
|
+
read[relative] = hashes[original]
|
|
2366
|
+
elif not candidate.resolve().is_relative_to(adapter.memory_root):
|
|
2367
|
+
continue
|
|
2368
|
+
else: # QMD hashes UTF-8 text, including any original line endings.
|
|
2369
|
+
read[relative] = hashlib.sha256(candidate.read_bytes()).hexdigest()
|
|
2370
|
+
requested.setdefault(relative, []).append(original)
|
|
2371
|
+
except OSError:
|
|
2372
|
+
continue
|
|
2373
|
+
try:
|
|
2374
|
+
with closing(sqlite3.connect(index.as_uri() + "?mode=ro", uri=True)) as connection:
|
|
2375
|
+
connection.execute("BEGIN")
|
|
2376
|
+
schema = connection.execute(
|
|
2377
|
+
"SELECT sql FROM sqlite_master WHERE name = 'vectors_vec'"
|
|
2378
|
+
).fetchone()
|
|
2379
|
+
match = re.search(r"float\s*\[\s*(\d+)\s*\]", schema[0] if schema else "", re.I)
|
|
2380
|
+
if not match or dimension_prefix > int(match.group(1)):
|
|
2381
|
+
return result
|
|
2382
|
+
stored_dimensions = int(match.group(1))
|
|
2383
|
+
fresh: dict[str, list[str]] = {}
|
|
2384
|
+
for path, content_hash in connection.execute(
|
|
2385
|
+
"SELECT path, hash FROM documents WHERE collection = ? AND active = 1",
|
|
2386
|
+
(adapter.collection,),
|
|
2387
|
+
):
|
|
2388
|
+
if path in read and read[path] == content_hash:
|
|
2389
|
+
fresh.setdefault(content_hash, []).extend(requested[path])
|
|
2390
|
+
if not fresh:
|
|
2391
|
+
return result
|
|
2392
|
+
rows = []
|
|
2393
|
+
ordered_hashes = sorted(fresh)
|
|
2394
|
+
for offset in range(0, len(ordered_hashes), 500):
|
|
2395
|
+
batch = ordered_hashes[offset:offset + 500]
|
|
2396
|
+
rows.extend(connection.execute(
|
|
2397
|
+
"SELECT c.hash, c.seq, c.total_chunks, c.model, c.embed_fingerprint, "
|
|
2398
|
+
"r.chunk_id, r.chunk_offset FROM content_vectors c "
|
|
2399
|
+
"JOIN vectors_vec_rowids r ON r.id = c.hash || '_' || c.seq "
|
|
2400
|
+
"WHERE c.hash IN (" + ",".join("?" for _ in batch) + ") "
|
|
2401
|
+
"ORDER BY c.hash, c.seq", batch,
|
|
2402
|
+
).fetchall())
|
|
2403
|
+
models = {(str(row[3]), str(row[4])) for row in rows}
|
|
2404
|
+
result["fingerprint"] = hashlib.sha256(json.dumps(
|
|
2405
|
+
[INDEX_SPEC_VERSION, stored_dimensions, dimension_prefix, sorted(models)],
|
|
2406
|
+
separators=(",", ":"),
|
|
2407
|
+
).encode()).hexdigest()[:20]
|
|
2408
|
+
# An index being re-embedded can contain fresh text hashes from
|
|
2409
|
+
# two models at once. Those vectors are not comparable, including
|
|
2410
|
+
# chunks of the same document. Let the caller select its complete
|
|
2411
|
+
# fallback representation instead of mixing coordinate systems.
|
|
2412
|
+
if len(models) != 1:
|
|
2413
|
+
return result
|
|
2414
|
+
blocks = {}
|
|
2415
|
+
for chunk_id in sorted({row[5] for row in rows}):
|
|
2416
|
+
block = connection.execute(
|
|
2417
|
+
"SELECT vectors FROM vectors_vec_vector_chunks00 WHERE rowid = ?", (chunk_id,)
|
|
2418
|
+
).fetchone()
|
|
2419
|
+
if block:
|
|
2420
|
+
blocks[chunk_id] = block[0]
|
|
2421
|
+
pooled: dict[str, list[list[float]]] = {}
|
|
2422
|
+
expected: dict[str, int] = {}
|
|
2423
|
+
sequences: dict[str, set[int]] = {}
|
|
2424
|
+
invalid = set()
|
|
2425
|
+
decoder = struct.Struct("<" + "f" * dimension_prefix)
|
|
2426
|
+
for content_hash, seq, count, model, fingerprint, chunk_id, chunk_offset in rows:
|
|
2427
|
+
count = int(count)
|
|
2428
|
+
seen = sequences.setdefault(content_hash, set())
|
|
2429
|
+
if (not isinstance(seq, int) or count < 1 or not 0 <= seq < count
|
|
2430
|
+
or seq in seen or expected.get(content_hash, count) != count):
|
|
2431
|
+
invalid.add(content_hash)
|
|
2432
|
+
continue
|
|
2433
|
+
expected[content_hash] = count
|
|
2434
|
+
seen.add(seq)
|
|
2435
|
+
blob = blocks.get(chunk_id, b"")
|
|
2436
|
+
start = int(chunk_offset) * stored_dimensions * 4
|
|
2437
|
+
if start < 0 or start + decoder.size > len(blob):
|
|
2438
|
+
invalid.add(content_hash)
|
|
2439
|
+
continue
|
|
2440
|
+
vector = decoder.unpack_from(blob, start)
|
|
2441
|
+
norm = math.sqrt(sum(value * value for value in vector))
|
|
2442
|
+
if not math.isfinite(norm) or norm <= 0:
|
|
2443
|
+
invalid.add(content_hash)
|
|
2444
|
+
continue
|
|
2445
|
+
pooled.setdefault(content_hash, []).append([value / norm for value in vector])
|
|
2446
|
+
for content_hash, chunks in sorted(pooled.items()):
|
|
2447
|
+
if (content_hash in invalid or len(chunks) != expected[content_hash]
|
|
2448
|
+
or sequences[content_hash] != set(range(expected[content_hash]))):
|
|
2449
|
+
continue
|
|
2450
|
+
vector = [sum(values) for values in zip(*chunks)]
|
|
2451
|
+
norm = math.sqrt(sum(value * value for value in vector))
|
|
2452
|
+
if norm <= 0:
|
|
2453
|
+
continue
|
|
2454
|
+
vector = [value / norm for value in vector]
|
|
2455
|
+
for path in fresh[content_hash]:
|
|
2456
|
+
result["vectors"][path] = vector
|
|
2457
|
+
except (sqlite3.Error, OSError, ValueError, TypeError, struct.error):
|
|
2458
|
+
# A missing or incompatible optional index must leave callers able to
|
|
2459
|
+
# choose one complete fallback coordinate system for the export.
|
|
2460
|
+
result["vectors"] = {}
|
|
2461
|
+
return result
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
def vectors_receipt(repository_root: Path, *, environment: Mapping[str, str] | None = None) -> Path:
|
|
2465
|
+
"""Where a complete vector build leaves its receipt, found without starting anything: its
|
|
2466
|
+
change is the cheap sign that ``read_document_vectors`` may now read other vectors."""
|
|
2467
|
+
adapter = object.__new__(QmdLocalRetriever) # the path rules only; no Node check, no layout
|
|
2468
|
+
adapter.environment = dict(os.environ if environment is None else environment)
|
|
2469
|
+
adapter.memory_root = (Path(repository_root).resolve() / "memory").resolve()
|
|
2470
|
+
return adapter._semantic_state_path()
|
|
2471
|
+
|
|
2472
|
+
|
|
2001
2473
|
__all__ = [
|
|
2002
2474
|
"EMBEDDING_MODEL",
|
|
2003
2475
|
"INDEX_SPEC_VERSION",
|
|
@@ -2007,4 +2479,6 @@ __all__ = [
|
|
|
2007
2479
|
"QmdAdapterError",
|
|
2008
2480
|
"QmdLocalRetriever",
|
|
2009
2481
|
"QmdRuntimeMetadata",
|
|
2482
|
+
"read_document_vectors",
|
|
2483
|
+
"vectors_receipt",
|
|
2010
2484
|
]
|