@north-light/crouter 0.3.191 → 0.3.193

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 (169) hide show
  1. package/dist/api/client.d.ts +31 -10
  2. package/dist/api/client.js +83 -24
  3. package/dist/api/dto/bash-jobs.d.ts +22 -0
  4. package/dist/api/dto/bash-jobs.js +9 -0
  5. package/dist/api/dto/human.d.ts +3 -4
  6. package/dist/api/dto/inbox.d.ts +106 -65
  7. package/dist/api/dto/inbox.js +2 -8
  8. package/dist/api/index.d.ts +1 -0
  9. package/dist/api/index.js +1 -0
  10. package/dist/api/routes.d.ts +7 -0
  11. package/dist/api/routes.js +7 -0
  12. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/serial/provider-rotation.test.ts +101 -3
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -2
  15. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +68 -14
  16. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +87 -13
  17. package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +2 -2
  18. package/dist/clients/attach/__tests__/group-activity.test.js +2 -2
  19. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  20. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  21. package/dist/clients/attach/input/overlay-owner.js +1 -1
  22. package/dist/clients/attach/overlays/auth.js +34 -8
  23. package/dist/clients/attach/overlays/pickers.d.ts +5 -8
  24. package/dist/clients/attach/overlays/pickers.js +37 -43
  25. package/dist/clients/attach/viewer.js +643 -620
  26. package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +38 -128
  27. package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +72 -148
  28. package/dist/clients/inbox/controller.d.ts +2 -3
  29. package/dist/clients/inbox/controller.js +41 -42
  30. package/dist/clients/inbox/page-adapter.d.ts +33 -0
  31. package/dist/clients/inbox/page-adapter.js +54 -0
  32. package/dist/clients/inbox/resolve.d.ts +2 -2
  33. package/dist/clients/inbox/resolve.js +1 -1
  34. package/dist/clients/inbox/review-visibility.d.ts +1 -1
  35. package/dist/clients/inbox/review-visibility.js +1 -1
  36. package/dist/clients/inbox/tui/ansi.d.ts +1 -1
  37. package/dist/clients/inbox/tui/ansi.js +2 -2
  38. package/dist/clients/inbox/tui/input.d.ts +1 -3
  39. package/dist/clients/inbox/tui/input.js +242 -553
  40. package/dist/clients/inbox/tui/panel.d.ts +3 -1
  41. package/dist/clients/inbox/tui/panel.js +59 -187
  42. package/dist/clients/inbox/tui/render.d.ts +0 -25
  43. package/dist/clients/inbox/tui/render.js +165 -416
  44. package/dist/clients/inbox/tui/slots.d.ts +32 -0
  45. package/dist/clients/inbox/tui/slots.js +156 -0
  46. package/dist/clients/inbox/tui/types.d.ts +25 -13
  47. package/dist/clients/inbox/tui.js +5 -3
  48. package/dist/commands/attention.js +2 -2
  49. package/dist/commands/human/doc.js +66 -20
  50. package/dist/commands/human/prompts.d.ts +3 -3
  51. package/dist/commands/human/prompts.js +158 -112
  52. package/dist/commands/human/queue.d.ts +0 -1
  53. package/dist/commands/human/queue.js +34 -94
  54. package/dist/commands/human/shared.d.ts +10 -4
  55. package/dist/commands/human/shared.js +62 -13
  56. package/dist/commands/human.js +8 -4
  57. package/dist/commands/memory/read.js +13 -5
  58. package/dist/commands/node/bash.js +15 -28
  59. package/dist/commands/surface/node/focus.js +1 -1
  60. package/dist/core/__tests__/human-deliver.test.js +40 -46
  61. package/dist/core/__tests__/seam/broker-attach-stream.test.js +7 -1
  62. package/dist/core/__tests__/serial/human-deliver-e2e.test.js +40 -31
  63. package/dist/core/__tests__/serial/spawn-root.test.js +3 -1
  64. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +3 -1
  65. package/dist/core/auth-interaction.d.ts +2 -2
  66. package/dist/core/bash-jobs.d.ts +32 -0
  67. package/dist/core/bash-jobs.js +72 -1
  68. package/dist/core/canvas/__tests__/attention.test.js +16 -10
  69. package/dist/core/canvas/__tests__/render-remote.test.js +9 -3
  70. package/dist/core/canvas/attention.js +1 -1
  71. package/dist/core/canvas/browse/app.js +1 -1
  72. package/dist/core/canvas/nav-model.d.ts +1 -1
  73. package/dist/core/canvas/nav-model.js +1 -1
  74. package/dist/core/canvas/remote-canvas-source.js +1 -1
  75. package/dist/core/config.js +4 -1
  76. package/dist/core/human/__tests__/page-render.test.d.ts +1 -0
  77. package/dist/core/human/__tests__/page-render.test.js +49 -0
  78. package/dist/core/human/__tests__/page-tickets.test.d.ts +1 -0
  79. package/dist/core/human/__tests__/page-tickets.test.js +112 -0
  80. package/dist/core/human/__tests__/page.test.d.ts +1 -0
  81. package/dist/core/human/__tests__/page.test.js +139 -0
  82. package/dist/core/human/__tests__/serial/inbox-core.test.js +42 -32
  83. package/dist/core/human/claim.js +2 -2
  84. package/dist/core/human/convention.d.ts +4 -4
  85. package/dist/core/human/convention.js +6 -13
  86. package/dist/core/human/markdown-html.d.ts +8 -0
  87. package/dist/core/human/markdown-html.js +581 -0
  88. package/dist/core/human/page-catalog.d.ts +6 -0
  89. package/dist/core/human/page-catalog.js +43 -0
  90. package/dist/core/human/page-render.d.ts +3 -0
  91. package/dist/core/human/page-render.js +131 -0
  92. package/dist/core/human/page-schema.d.ts +312 -0
  93. package/dist/core/human/page-schema.js +291 -0
  94. package/dist/core/human/page-synth.d.ts +9 -0
  95. package/dist/core/human/page-synth.js +17 -0
  96. package/dist/core/human/page.d.ts +22 -0
  97. package/dist/core/human/page.js +427 -0
  98. package/dist/core/human/review-schema.d.ts +19 -0
  99. package/dist/core/human/review-schema.js +34 -0
  100. package/dist/core/human/scan.d.ts +12 -3
  101. package/dist/core/human/scan.js +69 -26
  102. package/dist/core/human/summary.d.ts +5 -6
  103. package/dist/core/human/summary.js +54 -37
  104. package/dist/core/human/tickets.d.ts +61 -11
  105. package/dist/core/human/tickets.js +100 -57
  106. package/dist/core/human/types.d.ts +21 -66
  107. package/dist/core/human/types.js +1 -5
  108. package/dist/core/preview-registry.js +0 -1
  109. package/dist/core/review/ticket-filter.d.ts +1 -1
  110. package/dist/core/review/ticket-filter.js +2 -8
  111. package/dist/core/runtime/broker/read-ops.js +3 -0
  112. package/dist/core/runtime/broker.js +19 -10
  113. package/dist/core/runtime/package-health.js +1 -1
  114. package/dist/core/termrender/termrender.d.ts +1 -1
  115. package/dist/core/termrender/termrender.js +2 -2
  116. package/dist/core/tui/page-host.d.ts +1 -1
  117. package/dist/core/tui/page-host.js +2 -2
  118. package/dist/core/user-settings.d.ts +19 -0
  119. package/dist/core/user-settings.js +22 -0
  120. package/dist/daemon/api/handlers/bash-jobs.d.ts +2 -0
  121. package/dist/daemon/api/handlers/bash-jobs.js +89 -0
  122. package/dist/daemon/api/handlers/human.js +16 -14
  123. package/dist/daemon/api/handlers/inbox.js +311 -227
  124. package/dist/daemon/api/router.d.ts +5 -1
  125. package/dist/daemon/api/router.js +5 -0
  126. package/dist/daemon/api/server.js +2 -0
  127. package/dist/daemon/cron-run.js +23 -10
  128. package/dist/daemon/human/finish.d.ts +8 -3
  129. package/dist/daemon/human/finish.js +35 -15
  130. package/dist/pages/bundle.css +1 -0
  131. package/dist/pages/bundle.js +1347 -0
  132. package/dist/pages/comments.d.ts +34 -0
  133. package/dist/pages/comments.js +72 -0
  134. package/dist/pages/elements/cards.d.ts +17 -0
  135. package/dist/pages/elements/cards.js +785 -0
  136. package/dist/pages/elements/chart.d.ts +1 -0
  137. package/dist/pages/elements/chart.js +648 -0
  138. package/dist/pages/elements/options.d.ts +21 -0
  139. package/dist/pages/elements/options.js +604 -0
  140. package/dist/pages/elements/pages.d.ts +24 -0
  141. package/dist/pages/elements/pages.js +355 -0
  142. package/dist/pages/elements/slot.d.ts +21 -0
  143. package/dist/pages/elements/slot.js +105 -0
  144. package/dist/pages/elements/table.d.ts +19 -0
  145. package/dist/pages/elements/table.js +906 -0
  146. package/dist/pages/elements/text.d.ts +25 -0
  147. package/dist/pages/elements/text.js +951 -0
  148. package/dist/pages/entry.d.ts +8 -0
  149. package/dist/pages/entry.js +8 -0
  150. package/dist/pages/host.d.ts +132 -0
  151. package/dist/pages/host.js +149 -0
  152. package/dist/pages/register.d.ts +2 -0
  153. package/dist/pages/register.js +3 -0
  154. package/dist/pages/slot-config.d.ts +36 -0
  155. package/dist/pages/slot-config.js +50 -0
  156. package/dist/pages/types.d.ts +98 -0
  157. package/dist/pages/types.js +23 -0
  158. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +13 -5
  159. package/dist/pi-extensions/canvas-structured-output.js +7 -3
  160. package/dist/types.d.ts +4 -0
  161. package/dist/types.js +2 -0
  162. package/package.json +8 -8
  163. package/runtime.lock.json +156 -101
  164. package/dist/clients/inbox/deck-adapter.d.ts +0 -29
  165. package/dist/clients/inbox/deck-adapter.js +0 -63
  166. package/dist/core/human/deck-factories.d.ts +0 -8
  167. package/dist/core/human/deck-factories.js +0 -15
  168. package/dist/core/human/deck-schema.d.ts +0 -70
  169. package/dist/core/human/deck-schema.js +0 -92
