@north-light/crouter 0.3.227 → 0.3.228

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 (286) hide show
  1. package/dist/api/client.d.ts +5 -5
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/dto/broker-ops.d.ts +8 -0
  4. package/dist/api/dto/memory.d.ts +8 -7
  5. package/dist/api/dto/memory.js +2 -2
  6. package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
  7. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
  8. package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
  9. package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
  10. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
  11. package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
  12. package/dist/builtin-memory/design/guide.md +38 -16
  13. package/dist/builtin-memory/design/roadmap.md +4 -4
  14. package/dist/builtin-memory/insights/init.md +5 -5
  15. package/dist/builtin-memory/internal/INDEX.md +1 -1
  16. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  17. package/dist/builtin-memory/internal/memory-loading.md +6 -6
  18. package/dist/builtin-memory/internal/plugins.md +1 -1
  19. package/dist/builtin-memory/plan/guide.md +53 -0
  20. package/dist/builtin-memory/plan/roadmap.md +10 -8
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
  22. package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
  23. package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
  24. package/dist/clients/attach/chrome/inbox-strip.js +11 -7
  25. package/dist/clients/attach/session/pane-tag.d.ts +28 -2
  26. package/dist/clients/attach/session/pane-tag.js +48 -22
  27. package/dist/clients/attach/session/profile-files.d.ts +6 -0
  28. package/dist/clients/attach/session/profile-files.js +44 -11
  29. package/dist/clients/attach/slash/dispatch.js +4 -7
  30. package/dist/clients/attach/viewer.js +588 -596
  31. package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
  32. package/dist/commands/api-client.d.ts +3 -7
  33. package/dist/commands/api-client.js +7 -12
  34. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
  35. package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
  36. package/dist/commands/memory/delete.js +51 -24
  37. package/dist/commands/memory/edit.js +50 -10
  38. package/dist/commands/memory/find.js +105 -56
  39. package/dist/commands/memory/history.js +51 -31
  40. package/dist/commands/memory/lint.d.ts +0 -9
  41. package/dist/commands/memory/lint.js +78 -322
  42. package/dist/commands/memory/list.d.ts +12 -4
  43. package/dist/commands/memory/list.js +64 -25
  44. package/dist/commands/memory/move.js +103 -73
  45. package/dist/commands/memory/origin.js +35 -5
  46. package/dist/commands/memory/read.d.ts +4 -0
  47. package/dist/commands/memory/read.js +132 -80
  48. package/dist/commands/memory/shared.d.ts +115 -25
  49. package/dist/commands/memory/shared.js +331 -74
  50. package/dist/commands/memory/write.js +72 -21
  51. package/dist/commands/memory.js +2 -2
  52. package/dist/commands/node/lifecycle.js +2 -2
  53. package/dist/commands/pkg/market-manage.js +25 -24
  54. package/dist/commands/pkg/plugin-manage.d.ts +22 -6
  55. package/dist/commands/pkg/plugin-manage.js +118 -25
  56. package/dist/commands/pkg/shared.d.ts +8 -0
  57. package/dist/commands/pkg/shared.js +23 -20
  58. package/dist/commands/surface-reload.d.ts +5 -0
  59. package/dist/commands/surface-reload.js +9 -1
  60. package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
  61. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
  62. package/dist/commands/sys/migrate.js +126 -140
  63. package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
  64. package/dist/commands/sys/panels/profiles-panel.js +599 -0
  65. package/dist/commands/sys/settings-shell.d.ts +4 -2
  66. package/dist/commands/sys/settings-shell.js +35 -4
  67. package/dist/commands/sys/settings.js +21 -5
  68. package/dist/commands/sys/sync-deps.d.ts +2 -0
  69. package/dist/commands/sys/sync-deps.js +26 -15
  70. package/dist/commands/sys/sync-project-guidance.js +228 -145
  71. package/dist/commands/sys/sync-skills.js +28 -17
  72. package/dist/commands/sys/update.js +11 -3
  73. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
  74. package/dist/core/__tests__/config-change-delta.test.js +106 -0
  75. package/dist/core/__tests__/context-intro.test.js +13 -9
  76. package/dist/core/__tests__/daemon-boot.test.js +22 -16
  77. package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
  78. package/dist/core/__tests__/human-deliver.test.js +19 -0
  79. package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
  80. package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
  81. package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
  82. package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
  83. package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
  84. package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
  85. package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
  86. package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
  87. package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
  88. package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
  89. package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
  90. package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
  91. package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
  92. package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
  93. package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
  94. package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
  95. package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
  96. package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
  97. package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
  98. package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
  99. package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
  100. package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
  101. package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
  102. package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
  103. package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
  104. package/dist/core/__tests__/kickoff.test.js +16 -5
  105. package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
  106. package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
  107. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  108. package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
  109. package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
  110. package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
  111. package/dist/core/__tests__/repository-association.test.d.ts +1 -0
  112. package/dist/core/__tests__/repository-association.test.js +153 -0
  113. package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
  114. package/dist/core/__tests__/repository-root-identity.test.js +219 -0
  115. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
  116. package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
  117. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
  118. package/dist/core/canvas/canvas.d.ts +3 -0
  119. package/dist/core/canvas/canvas.js +30 -10
  120. package/dist/core/canvas/db.js +28 -1
  121. package/dist/core/canvas/paths.d.ts +4 -6
  122. package/dist/core/canvas/paths.js +8 -6
  123. package/dist/core/canvas/render-source.js +2 -2
  124. package/dist/core/canvas/types.d.ts +9 -13
  125. package/dist/core/exclusive-lock.d.ts +2 -0
  126. package/dist/core/exclusive-lock.js +21 -0
  127. package/dist/core/git.d.ts +0 -1
  128. package/dist/core/git.js +0 -3
  129. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
  130. package/dist/core/human/feedback-companion.js +3 -0
  131. package/dist/core/human/scan.js +8 -1
  132. package/dist/core/keybindings/catalog.d.ts +2 -2
  133. package/dist/core/keybindings/catalog.js +2 -1
  134. package/dist/core/memory/doc-link-grammar.js +2 -2
  135. package/dist/core/memory/history.d.ts +24 -0
  136. package/dist/core/memory/history.js +66 -1
  137. package/dist/core/memory/identity.d.ts +65 -0
  138. package/dist/core/memory/identity.js +185 -0
  139. package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
  140. package/dist/core/memory/inline-ref-guidance.js +1 -1
  141. package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
  142. package/dist/core/memory/inline-ref-inventory.js +30 -62
  143. package/dist/core/memory/lint.d.ts +129 -0
  144. package/dist/core/memory/lint.js +517 -0
  145. package/dist/core/memory/project-namespace.d.ts +31 -0
  146. package/dist/core/memory/project-namespace.js +85 -0
  147. package/dist/core/memory/repository-association.d.ts +31 -0
  148. package/dist/core/memory/repository-association.js +129 -0
  149. package/dist/core/memory/tree.d.ts +39 -0
  150. package/dist/core/memory/tree.js +93 -0
  151. package/dist/core/memory-resolver.d.ts +193 -90
  152. package/dist/core/memory-resolver.js +461 -375
  153. package/dist/core/nested-stores.js +6 -13
  154. package/dist/core/profiles/select.d.ts +4 -1
  155. package/dist/core/profiles/select.js +108 -48
  156. package/dist/core/review/__tests__/capture-origin.test.js +2 -2
  157. package/dist/core/review/__tests__/stage-identity.test.js +2 -2
  158. package/dist/core/review/companion.js +11 -2
  159. package/dist/core/runtime/bearings.d.ts +2 -2
  160. package/dist/core/runtime/bearings.js +3 -3
  161. package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
  162. package/dist/core/runtime/broker/rebind.js +5 -0
  163. package/dist/core/runtime/broker-extension-render.d.ts +7 -3
  164. package/dist/core/runtime/broker-extension-render.js +10 -5
  165. package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
  166. package/dist/core/runtime/broker-persona-guidance.js +122 -27
  167. package/dist/core/runtime/deliver-live.d.ts +16 -4
  168. package/dist/core/runtime/deliver-live.js +29 -15
  169. package/dist/core/runtime/kickoff.d.ts +3 -3
  170. package/dist/core/runtime/kickoff.js +8 -16
  171. package/dist/core/runtime/lifecycle.js +2 -3
  172. package/dist/core/runtime/nodes.d.ts +3 -4
  173. package/dist/core/runtime/nodes.js +3 -4
  174. package/dist/core/runtime/persona.d.ts +8 -12
  175. package/dist/core/runtime/persona.js +24 -96
  176. package/dist/core/runtime/promote.d.ts +2 -2
  177. package/dist/core/runtime/promote.js +8 -21
  178. package/dist/core/runtime/revive.js +20 -17
  179. package/dist/core/runtime/spawn.js +18 -7
  180. package/dist/core/runtime/tmux-bindings.js +2 -3
  181. package/dist/core/runtime/warm-pool.js +3 -4
  182. package/dist/core/scope.js +2 -0
  183. package/dist/core/self-update.d.ts +0 -2
  184. package/dist/core/self-update.js +2 -35
  185. package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
  186. package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
  187. package/dist/core/substrate/index.d.ts +2 -2
  188. package/dist/core/substrate/index.js +1 -1
  189. package/dist/core/substrate/injected-store.d.ts +43 -27
  190. package/dist/core/substrate/injected-store.js +208 -104
  191. package/dist/core/substrate/listings.d.ts +19 -12
  192. package/dist/core/substrate/listings.js +75 -52
  193. package/dist/core/substrate/on-read-node.d.ts +4 -7
  194. package/dist/core/substrate/on-read-node.js +6 -8
  195. package/dist/core/substrate/on-read.d.ts +22 -25
  196. package/dist/core/substrate/on-read.js +103 -147
  197. package/dist/core/substrate/render-node.d.ts +4 -7
  198. package/dist/core/substrate/render-node.js +5 -7
  199. package/dist/core/substrate/render.d.ts +21 -3
  200. package/dist/core/substrate/render.js +291 -223
  201. package/dist/core/substrate/schema.d.ts +1 -13
  202. package/dist/core/substrate/schema.js +5 -40
  203. package/dist/core/substrate/session-cache.d.ts +14 -4
  204. package/dist/core/substrate/session-cache.js +40 -22
  205. package/dist/core/substrate/surface-match.d.ts +9 -7
  206. package/dist/core/substrate/surface-match.js +26 -25
  207. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
  208. package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
  209. package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
  210. package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
  211. package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
  212. package/dist/daemon/api/handlers/broker-ops.js +21 -16
  213. package/dist/daemon/api/handlers/memory.js +2 -0
  214. package/dist/daemon/crtrd.js +2 -0
  215. package/dist/daemon/human/finish.js +8 -1
  216. package/dist/daemon/manage.d.ts +0 -1
  217. package/dist/daemon/manage.js +7 -16
  218. package/dist/daemon/startup-policy.d.ts +1 -0
  219. package/dist/daemon/startup-policy.js +1 -0
  220. package/dist/migrations/001-surfaces-frontmatter.js +21 -109
  221. package/dist/migrations/002-profile-project-memory.js +1 -0
  222. package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
  223. package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
  224. package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
  225. package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
  226. package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
  227. package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
  228. package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
  229. package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
  230. package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
  231. package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
  232. package/dist/migrations/__tests__/activation.test.d.ts +1 -0
  233. package/dist/migrations/__tests__/activation.test.js +149 -0
  234. package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
  235. package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
  236. package/dist/migrations/activation.d.ts +16 -0
  237. package/dist/migrations/activation.js +78 -0
  238. package/dist/migrations/convergent.d.ts +14 -3
  239. package/dist/migrations/convergent.js +21 -10
  240. package/dist/migrations/corpus.d.ts +78 -0
  241. package/dist/migrations/corpus.js +497 -0
  242. package/dist/migrations/frontmatter-splice.d.ts +15 -0
  243. package/dist/migrations/frontmatter-splice.js +176 -0
  244. package/dist/migrations/registry.d.ts +6 -1
  245. package/dist/migrations/registry.js +7 -2
  246. package/dist/migrations/runner.d.ts +41 -0
  247. package/dist/migrations/runner.js +81 -0
  248. package/dist/migrations/types.d.ts +148 -9
  249. package/dist/migrations/types.js +9 -2
  250. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
  251. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
  252. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
  253. package/dist/pi-extensions/canvas-context-intro.js +50 -46
  254. package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
  255. package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
  256. package/dist/pi-extensions/canvas-stophook.js +6 -13
  257. package/dist/shared/generated-context.d.ts +0 -3
  258. package/dist/shared/generated-context.js +0 -57
  259. package/dist/shared/tool-groups.js +2 -3
  260. package/package.json +5 -4
  261. package/runtime.lock.json +2 -2
  262. /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
  263. /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
  264. /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
  265. /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
  266. /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
  267. /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
  268. /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
  269. /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
  270. /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
  271. /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
  272. /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
  273. /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
  274. /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
  275. /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
  276. /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
  277. /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
  278. /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
  279. /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
  280. /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
  281. /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
  282. /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
  283. /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
  284. /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
  285. /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
  286. /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
