@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,138 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { documentLines } from '../document-lines.js';
|
|
4
|
+
import { COMMENT_QUOTE_MAX, ReviewOperationError, } from './types.js';
|
|
5
|
+
const REVIEW_SOURCE_MAX_BYTES = 2 * 1024 * 1024;
|
|
6
|
+
function sourceUnreadable(file, phase, reason) {
|
|
7
|
+
throw new ReviewOperationError('source_unreadable', 'review source is unreadable', { phase, reason, file });
|
|
8
|
+
}
|
|
9
|
+
/** Read the reviewed document once, preserving the exact bytes used for its digest. */
|
|
10
|
+
export function readReviewSource(file, phase = 'submit') {
|
|
11
|
+
let size;
|
|
12
|
+
try {
|
|
13
|
+
const stat = statSync(file);
|
|
14
|
+
if (!stat.isFile())
|
|
15
|
+
return sourceUnreadable(file, phase, 'unreadable');
|
|
16
|
+
size = stat.size;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return sourceUnreadable(file, phase, error.code === 'ENOENT' ? 'missing' : 'unreadable');
|
|
20
|
+
}
|
|
21
|
+
if (size > REVIEW_SOURCE_MAX_BYTES)
|
|
22
|
+
return sourceUnreadable(file, phase, 'too_large');
|
|
23
|
+
let bytes;
|
|
24
|
+
try {
|
|
25
|
+
bytes = readFileSync(file);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return sourceUnreadable(file, phase, error.code === 'ENOENT' ? 'missing' : 'unreadable');
|
|
29
|
+
}
|
|
30
|
+
if (bytes.length > REVIEW_SOURCE_MAX_BYTES)
|
|
31
|
+
return sourceUnreadable(file, phase, 'too_large');
|
|
32
|
+
if (bytes.includes(0))
|
|
33
|
+
return sourceUnreadable(file, phase, 'binary');
|
|
34
|
+
let text;
|
|
35
|
+
try {
|
|
36
|
+
text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return sourceUnreadable(file, phase, 'binary');
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
bytes,
|
|
43
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
44
|
+
lines: documentLines(text),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function requireDocumentSource(review, expected_doc_hash) {
|
|
48
|
+
const source = readReviewSource(review.file, 'comment');
|
|
49
|
+
if (source.sha256 !== expected_doc_hash) {
|
|
50
|
+
throw new ReviewOperationError('stale_document', 'review document changed since it was rendered', {
|
|
51
|
+
reason: 'document_changed',
|
|
52
|
+
expected_sha256: expected_doc_hash,
|
|
53
|
+
actual_sha256: source.sha256,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (source.sha256 !== review.anchor_base_sha256) {
|
|
57
|
+
throw new ReviewOperationError('stale_document', 'review comment ranges have not been remapped to the current document', {
|
|
58
|
+
reason: 'anchor_base_stale',
|
|
59
|
+
expected_sha256: review.anchor_base_sha256,
|
|
60
|
+
actual_sha256: source.sha256,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return source;
|
|
64
|
+
}
|
|
65
|
+
/** Require both the caller's rendered source and the persisted range coordinate base to be current. */
|
|
66
|
+
export function requireCurrentDocument(review, expected_doc_hash) {
|
|
67
|
+
return requireDocumentSource(review, expected_doc_hash);
|
|
68
|
+
}
|
|
69
|
+
/** Verify a create-time source precondition from the review row held by the caller's canvas write transaction. */
|
|
70
|
+
export function requireCurrentDocumentLocked(db, review_id, expected_doc_hash) {
|
|
71
|
+
const review = db.prepare('SELECT file, anchor_base_sha256 FROM reviews WHERE review_id = ?').get(review_id);
|
|
72
|
+
if (review === undefined) {
|
|
73
|
+
throw new ReviewOperationError('unknown_review', 'review was not found', { received: review_id });
|
|
74
|
+
}
|
|
75
|
+
return requireDocumentSource(review, expected_doc_hash);
|
|
76
|
+
}
|
|
77
|
+
/** Reject malformed ranges rather than normalizing them into a different anchor. */
|
|
78
|
+
export function validateAnchorShape(anchor) {
|
|
79
|
+
const violations = [];
|
|
80
|
+
const hasStart = anchor.col_start !== undefined;
|
|
81
|
+
const hasEnd = anchor.col_end !== undefined;
|
|
82
|
+
if (!Number.isSafeInteger(anchor.line) || anchor.line < 1)
|
|
83
|
+
violations.push('line must be a positive integer');
|
|
84
|
+
if (!Number.isSafeInteger(anchor.end_line) || anchor.end_line < 1)
|
|
85
|
+
violations.push('end_line must be a positive integer');
|
|
86
|
+
if (Number.isSafeInteger(anchor.line) && Number.isSafeInteger(anchor.end_line) && anchor.end_line < anchor.line) {
|
|
87
|
+
violations.push('end_line must be greater than or equal to line');
|
|
88
|
+
}
|
|
89
|
+
if (hasStart !== hasEnd)
|
|
90
|
+
violations.push('col_start and col_end must be provided together');
|
|
91
|
+
if (hasStart && (!Number.isSafeInteger(anchor.col_start) || anchor.col_start < 0))
|
|
92
|
+
violations.push('col_start must be a non-negative integer');
|
|
93
|
+
if (hasEnd && (!Number.isSafeInteger(anchor.col_end) || anchor.col_end < 0))
|
|
94
|
+
violations.push('col_end must be a non-negative integer');
|
|
95
|
+
if (hasStart
|
|
96
|
+
&& hasEnd
|
|
97
|
+
&& anchor.line === anchor.end_line
|
|
98
|
+
&& Number.isSafeInteger(anchor.col_start)
|
|
99
|
+
&& Number.isSafeInteger(anchor.col_end)
|
|
100
|
+
&& anchor.col_end <= anchor.col_start) {
|
|
101
|
+
violations.push('col_end must be greater than col_start for a single-line anchor');
|
|
102
|
+
}
|
|
103
|
+
if (anchor.quote !== undefined && typeof anchor.quote !== 'string')
|
|
104
|
+
violations.push('quote must be a string');
|
|
105
|
+
if (typeof anchor.quote === 'string' && Buffer.byteLength(anchor.quote, 'utf8') > COMMENT_QUOTE_MAX) {
|
|
106
|
+
violations.push(`quote exceeds ${COMMENT_QUOTE_MAX} bytes`);
|
|
107
|
+
}
|
|
108
|
+
if (violations.length > 0) {
|
|
109
|
+
throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', { violations });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isUtf8Boundary(bytes, offset) {
|
|
113
|
+
return offset === bytes.length || (bytes[offset] & 0xc0) !== 0x80;
|
|
114
|
+
}
|
|
115
|
+
/** Capture the full source range used to render an anchor, without silently clamping it. */
|
|
116
|
+
export function captureAnchorText(lines, anchor) {
|
|
117
|
+
validateAnchorShape(anchor);
|
|
118
|
+
if (anchor.line > lines.length || anchor.end_line > lines.length) {
|
|
119
|
+
throw new ReviewOperationError('anchor_out_of_range', 'comment anchor exceeds the reviewed document', {
|
|
120
|
+
line_count: lines.length,
|
|
121
|
+
requested: { line: anchor.line, end_line: anchor.end_line },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (anchor.col_start !== undefined && anchor.col_end !== undefined) {
|
|
125
|
+
const startLine = Buffer.from(lines[anchor.line - 1], 'utf8');
|
|
126
|
+
const endLine = Buffer.from(lines[anchor.end_line - 1], 'utf8');
|
|
127
|
+
if (anchor.col_start > startLine.length
|
|
128
|
+
|| anchor.col_end > endLine.length
|
|
129
|
+
|| !isUtf8Boundary(startLine, anchor.col_start)
|
|
130
|
+
|| !isUtf8Boundary(endLine, anchor.col_end)) {
|
|
131
|
+
throw new ReviewOperationError('anchor_out_of_range', 'comment anchor columns exceed the reviewed document', {
|
|
132
|
+
line_count: lines.length,
|
|
133
|
+
requested: { line: anchor.line, end_line: anchor.end_line },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return lines.slice(anchor.line - 1, anchor.end_line).join('\n');
|
|
138
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FeedbackComment } from '../human/types.js';
|
|
2
|
+
import type { ReviewRecord, ReviewResult } from './types.js';
|
|
3
|
+
export interface ReviewCommentSnapshot {
|
|
4
|
+
comments: FeedbackComment[];
|
|
5
|
+
total: number;
|
|
6
|
+
unresolved: number;
|
|
7
|
+
}
|
|
8
|
+
/** Build the immutable approval result from the review row and one comment snapshot. */
|
|
9
|
+
export declare function buildReviewResult(review: ReviewRecord, snapshot: ReviewCommentSnapshot, approvedSha256: string, at: string): ReviewResult;
|
|
10
|
+
/** Project one immutable result to ticket/output or inline result surfaces. */
|
|
11
|
+
export declare function projectReviewResult(review: ReviewRecord, result: ReviewResult): {
|
|
12
|
+
destinations: string[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { atomicWriteJson, publishJsonExclusive, responsePath } from '../human/convention.js';
|
|
3
|
+
import { ticketDir } from '../human/root.js';
|
|
4
|
+
import { reviewResultPath } from '../canvas/paths.js';
|
|
5
|
+
/** Build the immutable approval result from the review row and one comment snapshot. */
|
|
6
|
+
export function buildReviewResult(review, snapshot, approvedSha256, at) {
|
|
7
|
+
return {
|
|
8
|
+
schema: 'humanloop.review-response/v1',
|
|
9
|
+
review_id: review.review_id,
|
|
10
|
+
file: review.file,
|
|
11
|
+
submitted: true,
|
|
12
|
+
approved: true,
|
|
13
|
+
changed: approvedSha256 !== review.baseline_sha256,
|
|
14
|
+
baseline_sha256: review.baseline_sha256,
|
|
15
|
+
approved_sha256: approvedSha256,
|
|
16
|
+
companion_node_id: review.companion_node_id,
|
|
17
|
+
comments_total: snapshot.total,
|
|
18
|
+
comments_unresolved: snapshot.unresolved,
|
|
19
|
+
comments: snapshot.comments,
|
|
20
|
+
submittedAt: at,
|
|
21
|
+
savedAt: at,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Project one immutable result to ticket/output or inline result surfaces. */
|
|
25
|
+
export function projectReviewResult(review, result) {
|
|
26
|
+
const destinations = [];
|
|
27
|
+
if (review.origin_kind === 'ticket') {
|
|
28
|
+
if (review.bridge_node_id === null)
|
|
29
|
+
throw new Error(`ticket review has no bridge node: ${review.review_id}`);
|
|
30
|
+
const dir = ticketDir(review.bridge_node_id);
|
|
31
|
+
const feedback = {
|
|
32
|
+
file: result.file,
|
|
33
|
+
submitted: result.submitted,
|
|
34
|
+
approved: result.approved,
|
|
35
|
+
comments: result.comments,
|
|
36
|
+
submittedAt: result.submittedAt,
|
|
37
|
+
savedAt: result.savedAt,
|
|
38
|
+
reviewId: result.review_id,
|
|
39
|
+
changed: result.changed,
|
|
40
|
+
baselineSha256: result.baseline_sha256,
|
|
41
|
+
approvedSha256: result.approved_sha256,
|
|
42
|
+
companionNodeId: result.companion_node_id,
|
|
43
|
+
commentsTotal: result.comments_total,
|
|
44
|
+
commentsUnresolved: result.comments_unresolved,
|
|
45
|
+
};
|
|
46
|
+
const response = responsePath(dir);
|
|
47
|
+
publishJsonExclusive(response, { schema: 'humanloop.review-response/v1', kind: 'review', result: feedback, completedAt: result.submittedAt });
|
|
48
|
+
destinations.push(response);
|
|
49
|
+
const output = review.output_path ?? join(dir, 'feedback.json');
|
|
50
|
+
atomicWriteJson(output, feedback);
|
|
51
|
+
destinations.push(output);
|
|
52
|
+
return { destinations };
|
|
53
|
+
}
|
|
54
|
+
const output = reviewResultPath(review.review_id);
|
|
55
|
+
atomicWriteJson(output, result);
|
|
56
|
+
destinations.push(output);
|
|
57
|
+
return { destinations };
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NodeStatus } from '../canvas/types.js';
|
|
2
|
+
import { type ReviewRecord } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Finish a committed binding without ever recapturing its origin. Each step is
|
|
5
|
+
* safe to repeat after a crash; review.json is the final visibility switch.
|
|
6
|
+
*/
|
|
7
|
+
export declare function realizeReview(reviewId: string): Promise<ReviewRecord>;
|
|
8
|
+
/**
|
|
9
|
+
* Reopen one already-open companion when needed. The surface only attaches to
|
|
10
|
+
* the returned node; it never owns a second revive path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function openExistingReview(reviewId: string): Promise<{
|
|
13
|
+
review: ReviewRecord;
|
|
14
|
+
disposition: 'terminal';
|
|
15
|
+
} | {
|
|
16
|
+
review: ReviewRecord;
|
|
17
|
+
companion_status: NodeStatus;
|
|
18
|
+
disposition: 'open';
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { getNode, subscribersOf, subscribe } from '../canvas/index.js';
|
|
4
|
+
import { withCanvasWrite } from '../canvas/db.js';
|
|
5
|
+
import { atomicWriteJson } from '../human/convention.js';
|
|
6
|
+
import { ticketDir } from '../human/root.js';
|
|
7
|
+
import { submitReview } from '../human/tickets.js';
|
|
8
|
+
import { isBrokerLive } from '../runtime/model-swap.js';
|
|
9
|
+
import { NodeIdConflictError, spawnNode } from '../runtime/nodes.js';
|
|
10
|
+
import { transition } from '../runtime/lifecycle.js';
|
|
11
|
+
import { reviveNode } from '../runtime/revive.js';
|
|
12
|
+
import { awaitCompanionBind, spawnReviewCompanion } from './companion.js';
|
|
13
|
+
import { signalReviewActivity } from './signal.js';
|
|
14
|
+
import { bindBridgeNode, getReview, openReviewRow } from './store.js';
|
|
15
|
+
import { COMPANION_BIND_DEADLINE_MS, ReviewOperationError } from './types.js';
|
|
16
|
+
function unknownReview(reviewId) {
|
|
17
|
+
return new ReviewOperationError('unknown_review', 'review was not found', { received: reviewId });
|
|
18
|
+
}
|
|
19
|
+
function originMissing(originNodeId) {
|
|
20
|
+
return new ReviewOperationError('review_origin_missing', 'review origin was not found', {
|
|
21
|
+
origin_node_id: originNodeId,
|
|
22
|
+
reason: 'origin_missing',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function companionUnbound(reviewId, waitedMs) {
|
|
26
|
+
return new ReviewOperationError('review_companion_unbound', 'review companion did not bind in time', {
|
|
27
|
+
review_id: reviewId,
|
|
28
|
+
waited_ms: waitedMs,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function bridgeUnsubscribed(review, bridgeNodeId) {
|
|
32
|
+
return new ReviewOperationError('review_bridge_unsubscribed', 'review bridge is not subscribed to its origin', {
|
|
33
|
+
review_id: review.review_id,
|
|
34
|
+
bridge_node_id: bridgeNodeId,
|
|
35
|
+
origin_node_id: review.origin_node_id,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** Cancel only a binding whose immutable branch has disappeared. No later pass may recapture it. */
|
|
39
|
+
function cancelLostBinding(review) {
|
|
40
|
+
const canceled = withCanvasWrite((db) => db.prepare(`
|
|
41
|
+
UPDATE reviews
|
|
42
|
+
SET state = 'canceled', canceled_at = ?, cancel_reason = 'branch_lost', cancel_actor = NULL
|
|
43
|
+
WHERE review_id = ? AND state = 'binding'
|
|
44
|
+
`).run(new Date().toISOString(), review.review_id).changes > 0);
|
|
45
|
+
if (canceled)
|
|
46
|
+
signalReviewActivity();
|
|
47
|
+
}
|
|
48
|
+
function requireReview(reviewId) {
|
|
49
|
+
const review = getReview(reviewId);
|
|
50
|
+
if (review === null)
|
|
51
|
+
throw unknownReview(reviewId);
|
|
52
|
+
return review;
|
|
53
|
+
}
|
|
54
|
+
function requireOrigin(review) {
|
|
55
|
+
const origin = getNode(review.origin_node_id);
|
|
56
|
+
if (origin === null)
|
|
57
|
+
throw originMissing(review.origin_node_id);
|
|
58
|
+
return origin;
|
|
59
|
+
}
|
|
60
|
+
function spawnCompanion(review) {
|
|
61
|
+
if (getNode(review.companion_node_id) !== null)
|
|
62
|
+
return;
|
|
63
|
+
const origin = requireOrigin(review);
|
|
64
|
+
try {
|
|
65
|
+
spawnReviewCompanion({
|
|
66
|
+
nodeId: review.companion_node_id,
|
|
67
|
+
reviewId: review.review_id,
|
|
68
|
+
originNodeId: review.origin_node_id,
|
|
69
|
+
branchFile: review.branch_file,
|
|
70
|
+
targetFile: review.file,
|
|
71
|
+
modelSpec: review.model_spec,
|
|
72
|
+
cwd: origin.cwd,
|
|
73
|
+
profileId: origin.profile_id ?? null,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (!(error instanceof NodeIdConflictError))
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function cancelInvisibleBridge(nodeId) {
|
|
82
|
+
// A bridge which lost bindBridgeNode() was never exposed, so canceling it
|
|
83
|
+
// cannot produce a final report or notify the origin.
|
|
84
|
+
const node = getNode(nodeId);
|
|
85
|
+
if (node !== null && node.status !== 'done' && node.status !== 'canceled')
|
|
86
|
+
transition(nodeId, 'cancel');
|
|
87
|
+
}
|
|
88
|
+
/** Assert the only route a bridge has back to its origin before ticket exposure. */
|
|
89
|
+
function ensureBridgeSubscription(review, bridgeNodeId) {
|
|
90
|
+
const hasActiveOrigin = () => subscribersOf(bridgeNodeId).some((subscriber) => subscriber.node_id === review.origin_node_id && subscriber.active);
|
|
91
|
+
if (!hasActiveOrigin())
|
|
92
|
+
subscribe(review.origin_node_id, bridgeNodeId, true);
|
|
93
|
+
if (!hasActiveOrigin())
|
|
94
|
+
throw bridgeUnsubscribed(review, bridgeNodeId);
|
|
95
|
+
}
|
|
96
|
+
/** R2: create/bind the ticket bridge and prove its active origin subscription. */
|
|
97
|
+
function realizeBridge(review) {
|
|
98
|
+
if (review.origin_kind !== 'ticket')
|
|
99
|
+
return review;
|
|
100
|
+
let current = review;
|
|
101
|
+
if (current.bridge_node_id === null) {
|
|
102
|
+
const origin = requireOrigin(current);
|
|
103
|
+
const bridge = spawnNode({
|
|
104
|
+
kind: 'human',
|
|
105
|
+
lifecycle: 'terminal',
|
|
106
|
+
parent: current.origin_node_id,
|
|
107
|
+
cwd: origin.cwd,
|
|
108
|
+
profile_id: origin.profile_id ?? null,
|
|
109
|
+
name: 'human-review',
|
|
110
|
+
});
|
|
111
|
+
atomicWriteJson(join(ticketDir(bridge.node_id), 'run.json'), { mode: 'review' });
|
|
112
|
+
if (!bindBridgeNode(current.review_id, bridge.node_id)) {
|
|
113
|
+
cancelInvisibleBridge(bridge.node_id);
|
|
114
|
+
current = requireReview(current.review_id);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
current = requireReview(current.review_id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (current.bridge_node_id === null)
|
|
121
|
+
throw bridgeUnsubscribed(current, '');
|
|
122
|
+
ensureBridgeSubscription(current, current.bridge_node_id);
|
|
123
|
+
return current;
|
|
124
|
+
}
|
|
125
|
+
async function awaitAttachableCompanion(nodeId, deadlineMs) {
|
|
126
|
+
const deadline = Date.now() + deadlineMs;
|
|
127
|
+
for (;;) {
|
|
128
|
+
const companion = getNode(nodeId);
|
|
129
|
+
if (companion !== null && isBrokerLive(companion))
|
|
130
|
+
return true;
|
|
131
|
+
const remaining = deadline - Date.now();
|
|
132
|
+
if (remaining <= 0)
|
|
133
|
+
return false;
|
|
134
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, Math.min(100, remaining)));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function publishTicket(review) {
|
|
138
|
+
if (review.bridge_node_id === null)
|
|
139
|
+
throw bridgeUnsubscribed(review, '');
|
|
140
|
+
if (review.subtitle === null)
|
|
141
|
+
throw new Error(`ticket review has no subtitle: ${review.review_id}`);
|
|
142
|
+
submitReview({
|
|
143
|
+
dir: ticketDir(review.bridge_node_id),
|
|
144
|
+
review: {
|
|
145
|
+
file: review.file,
|
|
146
|
+
...(review.output_path !== null ? { output: review.output_path } : {}),
|
|
147
|
+
title: basename(review.file),
|
|
148
|
+
subtitle: review.subtitle,
|
|
149
|
+
source: { nodeId: review.origin_node_id },
|
|
150
|
+
blockedSince: review.created,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Finish a committed binding without ever recapturing its origin. Each step is
|
|
156
|
+
* safe to repeat after a crash; review.json is the final visibility switch.
|
|
157
|
+
*/
|
|
158
|
+
export async function realizeReview(reviewId) {
|
|
159
|
+
let review = requireReview(reviewId);
|
|
160
|
+
if (review.state !== 'binding')
|
|
161
|
+
return review;
|
|
162
|
+
if (!existsSync(review.branch_file)) {
|
|
163
|
+
cancelLostBinding(review);
|
|
164
|
+
throw new ReviewOperationError('branch_lost', 'review branch was lost before realization', { review_id: review.review_id });
|
|
165
|
+
}
|
|
166
|
+
// R1: creation consumes only the persisted coordinates and preallocated id.
|
|
167
|
+
spawnCompanion(review);
|
|
168
|
+
// R2: ticket bridge birth and route assertion precede all public exposure.
|
|
169
|
+
review = realizeBridge(requireReview(reviewId));
|
|
170
|
+
if (review.state !== 'binding')
|
|
171
|
+
return review;
|
|
172
|
+
// R3: only realization owns this initial launch/retry.
|
|
173
|
+
const companion = getNode(review.companion_node_id);
|
|
174
|
+
if (companion === null)
|
|
175
|
+
throw companionUnbound(review.review_id, 0);
|
|
176
|
+
if (companion.pi_session_id === null)
|
|
177
|
+
reviveNode(companion.node_id, { resume: false });
|
|
178
|
+
// R4: session_start is the durable bind boundary.
|
|
179
|
+
if (!await awaitCompanionBind(review.companion_node_id, COMPANION_BIND_DEADLINE_MS)) {
|
|
180
|
+
throw companionUnbound(review.review_id, COMPANION_BIND_DEADLINE_MS);
|
|
181
|
+
}
|
|
182
|
+
// R5: SQLite visibility commits before the ticket projection. A missing
|
|
183
|
+
// projection is deliberately under-exposed and recovered by the startup sweep.
|
|
184
|
+
if (openReviewRow(review.review_id, new Date().toISOString())) {
|
|
185
|
+
signalReviewActivity();
|
|
186
|
+
if (review.origin_kind === 'ticket')
|
|
187
|
+
publishTicket(review);
|
|
188
|
+
}
|
|
189
|
+
return requireReview(reviewId);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Reopen one already-open companion when needed. The surface only attaches to
|
|
193
|
+
* the returned node; it never owns a second revive path.
|
|
194
|
+
*/
|
|
195
|
+
export async function openExistingReview(reviewId) {
|
|
196
|
+
const review = requireReview(reviewId);
|
|
197
|
+
if (review.state === 'binding')
|
|
198
|
+
throw unknownReview(reviewId);
|
|
199
|
+
if (review.state !== 'open')
|
|
200
|
+
return { review, disposition: 'terminal' };
|
|
201
|
+
const companion = getNode(review.companion_node_id);
|
|
202
|
+
if (companion === null)
|
|
203
|
+
throw companionUnbound(review.review_id, 0);
|
|
204
|
+
if (!isBrokerLive(companion))
|
|
205
|
+
reviveNode(companion.node_id, { resume: true });
|
|
206
|
+
if (!await awaitAttachableCompanion(review.companion_node_id, COMPANION_BIND_DEADLINE_MS)) {
|
|
207
|
+
throw companionUnbound(review.review_id, COMPANION_BIND_DEADLINE_MS);
|
|
208
|
+
}
|
|
209
|
+
const attachable = getNode(review.companion_node_id);
|
|
210
|
+
if (attachable === null)
|
|
211
|
+
throw companionUnbound(review.review_id, 0);
|
|
212
|
+
return {
|
|
213
|
+
review: requireReview(reviewId),
|
|
214
|
+
companion_status: attachable.status,
|
|
215
|
+
disposition: 'open',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CommentAnchor, type CommentAuthorKind } from './types.js';
|
|
2
|
+
export interface RangeBatchEntry {
|
|
3
|
+
comment_id: string;
|
|
4
|
+
anchor?: CommentAnchor;
|
|
5
|
+
detached?: true;
|
|
6
|
+
}
|
|
7
|
+
interface Actor {
|
|
8
|
+
kind: CommentAuthorKind;
|
|
9
|
+
node_id: string | null;
|
|
10
|
+
}
|
|
11
|
+
/** Atomically replace every live review coordinate with ranges expressed against the current source. */
|
|
12
|
+
export declare function applyRangeBatch(args: {
|
|
13
|
+
review_id: string;
|
|
14
|
+
from_doc_hash: string;
|
|
15
|
+
to_doc_hash: string;
|
|
16
|
+
entries: RangeBatchEntry[];
|
|
17
|
+
actor: Actor;
|
|
18
|
+
operation_id: string;
|
|
19
|
+
}): {
|
|
20
|
+
updated: number;
|
|
21
|
+
detached: number;
|
|
22
|
+
anchor_base_sha256: string;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { withCanvasWrite } from '../canvas/db.js';
|
|
2
|
+
import { nowIso } from '../fs-utils.js';
|
|
3
|
+
import { signalReviewActivity } from './signal.js';
|
|
4
|
+
import { replaceAnchorBaseLocked } from './store.js';
|
|
5
|
+
import { ReviewOperationError, } from './types.js';
|
|
6
|
+
import { captureAnchorText, readReviewSource, validateAnchorShape } from './document.js';
|
|
7
|
+
function anchorFromRow(row) {
|
|
8
|
+
return {
|
|
9
|
+
line: row.anchor_line,
|
|
10
|
+
end_line: row.anchor_end_line,
|
|
11
|
+
...(row.anchor_col_start === null ? {} : { col_start: row.anchor_col_start }),
|
|
12
|
+
...(row.anchor_col_end === null ? {} : { col_end: row.anchor_col_end }),
|
|
13
|
+
...(row.anchor_quote === null ? {} : { quote: row.anchor_quote }),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function rangeBatchStale(reason, expected, actual, comment_id) {
|
|
17
|
+
throw new ReviewOperationError('range_batch_stale', 'review comment ranges are stale', {
|
|
18
|
+
reason,
|
|
19
|
+
expected,
|
|
20
|
+
actual,
|
|
21
|
+
...(comment_id === undefined ? {} : { comment_id }),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function validateEntry(entry) {
|
|
25
|
+
if (typeof entry.comment_id !== 'string' || entry.comment_id === '') {
|
|
26
|
+
throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', {
|
|
27
|
+
violations: ['comment_id must be a non-empty string'],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const hasAnchor = entry.anchor !== undefined;
|
|
31
|
+
const detached = entry.detached === true;
|
|
32
|
+
if (hasAnchor === detached) {
|
|
33
|
+
throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', {
|
|
34
|
+
violations: ['each range batch entry must provide exactly one of anchor or detached'],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (hasAnchor)
|
|
38
|
+
validateAnchorShape(entry.anchor);
|
|
39
|
+
}
|
|
40
|
+
function appendReanchorEvent(db, args) {
|
|
41
|
+
db.prepare(`
|
|
42
|
+
INSERT INTO review_comment_events
|
|
43
|
+
(review_id, comment_id, kind, state_change, revision_after, actor_kind, actor_node_id, at, operation_id, payload)
|
|
44
|
+
SELECT review_id, ?, 'reanchored', 1, ?, ?, ?, ?, ?, ?
|
|
45
|
+
FROM review_comments WHERE comment_id = ?
|
|
46
|
+
`).run(args.row.comment_id, args.row.revision + 1, args.actor.kind, args.actor.node_id, args.at, args.operation_id, JSON.stringify({
|
|
47
|
+
previous_anchor: args.previous_anchor,
|
|
48
|
+
anchor: args.anchor,
|
|
49
|
+
previous_anchor_state: args.previous_anchor_state,
|
|
50
|
+
anchor_state: args.anchor_state,
|
|
51
|
+
previous_doc_hash: args.previous_doc_hash,
|
|
52
|
+
doc_hash: args.doc_hash,
|
|
53
|
+
line_text: args.line_text,
|
|
54
|
+
}), args.row.comment_id);
|
|
55
|
+
}
|
|
56
|
+
/** Atomically replace every live review coordinate with ranges expressed against the current source. */
|
|
57
|
+
export function applyRangeBatch(args) {
|
|
58
|
+
const result = withCanvasWrite((db) => {
|
|
59
|
+
const review = db.prepare('SELECT file, anchor_base_sha256 FROM reviews WHERE review_id = ?').get(args.review_id);
|
|
60
|
+
if (review === undefined) {
|
|
61
|
+
throw new ReviewOperationError('unknown_review', 'review was not found', { received: args.review_id });
|
|
62
|
+
}
|
|
63
|
+
const source = readReviewSource(review.file, 'comment');
|
|
64
|
+
if (source.sha256 !== args.to_doc_hash)
|
|
65
|
+
rangeBatchStale('to_doc_hash', args.to_doc_hash, source.sha256);
|
|
66
|
+
if (review.anchor_base_sha256 !== args.from_doc_hash) {
|
|
67
|
+
rangeBatchStale('from_doc_hash', review.anchor_base_sha256, args.from_doc_hash);
|
|
68
|
+
}
|
|
69
|
+
const comments = db.prepare(`
|
|
70
|
+
SELECT comment_id, status, revision, text, anchor_line, anchor_end_line,
|
|
71
|
+
anchor_col_start, anchor_col_end, anchor_quote, anchor_line_text,
|
|
72
|
+
anchor_doc_hash, anchor_state
|
|
73
|
+
FROM review_comments
|
|
74
|
+
WHERE review_id = ? AND status != 'deleted'
|
|
75
|
+
ORDER BY created_seq
|
|
76
|
+
`).all(args.review_id);
|
|
77
|
+
for (const comment of comments) {
|
|
78
|
+
if (comment.anchor_doc_hash !== args.from_doc_hash) {
|
|
79
|
+
rangeBatchStale('comment_hash', args.from_doc_hash, comment.anchor_doc_hash, comment.comment_id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const entries = new Map();
|
|
83
|
+
const extra = [];
|
|
84
|
+
for (const entry of args.entries) {
|
|
85
|
+
validateEntry(entry);
|
|
86
|
+
if (entries.has(entry.comment_id)) {
|
|
87
|
+
extra.push(entry.comment_id);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
entries.set(entry.comment_id, entry);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const commentIds = new Set(comments.map((comment) => comment.comment_id));
|
|
94
|
+
for (const commentId of entries.keys()) {
|
|
95
|
+
if (!commentIds.has(commentId))
|
|
96
|
+
extra.push(commentId);
|
|
97
|
+
}
|
|
98
|
+
const missing = comments.filter((comment) => !entries.has(comment.comment_id)).map((comment) => comment.comment_id);
|
|
99
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
100
|
+
throw new ReviewOperationError('range_batch_incomplete', 'range batch must cover every non-deleted comment exactly once', {
|
|
101
|
+
missing,
|
|
102
|
+
extra,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
let updated = 0;
|
|
106
|
+
let detached = 0;
|
|
107
|
+
const at = nowIso();
|
|
108
|
+
for (const comment of comments) {
|
|
109
|
+
const entry = entries.get(comment.comment_id);
|
|
110
|
+
const previousAnchor = anchorFromRow(comment);
|
|
111
|
+
const nextAnchor = entry.detached === true ? previousAnchor : entry.anchor;
|
|
112
|
+
const nextState = entry.detached === true ? 'detached' : 'live';
|
|
113
|
+
const nextLineText = entry.detached === true
|
|
114
|
+
? comment.anchor_line_text
|
|
115
|
+
: captureAnchorText(source.lines, nextAnchor);
|
|
116
|
+
const changed = comment.anchor_state !== nextState
|
|
117
|
+
|| comment.anchor_doc_hash !== source.sha256
|
|
118
|
+
|| comment.anchor_line !== nextAnchor.line
|
|
119
|
+
|| comment.anchor_end_line !== nextAnchor.end_line
|
|
120
|
+
|| comment.anchor_col_start !== (nextAnchor.col_start ?? null)
|
|
121
|
+
|| comment.anchor_col_end !== (nextAnchor.col_end ?? null)
|
|
122
|
+
|| comment.anchor_quote !== (nextAnchor.quote ?? null)
|
|
123
|
+
|| comment.anchor_line_text !== nextLineText;
|
|
124
|
+
if (!changed)
|
|
125
|
+
continue;
|
|
126
|
+
const revision = comment.revision + 1;
|
|
127
|
+
db.prepare(`
|
|
128
|
+
UPDATE review_comments
|
|
129
|
+
SET revision = ?, anchor_line = ?, anchor_end_line = ?, anchor_col_start = ?, anchor_col_end = ?,
|
|
130
|
+
anchor_quote = ?, anchor_line_text = ?, anchor_doc_hash = ?, anchor_state = ?,
|
|
131
|
+
updated_by_kind = ?, updated_by_node_id = ?, updated_at = ?
|
|
132
|
+
WHERE comment_id = ?
|
|
133
|
+
`).run(revision, nextAnchor.line, nextAnchor.end_line, nextAnchor.col_start ?? null, nextAnchor.col_end ?? null, nextAnchor.quote ?? null, nextLineText, source.sha256, nextState, args.actor.kind, args.actor.node_id, at, comment.comment_id);
|
|
134
|
+
appendReanchorEvent(db, {
|
|
135
|
+
row: comment,
|
|
136
|
+
actor: args.actor,
|
|
137
|
+
operation_id: args.operation_id,
|
|
138
|
+
at,
|
|
139
|
+
previous_anchor: previousAnchor,
|
|
140
|
+
anchor: nextAnchor,
|
|
141
|
+
previous_anchor_state: comment.anchor_state,
|
|
142
|
+
anchor_state: nextState,
|
|
143
|
+
previous_doc_hash: comment.anchor_doc_hash,
|
|
144
|
+
doc_hash: source.sha256,
|
|
145
|
+
line_text: nextLineText,
|
|
146
|
+
});
|
|
147
|
+
updated += 1;
|
|
148
|
+
if (nextState === 'detached')
|
|
149
|
+
detached += 1;
|
|
150
|
+
}
|
|
151
|
+
replaceAnchorBaseLocked(db, args.review_id, source.bytes, source.sha256);
|
|
152
|
+
return { updated, detached, anchor_base_sha256: source.sha256 };
|
|
153
|
+
});
|
|
154
|
+
signalReviewActivity();
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Signal one durable review mutation to every open viewer without watching every review directory. */
|
|
2
|
+
export declare function reviewActivityPath(): string;
|
|
3
|
+
/** Signal one review mutation (comment create/edit/resolve/delete/reanchor/delivery) to every open review surface. */
|
|
4
|
+
export declare function signalReviewActivity(): void;
|
|
5
|
+
export declare function watchReviewActivity(onChange: () => void): () => void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { basename, dirname, join } from 'node:path';
|
|
2
|
+
import { mkdirSync, watch, writeFileSync } from 'node:fs';
|
|
3
|
+
import { crtrHome } from '../canvas/paths.js';
|
|
4
|
+
/** Signal one durable review mutation to every open viewer without watching every review directory. */
|
|
5
|
+
export function reviewActivityPath() {
|
|
6
|
+
return join(crtrHome(), 'signals', 'review-activity');
|
|
7
|
+
}
|
|
8
|
+
/** Signal one review mutation (comment create/edit/resolve/delete/reanchor/delivery) to every open review surface. */
|
|
9
|
+
export function signalReviewActivity() {
|
|
10
|
+
try {
|
|
11
|
+
mkdirSync(dirname(reviewActivityPath()), { recursive: true, mode: 0o700 });
|
|
12
|
+
writeFileSync(reviewActivityPath(), `${Date.now()}\n`, { mode: 0o600 });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// A later review read is still authoritative.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function watchReviewActivity(onChange) {
|
|
19
|
+
const activityPath = reviewActivityPath();
|
|
20
|
+
try {
|
|
21
|
+
mkdirSync(dirname(activityPath), { recursive: true, mode: 0o700 });
|
|
22
|
+
const watcher = watch(dirname(activityPath), (_event, filename) => {
|
|
23
|
+
if (String(filename) === basename(activityPath))
|
|
24
|
+
onChange();
|
|
25
|
+
});
|
|
26
|
+
return () => watcher.close();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return () => { };
|
|
30
|
+
}
|
|
31
|
+
}
|