@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
@@ -1,16 +1,17 @@
1
1
  import { after, afterEach, beforeEach, test } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { mkdirSync, mkdtempSync, rmSync } from 'node:fs';
3
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { createNode, getNode, listNodes } from '../../../core/canvas/canvas.js';
7
7
  import { closeDb } from '../../../core/canvas/db.js';
8
8
  import { CrtrError } from '../../../core/errors.js';
9
- import { createProfile, loadProfileManifest, pauseProfile, setProfileDefaultKind } from '../../../core/profiles/manifest.js';
9
+ import { createProfile, loadProfileManifest, pauseProfile, profileManifestPath, ROOT_PROFILE_ID, setProfileDefaultKind } from '../../../core/profiles/manifest.js';
10
10
  import { resetScopeCache } from '../../../core/scope.js';
11
11
  import { reviveNode } from '../../../core/runtime/revive.js';
12
+ import { resolveLaunchTarget } from '../../../core/runtime/launch-target.js';
12
13
  import { spawnNode } from '../../../core/runtime/nodes.js';
13
- import { handleCreate, resolveCreateKind } from '../handlers/nodes.js';
14
+ import { handleConfig, handleCreate, resolveCreateKind } from '../handlers/nodes.js';
14
15
  const previousCrtrHome = process.env['CRTR_HOME'];
15
16
  const previousHome = process.env.HOME;
16
17
  let home = '';
@@ -42,7 +43,7 @@ after(() => {
42
43
  resetScopeCache();
43
44
  });