@@ -6,7 +6,7 @@
6
6
  // flat ancestor+profile stack (on-read delivery finds nested stores through
7
7
  // its own read-file walk instead).
8
8
  //
9
- // Two discovery modes per owner dir, both under one shared 500 ms budget:
9
+ // Two discovery modes per owner dir, each under its own 500 ms budget:
10
10
  // • Git root — `git ls-files` over tracked+untracked (respecting .gitignore),
11
11
  // so junk dirs and ignored trees cost nothing and embedded repos are
12
12
  // auto-excluded (ls-files does not descend into submodules).
@@ -55,12 +55,7 @@ export function descendantStoreRoots(ancestorRoots) {
55
55
  ownerSeen.add(real);
56
56
  owners.push(owner);
57
57
  }
58
- const deadline = Date.now() + TIME_BUDGET_MS;
59
- let warned = false;
60
58
  const tripBudget = (owner) => {
61
- if (warned)
62
- return;
63
- warned = true;
64
59
  warn(`memory: nested-store discovery under ${owner} exceeded its ${TIME_BUDGET_MS}ms budget — nested stores may be missing from this listing; register deep stores explicitly (add their dirs to the selected profile's projects)`);
65
60
  };
66
61
  const found = [];
@@ -72,17 +67,15 @@ export function descendantStoreRoots(ancestorRoots) {
72
67
  foundSeen.add(real);
73
68
  found.push(storeRoot);
74
69
  };
