@north-light/crouter 0.3.170 → 0.3.172
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 +37 -11
- package/dist/api/client.js +84 -15
- package/dist/api/dto/human.d.ts +53 -39
- package/dist/api/dto/human.js +2 -3
- package/dist/api/dto/inbox.d.ts +0 -1
- package/dist/api/dto/nodes.d.ts +12 -0
- package/dist/api/dto/review-comments.d.ts +137 -0
- package/dist/api/dto/review-comments.js +5 -0
- package/dist/api/dto/reviews.d.ts +109 -0
- package/dist/api/dto/reviews.js +5 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +17 -4
- package/dist/api/routes.js +21 -5
- package/dist/build-root.d.ts +1 -16
- package/dist/build-root.js +1 -25
- package/dist/builtin-memory/00-runtime-base.md +7 -5
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
- package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
- package/dist/builtin-memory/design.md +2 -2
- package/dist/builtin-memory/insights/capture.md +50 -0
- package/dist/builtin-memory/insights/init.md +45 -0
- package/dist/builtin-memory/insights/listen.md +9 -0
- package/dist/builtin-memory/internal/agent-shaping.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +12 -11
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
- package/dist/builtin-memory/plan.md +19 -0
- package/dist/builtin-memory/spec/guide.md +38 -0
- package/dist/builtin-memory/spec/requirements.md +31 -0
- package/dist/builtin-memory/spec/roadmap.md +35 -0
- package/dist/builtin-memory/spec.md +10 -86
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
- package/dist/cli.js +2 -6
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
- package/dist/clients/attach/__tests__/context-message.test.js +142 -6
- package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
- package/dist/clients/attach/__tests__/diagram.test.js +1 -1
- package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
- package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
- package/dist/clients/attach/chrome/header.d.ts +10 -0
- package/dist/clients/attach/chrome/header.js +30 -0
- package/dist/clients/attach/chrome/recap.d.ts +7 -0
- package/dist/clients/attach/chrome/recap.js +19 -0
- package/dist/clients/attach/chrome/status-line.d.ts +3 -0
- package/dist/clients/attach/chrome/status-line.js +5 -0
- package/dist/clients/attach/chrome/widgets.js +11 -21
- package/dist/clients/attach/command.js +11 -7
- package/dist/clients/attach/config.d.ts +4 -0
- package/dist/clients/attach/config.js +3 -0
- package/dist/clients/attach/input/controller.d.ts +19 -0
- package/dist/clients/attach/input/controller.js +62 -28
- package/dist/clients/attach/input/titled-editor.d.ts +0 -4
- package/dist/clients/attach/input/titled-editor.js +2 -10
- package/dist/clients/attach/overlays/file-review.d.ts +9 -5
- package/dist/clients/attach/overlays/file-review.js +9 -70
- package/dist/clients/attach/overlays/help.d.ts +33 -0
- package/dist/clients/attach/overlays/help.js +204 -0
- package/dist/clients/attach/render/activity-slot.d.ts +4 -0
- package/dist/clients/attach/render/activity-slot.js +13 -0
- package/dist/clients/attach/render/chat-view.d.ts +117 -12
- package/dist/clients/attach/render/chat-view.js +604 -135
- package/dist/clients/attach/render/context-message.d.ts +2 -1
- package/dist/clients/attach/render/context-message.js +17 -13
- package/dist/clients/attach/render/crtr-output.js +1 -3
- package/dist/clients/attach/render/diagram.js +2 -2
- package/dist/clients/attach/render/edit-diff.d.ts +4 -1
- package/dist/clients/attach/render/edit-diff.js +7 -11
- package/dist/clients/attach/render/group-activity.d.ts +32 -0
- package/dist/clients/attach/render/group-activity.js +206 -0
- package/dist/clients/attach/render/group-recap.d.ts +10 -0
- package/dist/clients/attach/render/group-recap.js +93 -0
- package/dist/clients/attach/render/html-markdown.d.ts +17 -0
- package/dist/clients/attach/render/html-markdown.js +228 -0
- package/dist/clients/attach/render/measured-container.d.ts +96 -0
- package/dist/clients/attach/render/measured-container.js +322 -0
- package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
- package/dist/clients/attach/render/scroll-animation.js +80 -0
- package/dist/clients/attach/render/tool-calls.js +6 -6
- package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
- package/dist/clients/attach/render/transcript-copy.js +81 -0
- package/dist/clients/attach/render/viewport.d.ts +29 -0
- package/dist/clients/attach/render/viewport.js +159 -0
- package/dist/clients/attach/session/bindings.d.ts +10 -7
- package/dist/clients/attach/session/bindings.js +23 -8
- package/dist/clients/attach/session/connection.js +4 -5
- package/dist/clients/attach/session/context.d.ts +6 -4
- package/dist/clients/attach/session/frame.d.ts +34 -0
- package/dist/clients/attach/session/frame.js +176 -0
- package/dist/clients/attach/session/frames.d.ts +4 -1
- package/dist/clients/attach/session/frames.js +14 -1
- package/dist/clients/attach/session/identity.js +4 -3
- package/dist/clients/attach/session/input-wiring.d.ts +14 -4
- package/dist/clients/attach/session/input-wiring.js +15 -13
- package/dist/clients/attach/session/invariant.d.ts +9 -0
- package/dist/clients/attach/session/invariant.js +15 -0
- package/dist/clients/attach/session/keys.d.ts +10 -2
- package/dist/clients/attach/session/keys.js +13 -5
- package/dist/clients/attach/session/layout.d.ts +10 -8
- package/dist/clients/attach/session/layout.js +33 -34
- package/dist/clients/attach/session/mouse.d.ts +18 -0
- package/dist/clients/attach/session/mouse.js +64 -0
- package/dist/clients/attach/session/pane-tag.d.ts +3 -0
- package/dist/clients/attach/session/pane-tag.js +51 -8
- package/dist/clients/attach/session/reconnect.d.ts +3 -0
- package/dist/clients/attach/session/reconnect.js +42 -5
- package/dist/clients/attach/session/surface-frame.d.ts +20 -0
- package/dist/clients/attach/session/surface-frame.js +73 -0
- package/dist/clients/attach/session/surface-host.d.ts +64 -0
- package/dist/clients/attach/session/surface-host.js +255 -0
- package/dist/clients/attach/session/whip.d.ts +17 -0
- package/dist/clients/attach/session/whip.js +334 -0
- package/dist/clients/attach/slash/dispatch.d.ts +13 -1
- package/dist/clients/attach/slash/dispatch.js +51 -29
- package/dist/clients/attach/viewer.js +907 -819
- package/dist/clients/conversation/conversation.d.ts +65 -0
- package/dist/clients/conversation/conversation.js +318 -0
- package/dist/clients/conversation/frames.d.ts +30 -0
- package/dist/clients/conversation/frames.js +52 -0
- package/dist/clients/conversation/region.d.ts +51 -0
- package/dist/clients/conversation/region.js +265 -0
- package/dist/clients/conversation/submit.d.ts +38 -0
- package/dist/clients/conversation/submit.js +51 -0
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
- package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
- package/dist/clients/inbox/controller.d.ts +79 -0
- package/dist/clients/inbox/controller.js +448 -0
- package/dist/clients/inbox/deck-adapter.d.ts +29 -0
- package/dist/clients/inbox/deck-adapter.js +62 -0
- package/dist/clients/inbox/layout.d.ts +8 -0
- package/dist/clients/inbox/layout.js +9 -0
- package/dist/clients/inbox/resolve.d.ts +10 -0
- package/dist/clients/inbox/resolve.js +34 -0
- package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
- package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
- package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
- package/dist/clients/inbox/review/anchors.d.ts +41 -0
- package/dist/clients/inbox/review/anchors.js +89 -0
- package/dist/clients/inbox/review/columns.d.ts +22 -0
- package/dist/clients/inbox/review/columns.js +40 -0
- package/dist/clients/inbox/review/comments-client.d.ts +66 -0
- package/dist/clients/inbox/review/comments-client.js +98 -0
- package/dist/clients/inbox/review/composition.d.ts +60 -0
- package/dist/clients/inbox/review/composition.js +278 -0
- package/dist/clients/inbox/review/document-region.d.ts +120 -0
- package/dist/clients/inbox/review/document-region.js +902 -0
- package/dist/clients/inbox/review/frame.d.ts +45 -0
- package/dist/clients/inbox/review/frame.js +215 -0
- package/dist/clients/inbox/review/keys.d.ts +67 -0
- package/dist/clients/inbox/review/keys.js +130 -0
- package/dist/clients/inbox/review/launch.d.ts +10 -0
- package/dist/clients/inbox/review/launch.js +129 -0
- package/dist/clients/inbox/review/live-source.d.ts +24 -0
- package/dist/clients/inbox/review/live-source.js +160 -0
- package/dist/clients/inbox/review/nvim.d.ts +13 -0
- package/dist/clients/inbox/review/nvim.js +42 -0
- package/dist/clients/inbox/review/remap.d.ts +51 -0
- package/dist/clients/inbox/review/remap.js +157 -0
- package/dist/clients/inbox/review/review-client.d.ts +30 -0
- package/dist/clients/inbox/review/review-client.js +107 -0
- package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
- package/dist/clients/inbox/review/roundtrip.js +36 -0
- package/dist/clients/inbox/review/state.d.ts +143 -0
- package/dist/clients/inbox/review/state.js +282 -0
- package/dist/clients/inbox/review-adapter.d.ts +29 -0
- package/dist/clients/inbox/review-adapter.js +92 -0
- package/dist/clients/inbox/review-visibility.d.ts +5 -0
- package/dist/clients/inbox/review-visibility.js +19 -0
- package/dist/clients/inbox/surface.d.ts +19 -0
- package/dist/clients/inbox/surface.js +128 -0
- package/dist/clients/inbox/tui/ansi.d.ts +66 -0
- package/dist/clients/inbox/tui/ansi.js +317 -0
- package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
- package/dist/clients/inbox/tui/clipboard.js +26 -0
- package/dist/clients/inbox/tui/input.d.ts +7 -0
- package/dist/clients/inbox/tui/input.js +653 -0
- package/dist/clients/inbox/tui/keys.d.ts +28 -0
- package/dist/clients/inbox/tui/keys.js +111 -0
- package/dist/clients/inbox/tui/panel.d.ts +2 -0
- package/dist/clients/inbox/tui/panel.js +217 -0
- package/dist/clients/inbox/tui/render.d.ts +37 -0
- package/dist/clients/inbox/tui/render.js +476 -0
- package/dist/clients/inbox/tui/types.d.ts +57 -0
- package/dist/clients/inbox/tui/types.js +1 -0
- package/dist/clients/inbox/tui.d.ts +6 -0
- package/dist/clients/inbox/tui.js +74 -0
- package/dist/clients/surfaces/host.d.ts +66 -0
- package/dist/clients/surfaces/host.js +1 -0
- package/dist/clients/surfaces/region.d.ts +16 -0
- package/dist/clients/surfaces/region.js +1 -0
- package/dist/clients/web/__tests__/push-engine.test.js +14 -158
- package/dist/clients/web/dev-server.d.ts +3 -3
- package/dist/clients/web/dev-server.js +9 -23
- package/dist/clients/web/events.js +4 -6
- package/dist/clients/web/push-engine.d.ts +2 -66
- package/dist/clients/web/push-engine.js +30 -161
- package/dist/clients/web/push-registry.d.ts +0 -4
- package/dist/clients/web/push-registry.js +2 -7
- package/dist/clients/web/server.js +3 -35
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
- package/dist/clients/web/web-cmd.js +2 -2
- package/dist/commands/__tests__/human.test.js +27 -15
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-issue.js +1 -1
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human/doc.d.ts +2 -0
- package/dist/commands/human/doc.js +98 -0
- package/dist/commands/human/inbox.d.ts +2 -0
- package/dist/commands/human/inbox.js +101 -0
- package/dist/commands/human/prompts.d.ts +2 -3
- package/dist/commands/human/prompts.js +27 -180
- package/dist/commands/human/queue.d.ts +1 -5
- package/dist/commands/human/queue.js +54 -216
- package/dist/commands/human/review.d.ts +2 -0
- package/dist/commands/human/review.js +298 -0
- package/dist/commands/human/shared.d.ts +2 -69
- package/dist/commands/human/shared.js +1 -181
- package/dist/commands/human.js +15 -20
- package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint.test.js +15 -0
- package/dist/commands/memory/delete.js +2 -2
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +21 -3
- package/dist/commands/memory/read.js +4 -4
- package/dist/commands/node.js +8 -7
- package/dist/commands/pkg/plugin-manage.js +143 -68
- package/dist/commands/surface-inspect.js +2 -2
- package/dist/commands/surface.js +6 -12
- package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
- package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
- package/dist/commands/sys/config.js +29 -3
- package/dist/commands/sys/feedback.js +1 -1
- package/dist/commands/sys/setup-core.d.ts +12 -11
- package/dist/commands/sys/setup-core.js +55 -46
- package/dist/commands/sys/setup-wizard.d.ts +4 -1
- package/dist/commands/sys/setup-wizard.js +50 -9
- package/dist/commands/sys/sync-deps.js +1 -1
- package/dist/commands/sys.js +4 -6
- package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
- package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
- package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
- package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
- package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
- package/dist/core/__tests__/full/detach-focus.test.js +1 -1
- package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
- package/dist/core/__tests__/human-deliver.test.js +71 -345
- package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
- package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
- package/dist/core/__tests__/migration.test.js +10 -0
- package/dist/core/__tests__/model-pin-durability.test.js +14 -0
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
- package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-store.test.js +143 -0
- package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
- package/dist/core/__tests__/revive.test.js +22 -53
- package/dist/core/__tests__/session-cycles.test.js +48 -18
- package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
- package/dist/core/__tests__/stop-guard.test.js +16 -1
- package/dist/core/__tests__/tmux-surface.test.js +21 -25
- package/dist/core/asset-root.d.ts +7 -0
- package/dist/core/asset-root.js +18 -0
- package/dist/core/broker-client/client.d.ts +0 -7
- package/dist/core/broker-client/client.js +4 -10
- package/dist/core/broker-client/transport-relay.d.ts +3 -4
- package/dist/core/broker-client/transport-relay.js +3 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
- package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/attention.js +4 -4
- package/dist/core/canvas/canvas.d.ts +6 -11
- package/dist/core/canvas/canvas.js +11 -33
- package/dist/core/canvas/db.js +207 -38
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +8 -1
- package/dist/core/canvas/index.d.ts +0 -1
- package/dist/core/canvas/index.js +0 -1
- package/dist/core/canvas/nav-model.d.ts +1 -1
- package/dist/core/canvas/nav-model.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +16 -0
- package/dist/core/canvas/remote-canvas-source.js +2 -2
- package/dist/core/canvas/remote-transport.d.ts +7 -0
- package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/status-glyph.js +4 -12
- package/dist/core/canvas/types.d.ts +10 -35
- package/dist/core/clipboard-text.d.ts +8 -0
- package/dist/core/clipboard-text.js +18 -0
- package/dist/core/command-manifests/registry.d.ts +1 -3
- package/dist/core/command-plugins/bundle.d.ts +29 -0
- package/dist/core/command-plugins/bundle.js +198 -0
- package/dist/core/command-plugins/discovery.d.ts +0 -8
- package/dist/core/command-plugins/discovery.js +2 -39
- package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
- package/dist/core/command-plugins/transport/http-fetch.js +17 -95
- package/dist/core/command.d.ts +1 -13
- package/dist/core/command.js +1 -16
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +28 -3
- package/dist/core/document-lines.d.ts +4 -0
- package/dist/core/document-lines.js +6 -0
- package/dist/core/feed/feed.d.ts +2 -0
- package/dist/core/feed/feed.js +11 -7
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
- package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-core.test.js +144 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
- package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
- package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
- package/dist/core/human/claim.d.ts +23 -0
- package/dist/core/human/claim.js +67 -0
- package/dist/core/human/convention.d.ts +30 -0
- package/dist/core/human/convention.js +202 -0
- package/dist/core/human/deck-factories.d.ts +8 -0
- package/dist/core/human/deck-factories.js +15 -0
- package/dist/core/human/deck-schema.d.ts +70 -0
- package/dist/core/human/deck-schema.js +92 -0
- package/dist/core/human/root.d.ts +7 -0
- package/dist/core/human/root.js +37 -0
- package/dist/core/human/scan.d.ts +3 -0
- package/dist/core/human/scan.js +76 -0
- package/dist/core/human/summary.d.ts +8 -0
- package/dist/core/human/summary.js +44 -0
- package/dist/core/human/tickets.d.ts +49 -0
- package/dist/core/human/tickets.js +190 -0
- package/dist/core/human/types.d.ts +136 -0
- package/dist/core/human/types.js +5 -0
- package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
- package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
- package/dist/core/inspector/chrome.d.ts +6 -0
- package/dist/core/inspector/chrome.js +14 -0
- package/dist/core/inspector/contract.d.ts +105 -0
- package/dist/core/inspector/contract.js +1 -0
- package/dist/core/inspector/core.d.ts +4 -5
- package/dist/core/inspector/core.js +3 -5
- package/dist/core/inspector/host.d.ts +46 -0
- package/dist/core/{tui → inspector}/host.js +42 -208
- package/dist/core/inspector/text.d.ts +2 -2
- package/dist/core/inspector/transport.d.ts +6 -0
- package/dist/core/inspector/transport.js +36 -0
- package/dist/core/inspector/tui.d.ts +2 -2
- package/dist/core/inspector/tui.js +15 -15
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
- package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
- package/dist/core/keybindings/attach-control.d.ts +9 -3
- package/dist/core/keybindings/attach-control.js +3 -1
- package/dist/core/keybindings/catalog.d.ts +6 -5
- package/dist/core/keybindings/catalog.js +61 -133
- package/dist/core/keybindings/inbox.d.ts +5 -6
- package/dist/core/keybindings/inbox.js +9 -10
- package/dist/core/keybindings/index.d.ts +3 -3
- package/dist/core/keybindings/index.js +3 -3
- package/dist/core/keybindings/persistence.d.ts +5 -1
- package/dist/core/keybindings/persistence.js +24 -6
- package/dist/core/keybindings/resolve.d.ts +6 -0
- package/dist/core/keybindings/resolve.js +10 -3
- package/dist/core/memory/inline-ref-grammar.js +2 -3
- package/dist/core/memory-resolver.d.ts +27 -29
- package/dist/core/memory-resolver.js +45 -39
- package/dist/core/model-routes.d.ts +33 -0
- package/dist/core/model-routes.js +42 -3
- package/dist/core/preview-registry.js +17 -55
- package/dist/core/profiles/select.js +3 -3
- package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
- package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
- package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
- package/dist/core/review/__tests__/capture-origin.test.js +139 -0
- package/dist/core/review/__tests__/comments.test.d.ts +1 -0
- package/dist/core/review/__tests__/comments.test.js +98 -0
- package/dist/core/review/__tests__/project.test.d.ts +1 -0
- package/dist/core/review/__tests__/project.test.js +71 -0
- package/dist/core/review/__tests__/remap.test.d.ts +1 -0
- package/dist/core/review/__tests__/remap.test.js +159 -0
- package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
- package/dist/core/review/__tests__/stage-identity.test.js +140 -0
- package/dist/core/review/birth.d.ts +7 -0
- package/dist/core/review/birth.js +23 -0
- package/dist/core/review/comments.d.ts +69 -0
- package/dist/core/review/comments.js +295 -0
- package/dist/core/review/companion.d.ts +35 -0
- package/dist/core/review/companion.js +205 -0
- package/dist/core/review/document.d.ts +27 -0
- package/dist/core/review/document.js +138 -0
- package/dist/core/review/project.d.ts +13 -0
- package/dist/core/review/project.js +58 -0
- package/dist/core/review/realize.d.ts +19 -0
- package/dist/core/review/realize.js +217 -0
- package/dist/core/review/remap.d.ts +24 -0
- package/dist/core/review/remap.js +156 -0
- package/dist/core/review/signal.d.ts +5 -0
- package/dist/core/review/signal.js +31 -0
- package/dist/core/review/stage.d.ts +26 -0
- package/dist/core/review/stage.js +184 -0
- package/dist/core/review/store.d.ts +46 -0
- package/dist/core/review/store.js +163 -0
- package/dist/core/review/ticket-filter.d.ts +17 -0
- package/dist/core/review/ticket-filter.js +32 -0
- package/dist/core/review/types.d.ts +130 -0
- package/dist/core/review/types.js +23 -0
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
- package/dist/core/runtime/bearings.d.ts +5 -17
- package/dist/core/runtime/bearings.js +88 -71
- package/dist/core/runtime/broker-protocol.d.ts +31 -8
- package/dist/core/runtime/broker.d.ts +15 -1
- package/dist/core/runtime/broker.js +573 -141
- package/dist/core/runtime/canvas-extensions.d.ts +4 -5
- package/dist/core/runtime/canvas-extensions.js +5 -5
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/headless-pi.d.ts +24 -0
- package/dist/core/runtime/headless-pi.js +152 -0
- package/dist/core/runtime/kickoff.d.ts +1 -5
- package/dist/core/runtime/kickoff.js +1 -5
- package/dist/core/runtime/model-registry.d.ts +3 -0
- package/dist/core/runtime/model-registry.js +13 -0
- package/dist/core/runtime/naming-persist.d.ts +4 -0
- package/dist/core/runtime/naming-persist.js +27 -1
- package/dist/core/runtime/naming.d.ts +17 -12
- package/dist/core/runtime/naming.js +91 -140
- package/dist/core/runtime/node-read.js +19 -10
- package/dist/core/runtime/nodes.d.ts +9 -14
- package/dist/core/runtime/nodes.js +3 -53
- package/dist/core/runtime/package-health.d.ts +6 -21
- package/dist/core/runtime/package-health.js +12 -123
- package/dist/core/runtime/placement-tmux.d.ts +2 -1
- package/dist/core/runtime/placement-tmux.js +6 -4
- package/dist/core/runtime/placement.d.ts +1 -0
- package/dist/core/runtime/placement.js +4 -8
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/recap.d.ts +2 -7
- package/dist/core/runtime/recap.js +35 -69
- package/dist/core/runtime/revive.js +34 -77
- package/dist/core/runtime/session-cycles.d.ts +4 -0
- package/dist/core/runtime/session-cycles.js +14 -1
- package/dist/core/runtime/session-visibility.d.ts +37 -0
- package/dist/core/runtime/session-visibility.js +83 -0
- package/dist/core/runtime/spawn.js +10 -22
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -13
- package/dist/core/runtime/surface-bg.js +1 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +35 -7
- package/dist/core/runtime/tmux.js +439 -62
- package/dist/core/runtime/tool-group-summary.d.ts +19 -0
- package/dist/core/runtime/tool-group-summary.js +88 -0
- package/dist/core/scope.d.ts +2 -7
- package/dist/core/scope.js +9 -35
- package/dist/core/substrate/schema.d.ts +2 -2
- package/dist/core/termrender/code-doc.d.ts +12 -0
- package/dist/core/termrender/code-doc.js +92 -0
- package/dist/core/termrender/display.d.ts +12 -0
- package/dist/core/termrender/display.js +19 -0
- package/dist/core/termrender/termrender.d.ts +73 -0
- package/dist/core/termrender/termrender.js +795 -0
- package/dist/core/termrender/version.d.ts +1 -0
- package/dist/core/termrender/version.js +1 -0
- package/dist/core/tui/terminal.js +1 -2
- package/dist/core/user-settings.d.ts +71 -3
- package/dist/core/user-settings.js +83 -1
- package/dist/daemon/api/__tests__/inbox.test.js +44 -133
- package/dist/daemon/api/handlers/human.d.ts +0 -10
- package/dist/daemon/api/handlers/human.js +104 -702
- package/dist/daemon/api/handlers/inbox.js +113 -142
- package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/review-comments.js +500 -0
- package/dist/daemon/api/handlers/reviews.d.ts +2 -0
- package/dist/daemon/api/handlers/reviews.js +287 -0
- package/dist/daemon/api/map.d.ts +22 -7
- package/dist/daemon/api/map.js +159 -11
- package/dist/daemon/api/server.js +4 -0
- package/dist/daemon/cron-run.js +6 -9
- package/dist/daemon/crtrd.js +17 -33
- package/dist/daemon/fleet.d.ts +1 -8
- package/dist/daemon/fleet.js +4 -30
- package/dist/daemon/human/finish.d.ts +14 -0
- package/dist/daemon/human/finish.js +115 -0
- package/dist/daemon/human/sweep.d.ts +6 -0
- package/dist/daemon/human/sweep.js +49 -0
- package/dist/daemon/messaging/node-message.d.ts +18 -0
- package/dist/daemon/messaging/node-message.js +70 -0
- package/dist/daemon/review/comment-notify.d.ts +26 -0
- package/dist/daemon/review/comment-notify.js +107 -0
- package/dist/daemon/review/deliver.d.ts +7 -0
- package/dist/daemon/review/deliver.js +56 -0
- package/dist/daemon/review/finish.d.ts +22 -0
- package/dist/daemon/review/finish.js +254 -0
- package/dist/daemon/review/sweep.d.ts +11 -0
- package/dist/daemon/review/sweep.js +120 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
- package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
- package/dist/pi-extensions/canvas-context-intro.js +50 -104
- package/dist/pi-extensions/canvas-recap.d.ts +15 -2
- package/dist/pi-extensions/canvas-recap.js +192 -69
- package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
- package/dist/pi-extensions/canvas-review-boundary.js +81 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
- package/dist/pi-extensions/canvas-stophook.js +78 -128
- package/dist/pi-extensions/canvas-structured-output.js +52 -8
- package/dist/shared/generated-context.d.ts +29 -4
- package/dist/shared/generated-context.js +122 -7
- package/dist/shared/tool-groups.d.ts +23 -0
- package/dist/shared/tool-groups.js +60 -0
- package/dist/shared/working-activity.d.ts +8 -0
- package/dist/shared/working-activity.js +26 -0
- package/dist/types.d.ts +36 -2
- package/dist/types.js +51 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
- package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v4.md +2 -2
- package/docs/compat/hearth-crtr-v5.md +4 -2
- package/docs/public-api.md +3 -3
- package/package.json +21 -14
- package/runtime.lock.json +4661 -1453
- package/scripts/postinstall.mjs +15 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
- package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
- package/dist/builtin-views/_lib/states.mjs +0 -161
- package/dist/builtin-views/canvas/core.mjs +0 -657
- package/dist/builtin-views/canvas/text.mjs +0 -58
- package/dist/builtin-views/canvas/tui.mjs +0 -168
- package/dist/builtin-views/canvas/web.jsx +0 -121
- package/dist/builtin-views/chat/core.mjs +0 -684
- package/dist/builtin-views/chat/text.mjs +0 -101
- package/dist/builtin-views/chat/tui.mjs +0 -360
- package/dist/builtin-views/chat/web.jsx +0 -362
- package/dist/builtin-views/git-pr/core.mjs +0 -673
- package/dist/builtin-views/git-pr/text.mjs +0 -84
- package/dist/builtin-views/git-pr/tui.mjs +0 -301
- package/dist/builtin-views/git-pr/web.jsx +0 -216
- package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
- package/dist/builtin-views/inbox/core.mjs +0 -1273
- package/dist/builtin-views/inbox/text.mjs +0 -73
- package/dist/builtin-views/inbox/tui.mjs +0 -314
- package/dist/builtin-views/inbox/web.jsx +0 -188
- package/dist/builtin-views/linkedin/core.mjs +0 -906
- package/dist/builtin-views/linkedin/text.mjs +0 -69
- package/dist/builtin-views/linkedin/tui.mjs +0 -413
- package/dist/builtin-views/linkedin/web.jsx +0 -206
- package/dist/builtin-views/prompt-review/core.mjs +0 -735
- package/dist/builtin-views/prompt-review/text.mjs +0 -15
- package/dist/builtin-views/prompt-review/tui.mjs +0 -196
- package/dist/builtin-views/prompt-review/web.jsx +0 -484
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
- package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
- package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
- package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
- package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
- package/dist/clients/attach/session/mode.d.ts +0 -25
- package/dist/clients/attach/session/mode.js +0 -97
- package/dist/commands/sys/promptstudio.d.ts +0 -2
- package/dist/commands/sys/promptstudio.js +0 -65
- package/dist/commands/view-cycle.d.ts +0 -2
- package/dist/commands/view-cycle.js +0 -130
- package/dist/commands/view-list.d.ts +0 -2
- package/dist/commands/view-list.js +0 -66
- package/dist/commands/view-new.d.ts +0 -2
- package/dist/commands/view-new.js +0 -74
- package/dist/commands/view-pick.d.ts +0 -6
- package/dist/commands/view-pick.js +0 -129
- package/dist/commands/view-run.d.ts +0 -2
- package/dist/commands/view-run.js +0 -214
- package/dist/commands/view.d.ts +0 -2
- package/dist/commands/view.js +0 -31
- package/dist/commands/workspace.d.ts +0 -2
- package/dist/commands/workspace.js +0 -165
- package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
- package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
- package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
- package/dist/core/canvas/human-work-outbox.d.ts +0 -73
- package/dist/core/canvas/human-work-outbox.js +0 -261
- package/dist/core/command-plugins/store.d.ts +0 -16
- package/dist/core/command-plugins/store.js +0 -64
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
- package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
- package/dist/core/runtime/fault-recovery-nudge.js +0 -3
- package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
- package/dist/core/tui/host.d.ts +0 -66
- package/dist/core/view/__tests__/transport-cache.test.js +0 -62
- package/dist/core/view/bridge.d.ts +0 -10
- package/dist/core/view/bridge.js +0 -31
- package/dist/core/view/chrome.d.ts +0 -9
- package/dist/core/view/chrome.js +0 -22
- package/dist/core/view/contract.d.ts +0 -205
- package/dist/core/view/contract.js +0 -23
- package/dist/core/view/loader.d.ts +0 -31
- package/dist/core/view/loader.js +0 -188
- package/dist/core/view/stream-local.d.ts +0 -3
- package/dist/core/view/stream-local.js +0 -231
- package/dist/core/view/transport-cache.d.ts +0 -8
- package/dist/core/view/transport-cache.js +0 -38
- package/dist/core/view/transport-local.d.ts +0 -7
- package/dist/core/view/transport-local.js +0 -83
- package/dist/core/view/transport-remote.d.ts +0 -2
- package/dist/core/view/transport.d.ts +0 -10
- package/dist/core/view/transport.js +0 -15
- package/dist/pi-extensions/naming-tool.d.ts +0 -24
- package/dist/pi-extensions/naming-tool.js +0 -67
- package/dist/prompts/view.d.ts +0 -7
- package/dist/prompts/view.js +0 -172
- package/dist/web/ViewChrome.d.ts +0 -7
- package/dist/web/ViewChrome.js +0 -28
- package/dist/web/ViewPane.d.ts +0 -39
- package/dist/web/ViewPane.js +0 -48
- package/dist/web/index.d.ts +0 -8
- package/dist/web/index.js +0 -22
- package/dist/web/runtime.d.ts +0 -39
- package/dist/web/runtime.js +0 -232
- package/dist/web/states.d.ts +0 -24
- package/dist/web/states.js +0 -24
- package/dist/web/transport-http.d.ts +0 -5
- package/dist/web/transport-http.js +0 -34
- package/dist/web/transport-stream.d.ts +0 -3
- package/dist/web/transport-stream.js +0 -204
- package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
- package/dist/web-client/assets/index-aaeu4adv.css +0 -2
- /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
- /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
- /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
- /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
- /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Component } from '@earendil-works/pi-tui';
|
|
2
|
+
/**
|
|
3
|
+
* A surface root: a pi-tui component rendered inside a host-assigned rectangle.
|
|
4
|
+
* The host pushes the rectangle height before every render and on resize; a
|
|
5
|
+
* surface must render exactly that many rows and must not read terminal or
|
|
6
|
+
* process geometry itself. A surface must never place its cursor marker on row
|
|
7
|
+
* 0, which is reserved for host notices.
|
|
8
|
+
*/
|
|
9
|
+
export interface SurfaceComponent extends Component {
|
|
10
|
+
/** Set the number of rows assigned by the host for the next render. */
|
|
11
|
+
setRows(rows: number): void;
|
|
12
|
+
/** Consume wheel input at the terminal's 1-based column, when applicable. */
|
|
13
|
+
handleWheel?(direction: 'up' | 'down', lines: number, column: number): boolean;
|
|
14
|
+
}
|
|
15
|
+
/** The identities used for mounted Phase 3 surfaces. */
|
|
16
|
+
export type SurfaceId = 'inbox' | 'review';
|
|
17
|
+
/**
|
|
18
|
+
* The only host boundary available to a surface. Surfaces render only through
|
|
19
|
+
* the mounted component and use {@link notice} as their only output channel;
|
|
20
|
+
* they never write to the terminal, stdout, stderr, or raw input themselves.
|
|
21
|
+
*/
|
|
22
|
+
export interface SurfaceHost {
|
|
23
|
+
/**
|
|
24
|
+
* Mount a component into the host-assigned full rectangle. When the focused
|
|
25
|
+
* input component differs from the rendered root, pass it as `focus` so
|
|
26
|
+
* input and cursor ownership remain with that component.
|
|
27
|
+
*/
|
|
28
|
+
mount(component: SurfaceComponent, opts?: {
|
|
29
|
+
focus?: Component;
|
|
30
|
+
}): SurfaceHandle;
|
|
31
|
+
/** Report transient output through the host; this is the only surface output channel. */
|
|
32
|
+
notice(text: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Temporarily release the terminal for a child process and return a resume
|
|
35
|
+
* closure. Callers must invoke the closure in a `finally` block.
|
|
36
|
+
*/
|
|
37
|
+
suspendForChild(): () => void;
|
|
38
|
+
/** Bring a node's viewer to the front; failures are reported through notice and never reject. */
|
|
39
|
+
focusNode(nodeId: string): Promise<boolean>;
|
|
40
|
+
/** Mount the shared review-file picker and resolve to its chosen path or null on cancellation. */
|
|
41
|
+
pickReviewFile(spec: {
|
|
42
|
+
candidates: readonly string[];
|
|
43
|
+
cwd: string;
|
|
44
|
+
title?: string;
|
|
45
|
+
}): Promise<string | null>;
|
|
46
|
+
/** Capability facts resolved once by the host for this session. */
|
|
47
|
+
readonly capabilities: {
|
|
48
|
+
/** True when this viewer is attached to a remote session. */
|
|
49
|
+
readonly remote: boolean;
|
|
50
|
+
/** True when focusing another node's viewer is available. */
|
|
51
|
+
readonly canFocusNode: boolean;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** A mounted surface's lifecycle controls. */
|
|
55
|
+
export interface SurfaceHandle {
|
|
56
|
+
/** Close this surface and any surfaces mounted above it. Idempotent. */
|
|
57
|
+
close(): void;
|
|
58
|
+
/** Mark the surface dirty; the host schedules a redraw rather than drawing synchronously. */
|
|
59
|
+
invalidate(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Register the only sanctioned teardown hook for timers, watchers, listeners,
|
|
62
|
+
* and poll re-arms. Closing initiates both synchronous and asynchronous
|
|
63
|
+
* cleanup; asynchronous cleanup is not awaited by the host.
|
|
64
|
+
*/
|
|
65
|
+
onClose(fn: () => void | Promise<void>): void;
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SurfaceRegion {
|
|
2
|
+
/** Geometry is PUSHED. A region never reads terminal or process geometry. */
|
|
3
|
+
setViewport(cols: number, rows: number): void;
|
|
4
|
+
/** Exactly `rows` lines, each ≤ `cols` display cells. */
|
|
5
|
+
render(): string[];
|
|
6
|
+
/** True when the chunk was consumed. Unconsumed chunks are DROPPED, never re-offered. */
|
|
7
|
+
handleInput(data: string): boolean;
|
|
8
|
+
handleWheel(direction: 'up' | 'down', lines: number): boolean;
|
|
9
|
+
/** Only a focused region may emit CURSOR_MARKER. */
|
|
10
|
+
setFocused(value: boolean): void;
|
|
11
|
+
/** One line of state for the composition's status row. */
|
|
12
|
+
summary(): string;
|
|
13
|
+
/** The region's ONLY repaint channel. */
|
|
14
|
+
onDirty(fn: () => void): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -14,9 +14,6 @@ function makeStores(dir) {
|
|
|
14
14
|
lastSeen: new PushLastSeenStore(join(dir, 'last-seen.json')),
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
// A real, allowlisted vendor push-service endpoint shape (Chromium/FCM) —
|
|
18
|
-
// endpoints must resolve to one of the small set of known browser-vendor push
|
|
19
|
-
// services (see push-registry.ts's PUSH_SERVICE_HOST_ALLOWLIST).
|
|
20
17
|
function fakeSubscription(id, authScope = 'local') {
|
|
21
18
|
return {
|
|
22
19
|
id,
|
|
@@ -28,17 +25,11 @@ function fakeSubscription(id, authScope = 'local') {
|
|
|
28
25
|
prefs: { doneNotifications: true },
|
|
29
26
|
};
|
|
30
27
|
}
|
|
31
|
-
/** Seed a registry with one subscription directly (bypassing HTTP validation,
|
|
32
|
-
* which is covered in push-registry.test.ts). */
|
|
33
|
-
function seedRegistryWithOneSubscription(registry) {
|
|
34
|
-
registry.upsert(fakeSubscription('sub-1'));
|
|
35
|
-
}
|
|
36
28
|
function buildHarness(dir, opts = {}) {
|
|
37
29
|
const { registry, lastSeen } = makeStores(dir);
|
|
38
30
|
const sent = [];
|
|
39
31
|
const sentTo = [];
|
|
40
32
|
const nodes = [];
|
|
41
|
-
const asks = [];
|
|
42
33
|
const clock = { now: 1_000_000 };
|
|
43
34
|
const defaultSend = async (record, payload) => {
|
|
44
35
|
sent.push(payload);
|
|
@@ -49,26 +40,25 @@ function buildHarness(dir, opts = {}) {
|
|
|
49
40
|
registry,
|
|
50
41
|
lastSeen,
|
|
51
42
|
readCanvasNodes: () => nodes,
|
|
52
|
-
readPendingAsks: () => asks,
|
|
53
43
|
sendPush: opts.sendPush ?? defaultSend,
|
|
54
44
|
isTargetScope: opts.isTargetScope ?? (() => true),
|
|
55
45
|
now: () => clock.now,
|
|
56
46
|
rateWindowMs: opts.rateWindowMs,
|
|
57
47
|
});
|
|
58
|
-
return { engine, sent, sentTo, registry, lastSeen, nodes,
|
|
48
|
+
return { engine, sent, sentTo, registry, lastSeen, nodes, clock };
|
|
59
49
|
}
|
|
60
|
-
|
|
50
|
+
function seedRegistryWithOneSubscription(registry) {
|
|
51
|
+
registry.upsert(fakeSubscription('sub-1'));
|
|
52
|
+
}
|
|
53
|
+
test('seed() sends nothing regardless of current state', async () => {
|
|
61
54
|
const dir = tmpDir();
|
|
62
55
|
try {
|
|
63
56
|
const h = buildHarness(dir);
|
|
64
57
|
seedRegistryWithOneSubscription(h.registry);
|
|
65
58
|
h.nodes.push({ node_id: 'root-1', parent: null, status: 'done', attention_count: 0 });
|
|
66
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' });
|
|
67
59
|
await h.engine.seed();
|
|
68
60
|
assert.equal(h.sent.length, 0);
|
|
69
|
-
|
|
70
|
-
assert.deepEqual(snap.roots['root-1'], { status: 'done', attention_count: 0 });
|
|
71
|
-
assert.deepEqual(snap.asks['ask-1'], { present: true });
|
|
61
|
+
assert.deepEqual(h.lastSeen.snapshot().roots['root-1'], { status: 'done', attention_count: 0 });
|
|
72
62
|
}
|
|
73
63
|
finally {
|
|
74
64
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -81,13 +71,11 @@ test('active -> done emits exactly one done push, and a repeat pass does not dup
|
|
|
81
71
|
seedRegistryWithOneSubscription(h.registry);
|
|
82
72
|
h.nodes.push({ node_id: 'root-1', parent: null, status: 'active', attention_count: 0 });
|
|
83
73
|
await h.engine.seed();
|
|
84
|
-
assert.equal(h.sent.length, 0);
|
|
85
74
|
h.nodes[0].status = 'done';
|
|
86
75
|
await h.engine.onNodesInvalidation();
|
|
87
76
|
assert.equal(h.sent.length, 1);
|
|
88
77
|
assert.equal(h.sent[0].kind, 'done');
|
|
89
78
|
assert.equal(h.sent[0].conversationId, 'root-1');
|
|
90
|
-
// Repeated invalidation with the same (already-done) status must not re-fire.
|
|
91
79
|
await h.engine.onNodesInvalidation();
|
|
92
80
|
assert.equal(h.sent.length, 1);
|
|
93
81
|
}
|
|
@@ -95,71 +83,16 @@ test('active -> done emits exactly one done push, and a repeat pass does not dup
|
|
|
95
83
|
rmSync(dir, { recursive: true, force: true });
|
|
96
84
|
}
|
|
97
85
|
});
|
|
98
|
-
test('
|
|
99
|
-
const dir = tmpDir();
|
|
100
|
-
try {
|
|
101
|
-
const h = buildHarness(dir);
|
|
102
|
-
seedRegistryWithOneSubscription(h.registry);
|
|
103
|
-
await h.engine.seed(); // no asks yet
|
|
104
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' });
|
|
105
|
-
await h.engine.onInboxInvalidation();
|
|
106
|
-
assert.equal(h.sent.length, 1);
|
|
107
|
-
assert.equal(h.sent[0].kind, 'needs-you');
|
|
108
|
-
assert.equal(h.sent[0].askId, 'job-1');
|
|
109
|
-
// Same ask still pending on a second invalidation — no duplicate.
|
|
110
|
-
await h.engine.onInboxInvalidation();
|
|
111
|
-
assert.equal(h.sent.length, 1);
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
rmSync(dir, { recursive: true, force: true });
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
test('a resolved/missing ask never notifies', async () => {
|
|
86
|
+
test('push targeting is scope-filtered', async () => {
|
|
118
87
|
const dir = tmpDir();
|
|
119
88
|
try {
|
|
120
|
-
const h = buildHarness(dir);
|
|
121
|
-
seedRegistryWithOneSubscription(h.registry);
|
|
122
|
-
await h.engine.seed();
|
|
123
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' });
|
|
124
|
-
await h.engine.onInboxInvalidation();
|
|
125
|
-
assert.equal(h.sent.length, 1);
|
|
126
|
-
// The ask resolves — it disappears from the pending list.
|
|
127
|
-
h.asks.length = 0;
|
|
128
|
-
await h.engine.onInboxInvalidation();
|
|
129
|
-
assert.equal(h.sent.length, 1); // no new push
|
|
130
|
-
assert.deepEqual(h.lastSeen.snapshot().asks, {});
|
|
131
|
-
}
|
|
132
|
-
finally {
|
|
133
|
-
rmSync(dir, { recursive: true, force: true });
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
test('multiple new asks in the same conversation within the rate window coalesce into one push', async () => {
|
|
137
|
-
const dir = tmpDir();
|
|
138
|
-
try {
|
|
139
|
-
const h = buildHarness(dir, { rateWindowMs: 30_000 });
|
|
140
|
-
seedRegistryWithOneSubscription(h.registry);
|
|
141
|
-
await h.engine.seed();
|
|
142
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' }, { id: 'ask-2', jobId: 'job-2', title: 'Approve deploy', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:01.000Z' });
|
|
143
|
-
await h.engine.onInboxInvalidation();
|
|
144
|
-
assert.equal(h.sent.length, 1);
|
|
145
|
-
assert.match(h.sent[0].snippet, /\+1 more/);
|
|
146
|
-
}
|
|
147
|
-
finally {
|
|
148
|
-
rmSync(dir, { recursive: true, force: true });
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
test('push targeting is scope-filtered: only subscriptions on the watched auth boundary are sent to', async () => {
|
|
152
|
-
const dir = tmpDir();
|
|
153
|
-
try {
|
|
154
|
-
const h = buildHarness(dir, { isTargetScope: (s) => s === 'token:abc' });
|
|
155
|
-
// One local-scope sub and one token-scope sub in the SAME registry.
|
|
89
|
+
const h = buildHarness(dir, { isTargetScope: (scope) => scope === 'token:abc' });
|
|
156
90
|
h.registry.upsert(fakeSubscription('sub-local', 'local'));
|
|
157
91
|
h.registry.upsert(fakeSubscription('sub-token', 'token:abc'));
|
|
158
92
|
h.nodes.push({ node_id: 'root-1', parent: null, status: 'active', attention_count: 0 });
|
|
159
93
|
await h.engine.seed();
|
|
160
94
|
h.nodes[0].status = 'done';
|
|
161
95
|
await h.engine.onNodesInvalidation();
|
|
162
|
-
// Exactly one send, and it went to the token-scoped endpoint only.
|
|
163
96
|
assert.equal(h.sent.length, 1);
|
|
164
97
|
assert.deepEqual(h.sentTo, ['https://fcm.googleapis.com/fcm/send/sub-token']);
|
|
165
98
|
}
|
|
@@ -174,7 +107,7 @@ test('a transient send failure does not advance last-seen and is retried next pa
|
|
|
174
107
|
const attempts = [];
|
|
175
108
|
const h = buildHarness(dir, {
|
|
176
109
|
rateWindowMs: 30_000,
|
|
177
|
-
sendPush: async (
|
|
110
|
+
sendPush: async (_record, payload) => {
|
|
178
111
|
attempts.push(payload);
|
|
179
112
|
call += 1;
|
|
180
113
|
return call === 1 ? 'error' : 'sent';
|
|
@@ -185,40 +118,27 @@ test('a transient send failure does not advance last-seen and is retried next pa
|
|
|
185
118
|
await h.engine.seed();
|
|
186
119
|
h.nodes[0].status = 'done';
|
|
187
120
|
await h.engine.onNodesInvalidation();
|
|
188
|
-
// The send was attempted but failed transiently — last-seen root must stay
|
|
189
|
-
// pre-transition ('active') so the edge is re-detected next pass.
|
|
190
121
|
assert.equal(attempts.length, 1);
|
|
191
122
|
assert.equal(h.lastSeen.snapshot().roots['root-1'].status, 'active');
|
|
192
|
-
// Advance past the rate window (the failed pass set lastPushAt) and retry.
|
|
193
123
|
h.clock.now += 60_000;
|
|
194
124
|
await h.engine.onNodesInvalidation();
|
|
195
125
|
assert.equal(attempts.length, 2);
|
|
196
126
|
assert.equal(h.lastSeen.snapshot().roots['root-1'].status, 'done');
|
|
197
|
-
// A further pass does not re-fire.
|
|
198
|
-
h.clock.now += 60_000;
|
|
199
|
-
await h.engine.onNodesInvalidation();
|
|
200
|
-
assert.equal(attempts.length, 2);
|
|
201
127
|
}
|
|
202
128
|
finally {
|
|
203
129
|
rmSync(dir, { recursive: true, force: true });
|
|
204
130
|
}
|
|
205
131
|
});
|
|
206
|
-
test('a transient failure on one subscription does not
|
|
132
|
+
test('a transient failure on one subscription does not re-send to an already-delivered subscription', async () => {
|
|
207
133
|
const dir = tmpDir();
|
|
208
134
|
try {
|
|
209
|
-
// Keyed by endpoint, not `record.id` — upsert() derives the persisted id
|
|
210
|
-
// from `subscriptionIdForEndpoint(endpoint)`, discarding the fixture's
|
|
211
|
-
// caller-supplied id, so the endpoint is the stable fixture identity here.
|
|
212
135
|
const attemptsBySub = new Map();
|
|
213
136
|
const h = buildHarness(dir, {
|
|
214
137
|
rateWindowMs: 30_000,
|
|
215
|
-
sendPush: async (record
|
|
216
|
-
const
|
|
217
|
-
attemptsBySub.set(record.endpoint,
|
|
218
|
-
|
|
219
|
-
// attempt only, then succeeds on retry — so the second pass proves
|
|
220
|
-
// sub-ok is never touched again while sub-fail finally lands.
|
|
221
|
-
return record.endpoint.includes('sub-fail') && n === 1 ? 'error' : 'sent';
|
|
138
|
+
sendPush: async (record) => {
|
|
139
|
+
const attempts = (attemptsBySub.get(record.endpoint) ?? 0) + 1;
|
|
140
|
+
attemptsBySub.set(record.endpoint, attempts);
|
|
141
|
+
return record.endpoint.includes('sub-fail') && attempts === 1 ? 'error' : 'sent';
|
|
222
142
|
},
|
|
223
143
|
});
|
|
224
144
|
h.registry.upsert(fakeSubscription('sub-ok'));
|
|
@@ -227,19 +147,6 @@ test('a transient failure on one subscription does not cause a retry to re-send
|
|
|
227
147
|
await h.engine.seed();
|
|
228
148
|
h.nodes[0].status = 'done';
|
|
229
149
|
await h.engine.onNodesInvalidation();
|
|
230
|
-
// Both subscriptions were attempted once; the event is not fully delivered
|
|
231
|
-
// (sub-fail errored), so last-seen must stay pre-transition.
|
|
232
|
-
assert.equal(attemptsBySub.get('https://fcm.googleapis.com/fcm/send/sub-ok'), 1);
|
|
233
|
-
assert.equal(attemptsBySub.get('https://fcm.googleapis.com/fcm/send/sub-fail'), 1);
|
|
234
|
-
assert.equal(h.lastSeen.snapshot().roots['root-1'].status, 'active');
|
|
235
|
-
// Retry next pass (past the rate window): sub-ok must NOT be re-sent to —
|
|
236
|
-
// only sub-fail, which never succeeded, is retried.
|
|
237
|
-
h.clock.now += 60_000;
|
|
238
|
-
await h.engine.onNodesInvalidation();
|
|
239
|
-
assert.equal(attemptsBySub.get('https://fcm.googleapis.com/fcm/send/sub-ok'), 1, 'an already-delivered subscription must not be re-sent to on retry');
|
|
240
|
-
assert.equal(attemptsBySub.get('https://fcm.googleapis.com/fcm/send/sub-fail'), 2);
|
|
241
|
-
assert.equal(h.lastSeen.snapshot().roots['root-1'].status, 'done');
|
|
242
|
-
// Fully delivered now — a further pass does not re-fire for anyone.
|
|
243
150
|
h.clock.now += 60_000;
|
|
244
151
|
await h.engine.onNodesInvalidation();
|
|
245
152
|
assert.equal(attemptsBySub.get('https://fcm.googleapis.com/fcm/send/sub-ok'), 1);
|
|
@@ -249,54 +156,3 @@ test('a transient failure on one subscription does not cause a retry to re-send
|
|
|
249
156
|
rmSync(dir, { recursive: true, force: true });
|
|
250
157
|
}
|
|
251
158
|
});
|
|
252
|
-
test('overlapping inbox invalidations serialize — each distinct ask notifies exactly once', async () => {
|
|
253
|
-
const dir = tmpDir();
|
|
254
|
-
try {
|
|
255
|
-
const h = buildHarness(dir);
|
|
256
|
-
seedRegistryWithOneSubscription(h.registry);
|
|
257
|
-
await h.engine.seed();
|
|
258
|
-
// First invalidation kicked off but NOT awaited; a second ask + invalidation
|
|
259
|
-
// land while the first pass is in-flight. The drain queue must serialize
|
|
260
|
-
// them so neither ask is notified twice.
|
|
261
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' });
|
|
262
|
-
const p1 = h.engine.onInboxInvalidation();
|
|
263
|
-
h.asks.push({ id: 'ask-2', jobId: 'job-2', title: 'Approve deploy', conversationId: 'root-2', conversationTitle: 'Root Two', blockedSince: '2026-01-01T00:00:01.000Z' });
|
|
264
|
-
const p2 = h.engine.onInboxInvalidation();
|
|
265
|
-
await Promise.all([p1, p2]);
|
|
266
|
-
const askIds = h.sent.map((p) => p.askId).sort();
|
|
267
|
-
assert.deepEqual(askIds, ['job-1', 'job-2']);
|
|
268
|
-
assert.deepEqual(Object.keys(h.lastSeen.snapshot().asks).sort(), ['ask-1', 'ask-2']);
|
|
269
|
-
}
|
|
270
|
-
finally {
|
|
271
|
-
rmSync(dir, { recursive: true, force: true });
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
test('a rate-capped ask is suppressed within the window then retried once it clears — never lost, never duplicated', async () => {
|
|
275
|
-
const dir = tmpDir();
|
|
276
|
-
try {
|
|
277
|
-
const h = buildHarness(dir, { rateWindowMs: 30_000 });
|
|
278
|
-
seedRegistryWithOneSubscription(h.registry);
|
|
279
|
-
await h.engine.seed();
|
|
280
|
-
h.asks.push({ id: 'ask-1', jobId: 'job-1', title: 'Pick a color', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:00.000Z' });
|
|
281
|
-
await h.engine.onInboxInvalidation();
|
|
282
|
-
assert.equal(h.sent.length, 1); // first ask fires immediately
|
|
283
|
-
// A second, distinct ask lands in the same conversation moments later —
|
|
284
|
-
// still inside the rate window, so it must be suppressed, not sent.
|
|
285
|
-
h.asks.push({ id: 'ask-2', jobId: 'job-2', title: 'Approve deploy', conversationId: 'root-1', conversationTitle: 'Root One', blockedSince: '2026-01-01T00:00:05.000Z' });
|
|
286
|
-
h.clock.now += 5_000;
|
|
287
|
-
await h.engine.onInboxInvalidation();
|
|
288
|
-
assert.equal(h.sent.length, 1); // still suppressed
|
|
289
|
-
assert.equal(h.lastSeen.snapshot().asks['ask-2'], undefined); // left untracked so it retries
|
|
290
|
-
// Once the rate window clears, the still-pending ask-2 is retried exactly once.
|
|
291
|
-
h.clock.now += 30_000;
|
|
292
|
-
await h.engine.onInboxInvalidation();
|
|
293
|
-
assert.equal(h.sent.length, 2);
|
|
294
|
-
assert.equal(h.sent[1].askId, 'job-2');
|
|
295
|
-
// And it never fires a third time on a further no-op pass.
|
|
296
|
-
await h.engine.onInboxInvalidation();
|
|
297
|
-
assert.equal(h.sent.length, 2);
|
|
298
|
-
}
|
|
299
|
-
finally {
|
|
300
|
-
rmSync(dir, { recursive: true, force: true });
|
|
301
|
-
}
|
|
302
|
-
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Server as HttpServer } from 'node:http';
|
|
2
2
|
import type { ViteDevServer } from 'vite';
|
|
3
3
|
/** Create a Vite dev server in middleware mode, HMR bound to `httpServer`.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* It loads the shell's Vite config rather than duplicating its plugins: that
|
|
5
|
+
* config supplies the PWA virtual-module resolver as well as the production
|
|
6
|
+
* build settings. Lazily imports vite so the shipped path never loads it. */
|
|
7
7
|
export declare function createDevServer(httpServer: HttpServer): Promise<ViteDevServer>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// dev-server.ts — `crtr web serve --dev` Vite middleware mode.
|
|
2
2
|
//
|
|
3
3
|
// The shipped server serves a prebuilt static shell bundle from dist/web-client/
|
|
4
|
-
// (§11). In --dev a contributor iterating on the shell
|
|
5
|
-
//
|
|
4
|
+
// (§11). In --dev a contributor iterating on the shell gets HMR instead: a Vite
|
|
5
|
+
// dev server in MIDDLEWARE mode mounted on the SAME HTTP
|
|
6
6
|
// server as the crtrd proxy + attach tunnel. This is the one place Vite runs at
|
|
7
7
|
// runtime — the mainstream "Vite middleware in dev, static dist in prod" split.
|
|
8
8
|
//
|
|
@@ -22,32 +22,18 @@ function resolveShellRoot(packageRoot) {
|
|
|
22
22
|
return join(packageRoot, 'src', 'clients', 'web', 'web-client');
|
|
23
23
|
}
|
|
24
24
|
/** Create a Vite dev server in middleware mode, HMR bound to `httpServer`.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* It loads the shell's Vite config rather than duplicating its plugins: that
|
|
26
|
+
* config supplies the PWA virtual-module resolver as well as the production
|
|
27
|
+
* build settings. Lazily imports vite so the shipped path never loads it. */
|
|
28
28
|
export async function createDevServer(httpServer) {
|
|
29
29
|
const packageRoot = resolve(HERE, '../../..');
|
|
30
|
-
const nodeModules = join(packageRoot, 'node_modules');
|
|
31
30
|
const shellRoot = resolveShellRoot(packageRoot);
|
|
32
31
|
const { createServer } = await import('vite');
|
|
33
|
-
const react = (await import('@vitejs/plugin-react')).default;
|
|
34
|
-
const tailwindcss = (await import('@tailwindcss/vite')).default;
|
|
35
32
|
return createServer({
|
|
36
|
-
configFile:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
resolve: {
|
|
41
|
-
alias: {
|
|
42
|
-
react: join(nodeModules, 'react'),
|
|
43
|
-
'react-dom': join(nodeModules, 'react-dom'),
|
|
44
|
-
'@': shellRoot,
|
|
45
|
-
// The crtr web runtime is a self-subpath of crouter's own package;
|
|
46
|
-
// pin it to the built barrel so the shell can import it from any root.
|
|
47
|
-
'@north-light/crouter/web': join(packageRoot, 'dist', 'web', 'index.js'),
|
|
48
|
-
},
|
|
49
|
-
dedupe: ['react', 'react-dom'],
|
|
50
|
-
},
|
|
33
|
+
configFile: join(shellRoot, 'vite.config.ts'),
|
|
34
|
+
// The integrated middleware already owns /node, /v1, and /__crtr. The
|
|
35
|
+
// shell config uses this mode to omit its standalone-server proxies.
|
|
36
|
+
mode: 'middleware',
|
|
51
37
|
server: {
|
|
52
38
|
middlewareMode: true,
|
|
53
39
|
// Share our HTTP server for HMR; server.ts's upgrade handler hands every
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
// events.ts — the
|
|
1
|
+
// events.ts — the browser shell's realtime invalidation lane.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// invalidations
|
|
5
|
-
//
|
|
6
|
-
// fixed poll tick. SSE carries "something in this class changed"; the consumer
|
|
7
|
-
// decides what to re-read.
|
|
3
|
+
// One server-sent-events stream, `GET /__crtr/events`, carries change
|
|
4
|
+
// invalidations, never data. On an event the shell re-reads through the REST
|
|
5
|
+
// proxy rather than waiting for its fixed polling interval.
|
|
8
6
|
//
|
|
9
7
|
// Event vocabulary (deliberately minimal): { kind: 'nodes' | 'inbox', ts }.
|
|
10
8
|
// - 'nodes' — the node graph changed (topology, status/intent transitions,
|
|
@@ -1,49 +1,23 @@
|
|
|
1
1
|
import type { NodeLifeStatus } from './web-client/shared/protocol.js';
|
|
2
2
|
import type { PushLastSeenStore, PushRegistryStore, PushSubscriptionRecord } from './push-registry.js';
|
|
3
|
-
/** The subset of `NodeSummary` the diff engine needs — kept minimal and
|
|
4
|
-
* decoupled from the full wire type so the engine can be fed by any canvas
|
|
5
|
-
* reader (production: `canvas snapshot`; tests: a fixture array). */
|
|
6
3
|
export interface CanvasNodeRow {
|
|
7
4
|
node_id: string;
|
|
8
5
|
parent: string | null;
|
|
9
6
|
status: NodeLifeStatus;
|
|
10
7
|
attention_count: number;
|
|
11
8
|
}
|
|
12
|
-
/** A pending human ask with just the `DeckSummary` provenance the payload/dedupe
|
|
13
|
-
* logic needs (design: "ask provenance comes from DeckSummary"). */
|
|
14
|
-
export interface PendingAsk {
|
|
15
|
-
/** Opaque, stable inbox-entry id — the dedupe key (never re-notify the same
|
|
16
|
-
* unresolved ask). */
|
|
17
|
-
id: string;
|
|
18
|
-
/** The interaction job id — carried in the payload as `askId` for the deep-link. */
|
|
19
|
-
jobId: string;
|
|
20
|
-
title: string;
|
|
21
|
-
conversationId: string;
|
|
22
|
-
conversationTitle: string;
|
|
23
|
-
blockedSince: string;
|
|
24
|
-
}
|
|
25
9
|
export type PushSendResult = 'sent' | 'gone' | 'error';
|
|
26
10
|
export interface PushPayload {
|
|
27
11
|
conversationId: string;
|
|
28
|
-
kind: '
|
|
29
|
-
askId?: string;
|
|
12
|
+
kind: 'done';
|
|
30
13
|
title: string;
|
|
31
14
|
snippet: string;
|
|
32
15
|
}
|
|
33
16
|
export interface PushEngineDeps {
|
|
34
17
|
registry: PushRegistryStore;
|
|
35
18
|
lastSeen: PushLastSeenStore;
|
|
36
|
-
/** Re-read the live canvas roster. */
|
|
37
19
|
readCanvasNodes: () => Promise<CanvasNodeRow[]> | CanvasNodeRow[];
|
|
38
|
-
/** Re-read every currently pending ask (deck provenance). */
|
|
39
|
-
readPendingAsks: () => Promise<PendingAsk[]> | PendingAsk[];
|
|
40
|
-
/** Deliver one push to one subscription. Implementations classify a
|
|
41
|
-
* service-side 404/410 as `'gone'` so the engine can prune the record. */
|
|
42
20
|
sendPush: (record: PushSubscriptionRecord, payload: PushPayload) => Promise<PushSendResult>;
|
|
43
|
-
/** True iff a subscription's persisted authScope matches the canvas/user this
|
|
44
|
-
* engine is watching (design line 252: push is sent ONLY to subscriptions
|
|
45
|
-
* whose authScope is on the identical boundary that gates the /v1 proxy).
|
|
46
|
-
* Required — no lenient default — so a foreign scope can never be targeted. */
|
|
47
21
|
isTargetScope: (authScope: string) => boolean;
|
|
48
22
|
now?: () => number;
|
|
49
23
|
rateWindowMs?: number;
|
|
@@ -52,57 +26,19 @@ export declare class PushDiffEngine {
|
|
|
52
26
|
private readonly registry;
|
|
53
27
|
private readonly lastSeen;
|
|
54
28
|
private readonly readCanvasNodes;
|
|
55
|
-
private readonly readPendingAsks;
|
|
56
29
|
private readonly sendPush;
|
|
57
30
|
private readonly isTargetScope;
|
|
58
31
|
private readonly now;
|
|
59
32
|
private readonly rateWindowMs;
|
|
60
|
-
/** Drain-queue state: only one diff pass mutates last-seen at a time. An
|
|
61
|
-
* invalidation arriving mid-pass sets its pending flag and is reprocessed
|
|
62
|
-
* when the current pass finishes — no two passes race the same snapshot. */
|
|
63
33
|
private draining;
|
|
64
34
|
private pendingNodes;
|
|
65
|
-
private pendingInbox;
|
|
66
|
-
/** Per-conversation rate-cap clock. Deliberately in-memory/process-lifetime
|
|
67
|
-
* only (not persisted) — a restart re-seeding silently is already the
|
|
68
|
-
* no-storm guarantee; the rate cap only smooths a live process's bursts. */
|
|
69
35
|
private readonly lastPushAt;
|
|
70
|
-
/** Per-(conversationId+kind) set of subscription ids already delivered for
|
|
71
|
-
* the CURRENT still-pending notification. A transient failure on one
|
|
72
|
-
* subscription must never cause a later retry to re-send to subscriptions
|
|
73
|
-
* that already got `'sent'` — this is exactly what remembers them across
|
|
74
|
-
* passes. Cleared the moment the event fully succeeds (or has no eligible
|
|
75
|
-
* subscriber left), since a later, distinct event reusing the same key
|
|
76
|
-
* must start fresh. */
|
|
77
36
|
private readonly deliveredForKey;
|
|
78
37
|
constructor(deps: PushEngineDeps);
|
|
79
|
-
/**
|
|
80
|
-
* on server start — a restart must never be an attention event. */
|
|
38
|
+
/** Record the current canvas state without sending notifications. */
|
|
81
39
|
seed(): Promise<void>;
|
|
82
|
-
/** 'nodes' invalidation: re-read the canvas roster, emit a 'done' push for
|
|
83
|
-
* every conversation root that just crossed into a done|dead|canceled
|
|
84
|
-
* status from a non-done status. */
|
|
85
40
|
onNodesInvalidation(): Promise<void>;
|
|
86
|
-
onInboxInvalidation(): Promise<void>;
|
|
87
|
-
/** Serialize passes: if a pass is already running, just set the pending flag
|
|
88
|
-
* and return — the running drain loop will pick it up. Otherwise run pending
|
|
89
|
-
* passes until none remain. */
|
|
90
41
|
private drain;
|
|
91
42
|
private runNodesPass;
|
|
92
|
-
/** 'inbox' invalidation: re-read pending asks, emit exactly one 'needs-you'
|
|
93
|
-
* push per conversation for its NEW (not-yet-notified) asks, coalesced. An
|
|
94
|
-
* ask no longer present (resolved/missing) is simply dropped, never
|
|
95
|
-
* notified. */
|
|
96
|
-
private runInboxPass;
|
|
97
|
-
/** Send one push per ELIGIBLE subscription — one whose authScope is on the
|
|
98
|
-
* watched boundary (`isTargetScope`) AND that opted in for this payload kind.
|
|
99
|
-
* Respects the per-conversation rate cap; prunes any subscription the push
|
|
100
|
-
* service reports gone (404/410) or whose endpoint fails the push-service
|
|
101
|
-
* allowlist re-check. Tracks per-subscription delivery for this notification
|
|
102
|
-
* (`deliveredForKey`) so a retry after a transient failure on one
|
|
103
|
-
* subscription never re-sends to a subscription that already got `'sent'`.
|
|
104
|
-
* Returns true iff last-seen may advance: false when suppressed by the rate
|
|
105
|
-
* cap OR when a transient send error means the item must be retried next
|
|
106
|
-
* pass. A no-eligible pass returns true (nothing to retry). */
|
|
107
43
|
private emit;
|
|
108
44
|
}
|