44
45
  test('create rejects a paused profile before a node exists', async () => {
45
- const profile = createProfile('paused profile', [cwd]);
46
+ const profile = createProfile('paused profile', [{ path: cwd, memory: 'content' }]);
46
47
  pauseProfile(profile.profileId);
47
48
  const ctx = {
48
49
  method: 'POST',
@@ -62,7 +63,7 @@ test('create rejects a paused profile before a node exists', async () => {
62
63
  assert.equal(listNodes().length, beforeRows);
63
64
  });
64
65
  test('revive refuses a paused profile before launching a broker', () => {
65
- const profile = createProfile('paused revive', [cwd]);
66
+ const profile = createProfile('paused revive', [{ path: cwd, memory: 'content' }]);
66
67
  pauseProfile(profile.profileId);
67
68
  const nodeId = 'paused-revive-node';
68
69
  createNode({
@@ -90,7 +91,7 @@ test('revive refuses a paused profile before launching a broker', () => {
90
91
  // because they all reach the canvas through spawnNode and none of them call
91
92
  // handleCreate.
92
93
  test('the birth gate refuses a paused profile for a daemon-side spawn', () => {
93
- const profile = createProfile('paused spawn', [cwd]);
94
+ const profile = createProfile('paused spawn', [{ path: cwd, memory: 'content' }]);
94
95
  pauseProfile(profile.profileId);
95
96
  const beforeRows = listNodes().length;
96
97
  assert.throws(() => spawnNode({ kind: 'general', cwd, parent: null, profile_id: profile.profileId }), (error) => {
@@ -101,8 +102,56 @@ test('the birth gate refuses a paused profile for a daemon-side spawn', () => {
101
102
  });
102
103
  assert.equal(listNodes().length, beforeRows);
103
104
  });
105
+ test('prospective root selection stays read-only before the root profile is scaffolded', async () => {
106
+ const manifestPath = profileManifestPath(ROOT_PROFILE_ID);
107
+ assert.equal(existsSync(manifestPath), false);
108
+ const target = await resolveLaunchTarget({ root: true, cwd }, { readOnlyProfileSelection: true });
109
+ assert.equal(target.profileId, ROOT_PROFILE_ID);
110
+ assert.equal(target.profile, null);
111
+ assert.equal(target.kind, 'general');
112
+ assert.equal(target.cwd, cwd);
113
+ assert.equal(existsSync(manifestPath), false);
114
+ });
115
+ test('target-scoped prospective validation does not rescope node writes', async () => {
116
+ mkdirSync(join(cwd, '.crouter'), { recursive: true });
117
+ writeFileSync(join(cwd, '.crouter', 'config.json'), JSON.stringify({
118
+ kinds: { 'target-only': { whenToUse: 'Only in this project.' } },
119
+ }));
120
+ const target = await resolveLaunchTarget({ root: true, cwd, kind: 'target-only' }, { readOnlyProfileSelection: true });
121
+ assert.equal(target.kind, 'target-only');
122
+ const create = {
123
+ method: 'POST',
124
+ path: '/v1/nodes',
125
+ params: {},
126
+ query: new URLSearchParams(),
127
+ body: { root: true, profile: null, cwd, kind: 'target-only' },
128
+ };
129
+ await assert.rejects(() => handleCreate(create), /unknown kind: target-only/);
130
+ const id = 'ambient-kind-write';
131
+ createNode({
132
+ node_id: id,
133
+ name: id,
134
+ created: new Date().toISOString(),
135
+ cwd,
136
+ kind: 'general',
137
+ mode: 'base',
138
+ lifecycle: 'terminal',
139
+ status: 'active',
140
+ parent: null,
141
+ profile_id: null,
142
+ });
143
+ const patch = {
144
+ method: 'PATCH',
145
+ path: `/v1/nodes/${id}/config`,
146
+ params: { id },
147
+ query: new URLSearchParams(),
148
+ body: { kind: 'target-only' },
149
+ };
150
+ await assert.rejects(() => handleConfig(patch), /unknown kind: target-only/);
151
+ assert.equal(getNode(id)?.kind, 'general');
152
+ });
104
153
  test('create kind resolves from the profile default when omitted', () => {
105
- const profile = createProfile('default kind', [cwd]);
154
+ const profile = createProfile('default kind', [{ path: cwd, memory: 'content' }]);
106
155
  setProfileDefaultKind(profile.profileId, 'developer');
107
156
  const entry = loadProfileManifest(profile.profileId);
108
157
  assert.equal(resolveCreateKind(undefined, entry), 'developer');
@@ -0,0 +1,2 @@
1
+ import type { RouteTable } from '../router.js';
2
+ export declare const chatInventoryRoutes: RouteTable;
@@ -0,0 +1,25 @@
1
+ // Chat-inventory handler. `GET /v1/nodes/{id}/chat-inventory` answers what a
2
+ // non-terminal chat surface may offer for one node: the slash commands its live
3
+ // engine registered that opted into chat disclosure, and the memory documents an
4
+ // inline `/name` token resolves to.
5
+ //
6
+ // Read-only in both directions — it takes one observer round trip against a
7
+ // broker that is already running and writes no canvas state. A node whose broker
8
+ // is not live answers 200 with `broker_live: false` and empty arrays rather than
9
+ // 404 or a revival: "this node has nothing to offer right now" is a normal
10
+ // answer, not an error.
11
+ import { getNode } from '../../../core/canvas/canvas.js';
12
+ import { isSafeNodeId } from '../../../core/canvas/paths.js';
13
+ import { notFound, usage } from '../../../core/errors.js';
14
+ import { readChatInventory } from '../../../core/runtime/broker-inventory.js';
15
+ async function handleGet(ctx) {
16
+ const id = ctx.params['id'];
17
+ if (!isSafeNodeId(id))
18
+ throw usage(`invalid node id: ${id}`, { received: id });
19
+ if (getNode(id) === null)
20
+ throw notFound(`unknown node: ${id}`, { received: id });
21
+ return { status: 200, body: await readChatInventory(id) };
22
+ }
23
+ export const chatInventoryRoutes = [
24
+ { method: 'GET', pattern: '/v1/nodes/:id/chat-inventory', handler: handleGet },
25
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RouteTable } from '../router.js';
2
+ export declare const humanRequestRoutes: RouteTable;
@@ -0,0 +1,409 @@
1
+ // Durable programmatic human requests — crtrd `/v1/human/requests`. A request
2
+ // is an ordinary page ticket with an optional frozen completion action, so it
3
+ // is the same record the inbox lists, answers, and dismisses. Creation is the
4
+ // only surface that writes the action binding; every settlement path funnels
5
+ // through the shared first-writer-wins store.
6
+ import { randomBytes } from 'node:crypto';
7
+ import { statSync } from 'node:fs';
8
+ import { basename, isAbsolute } from 'node:path';
9
+ import { ApiError } from '../../../api/index.js';
10
+ import { readActionDelivery } from '../../../core/canvas/human-deliveries.js';
11
+ import { resolvePageComponents } from '../../../core/config.js';
12
+ import { resolveHumanAction } from '../../../core/human-actions.js';
13
+ import { HUMAN_ACTION_NAME_PATTERN, publishTicketActionBinding, readTicketActionBinding, } from '../../../core/human/action-binding.js';
14
+ import { completionEventFor } from '../../../core/human/completion.js';
15
+ import { parsePage } from '../../../core/human/page.js';
16
+ import { opaqueInboxTicketId, ticketDir } from '../../../core/human/root.js';
17
+ import { preparePageSource, publishPage, readPageTicket, readTicketResult, validatePageTicketResponses, } from '../../../core/human/tickets.js';
18
+ import { cancelHumanTicket, resolvePageTicket } from '../../human/finish.js';
19
+ import { resolveTicket, requireTicketId } from './inbox.js';
20
+ const REASON_MAX = 1000;
21
+ const ACTOR_MAX = 200;
22
+ const DEFAULT_DELIVERY = { placement: 'inline', inbox: true, reply: false };
23
+ const SOURCE_FIELDS = ['sessionName', 'askedBy', 'emittedAt', 'profileName', 'nodeId'];
24
+ // ===========================================================================
25
+ // Body validation
26
+ // ===========================================================================
27
+ function isPlainObject(value) {
28
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
29
+ }
30
+ function requireNoExtraKeys(obj, allowed, where) {
31
+ for (const key of Object.keys(obj)) {
32
+ if (!allowed.includes(key))
33
+ throw new ApiError(400, 'invalid_request', `unexpected field in ${where}: ${key}`);
34
+ }
35
+ }
36
+ function invalid(message) {
37
+ return new ApiError(400, 'invalid_request', message);
38
+ }
39
+ function validatePage(raw) {
40
+ if (!isPlainObject(raw))
41
+ throw invalid('`page` must be an object');
42
+ requireNoExtraKeys(raw, ['dialect', 'source'], '`page`');
43
+ const dialect = raw['dialect'];
44
+ if (dialect !== 'jsx' && dialect !== 'html')
45
+ throw invalid('`page.dialect` must be "jsx" or "html"');
46
+ const source = raw['source'];
47
+ if (typeof source !== 'string' || source.trim() === '')
48
+ throw invalid('`page.source` must be the authored page text');
49
+ return { dialect, source };
50
+ }
51
+ /** Creation pins a programmatic request to the inbox with no bridge to reply to. */
52
+ function validateCreateDelivery(raw) {
53
+ if (raw === undefined)
54
+ return DEFAULT_DELIVERY;
55
+ if (!isPlainObject(raw))
56
+ throw invalid('`delivery` must be an object');
57
+ requireNoExtraKeys(raw, ['placement', 'inbox', 'reply'], '`delivery`');
58
+ const placement = validatePlacement(raw['placement'], DEFAULT_DELIVERY.placement);
59
+ const inbox = raw['inbox'] ?? true;
60
+ const reply = raw['reply'] ?? false;
61
+ if (inbox !== true)
62
+ throw invalid('`delivery.inbox` must be true: a programmatic request must stay answerable');
63
+ if (reply !== false)
64
+ throw invalid('`delivery.reply` must be false: a programmatic request has no agent bridge to reply to');
65
+ return { placement, inbox: true, reply: false };
66
+ }
67
+ /** Replace re-presents a pending page; where the ticket is routed is fixed at
68
+ * creation, so only placement is replaceable. */
69
+ function validateReplaceDelivery(raw, prior) {
70
+ if (raw === undefined)
71
+ return prior;
72
+ if (!isPlainObject(raw))
73
+ throw invalid('`delivery` must be an object');
74
+ requireNoExtraKeys(raw, ['placement'], '`delivery`');
75
+ return { ...prior, placement: validatePlacement(raw['placement'], prior.placement) };
76
+ }
77
+ function validatePlacement(raw, fallback) {
78
+ const placement = raw ?? fallback;
79
+ if (placement !== 'inline' && placement !== 'panel')
80
+ throw invalid('`delivery.placement` must be "inline" or "panel"');
81
+ return placement;
82
+ }
83
+ function validateSource(raw) {
84
+ if (raw === undefined)
85
+ return undefined;
86
+ if (!isPlainObject(raw))
87
+ throw invalid('`source` must be an object');
88
+ requireNoExtraKeys(raw, SOURCE_FIELDS, '`source`');
89
+ const source = {};
90
+ for (const field of SOURCE_FIELDS) {
91
+ const value = raw[field];
92
+ if (value === undefined)
93
+ continue;
94
+ if (typeof value !== 'string' || value === '')
95
+ throw invalid(`\`source.${field}\` must be a nonempty string`);
96
+ source[field] = value;
97
+ }
98
+ return source;
99
+ }
100
+ function validateCreatorCwd(raw) {
101
+ if (typeof raw !== 'string' || raw === '')
102
+ throw invalid('`creator_cwd` is required');
103
+ if (!isAbsolute(raw))
104
+ throw invalid('`creator_cwd` must be an absolute path');
105
+ try {
106
+ if (!statSync(raw).isDirectory())
107
+ throw invalid(`\`creator_cwd\` is not a directory: ${raw}`);
108
+ }
109
+ catch (error) {
110
+ if (error instanceof ApiError)
111
+ throw error;
112
+ throw invalid(`\`creator_cwd\` does not exist: ${raw}`);
113
+ }
114
+ return raw;
115
+ }
116
+ function validateActionSelection(raw) {
117
+ if (raw === undefined)
118
+ return undefined;
119
+ if (!isPlainObject(raw))
120
+ throw invalid('`action` must be an object');
121
+ requireNoExtraKeys(raw, ['name', 'payload'], '`action`');
122
+ const name = raw['name'];
123
+ if (typeof name !== 'string' || !HUMAN_ACTION_NAME_PATTERN.test(name)) {
124
+ throw invalid('`action.name` must be a declared action name');
125
+ }
126
+ // The completion document always carries `action.payload`, so an omitted one
127
+ // is normalized here rather than serialized away downstream.
128
+ return { name, payload: raw['payload'] ?? null };
129
+ }
130
+ function validateCreateRequest(raw) {
131
+ if (!isPlainObject(raw))
132
+ throw invalid('request body is required');
133
+ requireNoExtraKeys(raw, ['page', 'delivery', 'source', 'creator_cwd', 'action'], 'the request body');
134
+ const source = validateSource(raw['source']);
135
+ const action = validateActionSelection(raw['action']);
136
+ return {
137
+ page: validatePage(raw['page']),
138
+ delivery: validateCreateDelivery(raw['delivery']),
139
+ ...(source === undefined ? {} : { source }),
140
+ creatorCwd: validateCreatorCwd(raw['creator_cwd']),
141
+ ...(action === undefined ? {} : { action }),
142
+ };
143
+ }
144
+ function validateReplaceRequest(raw, priorDelivery) {
145
+ if (!isPlainObject(raw))
146
+ throw invalid('request body is required');
147
+ requireNoExtraKeys(raw, ['page', 'delivery'], 'the request body');
148
+ return { page: validatePage(raw['page']), delivery: validateReplaceDelivery(raw['delivery'], priorDelivery) };
149
+ }
150
+ function optionalBoundedString(raw, field, max) {
151
+ if (raw === undefined)
152
+ return undefined;
153
+ if (typeof raw !== 'string')
154
+ throw invalid(`\`${field}\` must be a string`);
155
+ if (raw.trim() === '' || raw.length > max)
156
+ throw invalid(`\`${field}\` must be nonempty after trim and at most ${max} characters`);
157
+ return raw;
158
+ }
159
+ function validateRespondRequest(raw) {
160
+ if (!isPlainObject(raw))
161
+ throw invalid('request body is required');
162
+ requireNoExtraKeys(raw, ['responses', 'actor'], 'the request body');
163
+ if (!isPlainObject(raw['responses']))
164
+ throw invalid('`responses` must be an object');
165
+ const actor = optionalBoundedString(raw['actor'], 'actor', ACTOR_MAX);
166
+ return { responses: raw['responses'], ...(actor === undefined ? {} : { actor }) };
167
+ }
168
+ function validateSettleRequest(raw) {
169
+ if (raw === undefined)
170
+ return {};
171
+ if (!isPlainObject(raw))
172
+ throw invalid('request body must be an object');
173
+ requireNoExtraKeys(raw, ['reason', 'actor'], 'the request body');
174
+ const reason = optionalBoundedString(raw['reason'], 'reason', REASON_MAX);
175
+ const actor = optionalBoundedString(raw['actor'], 'actor', ACTOR_MAX);
176
+ return { ...(reason === undefined ? {} : { reason }), ...(actor === undefined ? {} : { actor }) };
177
+ }
178
+ function resolveRequest(ctx) {
179
+ const requestId = requireTicketId({ ticket_id: ctx.params['request_id'] ?? '' });
180
+ const resolved = resolveTicket(requestId);
181
+ if (resolved === null || resolved.kind !== 'page')
182
+ throw new ApiError(404, 'not_found', 'no request with that id');
183
+ let manifest;
184
+ try {
185
+ manifest = parsePage(resolved.dir);
186
+ }
187
+ catch {
188
+ throw new ApiError(500, 'internal', 'the request could not be read');
189
+ }
190
+ return {
191
+ requestId,
192
+ ticketId: resolved.ticketId,
193
+ dir: resolved.dir,
194
+ manifest,
195
+ result: readTicketResult(resolved.dir),
196
+ binding: readTicketActionBinding(resolved.dir),
197
+ };
198
+ }
199
+ function requestState(result) {
200
+ if (result === null)
201
+ return 'pending';
202
+ if (result.kind === 'canceled')
203
+ return completionEventFor(result);
204
+ return 'answered';
205
+ }
206
+ function deliveryDetail(row) {
207
+ if (row === null)
208
+ return { state: 'none', attempt: 0 };
209
+ const detail = { state: row.state, attempt: row.attempt };
210
+ // A next attempt is only meaningful for a pending retry; the enqueue instant
211
+ // of an untried delivery says nothing a client can act on.
212
+ if (row.state === 'pending' && row.lastFailure !== null)
213
+ detail.next_attempt_at = new Date(row.nextAttemptAt).toISOString();
214
+ if (row.acceptedAt !== null)
215
+ detail.accepted_at = new Date(row.acceptedAt).toISOString();
216
+ if (row.permanentFailedAt !== null)
217
+ detail.permanent_failed_at = new Date(row.permanentFailedAt).toISOString();
218
+ if (row.lastFailure !== null) {
219
+ const failure = row.lastFailure;
220
+ detail.last_failure = {
221
+ kind: failure.kind,
222
+ ...(failure.exitCode === undefined ? {} : { exit_code: failure.exitCode }),
223
+ ...(failure.signal === undefined ? {} : { signal: failure.signal }),
224
+ ...(failure.message === undefined ? {} : { message: failure.message }),
225
+ ...(failure.stderr === undefined ? {} : { stderr: failure.stderr }),
226
+ };
227
+ }
228
+ return detail;
229
+ }
230
+ function requestToDTO(request) {
231
+ const { manifest, result, binding } = request;
232
+ const emittedAt = manifest.source?.emittedAt;
233
+ if (emittedAt === undefined)
234
+ throw new ApiError(500, 'internal', 'the request carries no emitted time');
235
+ const state = requestState(result);
236
+ const dto = {
237
+ request_id: request.requestId,
238
+ state,
239
+ title: manifest.title,
240
+ source: manifest.source ?? {},
241
+ emitted_at: emittedAt,
242
+ };
243
+ if (manifest.subtitle !== undefined)
244
+ dto.subtitle = manifest.subtitle;
245
+ if (result !== null) {
246
+ dto.settled_at = result.kind === 'canceled' ? result.canceledAt : result.completedAt;
247
+ // Never fabricated for a dismissal or a withdrawal: only an answer has responses.
248
+ if (state === 'answered' && result.kind === 'page')
249
+ dto.responses = result.responses;
250
+ if (result.kind === 'canceled' && result.reason !== undefined)
251
+ dto.reason = result.reason;
252
+ const actor = result.kind === 'review' ? undefined : result.actor;
253
+ if (actor !== undefined)
254
+ dto.actor = actor;
255
+ }
256
+ if (binding !== null) {
257
+ dto.action = { name: binding.name, payload: binding.payload };
258
+ dto.delivery = deliveryDetail(readActionDelivery(request.requestId));
259
+ }
260
+ return dto;
261
+ }
262
+ // ===========================================================================
263
+ // POST /v1/human/requests — create
264
+ // ===========================================================================
265
+ /** Authoring failure is the caller's malformed page, never a server fault. */
266
+ function prepareOrReject(dialect, document, source, delivery) {
267
+ try {
268
+ return preparePageSource({ dialect, document, source, delivery, productKinds: resolvePageComponents() });
269
+ }
270
+ catch (error) {
271
+ throw invalid(error instanceof Error ? error.message : 'the page could not be prepared');
272
+ }
273
+ }
274
+ function handleCreate(ctx) {
275
+ const request = validateCreateRequest(ctx.body);
276
+ // Action resolution precedes every write: a rejected action must leave no
277
+ // inbox row for a human to answer.
278
+ let resolvedAction;
279
+ if (request.action !== undefined) {
280
+ const resolution = resolveHumanAction(request.action.name, request.creatorCwd);
281
+ if ('kind' in resolution) {
282
+ const detail = resolution.kind === 'unknown_action'
283
+ ? `no action named ${resolution.name} is declared from ${request.creatorCwd}`
284
+ : `${resolution.name} is declared in ${resolution.configOrigin} but unusable: ${resolution.reason}`;
285
+ throw new ApiError(400, resolution.kind, detail);
286
+ }
287
+ resolvedAction = resolution;
288
+ }
289
+ const prepared = prepareOrReject(request.page.dialect, request.page.source, request.source, request.delivery);
290
+ const dir = ticketDir(`page-${randomBytes(8).toString('hex')}`);
291
+ // The binding is written before the page exists, so a visible request is
292
+ // never momentarily unbound and `replace` can never reach the approved effect.
293
+ if (request.action !== undefined && resolvedAction !== undefined) {
294
+ publishTicketActionBinding(dir, {
295
+ schema: 'crtr.human-action-binding/v1',
296
+ name: request.action.name,
297
+ payload: request.action.payload,
298
+ creatorCwd: request.creatorCwd,
299
+ configOrigin: resolvedAction.configOrigin,
300
+ argv: resolvedAction.argv,
301
+ cwd: resolvedAction.cwd,
302
+ });
303
+ }
304
+ const published = publishPage(dir, prepared);
305
+ return {
306
+ status: 201,
307
+ body: {
308
+ request_id: opaqueInboxTicketId(basename(published.dir)),
309
+ state: 'pending',
310
+ ...(request.action === undefined ? {} : { action: { name: request.action.name } }),
311
+ delivery_state: 'none',
312
+ },
313
+ };
314
+ }
315
+ // ===========================================================================
316
+ // GET /v1/human/requests/:request_id — read
317
+ // ===========================================================================
318
+ function handleGet(ctx) {
319
+ return { status: 200, body: requestToDTO(resolveRequest(ctx)) };
320
+ }
321
+ // ===========================================================================
322
+ // POST /v1/human/requests/:request_id/replace — revise the pending page
323
+ // ===========================================================================
324
+ function handleReplace(ctx) {
325
+ const request = resolveRequest(ctx);
326
+ if (request.result !== null)
327
+ throw new ApiError(409, 'already_settled', 'the request already has a terminal result');
328
+ const body = validateReplaceRequest(ctx.body, request.manifest.delivery);
329
+ // Identity and provenance are the creation's, verbatim — a revision changes
330
+ // only what is on screen, never who asked or when.
331
+ const prepared = prepareOrReject(body.page.dialect, body.page.source, request.manifest.source, body.delivery);
332
+ publishPage(request.dir, prepared);
333
+ return { status: 200, body: requestToDTO({ ...request, manifest: prepared.manifest }) };
334
+ }
335
+ // ===========================================================================
336
+ // Settlement — respond, dismiss, cancel
337
+ // ===========================================================================
338
+ /** The settlement funnels speak the inbox's error vocabulary; requests speak
339
+ * the wire contract's. This is the one translation. */
340
+ function settlementError(error) {
341
+ if (error instanceof ApiError) {
342
+ if (error.code === 'ticket_already_resolved')
343
+ return new ApiError(409, 'already_settled', 'the request already has a terminal result');
344
+ if (error.code === 'page_has_no_response_lifecycle')
345
+ return new ApiError(409, 'not_settleable', 'this request carries no response lifecycle');
346
+ return error;
347
+ }
348
+ if (error instanceof Error && error.message === 'page_has_no_response_lifecycle') {
349
+ return new ApiError(409, 'not_settleable', 'this request carries no response lifecycle');
350
+ }
351
+ return new ApiError(500, 'internal', 'the request could not be settled');
352
+ }
353
+ function requireSettleable(request) {
354
+ if (request.result !== null)
355
+ throw new ApiError(409, 'already_settled', 'the request already has a terminal result');
356
+ if (readPageTicket(request.dir).state === 'passive') {
357
+ throw new ApiError(409, 'not_settleable', 'this request carries no response lifecycle');
358
+ }
359
+ }
360
+ async function handleRespond(ctx) {
361
+ const request = resolveRequest(ctx);
362
+ const body = validateRespondRequest(ctx.body);
363
+ requireSettleable(request);
364
+ try {
365
+ validatePageTicketResponses(request.dir, body.responses);
366
+ }
367
+ catch (error) {
368
+ // `requireSettleable` already refused a page with no response lifecycle, so
369
+ // anything left here is the caller's response map.
370
+ throw invalid(error instanceof Error ? error.message : 'the response map is invalid');
371
+ }
372
+ let result;
373
+ try {
374
+ result = await resolvePageTicket(request.ticketId, body.responses, body.actor === undefined ? {} : { actor: body.actor });
375
+ }
376
+ catch (error) {
377
+ throw settlementError(error);
378
+ }
379
+ return { status: 200, body: requestToDTO({ ...request, result, binding: request.binding }) };
380
+ }
381
+ async function settle(ctx, disposition) {
382
+ const request = resolveRequest(ctx);
383
+ const body = validateSettleRequest(ctx.body);
384
+ requireSettleable(request);
385
+ let result;
386
+ try {
387
+ result = await cancelHumanTicket(request.ticketId, { ...body, disposition });
388
+ }
389
+ catch (error) {
390
+ throw settlementError(error);
391
+ }
392
+ return { status: 200, body: requestToDTO({ ...request, result }) };
393
+ }
394
+ /** The recipient surface deliberately closing the request without answering. */
395
+ function handleDismiss(ctx) {
396
+ return settle(ctx, 'dismissed');
397
+ }
398
+ /** The requester withdrawing its own request. */
399
+ function handleCancel(ctx) {
400
+ return settle(ctx, 'canceled');
401
+ }
402
+ export const humanRequestRoutes = [
403
+ { method: 'POST', pattern: '/v1/human/requests', handler: handleCreate },
404
+ { method: 'GET', pattern: '/v1/human/requests/:request_id', handler: handleGet },
405
+ { method: 'POST', pattern: '/v1/human/requests/:request_id/replace', handler: handleReplace },
406
+ { method: 'POST', pattern: '/v1/human/requests/:request_id/respond', handler: handleRespond },
407
+ { method: 'POST', pattern: '/v1/human/requests/:request_id/dismiss', handler: handleDismiss },
408
+ { method: 'POST', pattern: '/v1/human/requests/:request_id/cancel', handler: handleCancel },
409
+ ];
@@ -108,6 +108,9 @@ async function handleCancel(ctx) {
108
108
  if (request !== undefined && (typeof request !== 'object' || request === null)) {
109
109
  throw new ApiError(400, 'invalid_request', 'ticket cancel body must be an object');
110
110
  }
111
+ if (request?.disposition !== undefined && request.disposition !== 'canceled' && request.disposition !== 'dismissed') {
112
+ throw new ApiError(400, 'invalid_request', '`disposition` must be "canceled" or "dismissed"');
113
+ }
111
114
  const dir = ticketDir(ticketId);
112
115
  const review = getReviewByBridge(ticketId);
113
116
  try {
@@ -364,6 +364,9 @@ async function handleCancel(ctx) {
364
364
  const result = await cancelHumanTicket(resolved.ticketId, {
365
365
  reason: request.reason ?? DEFAULT_CANCEL_REASON,
366
366
  actor: 'human',
367
+ // This is the recipient surface: closing a request here is a dismissal,
368
+ // never the requester withdrawing it.
369
+ disposition: 'dismissed',
367
370
  });
368
371
  if (result.kind !== 'canceled')
369
372
  throw new ApiError(409, 'ticket_already_resolved', 'ticket was already resolved');
@@ -1,14 +1,12 @@
1
1
  import { type ModelRegistryLike } from '../../../core/model-routes.js';
2
- import { assertProfileActive } from '../../../core/profiles/manifest.js';
3
2
  import type { HandlerResult, RequestContext, RouteTable } from '../router.js';
4
3
  interface NodeHandlerDeps {
5
4
  modelRegistry?: ModelRegistryLike;
6
5
  }
7
- export declare function resolveCreateKind(kind: string | undefined, profile: ReturnType<typeof assertProfileActive>): string;
6
+ export { resolveCreateKind } from '../../../core/runtime/launch-target.js';
8
7
  export declare function handleCreate(ctx: RequestContext, deps?: NodeHandlerDeps): Promise<HandlerResult>;
9
8
  export declare function handleList(ctx: RequestContext): HandlerResult;
10
9
  export declare function handlePromote(ctx: RequestContext, deps?: NodeHandlerDeps): Promise<HandlerResult>;
11
10
  export declare function handleYield(ctx: RequestContext, deps?: NodeHandlerDeps): Promise<HandlerResult>;
12
11
  export declare function handleConfig(ctx: RequestContext, deps?: NodeHandlerDeps): Promise<HandlerResult>;
13
12
  export declare const nodeRoutes: RouteTable;
14
- export {};