70
+ // Each owner gets the full budget: owners are independent trees, so one
71
+ // pathological tree must neither starve the owners after it nor pin the
72
+ // warning on whichever owner happened to be next when a shared clock ran out.
75
73
  for (const owner of owners) {
76
- const remaining = deadline - Date.now();
77
- if (remaining <= 0) {
78
- tripBudget(owner);
79
- break;
80
- }
81
74
  if (pathExists(join(owner, '.git'))) {
82
- collectGitStores(owner, add, remaining, () => tripBudget(owner));
75
+ collectGitStores(owner, add, TIME_BUDGET_MS, () => tripBudget(owner));
83
76
  }
84
77
  else {
85
- collectWalkStores(owner, add, deadline, () => tripBudget(owner));
78
+ collectWalkStores(owner, add, Date.now() + TIME_BUDGET_MS, () => tripBudget(owner));
86
79
  }
87
80
  }
88
81
  cache.set(key, found);
@@ -1,6 +1,6 @@
1
1
  import { type ProfileEntry } from './manifest.js';
2
2
  import { type BindingId, type BindingResolution, type RawTerminalInput } from '../keybindings/index.js';
3
- declare const PROFILE_MENU_ACTIONS: readonly ["crtr.profile.menu.abort", "crtr.profile.menu.up", "crtr.profile.menu.down", "crtr.profile.menu.select", "crtr.profile.menu.accept-default", "crtr.profile.menu.slot.1", "crtr.profile.menu.slot.2", "crtr.profile.menu.slot.3", "crtr.profile.menu.slot.4", "crtr.profile.menu.slot.5", "crtr.profile.menu.slot.6", "crtr.profile.menu.slot.7", "crtr.profile.menu.slot.8", "crtr.profile.menu.slot.9", "crtr.profile.menu.create", "crtr.profile.menu.search-existing", "crtr.profile.menu.use-root", "crtr.profile.menu.set-default", "crtr.profile.menu.add-directory", "crtr.profile.menu.unpin"];
3
+ declare const PROFILE_MENU_ACTIONS: readonly ["crtr.profile.menu.abort", "crtr.profile.menu.up", "crtr.profile.menu.down", "crtr.profile.menu.select", "crtr.profile.menu.accept-default", "crtr.profile.menu.slot.1", "crtr.profile.menu.slot.2", "crtr.profile.menu.slot.3", "crtr.profile.menu.slot.4", "crtr.profile.menu.slot.5", "crtr.profile.menu.slot.6", "crtr.profile.menu.slot.7", "crtr.profile.menu.slot.8", "crtr.profile.menu.slot.9", "crtr.profile.menu.create", "crtr.profile.menu.search-existing", "crtr.profile.menu.use-root", "crtr.profile.menu.set-default", "crtr.profile.menu.add-directory", "crtr.profile.menu.unpin", "crtr.profile.menu.manage"];
4
4
  declare const PROFILE_SEARCH_ACTIONS: readonly ["crtr.profile.search.abort", "crtr.profile.search.back", "crtr.profile.search.pick", "crtr.profile.search.up", "crtr.profile.search.down", "crtr.profile.search.delete"];
5
5
  export declare function resolveProfileMenuAction(bindings: BindingResolution<BindingId>, raw: RawTerminalInput): (typeof PROFILE_MENU_ACTIONS)[number] | null;
6
6
  export declare function resolveProfileSearchAction(bindings: BindingResolution<BindingId>, raw: RawTerminalInput): (typeof PROFILE_SEARCH_ACTIONS)[number] | null;
