@north-light/crouter 0.3.197 → 0.3.199
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.
- package/dist/api/client.d.ts +19 -19
- package/dist/api/client.js +29 -37
- package/dist/api/dto/broker-ops.d.ts +3 -1
- package/dist/api/dto/broker.d.ts +13 -2
- package/dist/api/dto/broker.js +4 -4
- package/dist/api/dto/human.d.ts +6 -5
- package/dist/api/dto/inbox.d.ts +126 -33
- package/dist/api/dto/nodes.d.ts +8 -2
- package/dist/api/dto/profiles.d.ts +23 -0
- package/dist/api/dto/profiles.js +2 -2
- package/dist/api/dto/worktree.d.ts +3 -5
- package/dist/api/routes.d.ts +3 -5
- package/dist/api/routes.js +4 -6
- package/dist/builtin-memory/00-runtime-base.md +8 -8
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +6 -6
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +2 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -1
- package/dist/builtin-memory/insights/capture.md +2 -2
- package/dist/builtin-memory/insights/init.md +3 -3
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +3 -3
- package/dist/builtin-memory/internal/plugins.md +59 -2
- package/dist/builtin-memory/internal/storage-tiers.md +3 -1
- package/dist/builtin-memory/spec/roadmap.md +2 -2
- package/dist/builtin-memory/testing.md +1 -1
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +1 -1
- package/dist/clients/attach/chrome/bash-jobs.js +3 -3
- package/dist/clients/attach/render/chat-view.d.ts +29 -9
- package/dist/clients/attach/render/chat-view.js +64 -35
- package/dist/clients/attach/render/markdown-source.js +0 -6
- package/dist/clients/attach/render/page-block.d.ts +34 -0
- package/dist/clients/attach/render/page-block.js +226 -0
- package/dist/clients/attach/render/tool-calls.js +16 -2
- package/dist/clients/attach/session/whip.d.ts +2 -0
- package/dist/clients/attach/session/whip.js +2 -0
- package/dist/clients/attach/viewer.js +1419 -982
- package/dist/clients/conversation/projection.js +18 -3
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +9 -6
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +13 -14
- package/dist/clients/inbox/controller.js +10 -16
- package/dist/clients/inbox/page-adapter.d.ts +3 -4
- package/dist/clients/inbox/page-adapter.js +3 -4
- package/dist/clients/inbox/resolve.d.ts +1 -1
- package/dist/clients/inbox/resolve.js +3 -3
- package/dist/clients/inbox/review/document-surface.js +3 -1
- package/dist/clients/inbox/review/state.d.ts +2 -1
- package/dist/clients/inbox/review/state.js +5 -16
- package/dist/clients/inbox/tui/input.js +37 -19
- package/dist/clients/inbox/tui/panel.js +5 -31
- package/dist/clients/inbox/tui/render.d.ts +11 -0
- package/dist/clients/inbox/tui/render.js +101 -36
- package/dist/clients/inbox/tui/slots.d.ts +4 -4
- package/dist/clients/inbox/tui/slots.js +19 -44
- package/dist/clients/inbox/tui/types.d.ts +6 -10
- package/dist/clients/inbox/tui.js +2 -2
- package/dist/commands/__tests__/human.test.js +118 -24
- package/dist/commands/__tests__/node-message.test.js +14 -0
- package/dist/commands/attention.js +4 -4
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-snapshot.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/human/{doc.d.ts → components.d.ts} +1 -1
- package/dist/commands/human/components.js +77 -0
- package/dist/commands/human/feedback.d.ts +2 -0
- package/dist/commands/human/feedback.js +108 -0
- package/dist/commands/human/prompts.d.ts +17 -8
- package/dist/commands/human/prompts.js +217 -226
- package/dist/commands/human/queue.js +57 -88
- package/dist/commands/human/shared.d.ts +4 -15
- package/dist/commands/human/shared.js +19 -61
- package/dist/commands/human.js +32 -19
- package/dist/commands/memory/origin.js +2 -2
- package/dist/commands/node/bash.js +4 -4
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +2 -2
- package/dist/commands/node-worktree.js +8 -8
- package/dist/commands/pkg/browse.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +129 -10
- package/dist/commands/profile/delete.js +41 -15
- package/dist/commands/profile.js +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/focus.js +29 -14
- package/dist/commands/surface-edit.js +3 -3
- package/dist/commands/surface-inbox.d.ts +2 -0
- package/dist/commands/{human/inbox.js → surface-inbox.js} +14 -18
- package/dist/commands/surface.js +2 -1
- package/dist/commands/sys/doctor.js +60 -3
- package/dist/commands/sys/feedback.js +79 -25
- package/dist/commands/sys/settings.js +1 -1
- package/dist/core/__tests__/cron-broker-capacity.test.js +1 -1
- package/dist/core/__tests__/daemon-boot.test.js +7 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +20 -36
- package/dist/core/__tests__/extension-abort.test.js +5 -2
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +13 -2
- package/dist/core/__tests__/fixtures/fake-engine.js +47 -3
- package/dist/core/__tests__/human-cancel-guard.test.js +31 -10
- package/dist/core/__tests__/human-deliver.test.js +41 -45
- package/dist/core/__tests__/human-node-not-supervised.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +157 -0
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +13 -11
- package/dist/core/__tests__/seam/dormancy-release.test.js +103 -4
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +2 -2
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +12 -16
- package/dist/core/__tests__/serial/live-mutation.test.js +1 -1
- package/dist/core/__tests__/serial/tmux-surface.test.js +4 -2
- package/dist/core/__tests__/serial/worktree.test.js +24 -198
- package/dist/core/__tests__/session-cycles.test.js +22 -0
- package/dist/core/__tests__/stop-guard.test.js +4 -4
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +19 -4
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +10 -0
- package/dist/core/canvas/__tests__/attention.test.js +8 -5
- package/dist/core/canvas/__tests__/render-remote.test.js +5 -5
- package/dist/core/canvas/attention.js +8 -6
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +21 -3
- package/dist/core/canvas/browse/model.d.ts +8 -2
- package/dist/core/canvas/browse/model.js +11 -4
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +5 -1
- package/dist/core/canvas/canvas.d.ts +33 -0
- package/dist/core/canvas/canvas.js +92 -0
- package/dist/core/canvas/crons.d.ts +9 -0
- package/dist/core/canvas/crons.js +51 -0
- package/dist/core/canvas/node-order.d.ts +22 -1
- package/dist/core/canvas/node-order.js +26 -1
- package/dist/core/canvas/render-source.d.ts +5 -0
- package/dist/core/canvas/render-source.js +1 -0
- package/dist/core/canvas/types.d.ts +9 -3
- package/dist/core/command-plugins/bundle.d.ts +6 -1
- package/dist/core/command-plugins/bundle.js +26 -13
- package/dist/core/command.js +8 -12
- package/dist/core/config.d.ts +47 -1
- package/dist/core/config.js +138 -4
- package/dist/core/help.d.ts +3 -0
- package/dist/core/help.js +1 -1
- package/dist/core/human/__tests__/page-catalog.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-catalog.test.js +20 -0
- package/dist/core/human/__tests__/page-tickets.test.js +68 -34
- package/dist/core/human/__tests__/page.test.js +60 -119
- package/dist/core/human/__tests__/serial/inbox-core.test.js +6 -16
- package/dist/core/human/answer-text.d.ts +3 -0
- package/dist/core/human/answer-text.js +98 -0
- package/dist/core/human/answer.d.ts +53 -0
- package/dist/core/human/answer.js +119 -0
- package/dist/core/human/component-docs.d.ts +18 -0
- package/dist/core/human/component-docs.js +434 -0
- package/dist/core/human/convention.d.ts +17 -3
- package/dist/core/human/convention.js +36 -5
- package/dist/core/human/feedback-companion.d.ts +20 -0
- package/dist/core/human/feedback-companion.js +98 -0
- package/dist/core/human/feedback.d.ts +65 -0
- package/dist/core/human/feedback.js +116 -0
- package/dist/core/human/page-catalog.d.ts +56 -5
- package/dist/core/human/page-catalog.js +151 -22
- package/dist/core/human/page-errors.d.ts +4 -0
- package/dist/core/human/page-errors.js +7 -0
- package/dist/core/human/page-eval.d.ts +20 -0
- package/dist/core/human/page-eval.js +123 -0
- package/dist/core/human/page-schema.d.ts +48 -116
- package/dist/core/human/page-schema.js +100 -72
- package/dist/core/human/page-synth.d.ts +2 -4
- package/dist/core/human/page-synth.js +20 -7
- package/dist/core/human/page.d.ts +13 -21
- package/dist/core/human/page.js +147 -391
- package/dist/core/human/review-schema.d.ts +1 -0
- package/dist/core/human/review-schema.js +1 -1
- package/dist/core/human/root.d.ts +6 -2
- package/dist/core/human/root.js +12 -4
- package/dist/core/human/scan.d.ts +3 -3
- package/dist/core/human/scan.js +21 -21
- package/dist/core/human/summary.d.ts +2 -6
- package/dist/core/human/summary.js +3 -59
- package/dist/core/human/tickets.d.ts +22 -15
- package/dist/core/human/tickets.js +90 -28
- package/dist/core/human/types.d.ts +14 -2
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/preview-registry.js +8 -10
- package/dist/core/profiles/default-binding.d.ts +4 -0
- package/dist/core/profiles/default-binding.js +24 -0
- package/dist/core/profiles/deletion-reservation.d.ts +8 -0
- package/dist/core/profiles/deletion-reservation.js +38 -0
- package/dist/core/profiles/manifest.d.ts +3 -0
- package/dist/core/profiles/manifest.js +12 -0
- package/dist/core/profiles/select.js +2 -2
- package/dist/core/review/realize.js +12 -4
- package/dist/core/review/store.d.ts +6 -0
- package/dist/core/review/store.js +31 -0
- package/dist/core/runtime/bearings.d.ts +0 -4
- package/dist/core/runtime/bearings.js +4 -62
- package/dist/core/runtime/bin-contributions.d.ts +44 -0
- package/dist/core/runtime/bin-contributions.js +186 -0
- package/dist/core/runtime/boot-root.js +6 -1
- package/dist/core/runtime/broker/client-registry.d.ts +4 -0
- package/dist/core/runtime/broker/client-registry.js +13 -1
- package/dist/core/runtime/broker/extension-abort.d.ts +1 -1
- package/dist/core/runtime/broker/extension-abort.js +4 -2
- package/dist/core/runtime/broker/fault-retry.js +4 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +3 -1
- package/dist/core/runtime/broker/frame-dispatch.js +5 -6
- package/dist/core/runtime/broker/read-ops.d.ts +1 -1
- package/dist/core/runtime/broker/read-ops.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +6 -1
- package/dist/core/runtime/broker-extension-render.js +19 -0
- package/dist/core/runtime/broker-protocol.d.ts +14 -0
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +25 -12
- package/dist/core/runtime/busy.d.ts +4 -3
- package/dist/core/runtime/busy.js +4 -3
- package/dist/core/runtime/front-door-env.d.ts +5 -0
- package/dist/core/runtime/front-door-env.js +16 -0
- package/dist/core/runtime/front-door.d.ts +1 -5
- package/dist/core/runtime/front-door.js +2 -5
- package/dist/core/runtime/host.d.ts +1 -1
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/invocation.js +10 -0
- package/dist/core/runtime/kickoff.js +34 -0
- package/dist/core/runtime/launch.d.ts +2 -6
- package/dist/core/runtime/node-read.js +4 -1
- package/dist/core/runtime/nodes.js +2 -0
- package/dist/core/runtime/promote.d.ts +1 -0
- package/dist/core/runtime/promote.js +3 -1
- package/dist/core/runtime/session-cycles.d.ts +6 -0
- package/dist/core/runtime/session-cycles.js +15 -1
- package/dist/core/runtime/session-visibility.d.ts +17 -8
- package/dist/core/runtime/session-visibility.js +19 -10
- package/dist/core/runtime/spawn-env.d.ts +9 -1
- package/dist/core/runtime/spawn-env.js +62 -2
- package/dist/core/runtime/spawn.js +4 -4
- package/dist/core/runtime/stop-guard.d.ts +2 -2
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/stop-signals.d.ts +1 -1
- package/dist/core/runtime/stop-signals.js +2 -2
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/session-model/session-state.d.ts +4 -3
- package/dist/core/session-model/session-state.js +14 -1
- package/dist/core/user-settings.d.ts +36 -2
- package/dist/core/user-settings.js +56 -14
- package/dist/core/worktree.d.ts +15 -45
- package/dist/core/worktree.js +59 -261
- package/dist/daemon/api/__tests__/seam/profile-delete.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +386 -0
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +9 -5
- package/dist/daemon/api/handlers/feedback-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/feedback-comments.js +207 -0
- package/dist/daemon/api/handlers/human.js +24 -20
- package/dist/daemon/api/handlers/inbox.d.ts +10 -0
- package/dist/daemon/api/handlers/inbox.js +48 -109
- package/dist/daemon/api/handlers/profiles.js +11 -29
- package/dist/daemon/api/map.d.ts +6 -1
- package/dist/daemon/api/map.js +23 -0
- package/dist/daemon/api/server.js +3 -1
- package/dist/daemon/companion-retire.d.ts +2 -0
- package/dist/daemon/companion-retire.js +33 -0
- package/dist/daemon/cron-run.js +29 -17
- package/dist/daemon/crtrd.js +1 -1
- package/dist/daemon/fleet.d.ts +22 -10
- package/dist/daemon/fleet.js +43 -24
- package/dist/daemon/human/finish.d.ts +12 -6
- package/dist/daemon/human/finish.js +134 -54
- package/dist/daemon/human/sweep.js +9 -3
- package/dist/daemon/profile-delete.d.ts +7 -0
- package/dist/daemon/profile-delete.js +306 -0
- package/dist/daemon/reconcilers/broker-supervision.js +14 -9
- package/dist/daemon/reconcilers/dormant-inbox.js +11 -6
- package/dist/daemon/reconcilers/live-obligation.d.ts +21 -1
- package/dist/daemon/reconcilers/live-obligation.js +30 -2
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +28 -1
- package/dist/daemon/review/deliver.js +3 -3
- package/dist/daemon/review/finish.d.ts +0 -2
- package/dist/daemon/review/finish.js +1 -29
- package/dist/daemon/review/sweep.js +12 -1
- package/dist/pi-extensions/__tests__/canvas-bash-valve.test.js +56 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-bash-valve.d.ts +9 -1
- package/dist/pi-extensions/canvas-bash-valve.js +31 -13
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/dist/pi-extensions/canvas-review-boundary.d.ts +4 -0
- package/dist/pi-extensions/canvas-review-boundary.js +18 -9
- package/dist/pi-extensions/canvas-stophook.js +4 -4
- package/dist/shared/generated-context.js +2 -2
- package/dist/types.d.ts +61 -5
- package/dist/types.js +8 -2
- package/package.json +4 -4
- package/runtime.lock.json +5 -33
- package/scripts/install-runtime.mjs +19 -24
- package/dist/builtin-memory/init.md +0 -38
- package/dist/builtin-memory/plan.md +0 -19
- package/dist/builtin-memory/spec.md +0 -19
- package/dist/clients/attach/render/html-markdown.d.ts +0 -13
- package/dist/clients/attach/render/html-markdown.js +0 -206
- package/dist/commands/human/doc.js +0 -144
- package/dist/commands/human/inbox.d.ts +0 -2
- package/dist/core/human/__tests__/page-render.test.js +0 -49
- package/dist/core/human/markdown-html.d.ts +0 -8
- package/dist/core/human/markdown-html.js +0 -581
- package/dist/core/human/page-render.d.ts +0 -3
- package/dist/core/human/page-render.js +0 -131
- package/dist/pages/bundle.css +0 -1
- package/dist/pages/bundle.js +0 -1347
- package/dist/pages/comments.d.ts +0 -43
- package/dist/pages/comments.js +0 -125
- package/dist/pages/elements/cards.d.ts +0 -17
- package/dist/pages/elements/cards.js +0 -802
- package/dist/pages/elements/chart.js +0 -648
- package/dist/pages/elements/options.d.ts +0 -21
- package/dist/pages/elements/options.js +0 -629
- package/dist/pages/elements/pages.d.ts +0 -24
- package/dist/pages/elements/pages.js +0 -355
- package/dist/pages/elements/slot.d.ts +0 -21
- package/dist/pages/elements/slot.js +0 -105
- package/dist/pages/elements/table.d.ts +0 -19
- package/dist/pages/elements/table.js +0 -935
- package/dist/pages/elements/text.d.ts +0 -25
- package/dist/pages/elements/text.js +0 -968
- package/dist/pages/entry.d.ts +0 -8
- package/dist/pages/entry.js +0 -8
- package/dist/pages/host.d.ts +0 -158
- package/dist/pages/host.js +0 -182
- package/dist/pages/register.d.ts +0 -2
- package/dist/pages/register.js +0 -3
- package/dist/pages/slot-config.d.ts +0 -36
- package/dist/pages/slot-config.js +0 -50
- package/dist/pages/types.d.ts +0 -98
- package/dist/pages/types.js +0 -23
- /package/dist/{core/human/__tests__/page-render.test.d.ts → commands/__tests__/node-message.test.d.ts} +0 -0
- /package/dist/{pages/elements/chart.d.ts → core/__tests__/plugin-page-components.test.d.ts} +0 -0
|
@@ -12,20 +12,10 @@ const temp = mkdtempSync(join(tmpdir(), 'crtr-inbox-core-'));
|
|
|
12
12
|
process.env['CRTR_HOME'] = temp;
|
|
13
13
|
const source = join(temp, 'source.md');
|
|
14
14
|
writeFileSync(source, '# Source\n');
|
|
15
|
-
const pageSource = join(temp, 'page.
|
|
16
|
-
writeFileSync(pageSource,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'subtitle: Go? requires your attention.',
|
|
20
|
-
'---',
|
|
21
|
-
'',
|
|
22
|
-
'```slot:text',
|
|
23
|
-
'{ "id": "go", "initialText": "", "editable": true }',
|
|
24
|
-
'```',
|
|
25
|
-
'',
|
|
26
|
-
].join('\n'));
|
|
27
|
-
const page = (id, blockedSince) => submitPage({ dir: ticketDir(id), sourceFile: pageSource, dialect: 'md', source: { blockedSince, nodeId: 'node-1' } });
|
|
28
|
-
const answer = { go: { text: 'yes', edited: true, comments: [] } };
|
|
15
|
+
const pageSource = join(temp, 'page.tsx');
|
|
16
|
+
writeFileSync(pageSource, 'export default () => (\n <Page title="A page" subtitle="Go? requires your attention.">\n <UserText id="go" initialText="" />\n </Page>\n);\n');
|
|
17
|
+
const page = (id, emittedAt) => submitPage({ dir: ticketDir(id), sourceFile: pageSource, source: { emittedAt, nodeId: 'node-1' }, delivery: { placement: 'inline', inbox: true, reply: true } });
|
|
18
|
+
const answer = { go: { text: 'yes', edited: true } };
|
|
29
19
|
async function waitFor(paths) {
|
|
30
20
|
for (let attempts = 0; !paths.every(existsSync); attempts++) {
|
|
31
21
|
if (attempts > 1_000)
|
|
@@ -42,7 +32,7 @@ assert.equal(existsSync(ticketDir('bad-review')), false, 'invalid submission doe
|
|
|
42
32
|
const prepared = ticketDir('prepared');
|
|
43
33
|
mkdirSync(prepared);
|
|
44
34
|
writeFileSync(join(prepared, 'asset.png'), 'prepared asset');
|
|
45
|
-
const preparedTicket = submitPage({ dir: prepared, sourceFile: pageSource,
|
|
35
|
+
const preparedTicket = submitPage({ dir: prepared, sourceFile: pageSource, source: { emittedAt: '2025-01-02T00:00:00.000Z', nodeId: 'node-1' }, delivery: { placement: 'inline', inbox: true, reply: true } });
|
|
46
36
|
assert.equal(preparedTicket.dir, realpathSync(prepared), 'submission accepts a crouter-precreated direct child and colocated assets');
|
|
47
37
|
// Same timestamp is stable by id; progress is resumable work, never a hidden lease.
|
|
48
38
|
atomicWriteJson(progressPath(newPage.dir), { kind: 'page', responses: {}, savedAt: new Date().toISOString() });
|
|
@@ -104,7 +94,7 @@ for (const [index, fixture] of rejectFixtures.entries()) {
|
|
|
104
94
|
const protocolDir = ticketDir('stale-protocol');
|
|
105
95
|
mkdirSync(protocolDir);
|
|
106
96
|
writeFileSync(responsePath(protocolDir), '{}');
|
|
107
|
-
assert.throws(() => submitPage({ dir: protocolDir, sourceFile: pageSource
|
|
97
|
+
assert.throws(() => submitPage({ dir: protocolDir, sourceFile: pageSource }), /protocol state/, 'precreated assets may not include humanloop lifecycle state');
|
|
108
98
|
const cancelDir = page('cancel-race', '2025-01-03T00:00:00.000Z').dir;
|
|
109
99
|
const canceled = cancelTicket(cancelDir, { reason: 'stop' });
|
|
110
100
|
assert.equal(canceled.status, 'canceled');
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
function inline(value) {
|
|
2
|
+
return `“${value}”`;
|
|
3
|
+
}
|
|
4
|
+
function verbatim(prefix, value) {
|
|
5
|
+
if (value === '')
|
|
6
|
+
return [`${prefix}(left empty)`];
|
|
7
|
+
if (!value.includes('\n'))
|
|
8
|
+
return [`${prefix}${inline(value)}`];
|
|
9
|
+
return [...(prefix === '' ? [] : [prefix.trimEnd()]), ...value.split('\n').map((line) => `> ${line}`)];
|
|
10
|
+
}
|
|
11
|
+
function code(value) {
|
|
12
|
+
const fence = '`'.repeat(Math.max(0, ...(value.match(/`+/g)?.map((run) => run.length) ?? [0])) + 1);
|
|
13
|
+
return `${fence}${value}${fence}`;
|
|
14
|
+
}
|
|
15
|
+
function item(item) {
|
|
16
|
+
return `${item.label} (${code(item.id)})`;
|
|
17
|
+
}
|
|
18
|
+
function groupName(group) {
|
|
19
|
+
if (group.group === 'option')
|
|
20
|
+
return 'Chose';
|
|
21
|
+
if (group.group === 'card')
|
|
22
|
+
return 'Cards';
|
|
23
|
+
return group.group === 'row' ? 'Rows' : 'Columns';
|
|
24
|
+
}
|
|
25
|
+
function groupLines(group) {
|
|
26
|
+
return [`${groupName(group)}: ${group.picked.length === 0 ? 'none' : group.picked.map(item).join(', ')}`];
|
|
27
|
+
}
|
|
28
|
+
function commentLines(comment) {
|
|
29
|
+
const prefix = comment.anchor.kind === 'whole' ? 'Comment: ' : `Comment on ${inline(comment.anchorLabel)}: `;
|
|
30
|
+
return verbatim(prefix, comment.text);
|
|
31
|
+
}
|
|
32
|
+
function shape(slot) {
|
|
33
|
+
if (slot.kind === 'options')
|
|
34
|
+
return `options, ${slot.groups[0]?.mode === 'single' ? 'pick one' : 'pick any'}`;
|
|
35
|
+
if (slot.kind === 'text') {
|
|
36
|
+
if (slot.text?.singleLine === true)
|
|
37
|
+
return `one-line text, ${slot.text.edited ? 'edited' : 'unchanged'}`;
|
|
38
|
+
return `text, ${slot.text?.edited === true ? 'edited' : 'accepted as written'}`;
|
|
39
|
+
}
|
|
40
|
+
if (slot.kind === 'table') {
|
|
41
|
+
const group = slot.groups.find((candidate) => candidate.group === 'row') ?? slot.groups.find((candidate) => candidate.group === 'column');
|
|
42
|
+
return `table, ${group === undefined ? 'no selection' : group.total > 0 ? `${group.picked.length} of ${group.total} ${group.group}s` : `${group.picked.length} ${group.group}${group.picked.length === 1 ? '' : 's'}`}`;
|
|
43
|
+
}
|
|
44
|
+
if (slot.kind === 'cards') {
|
|
45
|
+
const cards = slot.groups[0];
|
|
46
|
+
return `cards, ${cards?.total && cards.total > 0 ? `${cards.picked.length} of ${cards.total} cards` : `${cards?.picked.length ?? 0} cards`}`;
|
|
47
|
+
}
|
|
48
|
+
return slot.kind;
|
|
49
|
+
}
|
|
50
|
+
function slotHeading(slot, totalSlots) {
|
|
51
|
+
if (slot.label !== undefined && slot.label !== '')
|
|
52
|
+
return `**${slot.label}** — ${shape(slot)}`;
|
|
53
|
+
if (totalSlots === 1)
|
|
54
|
+
return undefined;
|
|
55
|
+
return `${code(slot.id)} — ${shape(slot)}`;
|
|
56
|
+
}
|
|
57
|
+
function slotLines(slot, totalSlots) {
|
|
58
|
+
const lines = [];
|
|
59
|
+
const heading = slotHeading(slot, totalSlots);
|
|
60
|
+
if (heading !== undefined)
|
|
61
|
+
lines.push(heading);
|
|
62
|
+
if (slot.raw !== undefined) {
|
|
63
|
+
lines.push(`Custom component (${code(slot.kind)}) response: ${JSON.stringify(slot.raw)}`);
|
|
64
|
+
}
|
|
65
|
+
else if (slot.text !== undefined) {
|
|
66
|
+
// A one-line field always shows its value inline — quoted, or "(left empty)" — because the
|
|
67
|
+
// whole answer is that one string; the heading already carries whether it was edited.
|
|
68
|
+
if (slot.text.singleLine === true || slot.text.edited)
|
|
69
|
+
lines.push(...verbatim('', slot.text.value));
|
|
70
|
+
else
|
|
71
|
+
lines.push('Accepted as written, unchanged');
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
for (const group of slot.groups)
|
|
75
|
+
lines.push(...groupLines(group));
|
|
76
|
+
if (slot.freetext !== undefined)
|
|
77
|
+
lines.push(...verbatim('Other: ', slot.freetext));
|
|
78
|
+
}
|
|
79
|
+
for (const comment of slot.comments)
|
|
80
|
+
lines.push(...commentLines(comment));
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
/** Render a PageAnswer as the deterministic Markdown report delivered to its asking agent. */
|
|
84
|
+
export function renderAnswerText(answer, responsePath) {
|
|
85
|
+
if (answer.kind === 'acknowledgement') {
|
|
86
|
+
return [
|
|
87
|
+
`The user saw **${answer.title}** and dismissed it. The page asked for nothing, so the acknowledgement is the whole answer.`,
|
|
88
|
+
'',
|
|
89
|
+
`Acknowledged ${answer.completedAt}. Response record: ${responsePath}`,
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
92
|
+
const answered = answer.slots.filter((slot) => slot.state === 'answered');
|
|
93
|
+
const lines = [`The user answered **${answer.title}**.`];
|
|
94
|
+
for (const slot of answered)
|
|
95
|
+
lines.push('', ...slotLines(slot, answer.slots.length));
|
|
96
|
+
lines.push('', `Answered ${answer.completedAt}. Typed responses: ${responsePath}`);
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type CommentAnchor, type PageResponses, type PageSlot } from './page-schema.js';
|
|
2
|
+
export type PageAnswer = {
|
|
3
|
+
title: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
completedAt: string;
|
|
6
|
+
kind: 'answer' | 'acknowledgement';
|
|
7
|
+
digest: string;
|
|
8
|
+
slots: PageAnswerSlot[];
|
|
9
|
+
};
|
|
10
|
+
export type PageAnswerSlot = {
|
|
11
|
+
id: string;
|
|
12
|
+
kind: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
state: 'answered' | 'unanswered';
|
|
15
|
+
blank: boolean;
|
|
16
|
+
groups: PageAnswerGroup[];
|
|
17
|
+
freetext?: string;
|
|
18
|
+
/** `singleLine` marks a one-line input, whose value is worth echoing even when unchanged. */
|
|
19
|
+
text?: {
|
|
20
|
+
value: string;
|
|
21
|
+
edited: boolean;
|
|
22
|
+
singleLine?: boolean;
|
|
23
|
+
};
|
|
24
|
+
comments: PageAnswerComment[];
|
|
25
|
+
raw?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
export type PageAnswerGroup = {
|
|
28
|
+
group: 'option' | 'card' | 'row' | 'column';
|
|
29
|
+
mode: 'single' | 'multi';
|
|
30
|
+
total: number;
|
|
31
|
+
picked: {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
}[];
|
|
35
|
+
};
|
|
36
|
+
/** Stored responses predating the `whole` anchor's removal still carry it; answers keep rendering them. */
|
|
37
|
+
export type LegacyCommentAnchor = CommentAnchor | {
|
|
38
|
+
kind: 'whole';
|
|
39
|
+
};
|
|
40
|
+
export type PageAnswerComment = {
|
|
41
|
+
anchor: LegacyCommentAnchor;
|
|
42
|
+
anchorLabel: string;
|
|
43
|
+
text: string;
|
|
44
|
+
};
|
|
45
|
+
export interface DescribePageAnswerOptions {
|
|
46
|
+
completedAt: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function answerCommentAnchorLabel(slot: PageSlot, anchor: LegacyCommentAnchor): string;
|
|
49
|
+
/** Derive the one shared answer model from the immutable page manifest and a response map. */
|
|
50
|
+
/** `manifest` is unknown deliberately: this boundary validates persisted JSON before deriving. */
|
|
51
|
+
export declare function describePageAnswer(manifest: unknown, responses: PageResponses, opts: DescribePageAnswerOptions): PageAnswer;
|
|
52
|
+
/** The stable one-line digest stored in response.json and shown in history. */
|
|
53
|
+
export declare function buildAnswerDigest(slots: PageAnswerSlot[]): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isResponseBearingSlot, validatePageManifest, } from './page-schema.js';
|
|
2
|
+
function hasOwn(object, key) {
|
|
3
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
4
|
+
}
|
|
5
|
+
function responseRecord(response) {
|
|
6
|
+
return response !== null && typeof response === 'object' ? response : {};
|
|
7
|
+
}
|
|
8
|
+
function strings(value) {
|
|
9
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
10
|
+
}
|
|
11
|
+
function comments(value) {
|
|
12
|
+
return Array.isArray(value) ? value.filter((item) => item !== null && typeof item === 'object' && 'anchor' in item && 'text' in item) : [];
|
|
13
|
+
}
|
|
14
|
+
function picked(ids, items) {
|
|
15
|
+
const labels = new Map(items.map((item) => [item.id, item.label]));
|
|
16
|
+
return ids.map((id) => ({ id, label: labels.get(id) ?? id }));
|
|
17
|
+
}
|
|
18
|
+
function rowLabel(config, id) {
|
|
19
|
+
const row = config.rows?.find((candidate) => candidate.id === id);
|
|
20
|
+
const firstColumn = config.columns[0];
|
|
21
|
+
if (row === undefined || firstColumn === undefined || !hasOwn(row.cells, firstColumn.id))
|
|
22
|
+
return id;
|
|
23
|
+
return String(row.cells[firstColumn.id]);
|
|
24
|
+
}
|
|
25
|
+
function slotLabel(slot) {
|
|
26
|
+
const label = slot.config.label;
|
|
27
|
+
return typeof label === 'string' && label !== '' ? label : slot.id ?? slot.kind;
|
|
28
|
+
}
|
|
29
|
+
export function answerCommentAnchorLabel(slot, anchor) {
|
|
30
|
+
if (anchor.kind === 'whole')
|
|
31
|
+
return slotLabel(slot); // legacy stored data only; new anchors are option/row
|
|
32
|
+
if (anchor.kind === 'option')
|
|
33
|
+
return slot.config.options.find((option) => option.id === anchor.optionId)?.label ?? anchor.optionId;
|
|
34
|
+
return rowLabel(slot.config, anchor.rowId);
|
|
35
|
+
}
|
|
36
|
+
function describeComments(slot, response) {
|
|
37
|
+
return comments(response.comments).map((comment) => ({ anchor: comment.anchor, anchorLabel: answerCommentAnchorLabel(slot, comment.anchor), text: comment.text }));
|
|
38
|
+
}
|
|
39
|
+
function describeSlot(slot, response) {
|
|
40
|
+
const state = response === undefined ? 'unanswered' : 'answered';
|
|
41
|
+
const value = responseRecord(response);
|
|
42
|
+
const configLabel = slot.config.label;
|
|
43
|
+
const base = {
|
|
44
|
+
id: slot.id,
|
|
45
|
+
kind: slot.kind,
|
|
46
|
+
...(typeof configLabel === 'string' && configLabel !== '' ? { label: configLabel } : {}),
|
|
47
|
+
state,
|
|
48
|
+
};
|
|
49
|
+
if (slot.unvalidated === true) {
|
|
50
|
+
return { ...base, blank: response === undefined, groups: [], comments: [], ...(response === undefined ? {} : { raw: value }) };
|
|
51
|
+
}
|
|
52
|
+
if (slot.kind === 'options') {
|
|
53
|
+
const config = slot.config;
|
|
54
|
+
const freetext = typeof value.freetext === 'string' && value.freetext !== '' ? value.freetext : undefined;
|
|
55
|
+
const group = { group: 'option', mode: config.mode, total: config.options.length, picked: picked(strings(value.selectedOptionIds), config.options) };
|
|
56
|
+
return { ...base, blank: group.picked.length === 0 && freetext === undefined, groups: [group], ...(freetext === undefined ? {} : { freetext }), comments: describeComments(slot, value) };
|
|
57
|
+
}
|
|
58
|
+
if (slot.kind === 'text') {
|
|
59
|
+
const text = value;
|
|
60
|
+
const written = typeof text.text === 'string' ? text.text : '';
|
|
61
|
+
const singleLine = slot.config.singleLine === true;
|
|
62
|
+
return { ...base, blank: singleLine && written === '', groups: [], text: { value: written, edited: text.edited === true, ...(singleLine ? { singleLine: true } : {}) }, comments: [] };
|
|
63
|
+
}
|
|
64
|
+
if (slot.kind === 'table') {
|
|
65
|
+
const config = slot.config;
|
|
66
|
+
const groups = [];
|
|
67
|
+
if (config.rowSelect === 'single' || config.rowSelect === 'multi')
|
|
68
|
+
groups.push({ group: 'row', mode: config.rowSelect, total: config.rows?.length ?? 0, picked: strings(value.selectedRowIds).map((id) => ({ id, label: rowLabel(config, id) })) });
|
|
69
|
+
if (config.columnSelect === 'single' || config.columnSelect === 'multi')
|
|
70
|
+
groups.push({ group: 'column', mode: config.columnSelect, total: config.columns.length, picked: picked(strings(value.selectedColumnIds), config.columns) });
|
|
71
|
+
return { ...base, blank: groups.every((group) => group.picked.length === 0), groups, comments: describeComments(slot, value) };
|
|
72
|
+
}
|
|
73
|
+
if (slot.kind === 'cards') {
|
|
74
|
+
const config = slot.config;
|
|
75
|
+
const cards = (config.cards ?? []).map((card) => ({ id: card.id, label: card.title }));
|
|
76
|
+
const group = { group: 'card', mode: config.mode, total: cards.length, picked: picked(strings(value.selectedCardIds), cards) };
|
|
77
|
+
return { ...base, blank: group.picked.length === 0, groups: [group], comments: [] };
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`slot "${slot.id}" kind "${slot.kind}" does not accept a response`);
|
|
80
|
+
}
|
|
81
|
+
/** Derive the one shared answer model from the immutable page manifest and a response map. */
|
|
82
|
+
/** `manifest` is unknown deliberately: this boundary validates persisted JSON before deriving. */
|
|
83
|
+
export function describePageAnswer(manifest, responses, opts) {
|
|
84
|
+
const page = validatePageManifest(manifest);
|
|
85
|
+
const slots = page.slots.filter(isResponseBearingSlot).map((slot) => describeSlot(slot, hasOwn(responses, slot.id) ? responses[slot.id] : undefined));
|
|
86
|
+
const answered = slots.filter((slot) => slot.state === 'answered');
|
|
87
|
+
return {
|
|
88
|
+
title: page.title,
|
|
89
|
+
...(page.subtitle === undefined ? {} : { subtitle: page.subtitle }),
|
|
90
|
+
completedAt: opts.completedAt,
|
|
91
|
+
kind: slots.length === 0 ? 'acknowledgement' : 'answer',
|
|
92
|
+
digest: buildAnswerDigest(answered),
|
|
93
|
+
slots,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function groupDigest(group) {
|
|
97
|
+
if (group.picked.length === 0)
|
|
98
|
+
return 'no selection';
|
|
99
|
+
if (group.group === 'option' || group.group === 'card')
|
|
100
|
+
return group.picked.map((item) => item.label).join(', ');
|
|
101
|
+
return group.total > 0 ? `${group.picked.length} of ${group.total} ${group.group}s` : `${group.picked.length} ${group.group}${group.picked.length === 1 ? '' : 's'}`;
|
|
102
|
+
}
|
|
103
|
+
/** The stable one-line digest stored in response.json and shown in history. */
|
|
104
|
+
export function buildAnswerDigest(slots) {
|
|
105
|
+
return slots.map((slot) => {
|
|
106
|
+
if (slot.raw !== undefined)
|
|
107
|
+
return `${slot.kind} response`;
|
|
108
|
+
// A one-line answer IS its value; "edited" would say nothing about what was typed.
|
|
109
|
+
if (slot.text !== undefined) {
|
|
110
|
+
if (slot.text.singleLine === true)
|
|
111
|
+
return slot.text.value === '' ? 'empty' : slot.text.value;
|
|
112
|
+
return slot.text.edited ? 'edited' : 'unchanged';
|
|
113
|
+
}
|
|
114
|
+
const groups = slot.groups.map(groupDigest);
|
|
115
|
+
if (slot.freetext !== undefined)
|
|
116
|
+
groups.push(slot.freetext);
|
|
117
|
+
return groups.join(', ') || 'no selection';
|
|
118
|
+
}).join(' · ');
|
|
119
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PageComponentRegistration } from '../../types.js';
|
|
2
|
+
export interface ComponentDoc {
|
|
3
|
+
/** Registry names this doc covers. A family (Card, CardHeader, …) shares one contract. */
|
|
4
|
+
names: readonly string[];
|
|
5
|
+
/** `structural` builds the page, `slot` is a registry component that gets its own manifest
|
|
6
|
+
* record (and, unless display-only, its own response), `display` is presentation only. */
|
|
7
|
+
group: 'structural' | 'slot' | 'display';
|
|
8
|
+
selectionLine: string;
|
|
9
|
+
contract: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const BUILTIN_COMPONENT_DOCS: readonly ComponentDoc[];
|
|
12
|
+
/** The structural and response-bearing rows — the ones that decide what a page can ask. `human -h` carries these;
|
|
13
|
+
* the display set is one line there and the full catalog is `human components list`. */
|
|
14
|
+
export declare const CORE_COMPONENT_SELECTION_LINES: string[];
|
|
15
|
+
export declare function componentDoc(name: string): ComponentDoc | undefined;
|
|
16
|
+
export declare function componentNames(productComponents: readonly PageComponentRegistration[]): string[];
|
|
17
|
+
export declare function renderComponentCatalog(productComponents: readonly PageComponentRegistration[]): string;
|
|
18
|
+
export declare function renderComponentContract(name: string, productComponents: readonly PageComponentRegistration[]): string | undefined;
|