@north-light/crouter 0.3.220 → 0.3.222

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 (271) hide show
  1. package/dist/api/client.d.ts +29 -0
  2. package/dist/api/client.js +44 -0
  3. package/dist/api/dto/chat-inventory.d.ts +56 -0
  4. package/dist/api/dto/chat-inventory.js +11 -0
  5. package/dist/api/dto/human-requests.d.ts +88 -0
  6. package/dist/api/dto/human-requests.js +4 -0
  7. package/dist/api/dto/human.d.ts +3 -0
  8. package/dist/api/dto/profiles.d.ts +19 -5
  9. package/dist/api/dto/profiles.js +2 -1
  10. package/dist/api/dto/reviews.d.ts +2 -0
  11. package/dist/api/index.d.ts +2 -0
  12. package/dist/api/index.js +2 -0
  13. package/dist/api/routes.d.ts +8 -0
  14. package/dist/api/routes.js +11 -0
  15. package/dist/build-root.d.ts +2 -6
  16. package/dist/build-root.js +51 -4
  17. package/dist/builtin-memory/00-runtime-base/00-authoring.md +31 -0
  18. package/dist/builtin-memory/00-runtime-base/01-escalation.md +14 -0
  19. package/dist/builtin-memory/{insights/listen.md → 00-runtime-base/02-insight-capture.md} +1 -0
  20. package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +27 -0
  21. package/dist/builtin-memory/{02-lifecycle/01-resident.md → 02-turn-lifecycle/02-resident.md} +5 -0
  22. package/dist/builtin-memory/04-base-worker.md +4 -8
  23. package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
  24. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -0
  25. package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +1 -0
  26. package/dist/builtin-memory/05-kinds/design/00-base.md +2 -1
  27. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +2 -1
  28. package/dist/builtin-memory/05-kinds/design/design-contract.md +19 -0
  29. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -0
  30. package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +1 -0
  31. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -0
  32. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -0
  33. package/dist/builtin-memory/05-kinds/general/00-base.md +1 -0
  34. package/dist/builtin-memory/05-kinds/plan/00-base.md +2 -1
  35. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +2 -1
  36. package/dist/builtin-memory/05-kinds/plan/plan-contract.md +28 -0
  37. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -0
  38. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -0
  39. package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +1 -0
  40. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -0
  41. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -0
  42. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -0
  43. package/dist/builtin-memory/05-kinds/review/00-base.md +1 -0
  44. package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -0
  45. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +1 -0
  46. package/dist/builtin-memory/05-kinds/review/security-findings.md +1 -0
  47. package/dist/builtin-memory/05-kinds/spec/00-base.md +4 -3
  48. package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -0
  49. package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -0
  50. package/dist/builtin-memory/design/guide.md +35 -0
  51. package/dist/builtin-memory/design/roadmap.md +21 -0
  52. package/dist/builtin-memory/insights/capture.md +1 -1
  53. package/dist/builtin-memory/internal/agent-shaping.md +3 -1
  54. package/dist/builtin-memory/internal/memory-loading.md +4 -0
  55. package/dist/builtin-memory/internal/plugins.md +10 -1
  56. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  57. package/dist/builtin-memory/plan/roadmap.md +6 -22
  58. package/dist/builtin-memory/spec/guide.md +23 -6
  59. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +1 -1
  60. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -1
  61. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +34 -16
  62. package/dist/clients/attach/__tests__/ref-autocomplete.test.js +1 -1
  63. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -1
  64. package/dist/clients/attach/overlays/file-review.js +2 -2
  65. package/dist/clients/attach/render/markdown-source.js +106 -1
  66. package/dist/clients/attach/session/file-links.d.ts +13 -4
  67. package/dist/clients/attach/session/file-links.js +54 -58
  68. package/dist/clients/attach/session/keys.d.ts +1 -1
  69. package/dist/clients/attach/session/profile-files.js +1 -1
  70. package/dist/clients/attach/viewer.js +698 -696
  71. package/dist/clients/inbox/controller.js +1 -1
  72. package/dist/clients/inbox/resolve.d.ts +1 -0
  73. package/dist/clients/inbox/review/launch.d.ts +8 -4
  74. package/dist/clients/inbox/review/launch.js +55 -5
  75. package/dist/clients/inbox/review/review-client.d.ts +1 -0
  76. package/dist/clients/inbox/review/review-client.js +7 -1
  77. package/dist/clients/inbox/review-adapter.d.ts +1 -8
  78. package/dist/clients/inbox/review-adapter.js +4 -52
  79. package/dist/commands/__tests__/human.test.js +2 -2
  80. package/dist/commands/human/request.d.ts +2 -0
  81. package/dist/commands/human/request.js +281 -0
  82. package/dist/commands/human.js +5 -2
  83. package/dist/commands/memory/lint.js +2 -1
  84. package/dist/commands/memory/read.js +1 -0
  85. package/dist/commands/memory.js +1 -1
  86. package/dist/commands/pkg/market-manage.js +165 -75
  87. package/dist/commands/pkg/plugin-inspect.js +19 -2
  88. package/dist/commands/pkg/plugin-manage.d.ts +8 -3
  89. package/dist/commands/pkg/plugin-manage.js +72 -24
  90. package/dist/commands/profile/default.js +6 -10
  91. package/dist/commands/profile/list.js +5 -3
  92. package/dist/commands/profile/new.js +21 -8
  93. package/dist/commands/profile/project.js +25 -19
  94. package/dist/commands/profile/show.js +3 -3
  95. package/dist/commands/surface-inbox.js +1 -0
  96. package/dist/commands/sys/__tests__/migrate.test.js +16 -5
  97. package/dist/commands/sys/config.js +2 -2
  98. package/dist/commands/sys/doctor.js +87 -5
  99. package/dist/commands/sys/migrate.js +38 -19
  100. package/dist/commands/sys/setup-core.js +1 -1
  101. package/dist/commands/sys/sync-project-guidance.js +1 -1
  102. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
  103. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +1 -1
  104. package/dist/core/__tests__/fixtures/c5-command-boundary-ext.js +24 -0
  105. package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -18
  106. package/dist/core/__tests__/fixtures/fake-engine.js +8 -1
  107. package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +71 -0
  108. package/dist/core/__tests__/human-action-delivery.test.d.ts +1 -0
  109. package/dist/core/__tests__/human-action-delivery.test.js +140 -0
  110. package/dist/core/__tests__/human-actions.test.d.ts +1 -0
  111. package/dist/core/__tests__/human-actions.test.js +116 -0
  112. package/dist/core/__tests__/inline-memory-refs.test.js +36 -2
  113. package/dist/core/__tests__/profile-project-memory-delivery.test.d.ts +1 -0
  114. package/dist/core/__tests__/profile-project-memory-delivery.test.js +217 -0
  115. package/dist/core/__tests__/prospective-inventory-capability-parity.test.d.ts +1 -0
  116. package/dist/core/__tests__/prospective-inventory-capability-parity.test.js +91 -0
  117. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.d.ts +1 -0
  118. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +127 -0
  119. package/dist/core/__tests__/seam/prospective-inventory-stdout.test.d.ts +1 -0
  120. package/dist/core/__tests__/seam/prospective-inventory-stdout.test.js +31 -0
  121. package/dist/core/__tests__/serial/broker-sdk-wiring.test.js +102 -2
  122. package/dist/core/bootstrap.js +6 -0
  123. package/dist/core/canvas/browse/app.js +5 -2
  124. package/dist/core/canvas/browse/model.d.ts +25 -15
  125. package/dist/core/canvas/browse/model.js +86 -65
  126. package/dist/core/canvas/db.js +23 -0
  127. package/dist/core/canvas/human-deliveries.d.ts +53 -0
  128. package/dist/core/canvas/human-deliveries.js +75 -0
  129. package/dist/core/canvas/render-source.d.ts +6 -0
  130. package/dist/core/canvas/render-source.js +7 -1
  131. package/dist/core/canvas/render.js +10 -2
  132. package/dist/core/command-hooks/artifact.d.ts +10 -0
  133. package/dist/core/command-hooks/artifact.js +129 -0
  134. package/dist/core/command-hooks/catalog.d.ts +14 -0
  135. package/dist/core/command-hooks/catalog.js +38 -0
  136. package/dist/core/command-hooks/compose.d.ts +15 -0
  137. package/dist/core/command-hooks/compose.js +99 -0
  138. package/dist/core/command-hooks/discovery.d.ts +87 -0
  139. package/dist/core/command-hooks/discovery.js +174 -0
  140. package/dist/core/command-hooks/help.d.ts +5 -0
  141. package/dist/core/command-hooks/help.js +18 -0
  142. package/dist/core/command-hooks/index.d.ts +6 -0
  143. package/dist/core/command-hooks/index.js +6 -0
  144. package/dist/core/command-hooks/report.d.ts +23 -0
  145. package/dist/core/command-hooks/report.js +19 -0
  146. package/dist/core/command-hooks/schema.d.ts +27 -0
  147. package/dist/core/command-hooks/schema.js +68 -0
  148. package/dist/core/command-hooks/transport/exec-invoke.d.ts +22 -0
  149. package/dist/core/command-hooks/transport/exec-invoke.js +274 -0
  150. package/dist/core/command-plugins/presence.d.ts +2 -0
  151. package/dist/core/command-plugins/presence.js +17 -0
  152. package/dist/core/command-plugins/transport/exec-invoke.d.ts +5 -0
  153. package/dist/core/command-plugins/transport/exec-invoke.js +58 -5
  154. package/dist/core/command.d.ts +8 -1
  155. package/dist/core/command.js +12 -10
  156. package/dist/core/config.d.ts +13 -1
  157. package/dist/core/config.js +51 -1
  158. package/dist/core/feed/inbox.d.ts +6 -0
  159. package/dist/core/feed/inbox.js +9 -1
  160. package/dist/core/help.d.ts +7 -1
  161. package/dist/core/human/action-binding.d.ts +21 -0
  162. package/dist/core/human/action-binding.js +40 -0
  163. package/dist/core/human/completion.d.ts +38 -0
  164. package/dist/core/human/completion.js +27 -0
  165. package/dist/core/human/convention.d.ts +2 -0
  166. package/dist/core/human/convention.js +2 -0
  167. package/dist/core/human/tickets.d.ts +25 -6
  168. package/dist/core/human/tickets.js +19 -13
  169. package/dist/core/human/types.d.ts +5 -0
  170. package/dist/core/human-actions.d.ts +25 -0
  171. package/dist/core/human-actions.js +101 -0
  172. package/dist/core/io.d.ts +9 -1
  173. package/dist/core/io.js +44 -2
  174. package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
  175. package/dist/core/memory/inline-ref-inventory.js +15 -8
  176. package/dist/core/memory-resolver.d.ts +13 -1
  177. package/dist/core/memory-resolver.js +26 -20
  178. package/dist/core/profiles/manifest.d.ts +13 -2
  179. package/dist/core/profiles/manifest.js +84 -18
  180. package/dist/core/profiles/select.d.ts +2 -0
  181. package/dist/core/profiles/select.js +29 -12
  182. package/dist/core/render.js +11 -0
  183. package/dist/core/runtime/advertised-command-invocation.d.ts +20 -0
  184. package/dist/core/runtime/advertised-command-invocation.js +233 -0
  185. package/dist/core/runtime/bearings.js +1 -1
  186. package/dist/core/runtime/broker/event-projection.js +7 -0
  187. package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
  188. package/dist/core/runtime/broker/frame-dispatch.js +13 -11
  189. package/dist/core/runtime/broker/read-ops.d.ts +4 -0
  190. package/dist/core/runtime/broker/read-ops.js +6 -2
  191. package/dist/core/runtime/broker-extension-render.js +1 -1
  192. package/dist/core/runtime/broker-inventory.d.ts +4 -0
  193. package/dist/core/runtime/broker-inventory.js +116 -0
  194. package/dist/core/runtime/broker-persona-guidance.js +1 -1
  195. package/dist/core/runtime/broker-protocol.d.ts +9 -2
  196. package/dist/core/runtime/broker.js +10 -1
  197. package/dist/core/runtime/chat-inventory-rows.d.ts +8 -0
  198. package/dist/core/runtime/chat-inventory-rows.js +105 -0
  199. package/dist/core/runtime/command-surface.d.ts +38 -0
  200. package/dist/core/runtime/command-surface.js +117 -0
  201. package/dist/core/runtime/launch-target.d.ts +25 -0
  202. package/dist/core/runtime/launch-target.js +54 -0
  203. package/dist/core/runtime/node-read.js +5 -0
  204. package/dist/core/runtime/persona.js +3 -3
  205. package/dist/core/runtime/prospective-inventory-cli.d.ts +1 -0
  206. package/dist/core/runtime/prospective-inventory-cli.js +61 -0
  207. package/dist/core/runtime/prospective-inventory.d.ts +10 -0
  208. package/dist/core/runtime/prospective-inventory.js +88 -0
  209. package/dist/core/runtime/spawn.d.ts +3 -1
  210. package/dist/core/runtime/spawn.js +5 -3
  211. package/dist/core/scope.d.ts +26 -1
  212. package/dist/core/scope.js +52 -12
  213. package/dist/core/substrate/on-read.d.ts +7 -1
  214. package/dist/core/substrate/on-read.js +30 -32
  215. package/dist/core/substrate/render-node.d.ts +3 -2
  216. package/dist/core/substrate/render-node.js +3 -2
  217. package/dist/core/substrate/render.js +65 -24
  218. package/dist/core/substrate/schema.d.ts +16 -2
  219. package/dist/core/substrate/schema.js +14 -5
  220. package/dist/core/user-settings.d.ts +4 -0
  221. package/dist/core/user-settings.js +1 -0
  222. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +56 -7
  223. package/dist/daemon/api/handlers/chat-inventory.d.ts +2 -0
  224. package/dist/daemon/api/handlers/chat-inventory.js +25 -0
  225. package/dist/daemon/api/handlers/human-requests.d.ts +2 -0
  226. package/dist/daemon/api/handlers/human-requests.js +409 -0
  227. package/dist/daemon/api/handlers/human.js +3 -0
  228. package/dist/daemon/api/handlers/inbox.js +3 -0
  229. package/dist/daemon/api/handlers/nodes.d.ts +1 -3
  230. package/dist/daemon/api/handlers/nodes.js +11 -46
  231. package/dist/daemon/api/handlers/profiles.js +7 -1
  232. package/dist/daemon/api/handlers/prospective-chat-inventory.d.ts +2 -0
  233. package/dist/daemon/api/handlers/prospective-chat-inventory.js +59 -0
  234. package/dist/daemon/api/handlers/reviews.js +10 -2
  235. package/dist/daemon/api/map.d.ts +2 -1
  236. package/dist/daemon/api/map.js +3 -2
  237. package/dist/daemon/api/server.js +6 -0
  238. package/dist/daemon/crtrd.js +6 -0
  239. package/dist/daemon/human/deliver-action.d.ts +16 -0
  240. package/dist/daemon/human/deliver-action.js +168 -0
  241. package/dist/daemon/human/finish.d.ts +8 -5
  242. package/dist/daemon/human/finish.js +45 -6
  243. package/dist/daemon/human/sweep.js +4 -1
  244. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +10 -0
  245. package/dist/daemon/reconcilers/human-delivery-lane.js +41 -0
  246. package/dist/daemon/review/finish.d.ts +8 -3
  247. package/dist/daemon/review/finish.js +19 -1
  248. package/dist/hook-authoring.d.ts +75 -0
  249. package/dist/hook-authoring.js +358 -0
  250. package/dist/hook-process.d.ts +7 -0
  251. package/dist/hook-process.js +34 -0
  252. package/dist/index.d.ts +2 -0
  253. package/dist/index.js +2 -0
  254. package/dist/migrations/002-profile-project-memory.d.ts +2 -0
  255. package/dist/migrations/002-profile-project-memory.js +71 -0
  256. package/dist/migrations/profile-manifests.d.ts +30 -0
  257. package/dist/migrations/profile-manifests.js +70 -0
  258. package/dist/migrations/registry.js +10 -5
  259. package/dist/migrations/types.d.ts +28 -1
  260. package/dist/migrations/types.js +15 -9
  261. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +21 -4
  262. package/dist/pi-extensions/canvas-structured-output.js +85 -2
  263. package/dist/types.d.ts +23 -6
  264. package/dist/types.js +1 -0
  265. package/package.json +1 -1
  266. package/runtime.lock.json +2 -2
  267. package/dist/builtin-memory/00-runtime-base.md +0 -55
  268. package/dist/builtin-memory/design.md +0 -55
  269. package/dist/clients/attach/__tests__/file-review-focus.test.js +0 -49
  270. /package/dist/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
  271. /package/dist/{clients/attach/__tests__/file-review-focus.test.d.ts → core/__tests__/fixtures/memory-slash-live-probe.d.ts} +0 -0