@@ -11,6 +11,9 @@ export declare function resolveProfileSearchAction(bindings: BindingResolution<B
11
11
  export declare function profileCoversCwd(entry: ProfileEntry, cwd: string): boolean;
12
12
  /** Resolve the headless selector against existing state without changing it. */
13
13
  export declare function selectProfileForCwdReadOnly(cwd: string): string;
14
+ /** Collapse the home prefix to `~` so project paths read short. Exported so the
15
+ * Profiles settings panel prints a project dir exactly as this menu does. */
16
+ export declare function tildify(p: string): string;
14
17
  /** Select the profile a node about to boot at `cwd` should run under.
15
18
  *
16
19
  * 1. `explicitProfile` present → resolve id/name via `loadProfileManifest`. If
@@ -7,6 +7,7 @@
7
7
  // root decision when nothing covers. This is the ONLY place that decision
8
8
  // runs; front-door.ts/
9
9
  // spawn.ts call it, never re-derive it.
10
+ import { spawnSync } from 'node:child_process';
10
11
  import { existsSync, realpathSync } from 'node:fs';
11
12
  import { homedir } from 'node:os';
12
13
  import { basename, resolve as resolvePath, sep } from 'node:path';
@@ -15,6 +16,8 @@ import { emitKeypressEvents } from 'node:readline';
15
16
  import { listProfiles, loadProfileManifest, updateProfileLastUsed, createProfile, addProfileProject, ensureRootProfile, ROOT_PROFILE_ID, } from './manifest.js';
16
17
  import { getDefaultProfileId, setDefaultProfileId, clearDefaultProfile, } from './default-binding.js';
17
18
  import { stdoutColor } from '../output.js';
19
+ import { inTmux } from '../runtime/placement-tmux.js';
20
+ import { surfaceTmuxStyleArgs } from '../runtime/surface-bg.js';
18
21
  import { formatBinding, matchesTerminalInput, resolveUserKeybindings, } from '../keybindings/index.js';
19
22
  const PROFILE_MENU_ACTIONS = [
20
23
  'crtr.profile.menu.abort', 'crtr.profile.menu.up', 'crtr.profile.menu.down',
@@ -24,6 +27,7 @@ const PROFILE_MENU_ACTIONS = [
24
27
  'crtr.profile.menu.slot.7', 'crtr.profile.menu.slot.8', 'crtr.profile.menu.slot.9',
25
28
  'crtr.profile.menu.create', 'crtr.profile.menu.search-existing', 'crtr.profile.menu.use-root',
26
29
  'crtr.profile.menu.set-default', 'crtr.profile.menu.add-directory', 'crtr.profile.menu.unpin',
30
+ 'crtr.profile.menu.manage',
27
31
  ];
28
32
  const PROFILE_SEARCH_ACTIONS = [
29
33
  'crtr.profile.search.abort', 'crtr.profile.search.back', 'crtr.profile.search.pick',
@@ -133,8 +137,9 @@ const accent = stdoutColor.cyan;
133
137
  function key(k) {
134
138
  return accent(bold(k));
135
139
  }
136
- /** Collapse the home prefix to `~` so project paths read short. */
137
- function tildify(p) {
140
+ /** Collapse the home prefix to `~` so project paths read short. Exported so the
141
+ * Profiles settings panel prints a project dir exactly as this menu does. */
142
+ export function tildify(p) {
138
143
  const home = homedir();
139
144
  if (p === home)
140
145
  return '~';
@@ -305,6 +310,34 @@ function runMenu(bindings, title, subtitle, rows, opts = {}) {
305
310
  draw();
306
311
  });
307
312
  }
313
+ // ---------------------------------------------------------------------------
314
+ // The profile manager. `v` on either selection gate opens the Profiles page of
315
+ // `crtr sys settings` in a tmux popup: this menu is a CHOOSER filtered to cwd,
316
+ // while that page is the whole map — every profile, every directory each one
317
+ // owns, every pin. The popup runs to completion with the raw-mode menu already
318
+ // torn down (runMenu cleans up before resolving an action), and the caller then
319
+ // re-runs the whole selector, so a directory just adopted or a default just
320
+ // pinned decides the menu you come back to.
321
+ // ---------------------------------------------------------------------------
322
+ /** Open the profiles page in a tmux popup and block until it closes. False when
323
+ * there is no tmux to host it — possible only on the `crtr node new` path, as
324
+ * the front door requires tmux before it ever selects — after printing the
325
+ * pointer, so the caller can simply redraw its menu. */
326
+ function openProfileManager(cwd) {
327
+ if (!inTmux()) {
328
+ process.stdout.write(` ${dim('the profile manager needs tmux \u2014 run `crtr sys settings` instead')}\n`);
329
+ return false;
330
+ }
331
+ spawnSync('tmux', [
332
+ 'display-popup', '-E', '-T', ' profiles ', '-w', '90%', '-h', '85%', '-d', cwd,
333
+ ...surfaceTmuxStyleArgs(),
334
+ 'crtr sys settings --page profiles',
335
+ ], { stdio: 'inherit' });
336
+ return true;
337
+ }
338
+ /** The gate answering "the user managed their profiles" — every prior reading of
339
+ * this directory is stale, so `selectProfileForCwd` decides again from the top. */
340
+ const MANAGED = Symbol('profile-manager-ran');
308
341
  /** The trailing readline prompt: ` › <dim hint> `. */
309
342
  function caret(hint) {
310
343
  return ` ${accent('\u203a')} ${dim(hint)} `;
@@ -484,7 +517,17 @@ async function promptCreateOrRoot(cwd, bindings) {
484
517
  for (;;) {
485
518
  const res = await runMenu(bindings, 'No profile covers this directory', undefined, rows, {
486
519
  initialIndex: 2,
520
+ actionBindingIds: ['crtr.profile.menu.manage'],
521
+ hint: `${formatBinding(bindings, 'crtr.profile.menu.manage')}=manage profiles`,
487
522
  });
523
+ // Managing may have given this directory an owner — re-decide from the top
524
+ // rather than re-showing a menu the manager just made wrong. With no tmux to
525
+ // host the manager nothing changed, so fall through to a redraw.
526
+ if (res.action === 'crtr.profile.menu.manage') {
527
+ if (openProfileManager(cwd))
528
+ return MANAGED;
529
+ continue;
530
+ }
488
531
  if (res.index === 0) {
489
532
  const rl = createInterface({ input: process.stdin, output: process.stdout });
490
533
  try {
@@ -559,7 +602,7 @@ async function promptPickProfileOrCreate(candidates, cwd, exact, pinnedId, bindi
559
602
  rows.push({ label: dim('create a new profile here'), detail: '', bindingId: 'crtr.profile.menu.create' });
560
603
  // Row-scoped actions pin the highlighted profile, widen its purview (hidden
561
604
  // when candidates already claim cwd exactly), or clear an existing pin.
562
- const actionBindingIds = ['crtr.profile.menu.set-default'];
605
+ const actionBindingIds = ['crtr.profile.menu.set-default', 'crtr.profile.menu.manage'];
563
606
  if (!exact)
564
607
  actionBindingIds.push('crtr.profile.menu.add-directory');
565
608
  if (pinned !== null)
@@ -569,9 +612,18 @@ async function promptPickProfileOrCreate(candidates, cwd, exact, pinnedId, bindi
569
612
  hintParts.push(`${formatBinding(bindings, 'crtr.profile.menu.add-directory')}=add dir here`);
570
613
  if (pinned !== null)
571
614
  hintParts.push(`${formatBinding(bindings, 'crtr.profile.menu.unpin')}=unpin`);
615
+ hintParts.push(`${formatBinding(bindings, 'crtr.profile.menu.manage')}=manage profiles`);
572
616
  const res = await runMenu(bindings, exact ? 'Choose a profile' : 'Select a profile', exact
573
617
  ? 'Several profiles claim this directory as their project'
574
618
  : 'This directory falls under existing profile(s)', rows, { actionBindingIds, hint: hintParts.join(' ') });
619
+ // The manager can adopt dirs, pin a default, create or delete profiles — every
620
+ // row here may be stale, so the caller re-resolves from the top. Without tmux
621
+ // nothing changed: re-show this same menu.
622
+ if (res.action === 'crtr.profile.menu.manage') {
623
+ return openProfileManager(cwd)
624
+ ? MANAGED
625
+ : await promptPickProfileOrCreate(candidates, cwd, exact, pinnedId, bindings);
626
+ }
575
627
  // The create row is last; selecting it (Enter on it, or `c`) creates here.
576
628
  if (res.index >= ordered.length) {
577
629
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -640,51 +692,59 @@ export async function selectProfileForCwd(cwd, explicitProfile, forcePicker = fa
640
692
  updateProfileLastUsed(entry.profileId);
641
693
  return entry.profileId;
642
694
  }
643
- const { covering, exact, pinnedId, pinned } = profileSelectionState(resolvedCwd);
644
- // A per-cwd pinned default (set from the menu with `d`) is the user's
645
- // explicit "default to THIS profile HERE". It outranks both the exact-single
646
- // auto-pick and global-MRU, so long as it still covers cwd; a stale pin
647
- // (profile deleted, or its dir removed from purview) is self-healed away.
648
- if (pinnedId !== null && pinned === null)
649
- clearDefaultProfile(resolvedCwd);
650
- // A valid pin decides it outright and STOPS ASKING — the whole point of
651
- // setting a default. Auto-pick it silently: the crouton banner the viewer
652
- // paints a beat later shows the active profile and the `crtr --pick-profile`
653
- // hint, so a boot-time breadcrumb here would just be redundant.
654
- // `crtr --pick-profile` (forcePicker) re-opens the menu to re-pin/unpin.
655
- if (pinned !== null && !forcePicker) {
656
- updateProfileLastUsed(pinned.profileId);
657
- return pinned.profileId;
658
- }
659
- // Exactly one profile's project dir IS cwd — unambiguous project root, use it
660
- // silently, never a prompt. (Several exact claims fall through to the menu.)
661
- // Suppressed under forcePicker so `--pick-profile` can pin even a lone owner.
662
- if (!forcePicker && pinned === null && exact.length === 1) {
663
- updateProfileLastUsed(exact[0].profileId);
664
- return exact[0].profileId;
665
- }
666
- // Non-interactive: cannot prompt. Prefer an exact claimant, else any covering
667
- // profile (both MRU), else root.
668
- if (!isInteractive()) {
669
- const pool = exact.length > 0 ? exact : covering;
670
- if (pool.length > 0) {
671
- const winner = pickMostRecent(pool);
672
- updateProfileLastUsed(winner.profileId);
673
- return winner.profileId;
695
+ // Looped for the one gate that changes the world before it answers: `v` opens
696
+ // the profile manager, which can adopt this directory, pin a default, or
697
+ // create a profile — so the decision is taken again from the top rather than
698
+ // resumed inside a menu the manager just invalidated.
699
+ for (;;) {
700
+ const { covering, exact, pinnedId, pinned } = profileSelectionState(resolvedCwd);
701
+ // A per-cwd pinned default (set from the menu with `d`) is the user's
702
+ // explicit "default to THIS profile HERE". It outranks both the exact-single
703
+ // auto-pick and global-MRU, so long as it still covers cwd; a stale pin
704
+ // (profile deleted, or its dir removed from purview) is self-healed away.
705
+ if (pinnedId !== null && pinned === null)
706
+ clearDefaultProfile(resolvedCwd);
707
+ // A valid pin decides it outright and STOPS ASKING — the whole point of
708
+ // setting a default. Auto-pick it silently: the crouton banner the viewer
709
+ // paints a beat later shows the active profile and the `crtr --pick-profile`
710
+ // hint, so a boot-time breadcrumb here would just be redundant.
711
+ // `crtr --pick-profile` (forcePicker) re-opens the menu to re-pin/unpin.
712
+ if (pinned !== null && !forcePicker) {
713
+ updateProfileLastUsed(pinned.profileId);
714
+ return pinned.profileId;
715
+ }
716
+ // Exactly one profile's project dir IS cwd — unambiguous project root, use it
717
+ // silently, never a prompt. (Several exact claims fall through to the menu.)
718
+ // Suppressed under forcePicker so `--pick-profile` can pin even a lone owner.
719
+ if (!forcePicker && pinned === null && exact.length === 1) {
720
+ updateProfileLastUsed(exact[0].profileId);
721
+ return exact[0].profileId;
722
+ }
723
+ // Non-interactive: cannot prompt. Prefer an exact claimant, else any covering
724
+ // profile (both MRU), else root.
725
+ if (!isInteractive()) {
726
+ const pool = exact.length > 0 ? exact : covering;
727
+ if (pool.length > 0) {
728
+ const winner = pickMostRecent(pool);
729
+ updateProfileLastUsed(winner.profileId);
730
+ return winner.profileId;
731
+ }
732
+ const root = ensureRootProfile();
733
+ process.stderr.write(rootProfileMessage(resolvedCwd));
734
+ return root.profileId;
674
735
  }
675
- const root = ensureRootProfile();
676
- process.stderr.write(rootProfileMessage(resolvedCwd));
677
- return root.profileId;
736
+ // Interactive. Several exact claimants → choose among them. Else covered from
737
+ // a parent/workspace dir → menu of covering profiles. Else nothing covers →
738
+ // the create-or-root prompt (root is only ever offered there).
739
+ const validPinnedId = pinned !== null ? pinned.profileId : null;
740
+ const chosen = exact.length > 1
741
+ ? await promptPickProfileOrCreate(exact, resolvedCwd, true, validPinnedId, bindings)
742
+ : covering.length > 0
743
+ ? await promptPickProfileOrCreate(covering, resolvedCwd, false, validPinnedId, bindings)
744
+ : await promptCreateOrRoot(resolvedCwd, bindings);
745
+ if (chosen === MANAGED)
746
+ continue;
747
+ updateProfileLastUsed(chosen);
748
+ return chosen;
678
749
  }
679
- // Interactive. Several exact claimants → choose among them. Else covered from
680
- // a parent/workspace dir → menu of covering profiles. Else nothing covers →
681
- // the create-or-root prompt (root is only ever offered there).
682
- const validPinnedId = pinned !== null ? pinned.profileId : null;
683
- const chosen = exact.length > 1
684
- ? await promptPickProfileOrCreate(exact, resolvedCwd, true, validPinnedId, bindings)
685
- : covering.length > 0
686
- ? await promptPickProfileOrCreate(covering, resolvedCwd, false, validPinnedId, bindings)
687
- : await promptCreateOrRoot(resolvedCwd, bindings);
688
- updateProfileLastUsed(chosen);
689
- return chosen;
690
750
  }
@@ -1,5 +1,5 @@
1
1
  import assert from 'node:assert/strict';
2
- import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdtempSync, readdirSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { afterEach, test } from 'node:test';
@@ -92,7 +92,7 @@ test('detached capture takes provider/id and explicit thinking from the launch r
92
92
  const { session, sourceFile } = seededOrigin({ launchModel: 'p/m:high', thinking: 'low' });
93
93
  const captured = await capture({ launchModel: 'p/m:high', thinking: 'low' });
94
94
  assert.deepEqual(captured, {
95
- sourceFile,
95
+ sourceFile: realpathSync(sourceFile),
96
96
  sourceSessionId: session.getSessionId(),
97
97
  leafId: session.getLeafId(),
98
98
  modelSpec: 'p/m:high',
@@ -1,5 +1,5 @@
1
1
  import assert from 'node:assert/strict';
2
- import { mkdtempSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { mkdtempSync, mkdirSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { afterEach, test } from 'node:test';
@@ -63,7 +63,7 @@ function setup() {
63
63
  const original = SessionManager.create(sessions);
64
64
  original.appendThinkingLevelChange('low');
65
65
  original.appendMessage(assistant('m'));
66
- const sourceFile = original.getSessionFile();
66
+ const sourceFile = realpathSync(original.getSessionFile());
67
67
  createNode({
68
68
  node_id: 'origin',
69
69
  name: 'origin',
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { basename, isAbsolute, resolve } from 'node:path';
4
4
  import { BrokerClient } from '../broker-client/index.js';
5
- import { getNode } from '../canvas/index.js';
5
+ import { deleteNode, getNode } from '../canvas/index.js';
6
6
  import { ensureReviewDir, reviewBranchPath } from '../canvas/paths.js';
7
7
  import { expandModelCandidates, selectInitialModelRoute } from '../model-routes.js';
8
8
  import { buildLaunchSpec } from '../runtime/launch.js';
@@ -12,6 +12,7 @@ import { spawnNode } from '../runtime/nodes.js';
12
12
  import { SessionManager } from '../runtime/broker-sdk.js';
13
13
  import { materializeSessionBranchAt } from '../runtime/session-branch.js';
14
14
  import { isReviewBoundary } from '../runtime/session-visibility.js';
15
+ import { initializeForkContextExposure } from '../substrate/injected-store.js';
15
16
  import { ReviewOperationError } from './types.js';
16
17
  const EXACT_MODEL_SPEC = /^[^/\s:]+\/[^/\s:]+:(?:off|minimal|low|medium|high|xhigh|max)$/;
17
18
  const BIND_POLL_MS = 100;
@@ -155,7 +156,7 @@ export function spawnReviewCompanion(spec) {
155
156
  cwd: spec.cwd,
156
157
  profileId: spec.profileId,
157
158
  });
158
- spawnNode({
159
+ const node = spawnNode({
159
160
  nodeId: spec.nodeId,
160
161
  kind: 'review/companion',
161
162
  mode: 'base',
@@ -165,6 +166,7 @@ export function spawnReviewCompanion(spec) {
165
166
  parent: spec.originNodeId,
166
167
  subscribe: false,
167
168
  forkFrom: spec.branchFile,
169
+ forkSourceFile: spec.branchFile,
168
170
  modelOverride: spec.modelSpec,
169
171
  reviewBinding: {
170
172
  review_id: spec.reviewId,
@@ -175,6 +177,13 @@ export function spawnReviewCompanion(spec) {
175
177
  name: `review ${basename(spec.targetFile)}`,
176
178
  launch,
177
179
  });
180
+ try {
181
+ initializeForkContextExposure(node.node_id, spec.originNodeId);
182
+ }
183
+ catch (error) {
184
+ deleteNode(node.node_id);
185
+ throw error;
186
+ }
178
187
  }
179
188
  /** A companion is bound only when its session coordinates and visible boundary are durable. */
180
189
  export function isReviewCompanionBound(nodeId) {
@@ -1,5 +1,5 @@
1
1
  import { type Cron } from '../canvas/index.js';
2
- import type { InjectedDocs } from '../substrate/injected-store.js';
2
+ import type { ExposureTarget } from '../substrate/injected-store.js';
3
3
  /** The bearings custom type is shared with every attached renderer. */
4
4
  export { CONTEXT_INTRO_CUSTOM_TYPE } from '../../shared/generated-context.js';
5
5
  /** The `<project_context>` block — the `<environment>` snapshot only: a
@@ -78,4 +78,4 @@ export declare function buildWakeBearings(origin: WakeOrigin): string;
78
78
  * AGENTS.md/CLAUDE.md injection stays suppressed (`noContextFiles` in
79
79
  * broker.ts); `crtr sys sync project-guidance` imports that guidance into
80
80
  * the directory's `.crouter/memory/INDEX.md` front door. */
81
- export declare function buildContextBearings(nodeId: string, seen?: InjectedDocs): string;
81
+ export declare function buildContextBearings(nodeId: string, target?: ExposureTarget): string;
@@ -346,7 +346,7 @@ function managedWorktreeNote(node) {
346
346
  * AGENTS.md/CLAUDE.md injection stays suppressed (`noContextFiles` in
347
347
  * broker.ts); `crtr sys sync project-guidance` imports that guidance into
348
348
  * the directory's `.crouter/memory/INDEX.md` front door. */
349
- export function buildContextBearings(nodeId, seen) {
349
+ export function buildContextBearings(nodeId, target) {
350
350
  const node = getNode(nodeId);
351
351
  const bearings = ['<crtr-bearings>', buildIdentityAssertion(nodeId)];
352
352
  const map = buildGraphMap(nodeId);
@@ -365,9 +365,9 @@ export function buildContextBearings(nodeId, seen) {
365
365
  bearings.push('', orchestratorContextNote(nodeId));
366
366
  bearings.push('</crtr-bearings>');
367
367
  // The consultable catalog rides BESIDE the bearings, not inside them.
368
- const knowledge = renderKnowledgeBlock(nodeId, seen);
368
+ const knowledge = renderKnowledgeBlock(nodeId, target);
369
369
  // A workspace mount is the initial read of its root (`on: workspace-open`).
370
- const workspaceContext = renderWorkspaceOpenDocs(nodeId, seen);
370
+ const workspaceContext = renderWorkspaceOpenDocs(nodeId, target);
371
371
  // Hidden applet/situation-origin ambient context (see situational-context.ts)
372
372
  // — the stored `situational` card, placed in the bearings BODY, so it is a
373
373
  // nested card inside the outer envelope. Never routed through the visible
@@ -1,4 +1,4 @@
1
- import type { BrokerExtensionStateDTO, BrokerGeneratedNameRequest, BrokerGeneratedNameResultDTO, BrokerInboxCursorDirective, BrokerInboxCursorRequest, BrokerModelCommitRequest, BrokerModelCommitResultDTO, BrokerPersonaAckRequest, BrokerSessionBoundRequest, BrokerSessionBoundResultDTO, BrokerSettleDirective, BrokerSettleRequest } from '../../../api/dto/broker-ops.js';
1
+ import type { BrokerExtensionStateDTO, BrokerGeneratedNameRequest, BrokerGeneratedNameResultDTO, BrokerInboxCursorDirective, BrokerInboxCursorRequest, BrokerModelCommitRequest, BrokerModelCommitResultDTO, BrokerPersonaAckRequest, BrokerPersonaAckResultDTO, BrokerSessionBoundRequest, BrokerSessionBoundResultDTO, BrokerSettleDirective, BrokerSettleRequest } from '../../../api/dto/broker-ops.js';
2
2
  import type { RelaunchRootResultDTO } from '../../../api/dto/lifecycle.js';
3
3
  import type { PushReportRequest } from '../../../api/dto/reports.js';
4
4
  export declare function bindBrokerSession(nodeId: string, request: BrokerSessionBoundRequest): Promise<BrokerSessionBoundResultDTO>;
@@ -9,7 +9,7 @@ export declare function advanceBrokerInboxCursor(nodeId: string, request: Broker
9
9
  export declare function commitBrokerModel(nodeId: string, request: BrokerModelCommitRequest): Promise<BrokerModelCommitResultDTO>;
10
10
  export declare function brokerExtensionState(nodeId: string): Promise<BrokerExtensionStateDTO>;
11
11
  export declare function commitBrokerGeneratedName(nodeId: string, request: BrokerGeneratedNameRequest): Promise<BrokerGeneratedNameResultDTO>;
12
- export declare function commitBrokerPersonaAck(nodeId: string, request: BrokerPersonaAckRequest): Promise<void>;
12
+ export declare function commitBrokerPersonaAck(nodeId: string, request: BrokerPersonaAckRequest): Promise<BrokerPersonaAckResultDTO>;
13
13
  /** Reports still use the canonical daemon report handler; this broker adapter
14
14
  * only preserves terminal structured output's idempotent finalization result. */
15
15
  export declare function publishBrokerReport(nodeId: string, request: PushReportRequest): Promise<void>;
@@ -3,6 +3,7 @@ import { join } from 'node:path';
3
3
  import { jobDir } from '../../canvas/paths.js';
4
4
  import { emitEvent } from '../../events/emit.js';
5
5
  import { operationIdContext } from '../../events/operation-id.js';
6
+ import { installPersonaTransitionGate } from '../broker-persona-guidance.js';
6
7
  import { abortSessionFromExtension } from './extension-abort.js';
7
8
  const MAX_ADMITTED_REPLACEMENTS = 2;
8
9
  const REPLACEMENT_DEADLINE_MS = 30_000;
@@ -79,6 +80,10 @@ export class RebindDriver {
79
80
  // fake-engine fixture has no `.agent`.
80
81
  const agent = candidateSession.agent;
81
82
  if (agent) {
83
+ installPersonaTransitionGate(this.deps.nodeId, {
84
+ agent,
85
+ sessionManager: candidateSession.sessionManager,
86
+ });
82
87
  agent.steeringMode = 'all';
83
88
  agent.followUpMode = 'all';
84
89
  }
@@ -1,4 +1,5 @@
1
- import type { InjectedDocs } from '../substrate/injected-store.js';
1
+ import type { NodeConfigSubject } from '../substrate/subject-fields.js';
2
+ import type { ExposureTarget } from '../substrate/injected-store.js';
2
3
  import type { BrokerExtensionNodeDTO, BrokerExtensionStateDTO } from '../../api/dto/broker-ops.js';
3
4
  export declare function editorLabelForBrokerNode(node: BrokerExtensionNodeDTO): string;
4
5
  /** Same environment-only project context rendered at normal boot, without a
@@ -7,9 +8,12 @@ export declare function buildProjectContextBlockForBroker(cwd: string): string;
7
8
  /** `situational` is the node's stored situational card, placed in the bearings
8
9
  * BODY — the one place in the system where a runtime card nests inside
9
10
  * another (see situational-context.ts). '' when none is set. */
10
- export declare function buildContextBearingsFromState(state: BrokerExtensionStateDTO, projectContext: string, situational?: string, seen?: InjectedDocs): string;
11
+ export declare function buildContextBearingsFromState(state: BrokerExtensionStateDTO, projectContext: string, situational?: string, target?: ExposureTarget): string;
11
12
  export declare function buildForkBearingsFromState(state: BrokerExtensionStateDTO, situational?: string): string;
12
- export declare function renderPreferencesFromState(state: BrokerExtensionStateDTO, seen?: InjectedDocs): string;
13
+ export declare function renderPreferencesFromState(state: BrokerExtensionStateDTO, target?: ExposureTarget): string;
14
+ /** The same preference block without the extension-state DTO, for the daemon-side
15
+ * claim path that holds a node's subject directly. */
16
+ export declare function renderPreferencesForNodeSubject(nodeId: string, subject: NodeConfigSubject | null, target?: ExposureTarget): string;
13
17
  /** A canonical report ref has already been validated by crtrd in state. The
14
18
  * broker reads only its local filesystem content; no canvas accessor is needed. */
15
19
  export declare function reportPathFromRef(ref: string, reportNodes: ReadonlyMap<string, unknown>): string | null;
@@ -142,7 +142,7 @@ function reviewNote(node) {
142
142
  /** `situational` is the node's stored situational card, placed in the bearings
143
143
  * BODY — the one place in the system where a runtime card nests inside
144
144
  * another (see situational-context.ts). '' when none is set. */
145
- export function buildContextBearingsFromState(state, projectContext, situational = '', seen) {
145
+ export function buildContextBearingsFromState(state, projectContext, situational = '', target) {
146
146
  const { node } = state;
147
147
  const bearings = ['<crtr-bearings>', identity(state)];
148
148
  const map = graphMap(state);
@@ -158,8 +158,8 @@ export function buildContextBearingsFromState(state, projectContext, situational
158
158
  if (node.mode === 'orchestrator')
159
159
  bearings.push('', `Because you persist across refresh cycles, your context directory (${contextDir(node.node_id)}) is also where a future cycle of you resumes the work — keep the working notes and decisions a refreshed you would need there, alongside the docs you share with the nodes you spawn.`);
160
160
  bearings.push('</crtr-bearings>');
161
- const knowledge = renderKnowledgeForSubject(state.subject, node.node_id, seen);
162
- const workspace = renderWorkspaceOpenDocsForSubject(state.subject, node.cwd, node.profile_id, seen);
161
+ const knowledge = renderKnowledgeForSubject(state.subject, node.node_id, target);
162
+ const workspace = renderWorkspaceOpenDocsForSubject(state.subject, node.cwd, node.profile_id, target);
163
163
  return [bearings.join('\n'), knowledge, workspace, situational, projectContext].filter((part) => part !== '').join('\n');
164
164
  }
165
165
  export function buildForkBearingsFromState(state, situational = '') {
@@ -186,8 +186,13 @@ export function buildForkBearingsFromState(state, situational = '') {
186
186
  bearings.push('', 'The first real user message after these bearings is your mandate.', '</crtr-bearings>');
187
187
  return [bearings.join('\n'), situational].filter((part) => part !== '').join('\n');
188
188
  }
189
- export function renderPreferencesFromState(state, seen) {
190
- return interpolateNodePaths(renderPreferencesForSubject(state.subject, seen).block, state.node.node_id);
189
+ export function renderPreferencesFromState(state, target) {
190
+ return renderPreferencesForNodeSubject(state.node.node_id, state.subject, target);
191
+ }
192
+ /** The same preference block without the extension-state DTO, for the daemon-side
193
+ * claim path that holds a node's subject directly. */
194
+ export function renderPreferencesForNodeSubject(nodeId, subject, target) {
195
+ return interpolateNodePaths(renderPreferencesForSubject(subject, target).block, nodeId);
191
196
  }
192
197
  /** A canonical report ref has already been validated by crtrd in state. The
193
198
  * broker reads only its local filesystem content; no canvas accessor is needed. */
@@ -1,13 +1,27 @@
1
- import type { BrokerExtensionNodeDTO } from '../../api/dto/broker-ops.js';
1
+ import type { Agent } from '@earendil-works/pi-agent-core';
2
+ import type { BrokerExtensionStateDTO } from '../../api/dto/broker-ops.js';
2
3
  export interface BrokerPersona {
4
+ kind: string;
3
5
  mode: 'base' | 'orchestrator';
4
6
  lifecycle: 'terminal' | 'resident';
5
7
  }
6
8
  export interface BrokerPersonaDrift {
7
9
  from: BrokerPersona;
8
10
  to: BrokerPersona;
9
- guidance: string;
10
11
  }
11
- export declare function transitionGuidanceFromBrokerState(node: BrokerExtensionNodeDTO, from: BrokerPersona, to: BrokerPersona): string;
12
- /** Return an unacknowledged persona transition from crtrd's fixed extension state. */
13
- export declare function personaDriftFromBrokerState(node: BrokerExtensionNodeDTO): BrokerPersonaDrift | null;
12
+ export declare function personaDriftFromBrokerState(state: BrokerExtensionStateDTO): BrokerPersonaDrift | null;
13
+ /** A new context's current snapshots make a rank delta unnecessary. */
14
+ export declare function acknowledgePersonaBaseline(state: BrokerExtensionStateDTO): Promise<boolean>;
15
+ /** Append pending transitions until the delivered persona matches current canvas state. */
16
+ export declare function deliverPendingPersonaTransition(initialState: BrokerExtensionStateDTO, deliver: (content: string) => void | Promise<void>): Promise<boolean>;
17
+ interface PersonaTransitionSession {
18
+ agent: Pick<Agent, 'state' | 'transformContext'>;
19
+ sessionManager: {
20
+ appendCustomMessageEntry: (customType: string, content: string, display: boolean, details?: {
21
+ nodeId: string;
22
+ }) => unknown;
23
+ };
24
+ }
25
+ /** Gate each provider request on durable delivery of the current persona delta. */
26
+ export declare function installPersonaTransitionGate(nodeId: string, session: PersonaTransitionSession): void;
27
+ export {};