@@ -18,10 +18,14 @@ export interface RequestContext {
18
18
  res: ServerResponse;
19
19
  }
20
20
  /** What a handler returns. `body` is JSON-serialized; omit it (or use 204) for
21
- * an empty response. `headers` are merged over the default `content-type`. */
21
+ * an empty response. `headers` are merged over the default `content-type`.
22
+ * `rawBody` is the escape hatch for the routes that serve bytes rather than
23
+ * JSON (the page runtime bundle): it is written verbatim and the handler owns
24
+ * the `content-type`. Setting both is a handler bug — `rawBody` wins. */
22
25
  export interface HandlerResult {
23
26
  status: number;
24
27
  body?: unknown;
28
+ rawBody?: string | Buffer;
25
29
  headers?: Record<string, string>;
26
30
  }
27
31
  /** A route handler — sync or async. A throw is caught and mapped by the router
@@ -164,6 +164,11 @@ function writeResult(res, result) {
164
164
  if (res.headersSent)
165
165
  return;
166
166
  const headers = { ...(result.headers ?? {}) };
167
+ if (result.rawBody !== undefined) {
168
+ res.writeHead(result.status, headers);
169
+ res.end(result.rawBody);
170
+ return;
171
+ }
167
172
  if (result.status === 204 || result.body === undefined) {
168
173
  res.writeHead(result.status, headers);
169
174
  res.end();
@@ -19,6 +19,7 @@ import { emitEvent } from '../../core/events/emit.js';
19
19
  import { Router } from './router.js';
20
20
  import { handleAttachUpgrade } from './bridge.js';
21
21
  import { attachRoutes } from './handlers/attach.js';
22
+ import { bashJobRoutes } from './handlers/bash-jobs.js';
22
23
  import { brokerOperationRoutes } from './handlers/broker-ops.js';
23
24
  import { canvasRoutes } from './handlers/canvas.js';
24
25
  import { daemonRoutes } from './handlers/daemon.js';
@@ -46,6 +47,7 @@ function buildRouter() {
46
47
  .registerAll(healthRoutes)
47
48
  .registerAll(daemonRoutes)
48
49
  .registerAll(nodeRoutes)
50
+ .registerAll(bashJobRoutes)
49
51
  .registerAll(brokerOperationRoutes)
50
52
  .registerAll(worktreeRoutes)
51
53
  .registerAll(messageRoutes)
@@ -32,9 +32,11 @@ import { spawn } from 'node:child_process';
32
32
  import { createHash, randomBytes, randomUUID } from 'node:crypto';
33
33
  import { join } from 'node:path';
34
34
  import { setTimeout as delay } from 'node:timers/promises';
35
- import { notifyDeck } from '../core/human/deck-factories.js';
35
+ import { rmSync } from 'node:fs';
36
+ import { synthesizedPageDocument, writeSynthesizedPage } from '../core/human/page-synth.js';
36
37
  import { ticketDir } from '../core/human/root.js';
37
- import { submitDeck } from '../core/human/tickets.js';
38
+ import { preparePage, publishPage } from '../core/human/tickets.js';
39
+ import { readConfig } from '../core/config.js';
38
40
  import { dueClockCrons, dueOverlapCrons, expiredIdleCrons, runningCrons, acquireCronRunLease, releaseCronRunLease, recordCronRun, listCronRuns, advanceCron, consumeCron, cancelCron, killLeasedCronRun, verifyLeasedCronRun, getCron, setCronState, setCronLastOutputHash, } from '../core/canvas/crons.js';
39
41
  import { getNode } from '../core/canvas/canvas.js';
40
42
  import { fullName } from '../core/canvas/labels.js';
@@ -211,19 +213,30 @@ async function deliverSpawnSink(c, kind, stdout) {
211
213
  });
212
214
  return res.node.node_id;
213
215
  }
214
- /** Deliver stdout through crouter as a durable notify deck (fire-and-forget
216
+ /** Deliver stdout through crouter as a durable notify page (fire-and-forget
215
217
  * inbox state — jobs whose output is a person's call). Mirrors `crtr human
216
- * notify` minus the CLI-env source stamp: the deck names the cron instead. */
218
+ * notify` minus the CLI-env source stamp: the page names the cron instead. */
217
219
  function deliverHumanSink(c, stdout) {
220
+ const synthesized = writeSynthesizedPage(synthesizedPageDocument(`⏰ cron ${c.name}`, `Scheduled cron "${c.name}" delivered output for your attention.`, stdout, false));
221
+ // Cron stdout rides into the page verbatim, so it can carry invalid page
222
+ // markup. Validate before minting the ticket, so a rejected page leaves no
223
+ // half-built `nfy-*` directory behind.
224
+ let prepared;
225
+ try {
226
+ prepared = preparePage({
227
+ sourceFile: synthesized.path,
228
+ dialect: 'md',
229
+ source: { sessionName: `cron ${c.name} (${c.cron_id})` },
230
+ productKinds: readConfig('user').page_components,
231
+ });
232
+ }
233
+ finally {
234
+ rmSync(synthesized.dir, { recursive: true, force: true });
235
+ }
218
236
  const id = `nfy-${randomBytes(4).toString('hex')}`;
219
237
  const rc = { mode: 'notify' };
220
238
  atomicWriteJson(join(ticketDir(id), 'run.json'), rc);
221
- let deck = notifyDeck(`⏰ cron ${c.name}`, {
222
- subtitle: `Scheduled cron "${c.name}" delivered output for your attention.`,
223
- body: stdout,
224
- });
225
- deck = { ...deck, source: { sessionName: `cron ${c.name} (${c.cron_id})` } };
226
- submitDeck({ dir: ticketDir(id), deck });
239
+ publishPage(ticketDir(id), prepared);
227
240
  }
228
241
  /** Route stdout to the cron's armed sink. Returns the delivered string on
229
242
  * success; throws on an undeliverable sink (the caller's failure path). */
@@ -1,6 +1,11 @@
1
- import type { InteractionResponse, TicketResult } from '../../core/human/types.js';
2
- /** Resolve a deck ticket under the store-owned claim-takeover lock boundary. */
3
- export declare function resolveDeckTicket(nodeId: string, responses: InteractionResponse[]): Promise<TicketResult>;
1
+ import type { TicketResult } from '../../core/human/types.js';
2
+ /**
3
+ * The one completion path for a page ticket: claim takeover, exclusive result
4
+ * publication, then the asking node's wake. Every addressing surface — the
5
+ * node-addressed resolve route and the ticket-id-addressed inbox respond route —
6
+ * finishes a page here, so an answer always reaches the node that asked.
7
+ */
8
+ export declare function resolvePageTicket(nodeId: string, responses: unknown, productKinds?: string[]): Promise<TicketResult>;
4
9
  /** Cancel a ticket with the store's first-writer-wins publication. */
5
10
  export declare function cancelHumanTicket(nodeId: string, opts: {
6
11
  reason?: string;
@@ -2,19 +2,35 @@ import { join } from 'node:path';
2
2
  import { ApiError } from '../../api/index.js';
3
3
  import { getNode, subscribersOf } from '../../core/canvas/canvas.js';
4
4
  import { ticketDir } from '../../core/human/root.js';
5
- import { cancelTicket, projectReviewOutput, readTicketResult, takeoverAndCompleteDeck, } from '../../core/human/tickets.js';
5
+ import { cancelTicket, projectReviewOutput, readTicketResult, takeoverAndCompletePage, } from '../../core/human/tickets.js';
6
6
  import { responsePath } from '../../core/human/convention.js';
7
7
  import { readJsonOrNull } from '../../core/fs-utils.js';
8
8
  import { appendInbox } from '../../core/feed/inbox.js';
9
9
  import { FinalDeliveryError, pushFinal } from '../../core/feed/feed.js';
10
10
  import { transition } from '../../core/runtime/lifecycle.js';
11
- function envelopeFromDeckResult(responses, summary, completedAt, resultPath) {
12
- return { summary, responsePath: resultPath, schema: 'humanloop.response/v2', responses, completedAt };
13
- }
14
- function renderDeckAnswer(env) {
15
- const summary = env.summary.trim();
16
- const body = summary === '' ? '(the human submitted no answer text)' : summary;
17
- return `${body}\n\nResponse metadata (question ids, selected options, timestamp): ${env.responsePath}`;
11
+ /**
12
+ * The answer a page's asking node wakes with. Slot responses are small and
13
+ * typed, so they are inlined as compact JSON rather than left behind the path:
14
+ * the node reads its answers out of its own report. An acknowledgement carries
15
+ * no responses at all and reads as one line saying the human saw the page.
16
+ * Interim rendering — the designed answer view is phase 5.
17
+ */
18
+ function renderPageAnswer(result, resultPath) {
19
+ const entries = Object.entries(result.responses);
20
+ const lines = [];
21
+ if (entries.length === 0) {
22
+ lines.push('The human read the page and dismissed it. No slot responses came back, so the acknowledgement is the whole answer.');
23
+ }
24
+ else {
25
+ lines.push(`The human answered ${entries.length} slot${entries.length === 1 ? '' : 's'}:`, '');
26
+ for (const [slotId, response] of entries)
27
+ lines.push(`${slotId}: ${JSON.stringify(response)}`);
28
+ const summary = result.summary.trim();
29
+ if (summary !== '')
30
+ lines.push('', summary);
31
+ }
32
+ lines.push('', `Response metadata (slot ids, typed responses, timestamp): ${resultPath}`);
33
+ return lines.join('\n');
18
34
  }
19
35
  function deliveryFailed(error) {
20
36
  // A canonical final is committed inside its own write boundary and can never
@@ -35,9 +51,14 @@ async function deliverOrConflict(nodeId) {
35
51
  }
36
52
  throw new ApiError(409, 'ticket_already_resolved', 'ticket already resolved');
37
53
  }
38
- /** Resolve a deck ticket under the store-owned claim-takeover lock boundary. */
39
- export async function resolveDeckTicket(nodeId, responses) {
40
- const completed = takeoverAndCompleteDeck(ticketDir(nodeId), responses, { host: 'crtrd', pid: process.pid });
54
+ /**
55
+ * The one completion path for a page ticket: claim takeover, exclusive result
56
+ * publication, then the asking node's wake. Every addressing surface — the
57
+ * node-addressed resolve route and the ticket-id-addressed inbox respond route —
58
+ * finishes a page here, so an answer always reaches the node that asked.
59
+ */
60
+ export async function resolvePageTicket(nodeId, responses, productKinds = []) {
61
+ const completed = takeoverAndCompletePage(ticketDir(nodeId), responses, { host: 'crtrd', pid: process.pid }, { productKinds });
41
62
  if (!completed.won)
42
63
  return deliverOrConflict(nodeId);
43
64
  try {
@@ -108,9 +129,8 @@ export async function deliverTerminalResult(nodeId) {
108
129
  transition(nodeId, 'finish');
109
130
  return true;
110
131
  }
111
- if (result.kind !== 'deck')
112
- return true;
113
- const envelope = envelopeFromDeckResult(result.responses, result.summary, result.completedAt, responsePath(dir));
114
- await pushFinal(nodeId, renderDeckAnswer(envelope));
132
+ if (result.kind === 'page') {
133
+ await pushFinal(nodeId, renderPageAnswer(result, responsePath(dir)));
134
+ }
115
135
  return true;
116
136
  }
@@ -0,0 +1 @@
1
+ :where(:root,:host){color-scheme:light dark;--crtr-color-background: #ffffff;--crtr-color-surface: #f7f7f8;--crtr-color-surface-hover: #eeeeef;--crtr-color-text: #1c1c1f;--crtr-color-text-muted: #68686f;--crtr-color-border: #d9d9de;--crtr-color-accent: #5753c9;--crtr-color-accent-contrast: #ffffff;--crtr-color-danger: #c73535;--crtr-color-focus: #706bea;--crtr-chart-1: #5753c9;--crtr-chart-2: #2c8c75;--crtr-chart-3: #cf7c2a;--crtr-chart-4: #b84c84;--crtr-chart-5: #477eb8;--crtr-font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--crtr-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;--crtr-font-size: .875rem;--crtr-line-height: 1.5;--crtr-radius-small: .375rem;--crtr-radius-medium: .625rem;--crtr-space-1: .25rem;--crtr-space-2: .5rem;--crtr-space-3: .75rem;--crtr-space-4: 1rem;--crtr-shadow: 0 1px 2px rgb(0 0 0 / .08)}:where(crtr-options,crtr-text,crtr-table,crtr-cards,crtr-chart,crtr-pages,crtr-page,crtr-slot){color:var(--crtr-color-text);font-family:var(--crtr-font-family);font-size:var(--crtr-font-size);line-height:var(--crtr-line-height)}@media(prefers-color-scheme:dark){:where(:root,:host){--crtr-color-background: #161618;--crtr-color-surface: #222225;--crtr-color-surface-hover: #2d2d31;--crtr-color-text: #f2f2f3;--crtr-color-text-muted: #aaaab1;--crtr-color-border: #414147;--crtr-color-accent: #9692ff;--crtr-color-accent-contrast: #18181b;--crtr-color-danger: #f06a6a;--crtr-color-focus: #aaa7ff}}