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.
Files changed (719) hide show
  1. package/assets/egregore-launcher.js +58 -6
  2. package/assets/egregore-launcher.sh +15 -2
  3. package/lib/claude-runtime.js +2 -0
  4. package/lib/codex-runtime.js +2 -0
  5. package/lib/setup.js +20 -3
  6. package/package.json +2 -2
  7. package/runtime/claude/.claude/context/commit-format.md +7 -4
  8. package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
  9. package/runtime/claude/.claude/context/notification-consent.md +12 -9
  10. package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
  11. package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
  12. package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
  13. package/runtime/claude/.claude/settings.json +1 -1
  14. package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
  15. package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
  16. package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
  17. package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
  18. package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
  19. package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
  20. package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
  21. package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
  22. package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
  23. package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
  24. package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
  25. package/runtime/claude/CLAUDE.md +59 -95
  26. package/runtime/claude/bin/agent.sh +231 -7
  27. package/runtime/claude/bin/api-call.sh +44 -23
  28. package/runtime/claude/bin/artifact-register.sh +7 -1
  29. package/runtime/claude/bin/artifact-writeback.sh +1 -1
  30. package/runtime/claude/bin/artifacts.sh +66 -265
  31. package/runtime/claude/bin/attendant.sh +4 -1
  32. package/runtime/claude/bin/boundary-guard.cjs +1848 -0
  33. package/runtime/claude/bin/boundary-hook.sh +41 -0
  34. package/runtime/claude/bin/boundary.sh +15 -155
  35. package/runtime/claude/bin/capture-run.sh +32 -2
  36. package/runtime/claude/bin/codex-render-spec.mjs +24 -46
  37. package/runtime/claude/bin/codex-session-start.sh +19 -0
  38. package/runtime/claude/bin/codex-skill-render.mjs +18 -0
  39. package/runtime/claude/bin/config-get.sh +10 -1
  40. package/runtime/claude/bin/connect-refresh.sh +16 -0
  41. package/runtime/claude/bin/corpus_map.py +5 -0
  42. package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
  43. package/runtime/claude/bin/genui.sh +19 -0
  44. package/runtime/claude/bin/graph-batch.sh +1 -1
  45. package/runtime/claude/bin/graph-op.sh +14 -0
  46. package/runtime/claude/bin/graph.sh +1 -1
  47. package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
  48. package/runtime/claude/bin/handoff-run.sh +11 -6
  49. package/runtime/claude/bin/ingest-item.sh +1 -1
  50. package/runtime/claude/bin/ingest-surface.html +22 -4
  51. package/runtime/claude/bin/issue.sh +1 -1
  52. package/runtime/claude/bin/knowledge.sh +1 -1
  53. package/runtime/claude/bin/lib/card-delivery.sh +329 -0
  54. package/runtime/claude/bin/lib/config.sh +148 -3
  55. package/runtime/claude/bin/lib/context.sh +38 -67
  56. package/runtime/claude/bin/lib/genui-entry.py +36 -0
  57. package/runtime/claude/bin/lib/git-message.sh +25 -6
  58. package/runtime/claude/bin/lib/git-sync.sh +12 -4
  59. package/runtime/claude/bin/lib/greeting.sh +255 -28
  60. package/runtime/claude/bin/lib/handoff_meta.py +294 -0
  61. package/runtime/claude/bin/lib/health-footer.sh +83 -2
  62. package/runtime/claude/bin/lib/identity.sh +31 -10
  63. package/runtime/claude/bin/lib/instance-registry.sh +71 -0
  64. package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
  65. package/runtime/claude/bin/lib/metrics.sh +121 -8
  66. package/runtime/claude/bin/lib/notices.sh +36 -0
  67. package/runtime/claude/bin/lib/record_outcomes.py +729 -0
  68. package/runtime/claude/bin/lib/reflexes.py +267 -0
  69. package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
  70. package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
  71. package/runtime/claude/bin/lib/session_record.py +3747 -0
  72. package/runtime/claude/bin/lib/session_trail.py +1983 -0
  73. package/runtime/claude/bin/lib/settings-drift.sh +8 -5
  74. package/runtime/claude/bin/lib/startup-restart.sh +41 -0
  75. package/runtime/claude/bin/lib/worktree-links.sh +92 -0
  76. package/runtime/claude/bin/memory-push.sh +6 -0
  77. package/runtime/claude/bin/merge-gate.sh +498 -0
  78. package/runtime/claude/bin/notify.sh +5 -2
  79. package/runtime/claude/bin/page-access.sh +103 -0
  80. package/runtime/claude/bin/person.sh +13 -2
  81. package/runtime/claude/bin/pi-render-spec.mjs +0 -1
  82. package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
  83. package/runtime/claude/bin/post-update.sh +181 -0
  84. package/runtime/claude/bin/prime-render-spec.mjs +0 -1
  85. package/runtime/claude/bin/publish-artifact.sh +89 -42
  86. package/runtime/claude/bin/qa-inventory.sh +2 -2
  87. package/runtime/claude/bin/quest.sh +1 -1
  88. package/runtime/claude/bin/records.sh +11 -0
  89. package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
  90. package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
  91. package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
  92. package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
  93. package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
  94. package/runtime/claude/bin/reflexes/30-cost.py +116 -0
  95. package/runtime/claude/bin/reflexes/40-shells.py +51 -0
  96. package/runtime/claude/bin/reflexes/README.md +61 -0
  97. package/runtime/claude/bin/reflexes.sh +7 -0
  98. package/runtime/claude/bin/runtime-identity.sh +140 -42
  99. package/runtime/claude/bin/screenshot.sh +77 -0
  100. package/runtime/claude/bin/scroll.sh +1 -1
  101. package/runtime/claude/bin/search.sh +31 -5
  102. package/runtime/claude/bin/session-autosave.sh +6 -3
  103. package/runtime/claude/bin/session-id.sh +147 -0
  104. package/runtime/claude/bin/session-log.sh +19 -0
  105. package/runtime/claude/bin/session-record.sh +26 -0
  106. package/runtime/claude/bin/session-start.sh +121 -151
  107. package/runtime/claude/bin/settings.sh +13 -8
  108. package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
  109. package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
  110. package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
  111. package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
  112. package/runtime/claude/bin/spirit-discovery.sh +4 -0
  113. package/runtime/claude/bin/startup-check.sh +1 -1
  114. package/runtime/claude/bin/telemetry.sh +11 -4
  115. package/runtime/claude/bin/test-baseline.sh +33 -5
  116. package/runtime/claude/bin/todo.sh +1 -1
  117. package/runtime/claude/bin/worktree-create.sh +15 -45
  118. package/runtime/claude/bin/worktree.sh +6 -0
  119. package/runtime/claude/docs/spirit-discovery.md +96 -0
  120. package/runtime/claude/egregore_runtime/__init__.py +18 -12
  121. package/runtime/claude/egregore_runtime/access.py +608 -0
  122. package/runtime/claude/egregore_runtime/access_cli.py +190 -0
  123. package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
  124. package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
  125. package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
  126. package/runtime/claude/egregore_runtime/artifacts.py +222 -16
  127. package/runtime/claude/egregore_runtime/contracts.py +27 -0
  128. package/runtime/claude/egregore_runtime/eligibility.py +5 -12
  129. package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
  130. package/runtime/claude/egregore_runtime/github_issues.py +2 -2
  131. package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
  132. package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
  133. package/runtime/claude/egregore_runtime/held.py +2436 -0
  134. package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
  135. package/runtime/claude/egregore_runtime/identity.py +196 -65
  136. package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
  137. package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
  138. package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
  139. package/runtime/claude/egregore_runtime/installation.py +119 -0
  140. package/runtime/claude/egregore_runtime/investigation.py +64 -41
  141. package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
  142. package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
  143. package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
  144. package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
  145. package/runtime/claude/egregore_runtime/issues.py +12 -6
  146. package/runtime/claude/egregore_runtime/knowledge.py +331 -5
  147. package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
  148. package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
  149. package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
  150. package/runtime/claude/egregore_runtime/line_ids.py +10 -0
  151. package/runtime/claude/egregore_runtime/memory_push.py +349 -0
  152. package/runtime/claude/egregore_runtime/observe.py +64 -25
  153. package/runtime/claude/egregore_runtime/observers.py +19 -0
  154. package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
  155. package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
  156. package/runtime/claude/egregore_runtime/policy.py +651 -24
  157. package/runtime/claude/egregore_runtime/private_result.py +2 -1
  158. package/runtime/claude/egregore_runtime/projection.py +946 -0
  159. package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
  160. package/runtime/claude/egregore_runtime/question_cli.py +15 -11
  161. package/runtime/claude/egregore_runtime/questions.py +10 -10
  162. package/runtime/claude/egregore_runtime/quests.py +10 -10
  163. package/runtime/claude/egregore_runtime/record_publish.py +548 -0
  164. package/runtime/claude/egregore_runtime/record_subject.py +564 -0
  165. package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
  166. package/runtime/claude/egregore_runtime/runtime.py +169 -60
  167. package/runtime/claude/egregore_runtime/sandbox.py +194 -0
  168. package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
  169. package/runtime/claude/egregore_runtime/scrolls.py +23 -15
  170. package/runtime/claude/egregore_runtime/shadow.py +262 -0
  171. package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
  172. package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
  173. package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
  174. package/runtime/claude/egregore_runtime/status.py +101 -44
  175. package/runtime/claude/egregore_runtime/status_cli.py +13 -5
  176. package/runtime/claude/egregore_runtime/sync.py +39 -21
  177. package/runtime/claude/egregore_runtime/taste.py +934 -0
  178. package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
  179. package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
  180. package/runtime/claude/egregore_runtime/telemetry.py +26 -3
  181. package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
  182. package/runtime/claude/egregore_runtime/todos.py +3 -2
  183. package/runtime/claude/egregore_runtime/upgrade.py +85 -58
  184. package/runtime/claude/egregore_runtime/writeback.py +330 -24
  185. package/runtime/claude/manifest.json +65 -5
  186. package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
  187. package/runtime/codex/.claude/context/notification-consent.md +12 -9
  188. package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
  189. package/runtime/codex/.codex/hooks.json +11 -0
  190. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
  191. package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
  192. package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
  193. package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
  194. package/runtime/codex/.codex/spec-manifest.json +13 -19
  195. package/runtime/codex/AGENTS.md +54 -105
  196. package/runtime/codex/bin/agent.sh +231 -7
  197. package/runtime/codex/bin/api-call.sh +44 -23
  198. package/runtime/codex/bin/artifact-register.sh +7 -1
  199. package/runtime/codex/bin/artifact-writeback.sh +1 -1
  200. package/runtime/codex/bin/artifacts.sh +66 -265
  201. package/runtime/codex/bin/attendant.sh +4 -1
  202. package/runtime/codex/bin/boundary-guard.cjs +1848 -0
  203. package/runtime/codex/bin/boundary-hook.sh +41 -0
  204. package/runtime/codex/bin/capture-run.sh +32 -2
  205. package/runtime/codex/bin/codex-render-spec.mjs +24 -46
  206. package/runtime/codex/bin/codex-session-start.sh +19 -0
  207. package/runtime/codex/bin/codex-skill-render.mjs +18 -0
  208. package/runtime/codex/bin/config-get.sh +10 -1
  209. package/runtime/codex/bin/connect-refresh.sh +16 -0
  210. package/runtime/codex/bin/corpus_map.py +5 -0
  211. package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
  212. package/runtime/codex/bin/genui.sh +19 -0
  213. package/runtime/codex/bin/graph-batch.sh +1 -1
  214. package/runtime/codex/bin/graph-op.sh +14 -0
  215. package/runtime/codex/bin/graph.sh +1 -1
  216. package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
  217. package/runtime/codex/bin/handoff-run.sh +11 -6
  218. package/runtime/codex/bin/ingest-item.sh +1 -1
  219. package/runtime/codex/bin/ingest-surface.html +22 -4
  220. package/runtime/codex/bin/issue.sh +1 -1
  221. package/runtime/codex/bin/knowledge.sh +1 -1
  222. package/runtime/codex/bin/lib/card-delivery.sh +329 -0
  223. package/runtime/codex/bin/lib/config.sh +148 -3
  224. package/runtime/codex/bin/lib/context.sh +38 -67
  225. package/runtime/codex/bin/lib/genui-entry.py +36 -0
  226. package/runtime/codex/bin/lib/git-message.sh +25 -6
  227. package/runtime/codex/bin/lib/git-sync.sh +12 -4
  228. package/runtime/codex/bin/lib/greeting.sh +255 -28
  229. package/runtime/codex/bin/lib/handoff_meta.py +294 -0
  230. package/runtime/codex/bin/lib/health-footer.sh +83 -2
  231. package/runtime/codex/bin/lib/identity.sh +31 -10
  232. package/runtime/codex/bin/lib/instance-registry.sh +71 -0
  233. package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
  234. package/runtime/codex/bin/lib/metrics.sh +121 -8
  235. package/runtime/codex/bin/lib/notices.sh +36 -0
  236. package/runtime/codex/bin/lib/record_outcomes.py +729 -0
  237. package/runtime/codex/bin/lib/reflexes.py +267 -0
  238. package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
  239. package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
  240. package/runtime/codex/bin/lib/session_record.py +3747 -0
  241. package/runtime/codex/bin/lib/session_trail.py +1983 -0
  242. package/runtime/codex/bin/lib/settings-drift.sh +8 -5
  243. package/runtime/codex/bin/lib/startup-restart.sh +41 -0
  244. package/runtime/codex/bin/lib/worktree-links.sh +92 -0
  245. package/runtime/codex/bin/memory-push.sh +6 -0
  246. package/runtime/codex/bin/merge-gate.sh +498 -0
  247. package/runtime/codex/bin/notify.sh +5 -2
  248. package/runtime/codex/bin/page-access.sh +103 -0
  249. package/runtime/codex/bin/person.sh +13 -2
  250. package/runtime/codex/bin/pi-render-spec.mjs +0 -1
  251. package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
  252. package/runtime/codex/bin/post-update.sh +181 -0
  253. package/runtime/codex/bin/prime-render-spec.mjs +0 -1
  254. package/runtime/codex/bin/publish-artifact.sh +89 -42
  255. package/runtime/codex/bin/qa-inventory.sh +2 -2
  256. package/runtime/codex/bin/quest.sh +1 -1
  257. package/runtime/codex/bin/records.sh +11 -0
  258. package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
  259. package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
  260. package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
  261. package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
  262. package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
  263. package/runtime/codex/bin/reflexes/30-cost.py +116 -0
  264. package/runtime/codex/bin/reflexes/40-shells.py +51 -0
  265. package/runtime/codex/bin/reflexes/README.md +61 -0
  266. package/runtime/codex/bin/reflexes.sh +7 -0
  267. package/runtime/codex/bin/runtime-identity.sh +140 -42
  268. package/runtime/codex/bin/screenshot.sh +77 -0
  269. package/runtime/codex/bin/scroll.sh +1 -1
  270. package/runtime/codex/bin/search.sh +31 -5
  271. package/runtime/codex/bin/session-autosave.sh +6 -3
  272. package/runtime/codex/bin/session-id.sh +147 -0
  273. package/runtime/codex/bin/session-log.sh +19 -0
  274. package/runtime/codex/bin/session-record.sh +26 -0
  275. package/runtime/codex/bin/settings.sh +13 -8
  276. package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
  277. package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
  278. package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
  279. package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
  280. package/runtime/codex/bin/spirit-discovery.sh +4 -0
  281. package/runtime/codex/bin/startup-check.sh +1 -1
  282. package/runtime/codex/bin/telemetry.sh +11 -4
  283. package/runtime/codex/bin/test-baseline.sh +33 -5
  284. package/runtime/codex/bin/todo.sh +1 -1
  285. package/runtime/codex/bin/worktree-create.sh +15 -45
  286. package/runtime/codex/bin/worktree.sh +6 -0
  287. package/runtime/codex/docs/spirit-discovery.md +96 -0
  288. package/runtime/codex/egregore_runtime/__init__.py +18 -12
  289. package/runtime/codex/egregore_runtime/access.py +608 -0
  290. package/runtime/codex/egregore_runtime/access_cli.py +190 -0
  291. package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
  292. package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
  293. package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
  294. package/runtime/codex/egregore_runtime/artifacts.py +222 -16
  295. package/runtime/codex/egregore_runtime/contracts.py +27 -0
  296. package/runtime/codex/egregore_runtime/eligibility.py +5 -12
  297. package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
  298. package/runtime/codex/egregore_runtime/github_issues.py +2 -2
  299. package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
  300. package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
  301. package/runtime/codex/egregore_runtime/held.py +2436 -0
  302. package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
  303. package/runtime/codex/egregore_runtime/identity.py +196 -65
  304. package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
  305. package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
  306. package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
  307. package/runtime/codex/egregore_runtime/installation.py +119 -0
  308. package/runtime/codex/egregore_runtime/investigation.py +64 -41
  309. package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
  310. package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
  311. package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
  312. package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
  313. package/runtime/codex/egregore_runtime/issues.py +12 -6
  314. package/runtime/codex/egregore_runtime/knowledge.py +331 -5
  315. package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
  316. package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
  317. package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
  318. package/runtime/codex/egregore_runtime/line_ids.py +10 -0
  319. package/runtime/codex/egregore_runtime/memory_push.py +349 -0
  320. package/runtime/codex/egregore_runtime/observe.py +64 -25
  321. package/runtime/codex/egregore_runtime/observers.py +19 -0
  322. package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
  323. package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
  324. package/runtime/codex/egregore_runtime/policy.py +651 -24
  325. package/runtime/codex/egregore_runtime/private_result.py +2 -1
  326. package/runtime/codex/egregore_runtime/projection.py +946 -0
  327. package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
  328. package/runtime/codex/egregore_runtime/question_cli.py +15 -11
  329. package/runtime/codex/egregore_runtime/questions.py +10 -10
  330. package/runtime/codex/egregore_runtime/quests.py +10 -10
  331. package/runtime/codex/egregore_runtime/record_publish.py +548 -0
  332. package/runtime/codex/egregore_runtime/record_subject.py +564 -0
  333. package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
  334. package/runtime/codex/egregore_runtime/runtime.py +169 -60
  335. package/runtime/codex/egregore_runtime/sandbox.py +194 -0
  336. package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
  337. package/runtime/codex/egregore_runtime/scrolls.py +23 -15
  338. package/runtime/codex/egregore_runtime/shadow.py +262 -0
  339. package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
  340. package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
  341. package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
  342. package/runtime/codex/egregore_runtime/status.py +101 -44
  343. package/runtime/codex/egregore_runtime/status_cli.py +13 -5
  344. package/runtime/codex/egregore_runtime/sync.py +39 -21
  345. package/runtime/codex/egregore_runtime/taste.py +934 -0
  346. package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
  347. package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
  348. package/runtime/codex/egregore_runtime/telemetry.py +26 -3
  349. package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
  350. package/runtime/codex/egregore_runtime/todos.py +3 -2
  351. package/runtime/codex/egregore_runtime/upgrade.py +85 -58
  352. package/runtime/codex/egregore_runtime/writeback.py +330 -24
  353. package/runtime/codex/manifest.json +66 -5
  354. package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
  355. package/runtime/pi/.claude/context/notification-consent.md +12 -9
  356. package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
  357. package/runtime/pi/.codex/hooks.json +11 -0
  358. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
  359. package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
  360. package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
  361. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
  362. package/runtime/pi/.codex/spec-manifest.json +13 -19
  363. package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
  364. package/runtime/pi/.pi/extensions/egregore.ts +13 -1
  365. package/runtime/pi/.pi/spec-manifest.json +2 -2
  366. package/runtime/pi/AGENTS.md +54 -105
  367. package/runtime/pi/bin/agent.sh +231 -7
  368. package/runtime/pi/bin/api-call.sh +44 -23
  369. package/runtime/pi/bin/artifact-register.sh +7 -1
  370. package/runtime/pi/bin/artifact-writeback.sh +1 -1
  371. package/runtime/pi/bin/artifacts.sh +66 -265
  372. package/runtime/pi/bin/attendant.sh +4 -1
  373. package/runtime/pi/bin/boundary-guard.cjs +1848 -0
  374. package/runtime/pi/bin/boundary-hook.sh +41 -0
  375. package/runtime/pi/bin/capture-run.sh +32 -2
  376. package/runtime/pi/bin/codex-render-spec.mjs +24 -46
  377. package/runtime/pi/bin/codex-session-start.sh +19 -0
  378. package/runtime/pi/bin/codex-skill-render.mjs +18 -0
  379. package/runtime/pi/bin/config-get.sh +10 -1
  380. package/runtime/pi/bin/connect-refresh.sh +16 -0
  381. package/runtime/pi/bin/corpus_map.py +5 -0
  382. package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
  383. package/runtime/pi/bin/genui.sh +19 -0
  384. package/runtime/pi/bin/graph-batch.sh +1 -1
  385. package/runtime/pi/bin/graph-op.sh +14 -0
  386. package/runtime/pi/bin/graph.sh +1 -1
  387. package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
  388. package/runtime/pi/bin/handoff-run.sh +11 -6
  389. package/runtime/pi/bin/ingest-item.sh +1 -1
  390. package/runtime/pi/bin/ingest-surface.html +22 -4
  391. package/runtime/pi/bin/issue.sh +1 -1
  392. package/runtime/pi/bin/knowledge.sh +1 -1
  393. package/runtime/pi/bin/lib/card-delivery.sh +329 -0
  394. package/runtime/pi/bin/lib/config.sh +148 -3
  395. package/runtime/pi/bin/lib/context.sh +38 -67
  396. package/runtime/pi/bin/lib/genui-entry.py +36 -0
  397. package/runtime/pi/bin/lib/git-message.sh +25 -6
  398. package/runtime/pi/bin/lib/git-sync.sh +12 -4
  399. package/runtime/pi/bin/lib/greeting.sh +255 -28
  400. package/runtime/pi/bin/lib/handoff_meta.py +294 -0
  401. package/runtime/pi/bin/lib/health-footer.sh +83 -2
  402. package/runtime/pi/bin/lib/identity.sh +31 -10
  403. package/runtime/pi/bin/lib/instance-registry.sh +71 -0
  404. package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
  405. package/runtime/pi/bin/lib/metrics.sh +121 -8
  406. package/runtime/pi/bin/lib/notices.sh +36 -0
  407. package/runtime/pi/bin/lib/record_outcomes.py +729 -0
  408. package/runtime/pi/bin/lib/reflexes.py +267 -0
  409. package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
  410. package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
  411. package/runtime/pi/bin/lib/session_record.py +3747 -0
  412. package/runtime/pi/bin/lib/session_trail.py +1983 -0
  413. package/runtime/pi/bin/lib/settings-drift.sh +8 -5
  414. package/runtime/pi/bin/lib/startup-restart.sh +41 -0
  415. package/runtime/pi/bin/lib/worktree-links.sh +92 -0
  416. package/runtime/pi/bin/memory-push.sh +6 -0
  417. package/runtime/pi/bin/merge-gate.sh +498 -0
  418. package/runtime/pi/bin/notify.sh +5 -2
  419. package/runtime/pi/bin/page-access.sh +103 -0
  420. package/runtime/pi/bin/person.sh +13 -2
  421. package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
  422. package/runtime/pi/bin/pi-observe-context.mjs +97 -4
  423. package/runtime/pi/bin/pi-render-spec.mjs +0 -1
  424. package/runtime/pi/bin/pi-session-start.sh +4 -1
  425. package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
  426. package/runtime/pi/bin/post-update.sh +181 -0
  427. package/runtime/pi/bin/prime-render-spec.mjs +0 -1
  428. package/runtime/pi/bin/publish-artifact.sh +89 -42
  429. package/runtime/pi/bin/qa-inventory.sh +2 -2
  430. package/runtime/pi/bin/quest.sh +1 -1
  431. package/runtime/pi/bin/records.sh +11 -0
  432. package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
  433. package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
  434. package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
  435. package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
  436. package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
  437. package/runtime/pi/bin/reflexes/30-cost.py +116 -0
  438. package/runtime/pi/bin/reflexes/40-shells.py +51 -0
  439. package/runtime/pi/bin/reflexes/README.md +61 -0
  440. package/runtime/pi/bin/reflexes.sh +7 -0
  441. package/runtime/pi/bin/runtime-identity.sh +140 -42
  442. package/runtime/pi/bin/screenshot.sh +77 -0
  443. package/runtime/pi/bin/scroll.sh +1 -1
  444. package/runtime/pi/bin/search.sh +31 -5
  445. package/runtime/pi/bin/session-autosave.sh +6 -3
  446. package/runtime/pi/bin/session-id.sh +147 -0
  447. package/runtime/pi/bin/session-log.sh +19 -0
  448. package/runtime/pi/bin/session-record.sh +26 -0
  449. package/runtime/pi/bin/settings.sh +13 -8
  450. package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
  451. package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
  452. package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
  453. package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
  454. package/runtime/pi/bin/spirit-discovery.sh +4 -0
  455. package/runtime/pi/bin/startup-check.sh +1 -1
  456. package/runtime/pi/bin/telemetry.sh +11 -4
  457. package/runtime/pi/bin/test-baseline.sh +33 -5
  458. package/runtime/pi/bin/todo.sh +1 -1
  459. package/runtime/pi/bin/worktree-create.sh +15 -45
  460. package/runtime/pi/bin/worktree.sh +6 -0
  461. package/runtime/pi/docs/spirit-discovery.md +96 -0
  462. package/runtime/pi/egregore_runtime/__init__.py +18 -12
  463. package/runtime/pi/egregore_runtime/access.py +608 -0
  464. package/runtime/pi/egregore_runtime/access_cli.py +190 -0
  465. package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
  466. package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
  467. package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
  468. package/runtime/pi/egregore_runtime/artifacts.py +222 -16
  469. package/runtime/pi/egregore_runtime/contracts.py +27 -0
  470. package/runtime/pi/egregore_runtime/eligibility.py +5 -12
  471. package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
  472. package/runtime/pi/egregore_runtime/github_issues.py +2 -2
  473. package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
  474. package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
  475. package/runtime/pi/egregore_runtime/held.py +2436 -0
  476. package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
  477. package/runtime/pi/egregore_runtime/identity.py +196 -65
  478. package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
  479. package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
  480. package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
  481. package/runtime/pi/egregore_runtime/installation.py +119 -0
  482. package/runtime/pi/egregore_runtime/investigation.py +64 -41
  483. package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
  484. package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
  485. package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
  486. package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
  487. package/runtime/pi/egregore_runtime/issues.py +12 -6
  488. package/runtime/pi/egregore_runtime/knowledge.py +331 -5
  489. package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
  490. package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
  491. package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
  492. package/runtime/pi/egregore_runtime/line_ids.py +10 -0
  493. package/runtime/pi/egregore_runtime/memory_push.py +349 -0
  494. package/runtime/pi/egregore_runtime/observe.py +64 -25
  495. package/runtime/pi/egregore_runtime/observers.py +19 -0
  496. package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
  497. package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
  498. package/runtime/pi/egregore_runtime/policy.py +651 -24
  499. package/runtime/pi/egregore_runtime/private_result.py +2 -1
  500. package/runtime/pi/egregore_runtime/projection.py +946 -0
  501. package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
  502. package/runtime/pi/egregore_runtime/question_cli.py +15 -11
  503. package/runtime/pi/egregore_runtime/questions.py +10 -10
  504. package/runtime/pi/egregore_runtime/quests.py +10 -10
  505. package/runtime/pi/egregore_runtime/record_publish.py +548 -0
  506. package/runtime/pi/egregore_runtime/record_subject.py +564 -0
  507. package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
  508. package/runtime/pi/egregore_runtime/runtime.py +169 -60
  509. package/runtime/pi/egregore_runtime/sandbox.py +194 -0
  510. package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
  511. package/runtime/pi/egregore_runtime/scrolls.py +23 -15
  512. package/runtime/pi/egregore_runtime/shadow.py +262 -0
  513. package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
  514. package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
  515. package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
  516. package/runtime/pi/egregore_runtime/status.py +101 -44
  517. package/runtime/pi/egregore_runtime/status_cli.py +13 -5
  518. package/runtime/pi/egregore_runtime/sync.py +39 -21
  519. package/runtime/pi/egregore_runtime/taste.py +934 -0
  520. package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
  521. package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
  522. package/runtime/pi/egregore_runtime/telemetry.py +26 -3
  523. package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
  524. package/runtime/pi/egregore_runtime/todos.py +3 -2
  525. package/runtime/pi/egregore_runtime/upgrade.py +85 -58
  526. package/runtime/pi/egregore_runtime/writeback.py +330 -24
  527. package/runtime/pi/manifest.json +66 -5
  528. package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
  529. package/runtime/prime/.claude/context/notification-consent.md +12 -9
  530. package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
  531. package/runtime/prime/.codex/hooks.json +11 -0
  532. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
  533. package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
  534. package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
  535. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
  536. package/runtime/prime/.codex/spec-manifest.json +13 -19
  537. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
  538. package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
  539. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
  540. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
  541. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  542. package/runtime/prime/AGENTS.md +54 -105
  543. package/runtime/prime/bin/agent.sh +231 -7
  544. package/runtime/prime/bin/api-call.sh +44 -23
  545. package/runtime/prime/bin/artifact-register.sh +7 -1
  546. package/runtime/prime/bin/artifact-writeback.sh +1 -1
  547. package/runtime/prime/bin/artifacts.sh +66 -265
  548. package/runtime/prime/bin/attendant.sh +4 -1
  549. package/runtime/prime/bin/boundary-guard.cjs +1848 -0
  550. package/runtime/prime/bin/boundary-hook.sh +41 -0
  551. package/runtime/prime/bin/capture-run.sh +32 -2
  552. package/runtime/prime/bin/codex-render-spec.mjs +24 -46
  553. package/runtime/prime/bin/codex-session-start.sh +19 -0
  554. package/runtime/prime/bin/codex-skill-render.mjs +18 -0
  555. package/runtime/prime/bin/config-get.sh +10 -1
  556. package/runtime/prime/bin/connect-refresh.sh +16 -0
  557. package/runtime/prime/bin/corpus_map.py +5 -0
  558. package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
  559. package/runtime/prime/bin/genui.sh +19 -0
  560. package/runtime/prime/bin/graph-batch.sh +1 -1
  561. package/runtime/prime/bin/graph-op.sh +14 -0
  562. package/runtime/prime/bin/graph.sh +1 -1
  563. package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
  564. package/runtime/prime/bin/handoff-run.sh +11 -6
  565. package/runtime/prime/bin/ingest-item.sh +1 -1
  566. package/runtime/prime/bin/ingest-surface.html +22 -4
  567. package/runtime/prime/bin/issue.sh +1 -1
  568. package/runtime/prime/bin/knowledge.sh +1 -1
  569. package/runtime/prime/bin/lib/card-delivery.sh +329 -0
  570. package/runtime/prime/bin/lib/config.sh +148 -3
  571. package/runtime/prime/bin/lib/context.sh +38 -67
  572. package/runtime/prime/bin/lib/genui-entry.py +36 -0
  573. package/runtime/prime/bin/lib/git-message.sh +25 -6
  574. package/runtime/prime/bin/lib/git-sync.sh +12 -4
  575. package/runtime/prime/bin/lib/greeting.sh +255 -28
  576. package/runtime/prime/bin/lib/handoff_meta.py +294 -0
  577. package/runtime/prime/bin/lib/health-footer.sh +83 -2
  578. package/runtime/prime/bin/lib/identity.sh +31 -10
  579. package/runtime/prime/bin/lib/instance-registry.sh +71 -0
  580. package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
  581. package/runtime/prime/bin/lib/metrics.sh +121 -8
  582. package/runtime/prime/bin/lib/notices.sh +36 -0
  583. package/runtime/prime/bin/lib/record_outcomes.py +729 -0
  584. package/runtime/prime/bin/lib/reflexes.py +267 -0
  585. package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
  586. package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
  587. package/runtime/prime/bin/lib/session_record.py +3747 -0
  588. package/runtime/prime/bin/lib/session_trail.py +1983 -0
  589. package/runtime/prime/bin/lib/settings-drift.sh +8 -5
  590. package/runtime/prime/bin/lib/startup-restart.sh +41 -0
  591. package/runtime/prime/bin/lib/worktree-links.sh +92 -0
  592. package/runtime/prime/bin/memory-push.sh +6 -0
  593. package/runtime/prime/bin/merge-gate.sh +498 -0
  594. package/runtime/prime/bin/notify.sh +5 -2
  595. package/runtime/prime/bin/page-access.sh +103 -0
  596. package/runtime/prime/bin/person.sh +13 -2
  597. package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
  598. package/runtime/prime/bin/pi-observe-context.mjs +97 -4
  599. package/runtime/prime/bin/pi-render-spec.mjs +0 -1
  600. package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
  601. package/runtime/prime/bin/post-update.sh +181 -0
  602. package/runtime/prime/bin/prime-render-spec.mjs +0 -1
  603. package/runtime/prime/bin/prime-session-start.sh +3 -1
  604. package/runtime/prime/bin/publish-artifact.sh +89 -42
  605. package/runtime/prime/bin/qa-inventory.sh +2 -2
  606. package/runtime/prime/bin/quest.sh +1 -1
  607. package/runtime/prime/bin/records.sh +11 -0
  608. package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
  609. package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
  610. package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
  611. package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
  612. package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
  613. package/runtime/prime/bin/reflexes/30-cost.py +116 -0
  614. package/runtime/prime/bin/reflexes/40-shells.py +51 -0
  615. package/runtime/prime/bin/reflexes/README.md +61 -0
  616. package/runtime/prime/bin/reflexes.sh +7 -0
  617. package/runtime/prime/bin/runtime-identity.sh +140 -42
  618. package/runtime/prime/bin/screenshot.sh +77 -0
  619. package/runtime/prime/bin/scroll.sh +1 -1
  620. package/runtime/prime/bin/search.sh +31 -5
  621. package/runtime/prime/bin/session-autosave.sh +6 -3
  622. package/runtime/prime/bin/session-id.sh +147 -0
  623. package/runtime/prime/bin/session-log.sh +19 -0
  624. package/runtime/prime/bin/session-record.sh +26 -0
  625. package/runtime/prime/bin/settings.sh +13 -8
  626. package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
  627. package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
  628. package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
  629. package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
  630. package/runtime/prime/bin/spirit-discovery.sh +4 -0
  631. package/runtime/prime/bin/startup-check.sh +1 -1
  632. package/runtime/prime/bin/telemetry.sh +11 -4
  633. package/runtime/prime/bin/test-baseline.sh +33 -5
  634. package/runtime/prime/bin/todo.sh +1 -1
  635. package/runtime/prime/bin/worktree-create.sh +15 -45
  636. package/runtime/prime/bin/worktree.sh +6 -0
  637. package/runtime/prime/docs/spirit-discovery.md +96 -0
  638. package/runtime/prime/egregore_runtime/__init__.py +18 -12
  639. package/runtime/prime/egregore_runtime/access.py +608 -0
  640. package/runtime/prime/egregore_runtime/access_cli.py +190 -0
  641. package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
  642. package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
  643. package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
  644. package/runtime/prime/egregore_runtime/artifacts.py +222 -16
  645. package/runtime/prime/egregore_runtime/contracts.py +27 -0
  646. package/runtime/prime/egregore_runtime/eligibility.py +5 -12
  647. package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
  648. package/runtime/prime/egregore_runtime/github_issues.py +2 -2
  649. package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
  650. package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
  651. package/runtime/prime/egregore_runtime/held.py +2436 -0
  652. package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
  653. package/runtime/prime/egregore_runtime/identity.py +196 -65
  654. package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
  655. package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
  656. package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
  657. package/runtime/prime/egregore_runtime/installation.py +119 -0
  658. package/runtime/prime/egregore_runtime/investigation.py +64 -41
  659. package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
  660. package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
  661. package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
  662. package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
  663. package/runtime/prime/egregore_runtime/issues.py +12 -6
  664. package/runtime/prime/egregore_runtime/knowledge.py +331 -5
  665. package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
  666. package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
  667. package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
  668. package/runtime/prime/egregore_runtime/line_ids.py +10 -0
  669. package/runtime/prime/egregore_runtime/memory_push.py +349 -0
  670. package/runtime/prime/egregore_runtime/observe.py +64 -25
  671. package/runtime/prime/egregore_runtime/observers.py +19 -0
  672. package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
  673. package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
  674. package/runtime/prime/egregore_runtime/policy.py +651 -24
  675. package/runtime/prime/egregore_runtime/private_result.py +2 -1
  676. package/runtime/prime/egregore_runtime/projection.py +946 -0
  677. package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
  678. package/runtime/prime/egregore_runtime/question_cli.py +15 -11
  679. package/runtime/prime/egregore_runtime/questions.py +10 -10
  680. package/runtime/prime/egregore_runtime/quests.py +10 -10
  681. package/runtime/prime/egregore_runtime/record_publish.py +548 -0
  682. package/runtime/prime/egregore_runtime/record_subject.py +564 -0
  683. package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
  684. package/runtime/prime/egregore_runtime/runtime.py +169 -60
  685. package/runtime/prime/egregore_runtime/sandbox.py +194 -0
  686. package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
  687. package/runtime/prime/egregore_runtime/scrolls.py +23 -15
  688. package/runtime/prime/egregore_runtime/shadow.py +262 -0
  689. package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
  690. package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
  691. package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
  692. package/runtime/prime/egregore_runtime/status.py +101 -44
  693. package/runtime/prime/egregore_runtime/status_cli.py +13 -5
  694. package/runtime/prime/egregore_runtime/sync.py +39 -21
  695. package/runtime/prime/egregore_runtime/taste.py +934 -0
  696. package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
  697. package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
  698. package/runtime/prime/egregore_runtime/telemetry.py +26 -3
  699. package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
  700. package/runtime/prime/egregore_runtime/todos.py +3 -2
  701. package/runtime/prime/egregore_runtime/upgrade.py +85 -58
  702. package/runtime/prime/egregore_runtime/writeback.py +330 -24
  703. package/runtime/prime/manifest.json +66 -5
  704. package/runtime/claude/bin/thread.sh +0 -9
  705. package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
  706. package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
  707. package/runtime/claude/egregore_runtime/threads.py +0 -521
  708. package/runtime/codex/bin/thread.sh +0 -9
  709. package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
  710. package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
  711. package/runtime/codex/egregore_runtime/threads.py +0 -521
  712. package/runtime/pi/bin/thread.sh +0 -9
  713. package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
  714. package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
  715. package/runtime/pi/egregore_runtime/threads.py +0 -521
  716. package/runtime/prime/bin/thread.sh +0 -9
  717. package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
  718. package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
  719. package/runtime/prime/egregore_runtime/threads.py +0 -521
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # Reflex (show): a session inside a sandbox says what runs degraded and
3
+ # what will not work.
4
+ #
5
+ # Positive markers only, the predicate bin/lib/secret_entry.py and
6
+ # egregore_runtime/sandbox.py use: CODEX_SANDBOX set to anything but empty,
7
+ # none, false or 0, or APP_SANDBOX_CONTAINER_ID set. No marker, no line, on
8
+ # every harness; a missing DISPLAY or a failing write never names a sandbox.
9
+ # With a marker, one file is created and removed where the runtime keeps its
10
+ # per-user state (search and telemetry, resolved as egregore_runtime does).
11
+ # The line follows the result: writable state, and search and browser checks
12
+ # will not work; refused state, and search runs degraded on the workspace
13
+ # copy, with a second line saying that state is read-only here.
14
+ set -u
15
+
16
+ marker="$(printf '%s' "${CODEX_SANDBOX:-}" | tr '[:upper:]' '[:lower:]')"
17
+ case "$marker" in
18
+ ""|none|false|0) [ -n "${APP_SANDBOX_CONTAINER_ID:-}" ] || exit 0 ;;
19
+ esac
20
+
21
+ # Create and remove one file in the nearest existing ancestor of a directory;
22
+ # fails when that location refuses the write or keeps the file. A probe file
23
+ # that outlives its test is removed again on the way out, and that retry
24
+ # never fails the probe, even under an inherited errexit.
25
+ probe_file=""
26
+ trap 'if [ -n "$probe_file" ]; then rm -f "$probe_file" 2>/dev/null || true; fi' EXIT
27
+ writable() {
28
+ local dir="$1" parent
29
+ case "$dir" in \~/*) dir="${HOME:-}/${dir#\~/}" ;; esac
30
+ # A relative override is relative to the current directory, as the runtime
31
+ # resolves it; the walk must reach that directory, never stop short of it.
32
+ case "$dir" in /*) ;; *) dir="$PWD/$dir" ;; esac
33
+ while [ ! -e "$dir" ]; do
34
+ parent="${dir%/*}"
35
+ [ -n "$parent" ] || parent=/
36
+ [ "$parent" != "$dir" ] || break
37
+ dir="$parent"
38
+ done
39
+ [ -d "$dir" ] || return 0
40
+ probe_file="$(mktemp "$dir/.egregore-write-probe.XXXXXX" 2>/dev/null)" || { probe_file=""; return 1; }
41
+ rm -f "$probe_file" 2>/dev/null || return 1
42
+ probe_file=""
43
+ }
44
+
45
+ # Both stores key their per-user directory by the resolved memory checkout
46
+ # (QmdLocalRetriever._instance_hash, LocalTelemetrySink's namespace).
47
+ root="${EGREGORE_ROOT:-.}"
48
+ memory="$(cd "$root/memory" 2>/dev/null && pwd -P)" \
49
+ || memory="$(cd "$root" 2>/dev/null && pwd -P)/memory"
50
+ digest=""
51
+ if command -v shasum >/dev/null 2>&1; then
52
+ digest="$(printf '%s' "$memory" | LC_ALL=C shasum -a 256 2>/dev/null)"
53
+ elif command -v sha256sum >/dev/null 2>&1; then
54
+ digest="$(printf '%s' "$memory" | sha256sum 2>/dev/null)"
55
+ fi
56
+ digest="${digest%% *}"
57
+
58
+ search_state="${EGREGORE_QMD_RUNTIME_DIR:-}"
59
+ telemetry_state="${EGREGORE_TELEMETRY_DIR:-}"
60
+ if [ -n "${HOME:-}" ]; then
61
+ [ -n "$search_state" ] || search_state="$HOME/.egregore/runtime/qmd${digest:+/${digest:0:16}}"
62
+ [ -n "$telemetry_state" ] || telemetry_state="$HOME/.egregore/telemetry${digest:+/${digest:0:20}}"
63
+ fi
64
+
65
+ refused=""
66
+ for state in "$search_state" "$telemetry_state"; do
67
+ [ -n "$state" ] || continue
68
+ if ! writable "$state"; then
69
+ refused=1
70
+ break
71
+ fi
72
+ done
73
+
74
+ # shellcheck disable=SC2016 # the backticks are part of each line
75
+ if [ -n "$refused" ]; then
76
+ # The runtime falls back to the workspace copy: lexical search, telemetry
77
+ # kept there. The read-only fact gets its own line, because this one uses
78
+ # 157 of the runner's 160 characters.
79
+ printf '%s\n' 'this session runs in a sandbox: search runs degraded, telemetry stays in the workspace and browser checks will not work · start through `egregore` to lift it'
80
+ printf '%s\n' 'per-user state is read-only here'
81
+ else
82
+ # Per-user state stays in use, but the sandbox refuses the loopback port
83
+ # of the query worker, so search fails instead of degrading.
84
+ printf '%s\n' 'this session runs in a sandbox: search and browser checks will not work · start through `egregore` to lift it'
85
+ fi
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env python3
2
+ """Reflex: your session cost over seven days. Show tier.
3
+
4
+ Reads the current person's session records from the last seven days, today
5
+ included, and only the files whose names fall in that window. A priced record
6
+ counts under its harness (a record priced at list price is a Codex session);
7
+ an unpriced record counts apart, with the input tokens from its Cost section.
8
+ Prints one line, leaving out the parts that are zero, and nothing without
9
+ records. Numbers only: no topic, branch or prompt text.
10
+ """
11
+ import datetime
12
+ import json
13
+ import math
14
+ import os
15
+ import re
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ DAYS = 7
20
+ READ_LIMIT = 256 * 1024
21
+ PRICED = ("ledger", "estimate", "list price")
22
+ LABELS = {"claude": "Claude", "codex": "Codex", "pi": "Pi", "prime": "Prime", "other": "Other"}
23
+ TOKENS = re.compile(r"^- tokens: (\d+) input\b", re.M)
24
+
25
+
26
+ def front_matter(text):
27
+ if not text.startswith("---\n"):
28
+ return {}
29
+ block = text[4:].partition("\n---")[0]
30
+ fields = {}
31
+ for line in block.splitlines():
32
+ key, sep, value = line.partition(":")
33
+ if not sep:
34
+ continue
35
+ value = value.strip()
36
+ try:
37
+ value = json.loads(value)
38
+ except ValueError:
39
+ pass
40
+ fields[key.strip()] = value
41
+ return fields
42
+
43
+
44
+ def amount(value):
45
+ try:
46
+ number = float(value)
47
+ except (TypeError, ValueError):
48
+ return 0.0
49
+ return number if math.isfinite(number) and number > 0 else 0.0
50
+
51
+
52
+ def compact(count):
53
+ value, unit = float(count), ""
54
+ for name in ("K", "M", "B"):
55
+ if round(value, 1) < 1000:
56
+ break
57
+ value, unit = value / 1000, name
58
+ return (f"{value:.1f}".removesuffix(".0") if unit else str(count)) + unit
59
+
60
+
61
+ def main():
62
+ memory = os.environ.get("EGREGORE_MEMORY", "")
63
+ author = re.sub(r"[^a-z0-9_-]", "", os.environ.get("EGREGORE_AUTHOR", "").strip().lower())
64
+ sessions = Path(memory) / "sessions" if memory else None
65
+ if not author or sessions is None or not sessions.is_dir():
66
+ return 0
67
+ today = datetime.date.today()
68
+ priced = dict.fromkeys(LABELS.values(), 0.0)
69
+ unpriced, tokens, seen = 0, 0, set()
70
+ for offset in range(DAYS):
71
+ day = today - datetime.timedelta(days=offset)
72
+ try:
73
+ paths = sorted((sessions / day.strftime("%Y-%m")).glob(day.strftime("%d-") + author + "-*.md"))
74
+ except OSError:
75
+ continue
76
+ for path in paths:
77
+ try:
78
+ with path.open("rb") as stream:
79
+ text = stream.read(READ_LIMIT).decode("utf-8", errors="replace")
80
+ except OSError:
81
+ continue
82
+ meta = front_matter(text)
83
+ owner = str(meta.get("from", "")).strip().lower()
84
+ # Producer-bearing records are never a person's session, including
85
+ # malformed metadata and records copied without a private adapter.
86
+ if any(key.strip().strip("\"'").casefold() in
87
+ {"producer", "producer_principal_id", "producer_identity", "record_owner_actor_id", "subject_kind"}
88
+ and (key != "subject_kind" or value != "human-session") for key, value in meta.items()):
89
+ continue
90
+ session = str(meta.get("session", path.name))
91
+ if meta.get("kind") != "session-record" or owner != author or session in seen:
92
+ continue
93
+ seen.add(session)
94
+ source = meta.get("cost_source", "estimate")
95
+ if source in PRICED:
96
+ harness = "codex" if source == "list price" else str(meta.get("harness") or "claude").lower()
97
+ priced[LABELS.get(harness, "Other")] += amount(meta.get("cost_usd"))
98
+ continue
99
+ unpriced += 1
100
+ match = TOKENS.search(text.partition("\n## Cost\n")[2])
101
+ tokens += int(match.group(1)) if match else 0
102
+ parts = []
103
+ total = sum(priced.values())
104
+ if round(total, 2) > 0:
105
+ split = ", ".join(f"{label} ${value:,.2f}" for label, value in priced.items() if round(value, 2) > 0)
106
+ parts.append(f"${total:,.2f} priced ({split})")
107
+ if unpriced:
108
+ part = f"{unpriced} unpriced session" + ("" if unpriced == 1 else "s")
109
+ parts.append(part + (f", {compact(tokens)} input tokens" if tokens else ""))
110
+ if parts:
111
+ print("7 days: " + " · ".join(parts))
112
+ return 0
113
+
114
+
115
+ if __name__ == "__main__":
116
+ sys.exit(main())
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python3
2
+ """Reflex: refused shell shapes. Show tier.
3
+
4
+ Reads yesterday's session records of the current person and sums the
5
+ `worktree-guard refusals` counter from their Friction sections. Three or
6
+ more prints one line with the count and the pointer to the plain forms.
7
+ """
8
+ import datetime
9
+ import os
10
+ import re
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ COUNTER = re.compile(r"^- worktree-guard refusals: (\d+)\s*$", re.M)
15
+ FROM = re.compile(r"^from:\s*(.+?)\s*$", re.M)
16
+
17
+
18
+ def main():
19
+ memory = os.environ.get("EGREGORE_MEMORY", "")
20
+ author = os.environ.get("EGREGORE_AUTHOR", "").strip().casefold()
21
+ sessions = Path(memory) / "sessions" if memory else None
22
+ if sessions is None or not sessions.is_dir():
23
+ return 0
24
+ yesterday = datetime.date.today() - datetime.timedelta(days=1)
25
+ folder = sessions / yesterday.strftime("%Y-%m")
26
+ total, files = 0, 0
27
+ try:
28
+ paths = sorted(folder.glob(yesterday.strftime("%d-") + "*.md"))
29
+ except OSError:
30
+ return 0
31
+ for path in paths:
32
+ try:
33
+ text = path.read_text(encoding="utf-8", errors="replace")[:200000]
34
+ except OSError:
35
+ continue
36
+ head = text.partition("\n---")[0]
37
+ owner = FROM.search(head)
38
+ if author and owner and owner.group(1).strip("\"'").casefold() != author:
39
+ continue
40
+ match = COUNTER.search(text)
41
+ if match:
42
+ total += int(match.group(1))
43
+ files += 1
44
+ if total >= 3:
45
+ noun = "session" if files == 1 else "sessions"
46
+ print(f"{total} refused shell commands yesterday across {files} {noun} · plain forms: .claude/context/plain-shell.md")
47
+ return 0
48
+
49
+
50
+ if __name__ == "__main__":
51
+ sys.exit(main())
@@ -0,0 +1,61 @@
1
+ # Reflexes
2
+
3
+ A reflex is a probe with a tier. The probe states a fact about yesterday; the
4
+ tier says how far the egregore may go on its own: say it (show), end the line
5
+ with the command to run (propose), or do it and leave a receipt (act). The
6
+ startup card is where the probes speak, on every harness. The act tier never
7
+ prints from here; it lives in the path it governs, such as the merge gate in
8
+ the save path.
9
+
10
+ ## A probe
11
+
12
+ One file in this directory: a `.sh` or `.py` file, or any executable. The
13
+ runner (`bin/reflexes.sh`, `bin/lib/reflexes.py`) runs them in name order,
14
+ one at a time, and prints each line a probe writes to stdout with the card's
15
+ sigil in front. A probe prints nothing when there is nothing to say, one line
16
+ for a fact, two at most. Lines are cut at 160 characters.
17
+
18
+ The runner never fails the card. A probe that exits non-zero, prints nothing,
19
+ or runs past one second is silent; the runner stops launching probes once
20
+ three seconds have passed in total. `bash bin/reflexes.sh --debug` names
21
+ every silent probe and the reason.
22
+
23
+ Sessions started minutes apart print the same card, so the runner keeps the
24
+ last card for each harness in `.egregore/runtime/reflexes/` and prints it
25
+ again, for up to ten minutes, while what the probes read is unchanged: the
26
+ probe and `bin/lib` files, everything under `memory/sessions` (records and
27
+ ledgers), the features in `egregore.json`, the person's names and records
28
+ switch in the state file, the local date and nightly's cache (`cache_key` in
29
+ `bin/lib/reflexes.py`). Anything else, such as the memory lookups the
30
+ retrieval line counts, can be up to ten minutes old on the card. `--debug`, a
31
+ sandbox marker, or a run in which a probe ran past its budget or never
32
+ started skips the cache.
33
+
34
+ Every probe gets, on top of the caller's environment:
35
+
36
+ - `EGREGORE_ROOT` the project root
37
+ - `EGREGORE_MEMORY` the memory directory, which may not exist
38
+ - `EGREGORE_STATE` the per-instance state file (`.egregore-state.json`)
39
+ - `EGREGORE_AUTHOR` the person's GitHub username, when known
40
+ - `EGREGORE_HARNESS` `claude`, `codex`, `pi` or `prime`
41
+
42
+ A probe reads records, state and local files; it never reaches the network
43
+ without a cache and a budget, never writes outside `tmp/` (beyond one file
44
+ created and removed to test a directory, as `20-sandbox.sh` does), and never
45
+ prints anything a record would withhold.
46
+
47
+ ## Adding one
48
+
49
+ Add a file here with a two-digit prefix for order (`20-nightly.sh`), make it
50
+ print its line only when its predicate holds, and give it a case in
51
+ `tests/test-reflexes.sh` or a test of its own. A probe that reads a file the
52
+ cache key does not cover shows its change up to ten minutes late; add the
53
+ file to `cache_key` when that matters. The runtime bundle ships this
54
+ directory whole (`BIN_DIRS` in the bundle builder), so a tracked probe needs
55
+ no allowlist entry. An organisation adds its own probes the same way.
56
+
57
+ ## Wording
58
+
59
+ A show line states the fact: `3 open items are older than three days and
60
+ have no owner`. A propose line ends with the command: `... · /records file
61
+ a1b2c3`. No exclamation marks, no advice, no explanation of the mechanism.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ # The reflexes: one runner, a directory of probes. See bin/reflexes/README.md.
3
+ # Prints the probes' lines for the startup card; never fails the card.
4
+ set -uo pipefail
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" || exit 0
6
+ command -v python3 >/dev/null 2>&1 || exit 0
7
+ exec python3 "$SCRIPT_DIR/bin/lib/reflexes.py" --root "$SCRIPT_DIR" "$@"
@@ -14,6 +14,8 @@ ROOT="${EGREGORE_INSTANCE_ROOT:-$RUNTIME_ROOT}"
14
14
  ROOT="$(cd "$ROOT" && pwd)"
15
15
  CONFIG="$ROOT/egregore.json"
16
16
  STATE="$ROOT/.egregore-state.json"
17
+ # shellcheck source=bin/lib/config.sh
18
+ source "$RUNTIME_ROOT/bin/lib/config.sh"
17
19
 
18
20
  resolved() {
19
21
  [ -f "$CONFIG" ] && [ -f "$STATE" ] || return 1
@@ -26,6 +28,114 @@ resolved() {
26
28
  ' "$STATE" >/dev/null 2>&1
27
29
  }
28
30
 
31
+ # The saved address is resolved: an egregore.json saved before the October
32
+ # 2026 move still names the retired origin, which never receives a token.
33
+ connected_config() {
34
+ api_url="$(_config_api_url)" || api_url=""
35
+ slug="$(jq -r '.slug // empty' "$CONFIG" 2>/dev/null)"
36
+ api_key="$(grep '^EGREGORE_API_KEY=' "$ROOT/.env" 2>/dev/null | cut -d= -f2- || true)"
37
+ [ -n "$api_url" ] && [ -n "$slug" ]
38
+ }
39
+
40
+ # The greeting's one note about the organization key (bin/lib/health-footer.sh
41
+ # renders it): "renewed" shows once; "needs_sign_in" shows until renewal works.
42
+ key_notice_state() {
43
+ jq -r '.hosted_key_notice.state // empty' "$STATE" 2>/dev/null || true
44
+ }
45
+
46
+ key_notice() {
47
+ local file tmp
48
+ [ -f "$STATE" ] || return 0
49
+ # A worktree links the checkout's state file: rename onto its target.
50
+ file="$(realpath "$STATE" 2>/dev/null)" || file="$STATE"
51
+ tmp="$(mktemp "$file.XXXXXX" 2>/dev/null)" || return 0
52
+ if [ "$1" = clear ]; then
53
+ jq 'if .hosted_key_notice.state == "needs_sign_in" then del(.hosted_key_notice) else . end' \
54
+ "$file" >"$tmp" 2>/dev/null
55
+ else
56
+ jq --arg state "$1" --arg at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
57
+ '.hosted_key_notice = {state: $state, at: $at}' "$file" >"$tmp" 2>/dev/null
58
+ fi && [ -s "$tmp" ] && mv "$tmp" "$file" || rm -f "$tmp"
59
+ }
60
+
61
+ # One authenticated status read; sets status_code and status_body.
62
+ probe_status() {
63
+ local response=""
64
+ status_code=""
65
+ status_body=""
66
+ [ -n "$api_key" ] || return 0
67
+ response="$(curl -s "$api_url/api/org/status" \
68
+ -H "Authorization: Bearer $api_key" --connect-timeout 2 --max-time 4 \
69
+ -w '\n%{http_code}' 2>/dev/null || true)"
70
+ status_code="${response##*$'\n'}"
71
+ status_body="${response%$'\n'*}"
72
+ }
73
+
74
+ status_ok() {
75
+ [ "$status_code" = 200 ] && printf '%s' "$status_body" | jq -e --arg slug "$slug" '
76
+ (.org_id | type == "string" and length > 0) and .slug == $slug
77
+ ' >/dev/null 2>&1
78
+ }
79
+
80
+ rejection_code() {
81
+ [ "$status_code" = 401 ] || return 0
82
+ printf '%s' "$status_body" | jq -r '.code // empty' 2>/dev/null || true
83
+ }
84
+
85
+ # Recover the configured org's key only through the membership-verifying
86
+ # endpoint, which renews a key retired in the October 2026 security reset.
87
+ # A historic path/namespace collision could also leave this checkout with
88
+ # another Egregore's key. Install it atomically, then verify it. No
89
+ # credential bytes are printed. $1 is the API's code for the rejected key.
90
+ recover_key() {
91
+ local rejected="${1:-}" github_token encoded_slug response code recovered renewed previous_key
92
+ github_token="$(grep '^GITHUB_TOKEN=' "$ROOT/.env" 2>/dev/null | cut -d= -f2- || true)"
93
+ if [ -z "$github_token" ]; then
94
+ [ "$rejected" != key_retired ] || key_notice needs_sign_in
95
+ return 1
96
+ fi
97
+ encoded_slug="$(printf '%s' "$slug" | jq -sRr @uri)"
98
+ response="$(curl -s "$api_url/api/org/$encoded_slug/key" \
99
+ -H "Authorization: Bearer $github_token" --connect-timeout 2 --max-time 12 \
100
+ -w '\n%{http_code}' 2>/dev/null || true)"
101
+ code="${response##*$'\n'}"
102
+ recovered="${response%$'\n'*}"
103
+ if [ "$code" = 401 ]; then
104
+ # The GitHub sign-in was rejected; a fresh one renews the key.
105
+ [ "$rejected" != key_retired ] || key_notice needs_sign_in
106
+ return 1
107
+ fi
108
+ [ "$code" = 200 ] || return 1
109
+ # The key itself must name this org (ek_<slug>_<secret>), not just the reply.
110
+ printf '%s' "$recovered" | jq -e --arg slug "$slug" '
111
+ (.api_key | type == "string" and startswith("ek_" + $slug + "_")
112
+ and length > ($slug | length) + 4)
113
+ and .org_slug == $slug
114
+ ' >/dev/null 2>&1 || return 1
115
+ renewed="$(printf '%s' "$recovered" | jq -r '.renewed == true' 2>/dev/null || echo false)"
116
+ # Prove the candidate works before it replaces the saved key.
117
+ previous_key="$api_key"
118
+ api_key="$(printf '%s' "$recovered" | jq -r '.api_key')"
119
+ probe_status
120
+ if ! status_ok; then
121
+ api_key="$previous_key"
122
+ return 1
123
+ fi
124
+ if ! printf '%s' "$recovered" | \
125
+ EGREGORE_ROOT="$ROOT" \
126
+ PYTHONSAFEPATH=1 PYTHONPATH="$RUNTIME_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
127
+ python3 -m egregore_runtime.identity_cli install-connected-key >/dev/null; then
128
+ api_key="$previous_key"
129
+ return 1
130
+ fi
131
+ api_key="$(grep '^EGREGORE_API_KEY=' "$ROOT/.env" 2>/dev/null | cut -d= -f2- || true)"
132
+ if [ "$rejected" = key_retired ] || [ "$renewed" = true ]; then
133
+ key_notice renewed
134
+ else
135
+ key_notice clear
136
+ fi
137
+ }
138
+
29
139
  case "${1:-ensure}" in
30
140
  status)
31
141
  if resolved; then
@@ -35,56 +145,44 @@ case "${1:-ensure}" in
35
145
  printf '{"status":"unresolved"}\n'
36
146
  exit 1
37
147
  ;;
148
+ recover)
149
+ # A hosted request just rejected the organization key ($2 is the API's
150
+ # code). Once a fresh sign-in is known to be needed, only session start
151
+ # retries, so the notice and the retry stay one per session.
152
+ [ "$(key_notice_state)" != needs_sign_in ] || exit 1
153
+ connected_config || exit 1
154
+ recover_key "${2:-}"
155
+ ;;
38
156
  ensure)
39
- resolved && exit 0
157
+ if resolved; then
158
+ # Healthy starts stop here, with no network call. After a recorded
159
+ # need for a fresh sign-in, retry so the greeting shows the outcome.
160
+ [ "$(key_notice_state)" = needs_sign_in ] || exit 0
161
+ connected_config || exit 0
162
+ probe_status
163
+ if status_ok; then
164
+ key_notice clear
165
+ else
166
+ recover_key "$(rejection_code)" || true
167
+ fi
168
+ exit 0
169
+ fi
40
170
  mode="$(jq -r 'if (.mode // "local") == "connected" or (.api_url // "") != "" then "connected" else "local" end' "$CONFIG" 2>/dev/null || echo local)"
41
171
  if [ "$mode" = "local" ]; then
42
172
  EGREGORE_ROOT="$ROOT" \
43
173
  PYTHONSAFEPATH=1 PYTHONPATH="$RUNTIME_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
44
174
  python3 -m egregore_runtime.identity_cli initialize-local >/dev/null
45
175
  else
46
- api_url="$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)"
47
- slug="$(jq -r '.slug // empty' "$CONFIG" 2>/dev/null)"
48
- api_key="$(grep '^EGREGORE_API_KEY=' "$ROOT/.env" 2>/dev/null | cut -d= -f2-)"
49
- [ -n "$api_url" ] && [ -n "$slug" ] || exit 1
176
+ connected_config || exit 1
50
177
  # Identity repair selects already-existing account and membership
51
- # records, then adopts their opaque IDs locally. If a stale credential
52
- # belongs to another org, recover only the configured org's existing key
53
- # through verified GitHub membership. No control-plane records are made.
54
- control_plane=""
55
- if [ -n "$api_key" ]; then
56
- control_plane="$(curl -sf "$api_url/api/org/status" \
57
- -H "Authorization: Bearer $api_key" --connect-timeout 2 --max-time 4 \
58
- 2>/dev/null || true)"
59
- fi
60
- if ! printf '%s' "$control_plane" | jq -e --arg slug "$slug" '
61
- (.org_id | type == "string" and length > 0) and .slug == $slug
62
- ' >/dev/null 2>&1; then
63
- # A historic path/namespace collision could leave this checkout with
64
- # another Egregore's API key. Recover the configured org's existing key
65
- # only through the membership-verifying endpoint, install it atomically,
66
- # and retry. No credential bytes are printed.
67
- github_token="$(grep '^GITHUB_TOKEN=' "$ROOT/.env" 2>/dev/null | cut -d= -f2-)"
68
- [ -n "$github_token" ] || exit 1
69
- encoded_slug="$(printf '%s' "$slug" | jq -sRr @uri)"
70
- recovered="$(curl -sf "$api_url/api/org/$encoded_slug/key" \
71
- -H "Authorization: Bearer $github_token" --connect-timeout 2 --max-time 12 \
72
- 2>/dev/null || true)"
73
- printf '%s' "$recovered" | jq -e --arg slug "$slug" '
74
- (.api_key | type == "string" and length > 0) and .org_slug == $slug
75
- ' >/dev/null 2>&1 || exit 1
76
- printf '%s' "$recovered" | \
77
- EGREGORE_ROOT="$ROOT" \
78
- PYTHONSAFEPATH=1 PYTHONPATH="$RUNTIME_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
79
- python3 -m egregore_runtime.identity_cli install-connected-key >/dev/null
80
- api_key="$(grep '^EGREGORE_API_KEY=' "$ROOT/.env" 2>/dev/null | cut -d= -f2-)"
81
- control_plane="$(curl -sf "$api_url/api/org/status" \
82
- -H "Authorization: Bearer $api_key" --connect-timeout 2 --max-time 4 \
83
- 2>/dev/null || true)"
84
- printf '%s' "$control_plane" | jq -e --arg slug "$slug" '
85
- (.org_id | type == "string" and length > 0) and .slug == $slug
86
- ' >/dev/null 2>&1 || exit 1
178
+ # records, then adopts their opaque IDs locally. A rejected key is
179
+ # recovered for the configured org only, through verified GitHub
180
+ # membership. No control-plane records are made.
181
+ probe_status
182
+ if ! status_ok; then
183
+ recover_key "$(rejection_code)" || exit 1
87
184
  fi
185
+ control_plane="$status_body"
88
186
  roster="$(curl -sf "$api_url/api/org/$slug/members?include_removed=true" \
89
187
  -H "Authorization: Bearer $api_key" --connect-timeout 2 --max-time 4 \
90
188
  2>/dev/null || true)"
@@ -102,7 +200,7 @@ case "${1:-ensure}" in
102
200
  resolved
103
201
  ;;
104
202
  *)
105
- echo 'usage: bash bin/runtime-identity.sh {ensure|status}' >&2
203
+ echo 'usage: bash bin/runtime-identity.sh {ensure|status|recover [code]}' >&2
106
204
  exit 2
107
205
  ;;
108
206
  esac
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # Render an HTML file or a URL to a PNG with headless Chrome.
3
+ # bin/screenshot.sh <file.html|url> <out.png> [width] [height] (1440 x 2400)
4
+ # Chrome: $EGREGORE_CHROME when set, with no fallback when it is wrong; else
5
+ # the macOS Google Chrome app; else google-chrome or chromium on PATH. An
6
+ # aborted render (no file, or zero bytes) is retried once; each try is capped
7
+ # at 30 s. Prints "<out>: <bytes> bytes"; a failure prints one line naming the
8
+ # cause and exits non-zero.
9
+ # Headless Chrome enforces a minimum window width near 500 px, so phone checks
10
+ # use 600. Heights above about 5400 px yield no file. Run one headless Chrome
11
+ # at a time. Chrome dies at launch under the Codex sandbox, so in a minion run
12
+ # the driver renders (.claude/skills/minion/SKILL.md, LAND).
13
+ set -u
14
+
15
+ die() { printf 'screenshot: %s\n' "$1" >&2; exit "${2:-1}"; }
16
+ whole() { for n in "$@"; do case $n in '' | 0* | *[!0-9]*) return 1 ;; esac; done; }
17
+
18
+ [ $# -ge 2 ] && [ $# -le 4 ] || die "usage: bin/screenshot.sh <file.html|url> <out.png> [width] [height]" 2
19
+ input=$1 out=$2 width=${3:-1440} height=${4:-2400}
20
+ whole "$width" "$height" || die "width and height must be whole pixels, got $width x $height" 2
21
+
22
+ if [[ $input =~ ^[A-Za-z][A-Za-z0-9+.-]*:// ]] || [[ $input == data:* ]] || [[ $input == about:* ]]; then
23
+ url=$input
24
+ else
25
+ [ -f "$input" ] && [ -r "$input" ] || die "input file missing: $input"
26
+ case $input in */*) dir=${input%/*} ;; *) dir=. ;; esac
27
+ dir=$(cd -- "${dir:-/}" && pwd -P) || die "input file missing: $input"
28
+ url=file://
29
+ # Percent-encode every byte outside the unreserved set, so a space, "#", "?",
30
+ # "%", a control character or a non-UTF-8 name reaches Chrome intact.
31
+ # shellcheck disable=SC2046 # the words are od's hex pairs; splitting them is the point
32
+ for byte in $(printf '%s' "${dir%/}/${input##*/}" | od -An -v -tx1); do
33
+ case $byte in
34
+ 2[d-f] | 3[0-9] | 4[1-9a-f] | 5[0-9a] | 5f | 6[1-9a-f] | 7[0-9a] | 7e)
35
+ printf -v char '%b' "\\x$byte"; url+=$char ;;
36
+ *) url+=%$byte ;;
37
+ esac
38
+ done
39
+ fi
40
+
41
+ case $out in */*) outdir=${out%/*} ;; *) outdir=. ;; esac
42
+ [ -d "${outdir:-/}" ] && [ -w "${outdir:-/}" ] || die "output directory missing or not writable: ${outdir:-/}"
43
+ [ ! -d "$out" ] || die "output path is a directory: $out"
44
+
45
+ if [ -n "${EGREGORE_CHROME:-}" ]; then
46
+ chrome=$EGREGORE_CHROME
47
+ [ -f "$chrome" ] && [ -x "$chrome" ] || die "no Chrome found: EGREGORE_CHROME=$chrome is not an executable file"
48
+ else
49
+ chrome="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
50
+ [ -x "$chrome" ] || chrome=$(command -v google-chrome || command -v chromium) \
51
+ || die "no Chrome found: set EGREGORE_CHROME, or install Google Chrome or Chromium"
52
+ fi
53
+
54
+ pid=
55
+ trap '[ -z "$pid" ] || kill "$pid" 2>/dev/null' EXIT
56
+ trap 'exit 130' INT TERM HUP
57
+
58
+ render() { # one try; true when Chrome left a non-empty file at $out
59
+ rm -f -- "$out"; timed_out=0 ticks=0
60
+ "$chrome" --headless=new --disable-gpu --hide-scrollbars --window-size="$width,$height" \
61
+ --virtual-time-budget=4000 --screenshot="$out" "$url" >/dev/null 2>&1 &
62
+ pid=$!
63
+ while kill -0 "$pid" 2>/dev/null; do
64
+ [ "$ticks" -lt 150 ] || { kill -9 "$pid" 2>/dev/null; timed_out=1; break; }
65
+ sleep 0.2; ticks=$((ticks + 1))
66
+ done
67
+ wait "$pid" 2>/dev/null; pid=
68
+ [ -f "$out" ] && [ -s "$out" ]
69
+ }
70
+
71
+ if ! render && ! render; then
72
+ cause="no file at $out"
73
+ [ "$timed_out" = 0 ] || cause="the last try timed out after 30 s"
74
+ [ "$height" -le 5400 ] || cause="$cause; heights above about 5400 px yield no file"
75
+ die "render aborted twice ($cause)"
76
+ fi
77
+ printf '%s: %s bytes\n' "$out" "$(wc -c < "$out" | tr -d ' ')"
@@ -3,7 +3,7 @@ set -euo pipefail
3
3
 
4
4
  ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
5
  export EGREGORE_ROOT="$ROOT"
6
- export EGREGORE_SESSION_ID="${EGREGORE_SESSION_ID:-$(cat "$ROOT/.egregore-session-id" 2>/dev/null || true)}"
6
+ export EGREGORE_SESSION_ID="${EGREGORE_SESSION_ID:-$(bash "$ROOT/bin/session-id.sh" --root "$ROOT" 2>/dev/null || cat "$ROOT/.egregore-session-id" 2>/dev/null || true)}"
7
7
  export PYTHONSAFEPATH=1 PYTHONPATH="$ROOT${PYTHONPATH:+:$PYTHONPATH}"
8
8
 
9
9
  exec python3 -m egregore_runtime.scroll_cli "$@"