@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
|
@@ -20,6 +20,7 @@ import { join } from 'node:path';
|
|
|
20
20
|
import { buildLaunchSpec, isProviderPinnedModelToken, normalizeModel } from '../runtime/launch.js';
|
|
21
21
|
import { defaultModelLaddersConfig } from '../../types.js';
|
|
22
22
|
import { persistDormantModel } from '../runtime/model-swap.js';
|
|
23
|
+
import { promote } from '../runtime/promote.js';
|
|
23
24
|
import { createNode, getNode } from '../canvas/canvas.js';
|
|
24
25
|
import { closeDb } from '../canvas/db.js';
|
|
25
26
|
const ladders = defaultModelLaddersConfig();
|
|
@@ -102,6 +103,19 @@ test('an explicit concrete pin survives a config/lifecycle rebuild whose portabl
|
|
|
102
103
|
// Direction 2: a deliberately TIER-run (unpinned) node must NOT be wrongly
|
|
103
104
|
// re-pinned when a subsequent rebuild/persist normalizes its override to the
|
|
104
105
|
// concrete cell it happens to share bytes with.
|
|
106
|
+
test('promotion preserves the exact-model contract required to revive a private human-work fork', () => {
|
|
107
|
+
const exactModel = 'openai-codex/gpt-5.6-sol:max';
|
|
108
|
+
createNode(node('exact-node', {
|
|
109
|
+
model_override: exactModel,
|
|
110
|
+
launch: { extensions: [], env: {}, model: exactModel, modelProviderPinned: true, modelExact: true },
|
|
111
|
+
}));
|
|
112
|
+
promote('exact-node');
|
|
113
|
+
const after = getNode('exact-node');
|
|
114
|
+
assert.equal(after?.mode, 'orchestrator');
|
|
115
|
+
assert.equal(after?.launch?.model, exactModel);
|
|
116
|
+
assert.equal(after?.launch?.modelProviderPinned, true);
|
|
117
|
+
assert.equal(after?.launch?.modelExact, true, 'promotion must not create a launch recipe that reviveNode rejects');
|
|
118
|
+
});
|
|
105
119
|
test('a deliberately tier-run node is not wrongly re-pinned when a rebuild or persist normalizes its override to the concrete cell', () => {
|
|
106
120
|
// The buildLaunchSpec seam: an explicit `false` carried through a rebuild wins
|
|
107
121
|
// over a concrete override that would otherwise read as pinned.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { test, before, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
7
|
+
import { closeDb, MIGRATIONS, openDb } from '../canvas/db.js';
|
|
8
|
+
import { canvasDbPath, ensureHome } from '../canvas/paths.js';
|
|
9
|
+
let home;
|
|
10
|
+
const legacyOutboxTable = ['consult', 'outbox'].join('_');
|
|
11
|
+
function userVersion(db) {
|
|
12
|
+
return db.prepare('PRAGMA user_version').get().user_version;
|
|
13
|
+
}
|
|
14
|
+
function names(db, type) {
|
|
15
|
+
return new Set(db.prepare('SELECT name FROM sqlite_master WHERE type = ?').all(type)
|
|
16
|
+
.map((row) => row.name));
|
|
17
|
+
}
|
|
18
|
+
function columns(db, table) {
|
|
19
|
+
return db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name);
|
|
20
|
+
}
|
|
21
|
+
function migrateToVersion(db, target) {
|
|
22
|
+
for (let version = userVersion(db); version < target; version += 1) {
|
|
23
|
+
MIGRATIONS[version](db);
|
|
24
|
+
db.exec(`PRAGMA user_version = ${version + 1}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
before(() => {
|
|
28
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-phase4-review-migration-'));
|
|
29
|
+
process.env['CRTR_HOME'] = home;
|
|
30
|
+
});
|
|
31
|
+
after(() => {
|
|
32
|
+
closeDb();
|
|
33
|
+
rmSync(home, { recursive: true, force: true });
|
|
34
|
+
delete process.env['CRTR_HOME'];
|
|
35
|
+
});
|
|
36
|
+
test('v27/v28 review schema, v29 anchor state, and v30 outbox drop migrate fresh and from v28', () => {
|
|
37
|
+
const db = openDb();
|
|
38
|
+
assert.equal(userVersion(db), 30);
|
|
39
|
+
assert.equal(names(db, 'table').has(legacyOutboxTable), false);
|
|
40
|
+
const tables = names(db, 'table');
|
|
41
|
+
assert.deepEqual(['reviews', 'review_comments', 'review_comment_events'].every((name) => tables.has(name)), true);
|
|
42
|
+
assert.deepEqual(columns(db, 'reviews'), [
|
|
43
|
+
'review_id', 'idempotency_key', 'origin_kind', 'origin_node_id', 'bridge_node_id',
|
|
44
|
+
'companion_node_id', 'review_dir', 'file', 'origin_session_file', 'origin_session_id',
|
|
45
|
+
'origin_leaf_id', 'model_spec', 'branch_file', 'baseline_sha256', 'baseline_bytes',
|
|
46
|
+
'anchor_base_sha256', 'anchor_base_bytes', 'state', 'created', 'captured_at', 'opened_at',
|
|
47
|
+
'output_path', 'subtitle', 'approved_at', 'approved_sha256', 'changed', 'comments_total',
|
|
48
|
+
'comments_unresolved', 'result_json', 'canceled_at', 'cancel_reason', 'cancel_actor',
|
|
49
|
+
'delivery_entry_id', 'delivered_at', 'delivery_error', 'projected_at', 'projection_error',
|
|
50
|
+
]);
|
|
51
|
+
assert.deepEqual(columns(db, 'review_comments'), [
|
|
52
|
+
'comment_id', 'review_id', 'status', 'revision', 'text', 'anchor_line', 'anchor_end_line',
|
|
53
|
+
'anchor_col_start', 'anchor_col_end', 'anchor_quote', 'anchor_line_text', 'anchor_doc_hash',
|
|
54
|
+
'author_kind', 'author_node_id', 'updated_by_kind', 'updated_by_node_id', 'created_at',
|
|
55
|
+
'updated_at', 'resolved_at', 'deleted_at', 'created_seq', 'anchor_state',
|
|
56
|
+
]);
|
|
57
|
+
assert.deepEqual(columns(db, 'review_comment_events'), [
|
|
58
|
+
'seq', 'review_id', 'comment_id', 'kind', 'state_change', 'revision_after', 'actor_kind',
|
|
59
|
+
'actor_node_id', 'at', 'operation_id', 'payload',
|
|
60
|
+
]);
|
|
61
|
+
const indexes = names(db, 'index');
|
|
62
|
+
for (const index of [
|
|
63
|
+
'idx_reviews_state', 'idx_reviews_origin', 'idx_reviews_file', 'idx_reviews_bridge',
|
|
64
|
+
'idx_review_comments_review_line_seq', 'idx_review_comments_review_status',
|
|
65
|
+
'idx_review_comment_events_review_seq', 'idx_review_comment_events_comment_seq',
|
|
66
|
+
]) {
|
|
67
|
+
assert.equal(indexes.has(index), true, `missing ${index}`);
|
|
68
|
+
}
|
|
69
|
+
closeDb();
|
|
70
|
+
rmSync(home, { recursive: true, force: true });
|
|
71
|
+
ensureHome();
|
|
72
|
+
const raw = new DatabaseSync(canvasDbPath());
|
|
73
|
+
migrateToVersion(raw, 28);
|
|
74
|
+
assert.equal(userVersion(raw), 28);
|
|
75
|
+
raw.prepare(`
|
|
76
|
+
INSERT INTO review_comments
|
|
77
|
+
(comment_id, review_id, status, revision, text, anchor_line, anchor_end_line,
|
|
78
|
+
anchor_line_text, anchor_doc_hash, author_kind, updated_by_kind, created_at,
|
|
79
|
+
updated_at, created_seq)
|
|
80
|
+
VALUES ('v28-comment', 'v28-review', 'open', 1, 'existing', 1, 1,
|
|
81
|
+
'source', 'hash', 'human', 'human', '2026-01-01T00:00:00.000Z',
|
|
82
|
+
'2026-01-01T00:00:00.000Z', 1)
|
|
83
|
+
`).run();
|
|
84
|
+
migrateToVersion(raw, 29);
|
|
85
|
+
assert.equal(userVersion(raw), 29);
|
|
86
|
+
raw.prepare(`
|
|
87
|
+
INSERT INTO ${legacyOutboxTable}
|
|
88
|
+
(node_id, ticket_node_id, request_id, work_kind, status, updated_at)
|
|
89
|
+
VALUES ('legacy-outbox-node', 'legacy-ticket-node', 'legacy-request', 'follow_up', 'pending', '2026-01-01T00:00:00.000Z')
|
|
90
|
+
`).run();
|
|
91
|
+
raw.close();
|
|
92
|
+
const migrated = openDb();
|
|
93
|
+
assert.equal(userVersion(migrated), 30);
|
|
94
|
+
assert.equal(names(migrated, 'table').has('reviews'), true);
|
|
95
|
+
assert.equal(names(migrated, 'table').has('review_comments'), true);
|
|
96
|
+
assert.equal(names(migrated, 'table').has('review_comment_events'), true);
|
|
97
|
+
assert.equal(migrated.prepare("SELECT anchor_state FROM review_comments WHERE comment_id = 'v28-comment'").get().anchor_state, 'live');
|
|
98
|
+
assert.throws(() => migrated.prepare("UPDATE review_comments SET anchor_state = 'invalid' WHERE comment_id = 'v28-comment'").run());
|
|
99
|
+
assert.equal(names(migrated, 'table').has(legacyOutboxTable), false);
|
|
100
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { test, before, after, beforeEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { closeDb, openDb } from '../canvas/db.js';
|
|
7
|
+
import { approveReviewLocked, bindBridgeNode, cancelReviewRow, commitBindingReview, getReview, getReviewByBridge, getReviewByCompanion, listReviews, markDelivered, markDeliveryError, markProjected, markProjectionError, openReviewRow, readAnchorBase, } from '../review/store.js';
|
|
8
|
+
let home;
|
|
9
|
+
function row(overrides = {}) {
|
|
10
|
+
return {
|
|
11
|
+
review_id: 'review-one',
|
|
12
|
+
idempotency_key: 'request-one',
|
|
13
|
+
origin_kind: 'inline',
|
|
14
|
+
origin_node_id: 'origin-one',
|
|
15
|
+
bridge_node_id: null,
|
|
16
|
+
companion_node_id: 'companion-one',
|
|
17
|
+
review_dir: '/tmp/review-one',
|
|
18
|
+
file: '/tmp/does-not-exist.txt',
|
|
19
|
+
origin_session_file: '/tmp/origin.jsonl',
|
|
20
|
+
origin_session_id: 'session-one',
|
|
21
|
+
origin_leaf_id: 'leaf-one',
|
|
22
|
+
model_spec: 'anthropic/claude:test',
|
|
23
|
+
branch_file: '/tmp/review-one/branch-point.jsonl',
|
|
24
|
+
baseline_sha256: 'baseline-hash',
|
|
25
|
+
baseline_bytes: 3,
|
|
26
|
+
anchor_base_sha256: 'anchor-hash',
|
|
27
|
+
anchor_base_bytes: Buffer.from('abc'),
|
|
28
|
+
state: 'binding',
|
|
29
|
+
created: '2026-01-01T00:00:00.000Z',
|
|
30
|
+
captured_at: '2026-01-01T00:00:00.000Z',
|
|
31
|
+
opened_at: null,
|
|
32
|
+
output_path: null,
|
|
33
|
+
subtitle: null,
|
|
34
|
+
approved_at: null,
|
|
35
|
+
approved_sha256: null,
|
|
36
|
+
changed: null,
|
|
37
|
+
comments_total: null,
|
|
38
|
+
comments_unresolved: null,
|
|
39
|
+
result_json: null,
|
|
40
|
+
canceled_at: null,
|
|
41
|
+
cancel_reason: null,
|
|
42
|
+
cancel_actor: null,
|
|
43
|
+
delivery_entry_id: null,
|
|
44
|
+
delivered_at: null,
|
|
45
|
+
delivery_error: null,
|
|
46
|
+
projected_at: null,
|
|
47
|
+
projection_error: null,
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
before(() => {
|
|
52
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-phase4-review-store-'));
|
|
53
|
+
process.env['CRTR_HOME'] = home;
|
|
54
|
+
});
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
closeDb();
|
|
57
|
+
rmSync(home, { recursive: true, force: true });
|
|
58
|
+
});
|
|
59
|
+
after(() => {
|
|
60
|
+
closeDb();
|
|
61
|
+
rmSync(home, { recursive: true, force: true });
|
|
62
|
+
delete process.env['CRTR_HOME'];
|
|
63
|
+
});
|
|
64
|
+
test('reviews DAO owns idempotent binding and conditional lifecycle writes', () => {
|
|
65
|
+
const first = row();
|
|
66
|
+
assert.equal(existsSync(first.review_dir), false, 'the birth commit does not create filesystem state');
|
|
67
|
+
assert.deepEqual(commitBindingReview(first), { review_id: 'review-one', won: true });
|
|
68
|
+
assert.deepEqual(commitBindingReview(row({ review_id: 'review-two', companion_node_id: 'companion-two' })), {
|
|
69
|
+
review_id: 'review-one',
|
|
70
|
+
won: false,
|
|
71
|
+
});
|
|
72
|
+
assert.equal(listReviews({}).length, 0, 'binding rows are hidden from ordinary review lists');
|
|
73
|
+
assert.equal(getReview(first.review_id)?.source_missing, true, 'source_missing is computed from the current filesystem');
|
|
74
|
+
assert.equal(getReviewByCompanion(first.companion_node_id), null);
|
|
75
|
+
assert.equal(bindBridgeNode(first.review_id, 'bridge-one'), true);
|
|
76
|
+
assert.equal(bindBridgeNode(first.review_id, 'bridge-two'), false);
|
|
77
|
+
assert.equal(getReviewByBridge('bridge-one'), null, 'binding rows are not exposed by bridge lookup');
|
|
78
|
+
assert.equal(openReviewRow(first.review_id, '2026-01-01T00:01:00.000Z'), true);
|
|
79
|
+
assert.equal(openReviewRow(first.review_id, '2026-01-01T00:02:00.000Z'), false);
|
|
80
|
+
assert.equal(getReviewByBridge('bridge-one')?.review_id, first.review_id);
|
|
81
|
+
const db = openDb();
|
|
82
|
+
assert.equal(approveReviewLocked(db, {
|
|
83
|
+
review_id: first.review_id,
|
|
84
|
+
approved_at: '2026-01-01T00:03:00.000Z',
|
|
85
|
+
approved_sha256: 'approved-hash',
|
|
86
|
+
changed: true,
|
|
87
|
+
comments_total: 2,
|
|
88
|
+
comments_unresolved: 1,
|
|
89
|
+
result_json: '{"approved":true}',
|
|
90
|
+
}), true);
|
|
91
|
+
assert.equal(approveReviewLocked(db, {
|
|
92
|
+
review_id: first.review_id,
|
|
93
|
+
approved_at: '2026-01-01T00:04:00.000Z',
|
|
94
|
+
approved_sha256: 'approved-hash-2',
|
|
95
|
+
changed: false,
|
|
96
|
+
comments_total: 0,
|
|
97
|
+
comments_unresolved: 0,
|
|
98
|
+
result_json: '{}',
|
|
99
|
+
}), false, 'a second approval cannot win the conditional write');
|
|
100
|
+
markProjectionError(first.review_id, 'temporary projection failure');
|
|
101
|
+
markProjected(first.review_id, '2026-01-01T00:05:00.000Z');
|
|
102
|
+
markDeliveryError(first.review_id, 'temporary delivery failure');
|
|
103
|
+
markDelivered(first.review_id, 'receipt-one', '2026-01-01T00:06:00.000Z');
|
|
104
|
+
const approved = getReview(first.review_id);
|
|
105
|
+
assert.equal(approved?.projection_error, null);
|
|
106
|
+
assert.equal(approved?.delivery_error, null);
|
|
107
|
+
assert.equal(approved?.delivered_at, '2026-01-01T00:06:00.000Z');
|
|
108
|
+
assert.deepEqual(readAnchorBase(first.review_id), { bytes: Buffer.from('abc'), sha256: 'anchor-hash' });
|
|
109
|
+
});
|
|
110
|
+
test('stale ledger errors cannot overwrite recorded success', () => {
|
|
111
|
+
const review = row({ review_id: 'review-ledgers', idempotency_key: 'request-ledgers', companion_node_id: 'companion-ledgers' });
|
|
112
|
+
commitBindingReview(review);
|
|
113
|
+
markProjected(review.review_id, '2026-01-01T00:05:00.000Z');
|
|
114
|
+
markProjectionError(review.review_id, 'stale projection failure');
|
|
115
|
+
markDelivered(review.review_id, 'receipt-ledgers', '2026-01-01T00:06:00.000Z');
|
|
116
|
+
markDeliveryError(review.review_id, 'stale delivery failure');
|
|
117
|
+
const recorded = getReview(review.review_id);
|
|
118
|
+
assert.equal(recorded?.projected_at, '2026-01-01T00:05:00.000Z');
|
|
119
|
+
assert.equal(recorded?.projection_error, null);
|
|
120
|
+
assert.equal(recorded?.delivered_at, '2026-01-01T00:06:00.000Z');
|
|
121
|
+
assert.equal(recorded?.delivery_error, null);
|
|
122
|
+
});
|
|
123
|
+
test('approval cannot win after cancellation and companion ids remain unique', () => {
|
|
124
|
+
const canceled = row({ review_id: 'review-canceled', idempotency_key: 'request-canceled', companion_node_id: 'companion-canceled' });
|
|
125
|
+
commitBindingReview(canceled);
|
|
126
|
+
assert.equal(openReviewRow(canceled.review_id, '2026-01-01T00:59:00.000Z'), true);
|
|
127
|
+
assert.equal(cancelReviewRow(canceled.review_id, { at: '2026-01-01T01:00:00.000Z', reason: 'not needed', actor: 'human' }), true);
|
|
128
|
+
assert.equal(approveReviewLocked(openDb(), {
|
|
129
|
+
review_id: canceled.review_id,
|
|
130
|
+
approved_at: '2026-01-01T01:01:00.000Z',
|
|
131
|
+
approved_sha256: 'hash',
|
|
132
|
+
changed: false,
|
|
133
|
+
comments_total: 0,
|
|
134
|
+
comments_unresolved: 0,
|
|
135
|
+
result_json: '{}',
|
|
136
|
+
}), false);
|
|
137
|
+
assert.throws(() => commitBindingReview(row({
|
|
138
|
+
review_id: 'review-duplicate-companion',
|
|
139
|
+
idempotency_key: 'request-duplicate-companion',
|
|
140
|
+
companion_node_id: 'companion-canceled',
|
|
141
|
+
state: 'binding',
|
|
142
|
+
})), /UNIQUE constraint failed: reviews\.companion_node_id/);
|
|
143
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// `remoteCanvas.targets.<name>` (`../config.ts`'s normalizeRemoteCanvasTarget,
|
|
3
3
|
// exercised through the public readConfig/updateConfig surface — it isn't
|
|
4
4
|
// exported directly) plus `resolveRemoteCanvasTarget`'s error paths
|
|
5
|
-
// (`../
|
|
5
|
+
// (`../remote-canvas-target.ts`). The one load-bearing invariant here:
|
|
6
6
|
// an error message about a missing/unknown target must never leak a token
|
|
7
7
|
// substring, even though it's guiding the agent toward the exact command
|
|
8
8
|
// that would set one.
|
|
@@ -13,7 +13,7 @@ import { tmpdir } from 'node:os';
|
|
|
13
13
|
import { join } from 'node:path';
|
|
14
14
|
import { readConfig, updateConfig } from '../config.js';
|
|
15
15
|
import { setRelayToken } from '../secrets.js';
|
|
16
|
-
import { getRemoteCanvasTarget, resolveRemoteCanvasTarget } from '../
|
|
16
|
+
import { getRemoteCanvasTarget, resolveRemoteCanvasTarget } from '../remote-canvas-target.js';
|
|
17
17
|
let home;
|
|
18
18
|
let realHome;
|
|
19
19
|
before(() => {
|
|
@@ -33,16 +33,15 @@ import { tmpdir } from 'node:os';
|
|
|
33
33
|
import { join, dirname } from 'node:path';
|
|
34
34
|
import { fileURLToPath } from 'node:url';
|
|
35
35
|
import { spawn, spawnSync } from 'node:child_process';
|
|
36
|
-
import { createNode, getNode, subscribe, updateNode
|
|
37
|
-
import { armFollowUpWork } from '../canvas/human-work-outbox.js';
|
|
36
|
+
import { createNode, getNode, subscribe, updateNode } from '../canvas/canvas.js';
|
|
38
37
|
import { armCron, hasPendingCancelOnWakeCron } from '../canvas/crons.js';
|
|
39
38
|
import { readInboxSince } from '../feed/inbox.js';
|
|
40
39
|
import { closeDb } from '../canvas/db.js';
|
|
41
40
|
import { buildPiArgv, CANVAS_EXTENSIONS } from '../runtime/launch.js';
|
|
42
|
-
import { CANVAS_INBOX_WATCHER_PATH } from '../runtime/canvas-extensions.js';
|
|
41
|
+
import { CANVAS_CONTEXT_INTRO_PATH, CANVAS_INBOX_WATCHER_PATH, CANVAS_REVIEW_BOUNDARY_PATH, CANVAS_STOPHOOK_PATH, } from '../runtime/canvas-extensions.js';
|
|
43
42
|
import { headlessBrokerHost } from '../runtime/host.js';
|
|
44
43
|
import { resumeArgs, reviveNode } from '../runtime/revive.js';
|
|
45
|
-
import { REVIVE_KICKOFF_SENTINEL, writeGoal } from '../runtime/kickoff.js';
|
|
44
|
+
import { REVIVE_KICKOFF_SENTINEL, readGoal, writeGoal } from '../runtime/kickoff.js';
|
|
46
45
|
import { seedRoadmap } from '../runtime/roadmap.js';
|
|
47
46
|
import { InputError } from '../io.js';
|
|
48
47
|
import { createHarness } from './helpers/harness.js';
|
|
@@ -193,6 +192,8 @@ test('buildPiArgv restores mandatory canvas extensions when persisted launch met
|
|
|
193
192
|
assert.equal(seen.get(ext), 1, `mandatory extension present exactly once: ${ext}`);
|
|
194
193
|
}
|
|
195
194
|
assert.deepEqual(extensions.slice(0, required.size), mandatory, 'mandatory extensions are loaded first and once');
|
|
195
|
+
assert.equal(extensions.indexOf(CANVAS_STOPHOOK_PATH), extensions.indexOf(CANVAS_REVIEW_BOUNDARY_PATH) + 1, 'review boundary runs immediately before the stophook commits a companion bind');
|
|
196
|
+
assert.ok(extensions.indexOf(CANVAS_CONTEXT_INTRO_PATH) > extensions.indexOf(CANVAS_REVIEW_BOUNDARY_PATH), 'review boundary still precedes fork bearings');
|
|
196
197
|
});
|
|
197
198
|
test('buildPiArgv replaces the previous mandatory prefix with the current canvas hooks', () => {
|
|
198
199
|
const historicalNames = [
|
|
@@ -424,66 +425,34 @@ test('reviveNode fresh-fallback clears the stale session identity (Major-3: no p
|
|
|
424
425
|
await h.dispose();
|
|
425
426
|
}
|
|
426
427
|
});
|
|
427
|
-
test('
|
|
428
|
+
test('an unbound ordinary fork replays its immutable source with and without its goal', () => {
|
|
428
429
|
const originalLaunch = headlessBrokerHost.launch;
|
|
429
|
-
// No harness here — bind a standalone in-process fleet so reviveNode's
|
|
430
|
-
// boundFleet() resolves; the launch stub below registers with it exactly as
|
|
431
|
-
// the real host does (registration is the launch seam's contract).
|
|
432
430
|
const tf = bindTestFleet();
|
|
433
|
-
const
|
|
434
|
-
const forkFile = join(home, 'private-source.jsonl');
|
|
435
|
-
const childFile = join(home, 'private-child.jsonl');
|
|
436
|
-
const exactModel = 'openai-codex/gpt-5.3-codex:xhigh';
|
|
437
|
-
const kickoff = 'private immutable kickoff';
|
|
431
|
+
const forkFile = join(home, 'ordinary-fork-source.jsonl');
|
|
438
432
|
const launches = [];
|
|
439
433
|
try {
|
|
440
434
|
mkdirSync(home, { recursive: true });
|
|
441
435
|
writeFileSync(forkFile, '{}\n', 'utf8');
|
|
442
|
-
createNode(node(privateId, {
|
|
443
|
-
kind: 'general',
|
|
444
|
-
cwd: home,
|
|
445
|
-
fork_from: forkFile,
|
|
446
|
-
human_work_target: { work_kind: 'follow_up', dir: '/ticket', request_id: 'request-1', root: '/root' },
|
|
447
|
-
private_fork_phase: 'fork_pending',
|
|
448
|
-
private_fork_recipe: { kickoff },
|
|
449
|
-
launch: { model: exactModel, modelExact: true, tools: [], extensions: [], env: {} },
|
|
450
|
-
}));
|
|
451
|
-
// reviveNode's locked launch-gate (Major-3) requires a still-armed matching
|
|
452
|
-
// human-work row before it will launch a private fork — arm one here to
|
|
453
|
-
// mirror the real handleFollowUpStart/handleVisualStart claim it recheck.
|
|
454
|
-
armFollowUpWork({ nodeId: privateId, dir: '/ticket', root: '/root', requestId: 'request-1' });
|
|
455
436
|
headlessBrokerHost.launch = (nodeId, inv) => {
|
|
456
|
-
launches.push(
|
|
437
|
+
launches.push([...inv.argv]);
|
|
457
438
|
const handle = { pid: disposableLivePid(), exited: new Promise(() => { }) };
|
|
458
|
-
// Mirror the real host contract: the entry IS liveness (design D-1).
|
|
459
439
|
tf.fleet.register(nodeId, handle);
|
|
460
440
|
return handle;
|
|
461
441
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
assert.equal(
|
|
467
|
-
assert.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
pi_session_id: 'child-session',
|
|
477
|
-
pi_session_file: childFile,
|
|
478
|
-
private_fork_phase: 'session_started',
|
|
479
|
-
});
|
|
480
|
-
reviveNode(privateId, { resume: false });
|
|
481
|
-
const started = launches[1];
|
|
482
|
-
const sessionIndex = started.argv.indexOf('--session');
|
|
483
|
-
assert.equal(started.argv[sessionIndex + 1], childFile, 'bound claim resumes only its writable child session');
|
|
484
|
-
assert.equal(started.argv.includes('--fork'), false, 'bound claim never forks provenance again');
|
|
485
|
-
assert.equal(started.argv.includes(kickoff), false, 'bound claim never replays its one-shot kickoff');
|
|
486
|
-
assert.equal(started.argv.includes('--crtr-new-cycle'), false, 'bound claim ignores a no-resume request rather than cycling');
|
|
442
|
+
createNode(node('ordinary-fork-promptless', { cwd: home, fork_from: forkFile }));
|
|
443
|
+
reviveNode('ordinary-fork-promptless', { resume: true });
|
|
444
|
+
const promptless = launches[0];
|
|
445
|
+
const promptlessFork = promptless.indexOf('--fork');
|
|
446
|
+
assert.equal(promptless[promptlessFork + 1], forkFile, 'an unbound ordinary fork uses its immutable source');
|
|
447
|
+
assert.deepEqual(promptless, buildPiArgv(getNode('ordinary-fork-promptless'), { forkFrom: forkFile }).argv, 'a promptless fork has no trailing positional prompt');
|
|
448
|
+
createNode(node('ordinary-fork-goal', { cwd: home, fork_from: forkFile }));
|
|
449
|
+
writeGoal('ordinary-fork-goal', 'continue the forked task');
|
|
450
|
+
const goal = readGoal('ordinary-fork-goal');
|
|
451
|
+
reviveNode('ordinary-fork-goal', { resume: false });
|
|
452
|
+
const goalCarrying = launches[1];
|
|
453
|
+
const goalFork = goalCarrying.indexOf('--fork');
|
|
454
|
+
assert.equal(goalCarrying[goalFork + 1], forkFile, 'an unbound ordinary fork uses its immutable source regardless of requested resume mode');
|
|
455
|
+
assert.equal(goalCarrying.at(-1), goal, 'a goal-carrying fork preserves its exact goal as the trailing prompt');
|
|
487
456
|
}
|
|
488
457
|
finally {
|
|
489
458
|
headlessBrokerHost.launch = originalLaunch;
|
|
@@ -19,6 +19,32 @@ function userMsg(id, parentId, text) {
|
|
|
19
19
|
message: { role: 'user', content: text, timestamp: 0 },
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
function assistantMsg(id, parentId, content, stopReason, errorMessage) {
|
|
23
|
+
return {
|
|
24
|
+
type: 'message',
|
|
25
|
+
id,
|
|
26
|
+
parentId,
|
|
27
|
+
timestamp: new Date(0).toISOString(),
|
|
28
|
+
message: { role: 'assistant', content, stopReason, errorMessage, timestamp: 0 },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function cycleManager(entries, currentId) {
|
|
32
|
+
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
33
|
+
const branchFrom = (fromId) => {
|
|
34
|
+
const path = [];
|
|
35
|
+
let current = byId.get(fromId);
|
|
36
|
+
while (current) {
|
|
37
|
+
path.push(current);
|
|
38
|
+
current = current.parentId ? byId.get(current.parentId) : undefined;
|
|
39
|
+
}
|
|
40
|
+
return path.reverse();
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
buildSessionContext: () => buildSessionContext(entries, currentId, byId),
|
|
44
|
+
getEntries: () => entries,
|
|
45
|
+
getBranch: (fromId) => branchFrom(fromId ?? currentId),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
22
48
|
test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContext (one summary, no raw pre-compaction messages)', () => {
|
|
23
49
|
// Cycle 0: m1 -> m2 -> [compaction c1, keeping from m2] -> m3 (cycle 0's leaf
|
|
24
50
|
// at yield time). Cycle marker cy1 roots a FRESH branch (parentId: null, per
|
|
@@ -46,25 +72,9 @@ test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContex
|
|
|
46
72
|
};
|
|
47
73
|
const m4 = userMsg('m4', 'cy1', 'cycle 1 live message');
|
|
48
74
|
const entries = [m1, m2, c1, m3, cy1, m4];
|
|
49
|
-
const byId = new Map(entries.map((e) => [e.id, e]));
|
|
50
|
-
function branchFrom(fromId) {
|
|
51
|
-
const path = [];
|
|
52
|
-
let current = fromId ? byId.get(fromId) : undefined;
|
|
53
|
-
while (current) {
|
|
54
|
-
path.push(current);
|
|
55
|
-
current = current.parentId ? byId.get(current.parentId) : undefined;
|
|
56
|
-
}
|
|
57
|
-
return path.reverse();
|
|
58
|
-
}
|
|
59
75
|
// Minimal fake mirroring the real SDK's SessionManager just enough to drive
|
|
60
|
-
// cycleAwareMessages: buildSessionContext()
|
|
61
|
-
|
|
62
|
-
const sm = {
|
|
63
|
-
buildSessionContext: () => buildSessionContext(entries, 'm4', byId),
|
|
64
|
-
getEntries: () => entries,
|
|
65
|
-
getBranch: (fromId) => branchFrom(fromId ?? 'm4'),
|
|
66
|
-
};
|
|
67
|
-
const out = cycleAwareMessages(sm);
|
|
76
|
+
// cycleAwareMessages: buildSessionContext() plus the tree accessors it reads.
|
|
77
|
+
const out = cycleAwareMessages(cycleManager(entries, 'm4'));
|
|
68
78
|
const summaries = out.filter((m) => m.role === 'compactionSummary');
|
|
69
79
|
assert.equal(summaries.length, 1, 'exactly one compactionSummary message from the replayed prior cycle');
|
|
70
80
|
const texts = out.map((m) => (typeof m.content === 'string' ? m.content : ''));
|
|
@@ -75,3 +85,23 @@ test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContex
|
|
|
75
85
|
const dividers = out.filter((m) => m.customType === 'crtr-cycle-divider');
|
|
76
86
|
assert.equal(dividers.length, 1, 'one divider between the replayed prior cycle and the live one');
|
|
77
87
|
});
|
|
88
|
+
test('cycleAwareMessages: a yielded cycle keeps terminal assistant content without an abort notice', () => {
|
|
89
|
+
const m1 = userMsg('m1', null, 'yield after this turn');
|
|
90
|
+
const toolCall = { type: 'toolCall', id: 'yield-call', name: 'bash', arguments: { command: 'crtr node yield' } };
|
|
91
|
+
const m2 = assistantMsg('m2', 'm1', [toolCall], 'aborted', 'Request was aborted');
|
|
92
|
+
const cy1 = {
|
|
93
|
+
type: 'custom',
|
|
94
|
+
id: 'cy1',
|
|
95
|
+
parentId: null,
|
|
96
|
+
timestamp: new Date(1).toISOString(),
|
|
97
|
+
customType: CRTR_CYCLE_CUSTOM_TYPE,
|
|
98
|
+
data: { cycle: 2, fromLeaf: 'm2' },
|
|
99
|
+
};
|
|
100
|
+
const m3 = userMsg('m3', 'cy1', 'fresh cycle');
|
|
101
|
+
const out = cycleAwareMessages(cycleManager([m1, m2, cy1, m3], 'm3'));
|
|
102
|
+
const yieldedAssistant = out.find((message) => message.role === 'assistant');
|
|
103
|
+
assert.deepEqual(yieldedAssistant.content, [toolCall], 'the yielded turn remains available to transcript renderers');
|
|
104
|
+
assert.equal(yieldedAssistant.stopReason, undefined, 'yield is not presented as a failed assistant turn');
|
|
105
|
+
assert.equal(yieldedAssistant.errorMessage, undefined, 'the provider abort text is omitted with the yield boundary');
|
|
106
|
+
assert.equal(out.filter((message) => message.customType === 'crtr-cycle-divider').length, 1);
|
|
107
|
+
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// spawnChild({ prompt: '' }) — the no-kickoff path `node new --root --no-kickoff`
|
|
2
|
-
// rides.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// (when forking) without an identity-reassertion turn either.
|
|
2
|
+
// rides. An empty prompt reaches buildPiArgv as `undefined`-equivalent, so the
|
|
3
|
+
// broker comes up idle and goal-less. Fork identity is carried by the canonical
|
|
4
|
+
// session-start bearings message rather than by either kickoff shape.
|
|
6
5
|
import { test, before, after, beforeEach } from 'node:test';
|
|
7
6
|
import assert from 'node:assert/strict';
|
|
8
7
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
@@ -82,7 +81,7 @@ test('no-kickoff root: no positional prompt reaches pi argv, and no goal is pers
|
|
|
82
81
|
rmSync(cwdWithKickoff, { recursive: true, force: true });
|
|
83
82
|
}
|
|
84
83
|
});
|
|
85
|
-
test('
|
|
84
|
+
test('fork-from keeps identity out of the task prompt with or without kickoff', async () => {
|
|
86
85
|
const cwdNoKickoff = mkdtempSync(join(tmpdir(), 'crtr-no-kickoff-fork-cwd-'));
|
|
87
86
|
const cwdWithKickoff = mkdtempSync(join(tmpdir(), 'crtr-kickoff-fork-cwd-'));
|
|
88
87
|
const seedPathNoKickoff = join(cwdNoKickoff, 'seed.jsonl');
|
|
@@ -101,14 +100,11 @@ test('no-kickoff + fork-from: the forked history rides through with NO identity-
|
|
|
101
100
|
assert.ok(argvNoKickoff.includes(seedPathNoKickoff), 'the resolved seed path is the no-kickoff fork source');
|
|
102
101
|
assert.ok(argvWithKickoff.includes('--fork'), 'the real-kickoff fork source still rides through to pi');
|
|
103
102
|
assert.ok(argvWithKickoff.includes(seedPathWithKickoff), 'the resolved seed path is the real-kickoff fork source');
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// no-kickoff fork gets neither that block nor any prompt at all, since no
|
|
107
|
-
// turn ever runs at spawn to carry it.
|
|
103
|
+
// A real kickoff carries exactly the user's task. The session-start
|
|
104
|
+
// bearings message owns fork identity for both prompt shapes.
|
|
108
105
|
const trailingWithKickoff = argvWithKickoff[argvWithKickoff.length - 1];
|
|
109
|
-
assert.
|
|
110
|
-
assert.
|
|
111
|
-
assert.notEqual(argvNoKickoff[argvNoKickoff.length - 1], trailingWithKickoff, 'the no-kickoff fork carries no identity/kickoff turn at all');
|
|
106
|
+
assert.equal(trailingWithKickoff, 'continue please', 'the real fork kickoff is the unmodified user task');
|
|
107
|
+
assert.notEqual(argvNoKickoff[argvNoKickoff.length - 1], trailingWithKickoff, 'the no-kickoff fork carries no task prompt');
|
|
112
108
|
}
|
|
113
109
|
finally {
|
|
114
110
|
cap.restore();
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
// final, so it may go dormant regardless of whether it has a parent.
|
|
6
6
|
import { test, before, after, beforeEach } from 'node:test';
|
|
7
7
|
import assert from 'node:assert/strict';
|
|
8
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
8
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { tmpdir } from 'node:os';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { createNode, deliverAndSettleMessageWait, getNode, subscribe, setMessageWait, setStatus } from '../canvas/canvas.js';
|
|
12
12
|
import { closeDb, openDb } from '../canvas/db.js';
|
|
13
|
+
import { contextDir } from '../canvas/paths.js';
|
|
14
|
+
import { bashJobPaths } from '../bash-jobs.js';
|
|
13
15
|
import { evaluateStop } from '../runtime/stop-guard.js';
|
|
14
16
|
import { writeOutputSchema } from '../runtime/structured-output.js';
|
|
15
17
|
let home;
|
|
@@ -67,6 +69,19 @@ test('a TERMINAL node with nothing live to await and no final pushed is reprompt
|
|
|
67
69
|
assert.equal(d.action, 'reprompt');
|
|
68
70
|
assert.equal(d.reason, 'stalled');
|
|
69
71
|
});
|
|
72
|
+
test('a TERMINAL node awaiting a background bash job may dorm until its completion message', () => {
|
|
73
|
+
createNode(node('bashWait', { parent: 'mgr', lifecycle: 'terminal' }));
|
|
74
|
+
const paths = bashJobPaths(contextDir('bashWait'), `${Date.now()}-fixture`);
|
|
75
|
+
mkdirSync(paths.dir, { recursive: true });
|
|
76
|
+
writeFileSync(paths.cmdSh, 'long-running command');
|
|
77
|
+
writeFileSync(paths.jobLog, '');
|
|
78
|
+
writeFileSync(paths.jobBg, '');
|
|
79
|
+
assert.deepEqual(evaluateStop('bashWait', noSignals), { action: 'allow', reason: 'awaiting' });
|
|
80
|
+
writeFileSync(paths.jobExit, '0');
|
|
81
|
+
const completed = evaluateStop('bashWait', noSignals);
|
|
82
|
+
assert.equal(completed.action, 'reprompt');
|
|
83
|
+
assert.equal(completed.reason, 'stalled');
|
|
84
|
+
});
|
|
70
85
|
test('only an explicit live controller wait permits dormancy; ancestry alone does not', () => {
|
|
71
86
|
createNode(node('parent', { lifecycle: 'terminal', status: 'active' }));
|
|
72
87
|
createNode(node('child', { parent: 'parent' }));
|