@@ -0,0 +1,105 @@
1
+ import { isDeterministicCommandExpansion } from './command-expansion.js';
2
+ const MEMORY_KINDS = new Set(['knowledge', 'preference']);
3
+ const MEMORY_SCOPES = new Set(['node', 'project', 'profile', 'user', 'builtin']);
4
+ function nonEmptyString(value) {
5
+ return typeof value === 'string' && value !== '';
6
+ }
7
+ /** Keep the command rows a Gateway can use. Malformed registrations are
8
+ * discarded individually so one producer cannot take down the inventory. */
9
+ export function gatewayCommandRows(rows) {
10
+ if (!Array.isArray(rows))
11
+ return [];
12
+ const out = [];
13
+ for (const row of rows) {
14
+ if (typeof row !== 'object' || row === null)
15
+ continue;
16
+ const record = row;
17
+ if (record['gatewayCapable'] !== true)
18
+ continue;
19
+ const source = record['source'];
20
+ if (source !== 'command' && source !== 'template')
21
+ continue;
22
+ if (!nonEmptyString(record['name']) || typeof record['description'] !== 'string')
23
+ continue;
24
+ const expansion = record['expansion'];
25
+ const template = record['template'];
26
+ const argumentHint = record['argumentHint'];
27
+ out.push({
28
+ name: record['name'],
29
+ description: record['description'],
30
+ source,
31
+ ...(nonEmptyString(argumentHint) ? { argument_hint: argumentHint } : {}),
32
+ ...(isDeterministicCommandExpansion(expansion) ? { expansion } : {}),
33
+ ...(typeof template === 'string' ? { template } : {}),
34
+ });
35
+ }
36
+ return out;
37
+ }
38
+ export function chatInventoryCommandRows(rows) {
39
+ if (!Array.isArray(rows))
40
+ return [];
41
+ return rows.filter((row) => {
42
+ if (typeof row !== 'object' || row === null)
43
+ return false;
44
+ const record = row;
45
+ if (!nonEmptyString(record['name']) || typeof record['description'] !== 'string')
46
+ return false;
47
+ if (record['source'] !== 'command' && record['source'] !== 'template')
48
+ return false;
49
+ if (record['argument_hint'] !== undefined && !nonEmptyString(record['argument_hint']))
50
+ return false;
51
+ if (record['template'] !== undefined && typeof record['template'] !== 'string')
52
+ return false;
53
+ return record['expansion'] === undefined || isDeterministicCommandExpansion(record['expansion']);
54
+ });
55
+ }
56
+ export function gatewayCommandRowsFromDetail(detail) {
57
+ if (detail === undefined)
58
+ return [];
59
+ try {
60
+ return gatewayCommandRows(JSON.parse(detail));
61
+ }
62
+ catch {
63
+ return [];
64
+ }
65
+ }
66
+ export function chatInventoryMemoryRows(rows) {
67
+ if (!Array.isArray(rows))
68
+ return [];
69
+ return rows.filter((row) => {
70
+ if (typeof row !== 'object' || row === null)
71
+ return false;
72
+ const record = row;
73
+ return nonEmptyString(record['name'])
74
+ && typeof record['shortForm'] === 'string'
75
+ && typeof record['kind'] === 'string'
76
+ && MEMORY_KINDS.has(record['kind'])
77
+ && typeof record['scope'] === 'string'
78
+ && MEMORY_SCOPES.has(record['scope']);
79
+ });
80
+ }
81
+ export function gatewayMemoryRows(refs) {
82
+ if (!Array.isArray(refs))
83
+ return [];
84
+ const out = [];
85
+ for (const row of refs) {
86
+ if (typeof row !== 'object' || row === null)
87
+ continue;
88
+ const record = row;
89
+ if (record['gatewayVisible'] !== true)
90
+ continue;
91
+ if (!nonEmptyString(record['name']) || typeof record['shortForm'] !== 'string')
92
+ continue;
93
+ if (typeof record['kind'] !== 'string' || !MEMORY_KINDS.has(record['kind']))
94
+ continue;
95
+ if (typeof record['scope'] !== 'string' || !MEMORY_SCOPES.has(record['scope']))
96
+ continue;
97
+ out.push({
98
+ name: record['name'],
99
+ kind: record['kind'],
100
+ scope: record['scope'],
101
+ shortForm: record['shortForm'],
102
+ });
103
+ }
104
+ return out;
105
+ }
@@ -0,0 +1,38 @@
1
+ /** The optional fields a shipped extension registration may add so its command
2
+ * is disclosed to chat surfaces. Registrations cast their options through
3
+ * `ChatCapableRegistration` rather than widening pi's own types. */
4
+ export interface ChatCommandMetadata {
5
+ /** Present and exactly `true` to opt this command into chat disclosure. */
6
+ gateway?: true;
7
+ /** Argument shape shown beside the name, e.g. `'[args]'`. */
8
+ argumentHint?: string;
9
+ }
10
+ /** Registration options plus the preserved custom fields crouter reads back off
11
+ * the resolved command. `T` is `Parameters<ExtensionAPI['registerCommand']>[1]`
12
+ * at the call site. */
13
+ export type ChatCapableRegistration<T> = T & ChatCommandMetadata;
14
+ /** What the command disclosure needs to know about one command's chat surface. */
15
+ export interface ChatCommandSurface {
16
+ gatewayCapable: boolean;
17
+ argumentHint?: string;
18
+ }
19
+ /** Read the chat surface off a resolved extension command. `command` is an
20
+ * arbitrary runtime object from another package, so every field is probed
21
+ * rather than trusted. */
22
+ export declare function registrationChatSurface(command: unknown): ChatCommandSurface;
23
+ /** Capture prompt-template chat metadata immediately after pi loads or reloads
24
+ * its templates. The opt-in is the template file's own frontmatter; the hint
25
+ * is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
26
+ * invalid YAML is not chat-capable. */
27
+ export declare function capturePromptTemplateChatSurfaces(templates: unknown): void;
28
+ /** Capture the Gateway capabilities declared by one completed resource load. */
29
+ export declare function captureChatCapabilities(input: {
30
+ commands: unknown;
31
+ templates: unknown;
32
+ }): void;
33
+ /** Capture every startup-declared command and prompt template a session
34
+ * currently exposes. A broken getter leaves that surface fail-closed. */
35
+ export declare function captureSessionChatCapabilities(session: unknown): void;
36
+ /** Read the chat surface captured for this loaded template. An uncaptured
37
+ * template is not disclosed to chat surfaces. */
38
+ export declare function templateChatSurface(template: unknown): ChatCommandSurface;
@@ -0,0 +1,117 @@
1
+ // command-surface.ts — crouter's own metadata about the commands the engine
2
+ // registers, and the one place that decides whether a command can complete its
3
+ // outcome from a non-terminal chat surface.
4
+ //
5
+ // Eligibility is OPT-IN and fail-closed. An extension registration declares
6
+ // `gateway: true`; a prompt template declares `gateway: true` in frontmatter.
7
+ // Anything else — a missing flag, a malformed value, an unreadable file — is
8
+ // not chat-capable. Capability is NEVER inferred from a command's name, its
9
+ // source, or a hard-coded list, because the command set is whatever the active
10
+ // VM registered, not a catalog crouter ships.
11
+ //
12
+ // pi keeps unknown registration fields on the resolved command object, so
13
+ // `gateway`/`argumentHint` ride along beside `expansion` without touching pi's
14
+ // global registration types. pi's prompt-template loader is the opposite: it
15
+ // parses `argument-hint` into `argumentHint` and DROPS every other frontmatter
16
+ // key. Capture `gateway` from the template file when the loader finishes, so
17
+ // the eligibility and loaded template content share the same reload boundary.
18
+ import { readFileSync } from 'node:fs';
19
+ import { parseFrontmatterGeneric } from '../frontmatter.js';
20
+ const NOT_CAPABLE = { gatewayCapable: false };
21
+ // One resource load declares Gateway capability by extension identity and pi's
22
+ // collision-resolved invocation name. Later registrations from that extension
23
+ // retain only names the load declared.
24
+ let startupGatewayNames = new WeakMap();
25
+ // Pi drops unknown prompt frontmatter keys. This cache restores crouter's
26
+ // metadata without re-reading a template during command disclosure.
27
+ const templateChatSurfaces = new WeakMap();
28
+ function hintOf(value) {
29
+ return typeof value === 'string' && value.trim() !== '' ? { argumentHint: value } : {};
30
+ }
31
+ /** Read the chat surface off a resolved extension command. `command` is an
32
+ * arbitrary runtime object from another package, so every field is probed
33
+ * rather than trusted. */
34
+ export function registrationChatSurface(command) {
35
+ if (typeof command !== 'object' || command === null)
36
+ return NOT_CAPABLE;
37
+ const record = command;
38
+ const { sourceInfo, invocationName } = record;
39
+ if (typeof sourceInfo !== 'object' ||
40
+ sourceInfo === null ||
41
+ typeof invocationName !== 'string' ||
42
+ !startupGatewayNames.get(sourceInfo)?.has(invocationName))
43
+ return NOT_CAPABLE;
44
+ return { gatewayCapable: true, ...hintOf(record['argumentHint']) };
45
+ }
46
+ function captureExtensionCommandSurfaces(commands) {
47
+ startupGatewayNames = new WeakMap();
48
+ if (!Array.isArray(commands))
49
+ return;
50
+ for (const command of commands) {
51
+ if (typeof command !== 'object' || command === null)
52
+ continue;
53
+ const record = command;
54
+ const { sourceInfo, invocationName } = record;
55
+ if (record['gateway'] !== true ||
56
+ typeof sourceInfo !== 'object' ||
57
+ sourceInfo === null ||
58
+ typeof invocationName !== 'string')
59
+ continue;
60
+ const names = startupGatewayNames.get(sourceInfo) ?? new Set();
61
+ names.add(invocationName);
62
+ startupGatewayNames.set(sourceInfo, names);
63
+ }
64
+ }
65
+ /** Capture prompt-template chat metadata immediately after pi loads or reloads
66
+ * its templates. The opt-in is the template file's own frontmatter; the hint
67
+ * is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
68
+ * invalid YAML is not chat-capable. */
69
+ export function capturePromptTemplateChatSurfaces(templates) {
70
+ if (!Array.isArray(templates))
71
+ return;
72
+ for (const template of templates) {
73
+ if (typeof template !== 'object' || template === null)
74
+ continue;
75
+ const { filePath, argumentHint } = template;
76
+ let surface = NOT_CAPABLE;
77
+ if (typeof filePath === 'string' && filePath !== '') {
78
+ try {
79
+ const frontmatter = parseFrontmatterGeneric(readFileSync(filePath, 'utf8')).data;
80
+ if (frontmatter?.['gateway'] === true)
81
+ surface = { gatewayCapable: true, ...hintOf(argumentHint) };
82
+ }
83
+ catch {
84
+ // Fail closed for malformed or unreadable template metadata.
85
+ }
86
+ }
87
+ templateChatSurfaces.set(template, surface);
88
+ }
89
+ }
90
+ /** Capture the Gateway capabilities declared by one completed resource load. */
91
+ export function captureChatCapabilities(input) {
92
+ captureExtensionCommandSurfaces(input.commands);
93
+ capturePromptTemplateChatSurfaces(input.templates);
94
+ }
95
+ /** Capture every startup-declared command and prompt template a session
96
+ * currently exposes. A broken getter leaves that surface fail-closed. */
97
+ export function captureSessionChatCapabilities(session) {
98
+ try {
99
+ if (typeof session !== 'object' || session === null)
100
+ return;
101
+ const value = session;
102
+ captureChatCapabilities({
103
+ commands: value.extensionRunner?.getRegisteredCommands?.(),
104
+ templates: value.promptTemplates,
105
+ });
106
+ }
107
+ catch {
108
+ // Fail closed when an engine cannot expose its loaded resources.
109
+ }
110
+ }
111
+ /** Read the chat surface captured for this loaded template. An uncaptured
112
+ * template is not disclosed to chat surfaces. */
113
+ export function templateChatSurface(template) {
114
+ if (typeof template !== 'object' || template === null)
115
+ return NOT_CAPABLE;
116
+ return templateChatSurfaces.get(template) ?? NOT_CAPABLE;
117
+ }
@@ -0,0 +1,25 @@
1
+ import type { CreateNodeRequest } from '../../api/dto/nodes.js';
2
+ import type { Mode } from '../canvas/types.js';
3
+ import { type ProfileEntry } from '../profiles/manifest.js';
4
+ import { buildLaunchSpecAsync } from './launch.js';
5
+ export interface ResolvedLaunchTarget {
6
+ profileId: string | null;
7
+ profile: ProfileEntry | null;
8
+ kind: string;
9
+ mode: Mode;
10
+ cwd: string;
11
+ launch: Awaited<ReturnType<typeof buildLaunchSpecAsync>>['launch'];
12
+ }
13
+ export declare function resolveCreateKind(kind: string | undefined, profile: ProfileEntry | null): string;
14
+ /** Validate a kind against the same cwd/profile configuration used to build its launch.
15
+ * Resolution is subprocess-isolated: crtrd is single-threaded and a config read can
16
+ * block indefinitely on a network filesystem. */
17
+ export declare function assertLaunchKind(kind: string, cwd?: string, profileId?: string | null): Promise<void>;
18
+ /** Resolve the exact profile, kind, cwd, mode and launch recipe a create will use. */
19
+ export declare function resolveLaunchTarget(req: CreateNodeRequest, options?: {
20
+ readOnlyProfileSelection?: boolean;
21
+ /** Preserve an existing caller's validation boundary before target resolution. */
22
+ validateKind?: (kind: string) => void | Promise<void>;
23
+ }): Promise<ResolvedLaunchTarget>;
24
+ /** The directory a new node is pinned to. */
25
+ export declare function resolveNodeCwd(req: CreateNodeRequest, parent: string | undefined, profileId: string | null, contextCwd: string): string;
@@ -0,0 +1,54 @@
1
+ import { resolve } from 'node:path';
2
+ import { getNode } from '../canvas/canvas.js';
3
+ import { usage } from '../errors.js';
4
+ import { assertProfileActive, profileHome } from '../profiles/manifest.js';
5
+ import { buildLaunchSpecAsync, readMergedLaunchConfigAsync } from './launch.js';
6
+ import { resolveProfileId } from './spawn.js';
7
+ export function resolveCreateKind(kind, profile) {
8
+ return kind ?? profile?.manifest.default_kind ?? 'general';
9
+ }
10
+ /** Validate a kind against the same cwd/profile configuration used to build its launch.
11
+ * Resolution is subprocess-isolated: crtrd is single-threaded and a config read can
12
+ * block indefinitely on a network filesystem. */
13
+ export async function assertLaunchKind(kind, cwd = process.cwd(), profileId = process.env['CRTR_PROFILE_ID'] || null) {
14
+ const kinds = Object.keys((await readMergedLaunchConfigAsync(cwd, profileId)).kinds);
15
+ if (!kinds.includes(kind)) {
16
+ throw usage(`unknown kind: ${kind}`, { received: kind, next: `Valid kinds: ${kinds.sort().join(', ')}.` });
17
+ }
18
+ }
19
+ /** Resolve the exact profile, kind, cwd, mode and launch recipe a create will use. */
20
+ export async function resolveLaunchTarget(req, options = {}) {
21
+ const parent = req.parent ?? undefined;
22
+ const contextCwd = req.cwd ?? process.cwd();
23
+ const profileId = await resolveProfileId(req.profile, parent ?? null, contextCwd, {
24
+ readOnlySelection: options.readOnlyProfileSelection,
25
+ });
26
+ const profile = assertProfileActive(profileId);
27
+ const kind = resolveCreateKind(req.kind, profile);
28
+ await options.validateKind?.(kind);
29
+ const cwd = resolveNodeCwd({ ...req, kind }, parent, profileId, contextCwd);
30
+ if (options.validateKind === undefined)
31
+ await assertLaunchKind(kind, cwd, profileId);
32
+ const mode = (req.mode ?? 'base');
33
+ const launch = (await buildLaunchSpecAsync(kind, mode, {
34
+ lifecycle: req.root === true ? (req.root_lifecycle ?? 'resident') : 'terminal',
35
+ hasManager: req.root !== true,
36
+ ...(req.model !== undefined ? { model: req.model } : {}),
37
+ cwd,
38
+ profileId,
39
+ })).launch;
40
+ return { profileId, profile, kind, mode, cwd, launch };
41
+ }
42
+ /** The directory a new node is pinned to. */
43
+ export function resolveNodeCwd(req, parent, profileId, contextCwd) {
44
+ if (req.pin_cwd !== undefined && req.pin_cwd !== '')
45
+ return resolve(req.pin_cwd);
46
+ if (parent != null) {
47
+ const spawner = getNode(parent);
48
+ if (spawner !== null)
49
+ return spawner.cwd;
50
+ }
51
+ if (req.cwd !== undefined && req.cwd !== '')
52
+ return resolve(req.cwd);
53
+ return profileHome(profileId) ?? contextCwd;
54
+ }
@@ -13,6 +13,7 @@ import { basename, join } from 'node:path';
13
13
  import { InputError } from '../io.js';
14
14
  import { fullName, getNode } from '../canvas/index.js';
15
15
  import { jobDir, nodeDir, sessionPtrPath } from '../canvas/paths.js';
16
+ import { hydratePersistedAdvertisedCommandMessages } from './advertised-command-invocation.js';
16
17
  import { SessionManager } from './broker-sdk.js';
17
18
  import { openModelRegistry } from './model-registry.js';
18
19
  import { BUILTIN_SLASH_COMMANDS } from './pi-vendored.js';
@@ -153,7 +154,11 @@ async function reconstructVisibleMessages(nodeId) {
153
154
  copyFileSync(sessionFile, copy);
154
155
  const manager = SessionManager.open(copy);
155
156
  const context = manager.buildSessionContext();
157
+ hydratePersistedAdvertisedCommandMessages(context.messages);
156
158
  const reconstructed = cycleAwareMessages(manager);
159
+ if (reconstructed !== undefined) {
160
+ hydratePersistedAdvertisedCommandMessages(reconstructed.map(({ message }) => message));
161
+ }
157
162
  const identified = reconstructed === undefined
158
163
  ? undefined
159
164
  : visibleMessages(reconstructed, { boundaryReviewId: node.review_binding?.review_id });
@@ -79,15 +79,15 @@ function baseModeGuidance() {
79
79
  }
80
80
  // The lifecycle transition prose is the SAME contract that's baked into the
81
81
  // static system prompt at birth — so both load from the one source
82
- // (`personas/lifecycle/{terminal,resident}.md`) and can never drift. The flip
82
+ // (`turn-lifecycle/{terminal,resident}.md`) and can never drift. The flip
83
83
  // only re-delivers that fragment as the node's new-state steer.
84
84
  /** terminal → resident: interactable, never forced to submit. */
85
85
  function residentLifecycleGuidance() {
86
- return loadMemoryBody('lifecycle/resident');
86
+ return loadMemoryBody('turn-lifecycle/resident');
87
87
  }
88
88
  /** resident → terminal: owes a final, reaps when done. */
89
89
  function terminalLifecycleGuidance() {
90
- return loadMemoryBody('lifecycle/terminal');
90
+ return loadMemoryBody('turn-lifecycle/terminal');
91
91
  }
92
92
  /** Build the injected transition prompt for a `from → to` persona change.
93
93
  * Concatenates the relevant section per changed axis (both when both changed).
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,61 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { DefaultResourceLoader, ExtensionRunner, getAgentDir, } from '@earendil-works/pi-coding-agent';
3
+ import { buildRefInventory } from '../memory/inline-ref-inventory.js';
4
+ import { gatewayCommandRows, gatewayMemoryRows } from './chat-inventory-rows.js';
5
+ import { captureChatCapabilities, registrationChatSurface, templateChatSurface, } from './command-surface.js';
6
+ function parseInput() {
7
+ const parsed = JSON.parse(readFileSync(0, 'utf8'));
8
+ if (typeof parsed !== 'object' || parsed === null)
9
+ throw new Error('preflight input must be an object');
10
+ const extensions = parsed.extensions;
11
+ if (!Array.isArray(extensions) || !extensions.every((value) => typeof value === 'string')) {
12
+ throw new Error('preflight input extensions must be strings');
13
+ }
14
+ return { extensions };
15
+ }
16
+ async function main() {
17
+ const { extensions } = parseInput();
18
+ const cwd = process.cwd();
19
+ const loader = new DefaultResourceLoader({
20
+ cwd,
21
+ agentDir: getAgentDir(),
22
+ additionalExtensionPaths: extensions,
23
+ noContextFiles: true,
24
+ });
25
+ await loader.reload();
26
+ const loaded = loader.getExtensions();
27
+ const templates = loader.getPrompts().prompts;
28
+ // getRegisteredCommands only reads extension maps and applies pi's collision
29
+ // naming. The unbound placeholders make every action path unavailable.
30
+ const runner = new ExtensionRunner(loaded.extensions, loaded.runtime, cwd, undefined, undefined);
31
+ const commands = runner.getRegisteredCommands();
32
+ captureChatCapabilities({ commands, templates });
33
+ const disclosures = [
34
+ ...commands.map((command) => {
35
+ const expansion = command.expansion;
36
+ return {
37
+ name: command.invocationName,
38
+ description: command.description ?? '',
39
+ source: 'command',
40
+ ...(expansion !== undefined ? { expansion } : {}),
41
+ ...registrationChatSurface(command),
42
+ };
43
+ }),
44
+ ...templates.map((template) => ({
45
+ name: template.name,
46
+ description: template.description,
47
+ source: 'template',
48
+ template: template.content,
49
+ ...templateChatSurface(template),
50
+ })),
51
+ ];
52
+ const memory = buildRefInventory();
53
+ writeFileSync(3, `${JSON.stringify({
54
+ commands: gatewayCommandRows(disclosures),
55
+ memory_refs: gatewayMemoryRows(memory.refs),
56
+ })}\n`);
57
+ }
58
+ main().catch((error) => {
59
+ process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
60
+ process.exitCode = 1;
61
+ });
@@ -0,0 +1,10 @@
1
+ import type { ChatInventoryCommandDTO, ChatInventoryMemoryRefDTO } from '../../api/dto/chat-inventory.js';
2
+ export interface ProspectiveInventoryRows {
3
+ commands: ChatInventoryCommandDTO[];
4
+ memory_refs: ChatInventoryMemoryRefDTO[];
5
+ }
6
+ export declare function runProspectiveInventory(input: {
7
+ cwd: string;
8
+ profileId: string | null;
9
+ extensions: string[];
10
+ }): Promise<ProspectiveInventoryRows>;
@@ -0,0 +1,88 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { chatInventoryCommandRows, chatInventoryMemoryRows } from './chat-inventory-rows.js';
5
+ import { FRONT_DOOR_ENV } from './front-door-env.js';
6
+ const PREFLIGHT_TIMEOUT_MS = 15_000;
7
+ function entryPath() {
8
+ return join(dirname(fileURLToPath(import.meta.url)), 'prospective-inventory-cli.js');
9
+ }
10
+ function parseDocument(response) {
11
+ const parsed = JSON.parse(response.trim());
12
+ if (typeof parsed !== 'object' || parsed === null)
13
+ throw new Error('preflight output must be an object');
14
+ const record = parsed;
15
+ if (!Array.isArray(record['commands']) || !Array.isArray(record['memory_refs'])) {
16
+ throw new Error('preflight output must contain command and memory-ref arrays');
17
+ }
18
+ return {
19
+ commands: chatInventoryCommandRows(record['commands']),
20
+ memory_refs: chatInventoryMemoryRows(record['memory_refs']),
21
+ };
22
+ }
23
+ export function runProspectiveInventory(input) {
24
+ return new Promise((resolve, reject) => {
25
+ const env = {
26
+ ...process.env,
27
+ CRTR_PROFILE_ID: input.profileId ?? '',
28
+ [FRONT_DOOR_ENV]: '1',
29
+ };
30
+ delete env['CRTR_NODE_ID'];
31
+ const child = spawn(process.execPath, [entryPath()], {
32
+ cwd: input.cwd,
33
+ env,
34
+ stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
35
+ });
36
+ let stdout = '';
37
+ let stderr = '';
38
+ let response = '';
39
+ let settled = false;
40
+ const timer = setTimeout(() => {
41
+ if (settled)
42
+ return;
43
+ settled = true;
44
+ child.kill('SIGKILL');
45
+ reject(new Error('prospective inventory preflight timed out'));
46
+ }, PREFLIGHT_TIMEOUT_MS);
47
+ timer.unref?.();
48
+ child.stdout.setEncoding('utf8');
49
+ child.stderr.setEncoding('utf8');
50
+ const responseStream = child.stdio[3];
51
+ if (responseStream === null || responseStream === undefined || !('setEncoding' in responseStream)) {
52
+ clearTimeout(timer);
53
+ child.kill('SIGKILL');
54
+ reject(new Error('prospective inventory preflight response channel unavailable'));
55
+ return;
56
+ }
57
+ responseStream.setEncoding('utf8');
58
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
59
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
60
+ responseStream.on('data', (chunk) => { response += chunk; });
61
+ child.on('error', (error) => {
62
+ if (settled)
63
+ return;
64
+ settled = true;
65
+ clearTimeout(timer);
66
+ reject(error);
67
+ });
68
+ child.on('close', (code, signal) => {
69
+ if (settled)
70
+ return;
71
+ settled = true;
72
+ clearTimeout(timer);
73
+ const diagnostics = [stderr.trim(), stdout.trim()].filter(Boolean).join('\n');
74
+ if (code !== 0) {
75
+ reject(new Error(`prospective inventory preflight exited ${code ?? signal ?? 'unknown'}${diagnostics === '' ? '' : `: ${diagnostics}`}`));
76
+ return;
77
+ }
78
+ try {
79
+ resolve(parseDocument(response));
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ reject(new Error(`${message}${diagnostics === '' ? '' : `: ${diagnostics}`}`));
84
+ }
85
+ });
86
+ child.stdin.end(JSON.stringify({ extensions: input.extensions }));
87
+ });
88
+ }
@@ -88,7 +88,9 @@ export declare function resolveForkSource(value: string): string;
88
88
  * shell, not from inside a node — there is no profile to inherit, so this runs
89
89
  * the same startup selector the front door uses (MRU covering `cwd`, else the
90
90
  * root-profile prompt/default). */
91
- export declare function resolveProfileId(explicit: string | null | undefined, spawner: string | null, cwd: string): Promise<string | null>;
91
+ export declare function resolveProfileId(explicit: string | null | undefined, spawner: string | null, cwd: string, options?: {
92
+ readOnlySelection?: boolean;
93
+ }): Promise<string | null>;
92
94
  /** Reject a permanently invalid launch recipe against the registry that will
93
95
  * serve it. Registered-but-unauthenticated/cooling requests intentionally pass
94
96
  * this authoring check so broker policy for transient availability is unchanged. */
@@ -13,7 +13,7 @@ import { accessSync, constants, cpSync, readdirSync, existsSync, rmSync } from '
13
13
  import { isAbsolute, resolve, join } from 'node:path';
14
14
  import { spawnNode, currentNodeContext, rootOfSpine, newNodeId, preflightNodeId } from './nodes.js';
15
15
  import { loadProfileManifest } from '../profiles/manifest.js';
16
- import { selectProfileForCwd } from '../profiles/select.js';
16
+ import { selectProfileForCwd, selectProfileForCwdReadOnly } from '../profiles/select.js';
17
17
  import { buildLaunchSpecAsync, buildPiArgv } from './launch.js';
18
18
  import { createManagedWorktree, rollbackManagedWorktree } from '../worktree.js';
19
19
  import { usage, brokerLaunchFailed } from '../errors.js';
@@ -153,7 +153,7 @@ export function resolveForkSource(value) {
153
153
  * shell, not from inside a node — there is no profile to inherit, so this runs
154
154
  * the same startup selector the front door uses (MRU covering `cwd`, else the
155
155
  * root-profile prompt/default). */
156
- export async function resolveProfileId(explicit, spawner, cwd) {
156
+ export async function resolveProfileId(explicit, spawner, cwd, options = {}) {
157
157
  if (explicit === null)
158
158
  return null;
159
159
  if (explicit !== undefined && explicit !== '') {
@@ -161,7 +161,9 @@ export async function resolveProfileId(explicit, spawner, cwd) {
161
161
  }
162
162
  if (spawner !== null)
163
163
  return getNode(spawner)?.profile_id ?? null;
164
- return selectProfileForCwd(cwd);
164
+ return options.readOnlySelection === true
165
+ ? selectProfileForCwdReadOnly(cwd)
166
+ : selectProfileForCwd(cwd);
165
167
  }
166
168
  function requestFromLaunch(launch) {
167
169
  const intent = launch.modelIntent === undefined
@@ -1,4 +1,17 @@
1
1
  import type { InstalledPlugin, Scope } from '../types.js';
2
+ import type { ProfileProjectMemory } from '../api/dto/profiles.js';
3
+ /** The ceiling a project root carries when no profile relationship contributes
4
+ * it — the top rung, i.e. no cap at all. */
5
+ export declare const NEUTRAL_PROJECT_MEMORY: ProfileProjectMemory;
6
+ /** One resolved project scope root plus the selected profile relationship's
7
+ * memory ceiling for it. The ceiling is INDEPENDENT of how the root was
8
+ * discovered: a root reached by both cwd and a profile entry still takes that
9
+ * entry's value (first entry in manifest order wins). */
10
+ export interface ProjectScopeRoot {
11
+ /** Absolute path of the `.crouter/` dir. */
12
+ root: string;
13
+ memory: ProfileProjectMemory;
14
+ }
2
15
  export declare function builtinMemoryRoot(): string;
3
16
  export declare function builtinPiPackagesDir(): string;
4
17
  export declare function builtinPiPackageDir(name: string): string;
@@ -15,10 +28,22 @@ export declare function findProjectScopeRoot(startDir?: string): string | null;
15
28
  * selected profile (`CRTR_PROFILE_ID`); a null/unresolvable profile collapses
16
29
  * to the single `[startDir]` pointer, reproducing today's behavior exactly.
17
30
  * Results are cached per (resolved startDir, profileId) — `resetScopeCache()`
18
- * invalidates on any profile mutation that can change project dirs or names. */
31
+ * invalidates on any profile mutation that can change project dirs or names.
32
+ *
33
+ * Each root also carries the selected profile relationship's memory ceiling
34
+ * (`findProjectScopeRootsWithMemory`), a projection layered onto this order
35
+ * rather than part of it: discovery provenance decides precedence, the
36
+ * relationship decides the ceiling, and the two never influence each other. */
19
37
  export declare function findProjectScopeRoots(startDir?: string, profileId?: string | null): string[];
38
+ /** `findProjectScopeRoots` with each root's profile memory ceiling attached —
39
+ * same roots, same order. A root contributed by a profile entry's ancestor
40
+ * walk takes that entry's `memory` (first entry in manifest order wins, even
41
+ * when cwd also reaches the root); every other root takes the neutral
42
+ * `content` ceiling. */
43
+ export declare function findProjectScopeRootsWithMemory(startDir?: string, profileId?: string | null): ProjectScopeRoot[];
20
44
  export declare function projectScopeRoot(startDir?: string): string | null;
21
45
  export declare function projectScopeRoots(startDir?: string, profileId?: string | null): string[];
46
+ export declare function projectScopeRootsWithMemory(startDir?: string, profileId?: string | null): ProjectScopeRoot[];
22
47
  export declare function scopeRoot(scope: Scope): string | null;
23
48
  export declare function requireScopeRoot(scope: Scope): string;
24
49
  export declare function ensureProjectScopeRoot(startDir?: string): string;