@north-light/crouter 0.3.311 → 0.3.313
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 +27 -30
- package/dist/api/client.js +80 -67
- package/dist/api/dto/broker-ops.d.ts +10 -20
- package/dist/api/dto/canvas.d.ts +1 -10
- package/dist/api/dto/health.d.ts +113 -0
- package/dist/api/dto/human-requests.d.ts +14 -3
- package/dist/api/dto/human.d.ts +2 -48
- package/dist/api/dto/human.js +3 -2
- package/dist/api/dto/inbox.d.ts +14 -0
- package/dist/api/dto/mail.d.ts +43 -0
- package/dist/api/dto/nodes.d.ts +11 -8
- package/dist/api/dto/recovery.d.ts +96 -0
- package/dist/api/dto/reviews.d.ts +2 -2
- package/dist/api/routes.d.ts +9 -6
- package/dist/api/routes.js +11 -11
- package/dist/build-root.d.ts +4 -0
- package/dist/build-root.js +11 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
- package/dist/cli.js +10 -1
- package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
- package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
- package/dist/clients/attach/chrome/canvas-panels.js +1 -1
- package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
- package/dist/clients/attach/chrome/inbox-strip.js +17 -9
- package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
- package/dist/clients/attach/chrome/ticket-panel.js +77 -46
- package/dist/clients/attach/overlays/graph.js +6 -9
- package/dist/clients/attach/render/chat-view.d.ts +5 -4
- package/dist/clients/attach/render/chat-view.js +22 -13
- package/dist/clients/attach/render/page-block.d.ts +11 -7
- package/dist/clients/attach/render/page-block.js +64 -55
- package/dist/clients/attach/viewer.js +746 -735
- package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
- package/dist/clients/inbox/controller.d.ts +42 -14
- package/dist/clients/inbox/controller.js +240 -124
- package/dist/clients/inbox/dto-adapt.d.ts +28 -0
- package/dist/clients/inbox/dto-adapt.js +93 -0
- package/dist/clients/inbox/page-adapter.d.ts +5 -4
- package/dist/clients/inbox/page-adapter.js +12 -12
- package/dist/clients/inbox/resolve.d.ts +10 -5
- package/dist/clients/inbox/resolve.js +13 -12
- package/dist/clients/inbox/review/review-client.d.ts +0 -1
- package/dist/clients/inbox/review/review-client.js +0 -4
- package/dist/clients/inbox/review-adapter.d.ts +2 -1
- package/dist/clients/inbox/review-adapter.js +3 -5
- package/dist/clients/inbox/surface.d.ts +2 -2
- package/dist/clients/inbox/surface.js +19 -8
- package/dist/clients/inbox/tui.d.ts +3 -3
- package/dist/commands/__tests__/human.test.js +31 -15
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
- package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
- package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
- package/dist/commands/api-client.d.ts +5 -4
- package/dist/commands/api-client.js +13 -7
- package/dist/commands/canvas-history/shared.js +1 -1
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-roster.js +1 -1
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/human/feedback.js +20 -17
- package/dist/commands/human/prompts.js +45 -36
- package/dist/commands/human/queue.js +73 -123
- package/dist/commands/human/review.js +6 -16
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
- package/dist/commands/node-inspect-artifacts.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +2 -2
- package/dist/commands/node-lifecycle-revive.js +9 -19
- package/dist/commands/pkg/plugin-manage.js +0 -1
- package/dist/commands/profile/show.js +4 -12
- package/dist/commands/surface-tmux-spread.js +1 -5
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
- package/dist/commands/sys/branch.d.ts +2 -0
- package/dist/commands/sys/branch.js +19 -0
- package/dist/commands/sys/config.js +106 -33
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/sys/daemon.js +130 -7
- package/dist/commands/sys/doctor.js +5 -15
- package/dist/commands/sys/migrate.js +21 -92
- package/dist/commands/sys/setup-settings-runner.js +4 -2
- package/dist/commands/sys.js +2 -15
- package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
- package/dist/core/__tests__/boot.test.js +32 -25
- package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
- package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
- package/dist/core/__tests__/canvas.test.js +18 -47
- package/dist/core/__tests__/child-death-wake.test.js +29 -7
- package/dist/core/__tests__/cli-help-contract.test.js +2 -0
- package/dist/core/__tests__/close.test.js +29 -10
- package/dist/core/__tests__/collectible.test.js +2 -1
- package/dist/core/__tests__/config-change-delta.test.js +2 -0
- package/dist/core/__tests__/context-intro.test.js +17 -5
- package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
- package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
- package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
- package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
- package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
- package/dist/core/__tests__/daemon-boot.test.js +51 -14
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
- package/dist/core/__tests__/focuses.test.js +10 -5
- package/dist/core/__tests__/helpers/harness.js +50 -18
- package/dist/core/__tests__/history-inbox.test.js +12 -16
- package/dist/core/__tests__/human-action-delivery.test.js +48 -28
- package/dist/core/__tests__/human-actions.test.js +7 -3
- package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
- package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
- package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
- package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
- package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +312 -1
- package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
- package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
- package/dist/core/__tests__/integration/revive.test.js +67 -21
- package/dist/core/__tests__/integration/spawn-root.test.js +62 -19
- package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
- package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
- package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
- package/dist/core/__tests__/kickoff.test.js +36 -39
- package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
- package/dist/core/__tests__/lifecycle.test.js +2 -1
- package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
- package/dist/core/__tests__/migration.test.js +145 -31
- package/dist/core/__tests__/model-pin-durability.test.js +4 -1
- package/dist/core/__tests__/model-routes-config.test.js +13 -5
- package/dist/core/__tests__/node-outcome.test.js +93 -26
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +4 -3
- package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
- package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
- package/dist/core/__tests__/passive-subscription.test.js +50 -157
- package/dist/core/__tests__/plugin-kinds.test.js +6 -4
- package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
- package/dist/core/__tests__/plugin-page-components.test.js +6 -4
- package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +35 -32
- package/dist/core/__tests__/prune.test.js +11 -5
- package/dist/core/__tests__/push-final-guard.test.js +14 -7
- package/dist/core/__tests__/relaunch-root.test.js +30 -2
- package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
- package/dist/core/__tests__/repository-root-identity.test.js +4 -0
- package/dist/core/__tests__/review-model-floor.test.js +6 -3
- package/dist/core/__tests__/revive-all.test.js +13 -3
- package/dist/core/__tests__/revive-capacity.test.js +26 -4
- package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
- package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
- package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
- package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
- package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +5 -4
- package/dist/core/__tests__/stop-guard.test.js +2 -1
- package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +19 -5
- package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
- package/dist/core/broker-client/transport-socket.d.ts +1 -0
- package/dist/core/broker-client/transport-socket.js +13 -5
- package/dist/core/canvas/__tests__/attention.test.js +29 -35
- package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
- package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
- package/dist/core/canvas/attention.d.ts +2 -5
- package/dist/core/canvas/attention.js +15 -64
- package/dist/core/canvas/boot.d.ts +6 -7
- package/dist/core/canvas/boot.js +5 -9
- package/dist/core/canvas/canvas.d.ts +13 -109
- package/dist/core/canvas/canvas.js +140 -401
- package/dist/core/canvas/crons.d.ts +2 -0
- package/dist/core/canvas/crons.js +16 -15
- package/dist/core/canvas/daemon-owner.js +10 -0
- package/dist/core/canvas/db.d.ts +2 -2
- package/dist/core/canvas/db.js +2 -18
- package/dist/core/canvas/deliveries.d.ts +104 -0
- package/dist/core/canvas/deliveries.js +354 -0
- package/dist/core/canvas/executions.d.ts +29 -0
- package/dist/core/canvas/executions.js +68 -0
- package/dist/core/canvas/history.js +35 -35
- package/dist/core/canvas/index.d.ts +3 -1
- package/dist/core/canvas/index.js +3 -1
- package/dist/core/canvas/migrations.js +20 -12
- package/dist/core/canvas/nav-model.js +1 -3
- package/dist/core/canvas/nav-render.d.ts +4 -4
- package/dist/core/canvas/nav-render.js +11 -21
- package/dist/core/canvas/node-sql-migration.d.ts +3 -0
- package/dist/core/canvas/node-sql-migration.js +261 -0
- package/dist/core/canvas/paths.d.ts +2 -16
- package/dist/core/canvas/paths.js +2 -31
- package/dist/core/canvas/recovery.d.ts +76 -0
- package/dist/core/canvas/recovery.js +588 -0
- package/dist/core/canvas/remote-canvas-source.js +4 -0
- package/dist/core/canvas/render-source.d.ts +16 -27
- package/dist/core/canvas/render-source.js +16 -45
- package/dist/core/canvas/sessions.d.ts +33 -0
- package/dist/core/canvas/sessions.js +78 -0
- package/dist/core/canvas/types.d.ts +33 -33
- package/dist/core/canvas/types.js +2 -5
- package/dist/core/canvas/waits.d.ts +18 -0
- package/dist/core/canvas/waits.js +70 -0
- package/dist/core/canvas/worktrees.d.ts +40 -0
- package/dist/core/canvas/worktrees.js +93 -0
- package/dist/core/command-hooks/artifact.d.ts +6 -0
- package/dist/core/command-hooks/artifact.js +22 -6
- package/dist/core/command-hooks/discovery.d.ts +2 -0
- package/dist/core/command-hooks/discovery.js +2 -0
- package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
- package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
- package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
- package/dist/core/command-hooks/lifecycle-create.js +51 -0
- package/dist/core/command-hooks/schema.d.ts +2 -2
- package/dist/core/command-hooks/schema.js +5 -4
- package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
- package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
- package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +4 -0
- package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
- package/dist/core/feed/feed.d.ts +0 -6
- package/dist/core/feed/feed.js +68 -56
- package/dist/core/feed/inbox.d.ts +7 -81
- package/dist/core/feed/inbox.js +46 -378
- package/dist/core/feed/messages.d.ts +58 -0
- package/dist/core/feed/messages.js +217 -0
- package/dist/core/fs-utils.d.ts +4 -0
- package/dist/core/fs-utils.js +21 -1
- package/dist/core/human/answer.d.ts +1 -1
- package/dist/core/human/answer.js +1 -1
- package/dist/core/human/claim.d.ts +1 -1
- package/dist/core/human/claim.js +2 -4
- package/dist/core/human/component-docs.js +1 -1
- package/dist/core/human/feedback-companion.js +4 -3
- package/dist/core/human/feedback.d.ts +5 -8
- package/dist/core/human/feedback.js +57 -85
- package/dist/core/human/page-authoring.d.ts +30 -0
- package/dist/core/human/page-authoring.js +19 -0
- package/dist/core/human/requests.d.ts +136 -0
- package/dist/core/human/requests.js +515 -0
- package/dist/core/human/root.d.ts +0 -7
- package/dist/core/human/root.js +0 -17
- package/dist/core/human/scan.d.ts +2 -7
- package/dist/core/human/scan.js +48 -132
- package/dist/core/human/summary.d.ts +1 -1
- package/dist/core/human/summary.js +1 -1
- package/dist/core/human/tickets.d.ts +23 -83
- package/dist/core/human/tickets.js +92 -243
- package/dist/core/human/types.d.ts +5 -1
- package/dist/core/installed-plugins.js +8 -3
- package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
- package/dist/core/locked-file.d.ts +6 -3
- package/dist/core/locked-file.js +36 -9
- package/dist/core/memory/extensions.js +2 -0
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/profiles/__tests__/env-store.test.js +2 -0
- package/dist/core/profiles/default-binding.d.ts +0 -4
- package/dist/core/profiles/default-binding.js +12 -58
- package/dist/core/profiles/env-store.js +8 -3
- package/dist/core/profiles/manifest.d.ts +2 -37
- package/dist/core/profiles/manifest.js +35 -161
- package/dist/core/profiles/select.js +3 -0
- package/dist/core/review/__tests__/capture-origin.test.js +13 -2
- package/dist/core/review/__tests__/comments.test.js +28 -10
- package/dist/core/review/__tests__/remap.test.js +28 -10
- package/dist/core/review/__tests__/stage-identity.test.js +17 -7
- package/dist/core/review/birth.d.ts +1 -1
- package/dist/core/review/birth.js +34 -5
- package/dist/core/review/comments.js +1 -1
- package/dist/core/review/companion.d.ts +1 -1
- package/dist/core/review/companion.js +2 -2
- package/dist/core/review/document.js +1 -1
- package/dist/core/review/project.d.ts +5 -1
- package/dist/core/review/project.js +14 -36
- package/dist/core/review/realize.d.ts +3 -6
- package/dist/core/review/realize.js +51 -168
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +3 -2
- package/dist/core/review/stage.js +2 -3
- package/dist/core/review/store.d.ts +3 -21
- package/dist/core/review/store.js +140 -186
- package/dist/core/review/types.d.ts +0 -1
- package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
- package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
- package/dist/core/runtime/broker/auth-reload.js +9 -10
- package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
- package/dist/core/runtime/broker/daemon-ops.js +42 -9
- package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
- package/dist/core/runtime/broker/extension-abort.js +11 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
- package/dist/core/runtime/broker/fault-retry.js +142 -95
- package/dist/core/runtime/broker/inbox.d.ts +4 -32
- package/dist/core/runtime/broker/inbox.js +4 -92
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +21 -12
- package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -13
- package/dist/core/runtime/broker.js +28 -22
- package/dist/core/runtime/close.d.ts +3 -9
- package/dist/core/runtime/close.js +16 -27
- package/dist/core/runtime/fault.d.ts +3 -93
- package/dist/core/runtime/fault.js +9 -419
- package/dist/core/runtime/kickoff.d.ts +3 -6
- package/dist/core/runtime/kickoff.js +12 -18
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +40 -7
- package/dist/core/runtime/nodes.d.ts +5 -1
- package/dist/core/runtime/nodes.js +26 -11
- package/dist/core/runtime/placement.js +2 -2
- package/dist/core/runtime/recycle.js +24 -14
- package/dist/core/runtime/reset.js +26 -7
- package/dist/core/runtime/revive.js +226 -217
- package/dist/core/runtime/spawn.d.ts +4 -1
- package/dist/core/runtime/spawn.js +43 -5
- package/dist/core/runtime/warm-pool.d.ts +2 -2
- package/dist/core/runtime/warm-pool.js +23 -3
- package/dist/core/scoped-state/db.d.ts +7 -0
- package/dist/core/scoped-state/db.js +92 -0
- package/dist/core/scoped-state/migrate.d.ts +11 -0
- package/dist/core/scoped-state/migrate.js +198 -0
- package/dist/core/scoped-state/paths.d.ts +9 -0
- package/dist/core/scoped-state/paths.js +27 -0
- package/dist/core/scoped-state/profiles.d.ts +12 -0
- package/dist/core/scoped-state/profiles.js +62 -0
- package/dist/core/scoped-state/providers.d.ts +16 -0
- package/dist/core/scoped-state/providers.js +8 -0
- package/dist/core/scoped-state/schema.d.ts +3 -0
- package/dist/core/scoped-state/schema.js +26 -0
- package/dist/core/scoped-state/settings.d.ts +28 -0
- package/dist/core/scoped-state/settings.js +82 -0
- package/dist/core/subscription-state.d.ts +29 -48
- package/dist/core/subscription-state.js +176 -712
- package/dist/core/worktree-close.js +4 -4
- package/dist/core/worktree-mutation-async.js +2 -2
- package/dist/core/worktree-sweep.js +11 -16
- package/dist/core/worktree.js +6 -12
- package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
- package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
- package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
- package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
- package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
- package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
- package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
- package/dist/daemon/api/__tests__/model-config.test.js +8 -8
- package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
- package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
- package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
- package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
- package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
- package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +6 -7
- package/dist/daemon/api/handlers/broker-ops.js +47 -84
- package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
- package/dist/daemon/api/handlers/broker-recovery.js +251 -0
- package/dist/daemon/api/handlers/canvas.js +2 -44
- package/dist/daemon/api/handlers/daemon.js +41 -0
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/health.js +10 -5
- package/dist/daemon/api/handlers/human-requests.js +168 -188
- package/dist/daemon/api/handlers/human.js +3 -148
- package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
- package/dist/daemon/api/handlers/inbox-page.js +89 -0
- package/dist/daemon/api/handlers/inbox.js +21 -120
- package/dist/daemon/api/handlers/messages.d.ts +1 -0
- package/dist/daemon/api/handlers/messages.js +73 -15
- package/dist/daemon/api/handlers/node-outcomes.js +58 -13
- package/dist/daemon/api/handlers/nodes.d.ts +1 -0
- package/dist/daemon/api/handlers/nodes.js +41 -42
- package/dist/daemon/api/handlers/reviews.js +4 -29
- package/dist/daemon/api/map.d.ts +25 -3
- package/dist/daemon/api/map.js +84 -14
- package/dist/daemon/api/server.js +68 -9
- package/dist/daemon/control.d.ts +18 -0
- package/dist/daemon/cron/disposition.d.ts +2 -0
- package/dist/daemon/cron/disposition.js +8 -5
- package/dist/daemon/cron/settle.js +37 -13
- package/dist/daemon/cron/sinks.d.ts +18 -1
- package/dist/daemon/cron/sinks.js +47 -93
- package/dist/daemon/crtrd-cli.js +4 -19
- package/dist/daemon/crtrd.d.ts +2 -0
- package/dist/daemon/crtrd.js +206 -81
- package/dist/daemon/fleet.js +5 -6
- package/dist/daemon/human/finish.d.ts +12 -24
- package/dist/daemon/human/finish.js +88 -285
- package/dist/daemon/human/settle.d.ts +40 -0
- package/dist/daemon/human/settle.js +89 -0
- package/dist/daemon/human/sweep.d.ts +20 -5
- package/dist/daemon/human/sweep.js +56 -60
- package/dist/daemon/manage.d.ts +21 -16
- package/dist/daemon/manage.js +85 -51
- package/dist/daemon/messaging/node-message.d.ts +1 -1
- package/dist/daemon/messaging/node-message.js +9 -12
- package/dist/daemon/park-activity.js +3 -3
- package/dist/daemon/reconcilers/bash-deadline.js +6 -7
- package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
- package/dist/daemon/reconcilers/broker-supervision.js +41 -19
- package/dist/daemon/reconcilers/controller-death.js +6 -18
- package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/delivery-lane.js +130 -0
- package/dist/daemon/reconcilers/live-obligation.js +3 -3
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
- package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
- package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
- package/dist/daemon/review/companion.js +5 -11
- package/dist/daemon/review/deliver.d.ts +1 -5
- package/dist/daemon/review/deliver.js +3 -52
- package/dist/daemon/review/finish.d.ts +2 -27
- package/dist/daemon/review/finish.js +79 -263
- package/dist/daemon/review/sweep.d.ts +3 -10
- package/dist/daemon/review/sweep.js +13 -131
- package/dist/daemon/startup-policy.d.ts +0 -5
- package/dist/daemon/startup-policy.js +0 -5
- package/dist/hook-authoring.d.ts +28 -2
- package/dist/hook-authoring.js +53 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
- package/dist/migrations/__tests__/activation.test.js +9 -6
- package/dist/migrations/activation.d.ts +3 -2
- package/dist/migrations/activation.js +2 -4
- package/dist/migrations/corpus.d.ts +2 -5
- package/dist/migrations/corpus.js +9 -20
- package/dist/migrations/runner.d.ts +3 -1
- package/dist/migrations/runner.js +6 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
- package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
- package/dist/pi-extensions/canvas-passive-context.js +85 -37
- package/dist/pi-extensions/canvas-stophook.js +47 -31
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.js +3 -0
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
- package/dist/clients/attach/__tests__/page-block.test.js +0 -54
- package/dist/clients/inbox/review-visibility.d.ts +0 -5
- package/dist/clients/inbox/review-visibility.js +0 -19
- package/dist/commands/canvas-rebuild-index.d.ts +0 -2
- package/dist/commands/canvas-rebuild-index.js +0 -46
- package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
- package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
- package/dist/core/__tests__/human-deliver.test.js +0 -256
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
- package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
- package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
- package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
- package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-store.test.js +0 -144
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
- package/dist/core/canvas/human-deliveries.d.ts +0 -39
- package/dist/core/canvas/human-deliveries.js +0 -75
- package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
- package/dist/core/canvas/node-outcome-deliveries.js +0 -159
- package/dist/core/feed/passive.d.ts +0 -3
- package/dist/core/feed/passive.js +0 -53
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
- package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
- package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
- package/dist/core/human/__tests__/page-tickets.test.js +0 -161
- package/dist/core/review/ticket-filter.d.ts +0 -17
- package/dist/core/review/ticket-filter.js +0 -21
- package/dist/core/runtime/broker/passive.d.ts +0 -17
- package/dist/core/runtime/broker/passive.js +0 -44
- package/dist/core/runtime/busy.d.ts +0 -46
- package/dist/core/runtime/busy.js +0 -150
- package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
- package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
- package/dist/daemon/human/deliver-action.d.ts +0 -3
- package/dist/daemon/human/deliver-action.js +0 -9
- package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
- package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
- package/dist/daemon/startup-block-marker.d.ts +0 -25
- package/dist/daemon/startup-block-marker.js +0 -99
- /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
- /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
- /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
|
@@ -46,7 +46,7 @@ export const filterParams = [
|
|
|
46
46
|
{ kind: 'flag', name: 'all-cwds', type: 'bool', required: false, constraint: 'Search every cwd on the canvas. Mutually exclusive with --cwd.' },
|
|
47
47
|
{ kind: 'flag', name: 'under', type: 'string', required: false, constraint: 'Restrict to a node and its subscription descendants (one initiative / sub-DAG).' },
|
|
48
48
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Restrict to specific node ids. Comma-separated for several.' },
|
|
49
|
-
{ kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind; inbox =
|
|
49
|
+
{ kind: 'flag', name: 'type', type: 'string', required: false, constraint: `Corpus to search; comma-separated for several. One of: ${TYPES.join(', ')}. report = push history; doc = context artifacts; roadmap = roadmap.md specifically; meta = node name/description/kind; inbox = inbox history including full un-clipped direct-message bodies (diagnostic, not curated work). Default: report, doc, roadmap, meta \u2014 inbox is opt-in only, pass --type inbox explicitly.` },
|
|
50
50
|
{ kind: 'flag', name: 'report-kind', type: 'enum', choices: ['final', 'update'], required: false, constraint: 'Narrow reports to outcome summaries (final) or progress (update). Implies --type report.' },
|
|
51
51
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: `Node kind; comma-separated for several. One of: ${NODE_KINDS.join(', ')}.` },
|
|
52
52
|
{ kind: 'flag', name: 'status', type: 'string', required: false, constraint: `Node lifecycle status; comma-separated for several. One of: ${STATUSES.join(', ')} (e.g. done = completed work only).` },
|
|
@@ -25,7 +25,7 @@ export const historyBranch = defineBranch({
|
|
|
25
25
|
help: {
|
|
26
26
|
name: 'canvas history',
|
|
27
27
|
summary: 'search and recall the cwd-wide content corpus — every node\'s reports and context docs in this cwd',
|
|
28
|
-
model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries), context docs (specs, designs, roadmaps, findings), and the conversations themselves. `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `grep` finds by an exact ECMAScript regex over body lines (pattern required, one hit per matching line — use this over `search` when you know the literal string/pattern, not just the topic); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref, `--format raw` for the verbatim bytes; `stats` counts the same filtered corpus by one dimension instead of listing hits. Corpus is a DIMENSION here, not a location: `--type` selects which record you search, and every leaf takes the same scope and filters. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. Already have a node id and want everything IT left behind (not a cwd-wide search)? Use `node inspect artifacts <node-id>` instead. Two corpora are opt-in diagnostics rather than curated work: `--type inbox` is one node\'s full
|
|
28
|
+
model: 'The accumulated record of every node that ran in a cwd and the artifacts it left — its reports (final/update outcome summaries), context docs (specs, designs, roadmaps, findings), and the conversations themselves. `search` finds by content (ranked, filtered, sorted; omit the query to browse by recency); `grep` finds by an exact ECMAScript regex over body lines (pattern required, one hit per matching line — use this over `search` when you know the literal string/pattern, not just the topic); `read` pulls one hit\'s full body by its <node-id>:<relpath> ref, `--format raw` for the verbatim bytes; `stats` counts the same filtered corpus by one dimension instead of listing hits. Corpus is a DIMENSION here, not a location: `--type` selects which record you search, and every leaf takes the same scope and filters. This is the episodic record of what was DONE — distinct from `crtr memory`, which is curated semantic knowledge. Already have a node id and want everything IT left behind (not a cwd-wide search)? Use `node inspect artifacts <node-id>` instead. Two corpora are opt-in diagnostics rather than curated work: `--type inbox` is one node\'s full inbox history including un-clipped message bodies, and `--type transcript` is the conversation itself, message by message, each inbound turn carrying the origin that drove it (`--origin`, `--role`, `--from`, and `stats --group-by origin` for the census). Transcripts require a narrowing flag because there is no index. To reopen a node you found, use `node lifecycle revive`; for its place in the graph, `node inspect show`.',
|
|
29
29
|
dynamicState: corpusBlock,
|
|
30
30
|
},
|
|
31
31
|
children: [searchLeaf, grepLeaf, readLeaf, statsLeaf],
|
|
@@ -15,7 +15,7 @@ export const canvasRosterLeaf = defineLeaf({
|
|
|
15
15
|
name: 'nodes',
|
|
16
16
|
type: 'object[]',
|
|
17
17
|
required: true,
|
|
18
|
-
constraint: 'Lean node rows
|
|
18
|
+
constraint: 'Lean node rows plus attention_count for the returned node set.',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
name: 'subscriptions',
|
package/dist/commands/canvas.js
CHANGED
|
@@ -15,7 +15,6 @@ import { reviveLeaf } from './revive.js';
|
|
|
15
15
|
import { attentionBranch } from './attention.js';
|
|
16
16
|
import { chordLeaf } from './chord.js';
|
|
17
17
|
import { canvasPruneLeaf } from './canvas-prune.js';
|
|
18
|
-
import { canvasRebuildIndexLeaf } from './canvas-rebuild-index.js';
|
|
19
18
|
import { historyBranch } from './canvas-history.js';
|
|
20
19
|
import { issueNewLeaf } from './canvas-issue.js';
|
|
21
20
|
import { canvasConfigBranch } from './canvas-config.js';
|
|
@@ -45,6 +44,6 @@ export function registerCanvas() {
|
|
|
45
44
|
summary: 'observe and supervise the whole agent graph',
|
|
46
45
|
model: 'Canvas-wide operations, distinct from per-node work (`node`) and a node\'s own spine write verb (`push`). `dashboard` renders the subscription forest as a tree; `browse` opens an interactive full-screen navigator (tabs/tree/search) over the whole canvas and resumes the chosen node; `attention` aggregates pending replies from the user across the graph; `revive --all` mass-reconnects every disconnected node (bringing back ONE named node is `node lifecycle revive <id>` instead); `history` searches and recalls the content record (reports + context docs) of past work in a cwd; `prune` bounds growth by deleting terminal nodes past a TTL. `config` registers/lists remote canvas targets (preview endpoint + relay token); `use` switches remote-capable read commands such as `node inspect` onto a configured target or back to local.',
|
|
47
46
|
},
|
|
48
|
-
children: [dashboardLeaf, canvasSnapshotLeaf, canvasRosterLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch, chordLeaf, issueBranch, canvasPruneLeaf,
|
|
47
|
+
children: [dashboardLeaf, canvasSnapshotLeaf, canvasRosterLeaf, browseLeaf, attentionBranch, reviveLeaf, historyBranch, chordLeaf, issueBranch, canvasPruneLeaf, canvasConfigBranch, canvasUseLeaf],
|
|
49
48
|
});
|
|
50
49
|
}
|
|
@@ -4,21 +4,24 @@
|
|
|
4
4
|
// Creating comments is the person's channel (the page surface); no agent
|
|
5
5
|
// authors, edits, or deletes them.
|
|
6
6
|
import { envNodeId } from '../../shared/env.js';
|
|
7
|
-
import { existsSync } from 'node:fs';
|
|
8
7
|
import { ApiError } from '../../api/index.js';
|
|
9
8
|
import { defineBranch, defineLeaf } from '../../core/command.js';
|
|
10
|
-
import { pageManifestPath } from '../../core/human/convention.js';
|
|
11
|
-
import { opaqueInboxTicketId, ticketDir } from '../../core/human/root.js';
|
|
12
9
|
import { InputError } from '../../core/io.js';
|
|
13
10
|
import { cliClient, rethrowAsCliError } from '../api-client.js';
|
|
14
|
-
/** Feedback tickets are addressed by
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
/** Feedback tickets are addressed by request identity; crtrd returns their inbox identity. */
|
|
12
|
+
async function requirePageTicketId(raw) {
|
|
13
|
+
const requestId = raw;
|
|
14
|
+
try {
|
|
15
|
+
const request = await cliClient().getHumanRequest(requestId);
|
|
16
|
+
if (request.kind !== 'page')
|
|
17
|
+
throw new InputError({ error: 'not_found', message: `no page ticket: ${requestId}`, field: 'ticket_id', next: 'Pass the ticket id from your bearings, or a page ticket_id from `crtr human list`.' });
|
|
18
|
+
return { requestId, inboxTicketId: request.inbox_ticket_id };
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof InputError)
|
|
22
|
+
throw error;
|
|
23
|
+
rethrowAsCliError(error, 'Pass the ticket id from your bearings, or a page ticket_id from `crtr human list`.');
|
|
20
24
|
}
|
|
21
|
-
return ticketId;
|
|
22
25
|
}
|
|
23
26
|
const humanFeedbackList = defineLeaf({
|
|
24
27
|
name: 'list',
|
|
@@ -39,10 +42,10 @@ const humanFeedbackList = defineLeaf({
|
|
|
39
42
|
effects: ['None. Read-only.'],
|
|
40
43
|
},
|
|
41
44
|
run: async (input) => {
|
|
42
|
-
const
|
|
45
|
+
const ticket = await requirePageTicketId(input['ticket_id']);
|
|
43
46
|
try {
|
|
44
|
-
const page = await cliClient().getHumanInboxPage(
|
|
45
|
-
return { ticket_id:
|
|
47
|
+
const page = await cliClient().getHumanInboxPage(ticket.inboxTicketId);
|
|
48
|
+
return { ticket_id: ticket.requestId, open_count: page.feedback.open_count, comments: page.feedback.comments };
|
|
46
49
|
}
|
|
47
50
|
catch (e) {
|
|
48
51
|
rethrowAsCliError(e);
|
|
@@ -80,14 +83,14 @@ const humanFeedbackResolve = defineLeaf({
|
|
|
80
83
|
if (binding === undefined || binding === null || binding.kind !== 'page_feedback') {
|
|
81
84
|
throw new InputError({ error: 'not_companion', message: 'this node is not a page-feedback companion — only the ticket\u2019s bound companion resolves its comments', next: 'Resolve from the companion conversation bound to the ticket.' });
|
|
82
85
|
}
|
|
83
|
-
const
|
|
86
|
+
const ticket = await requirePageTicketId(binding.review_id);
|
|
84
87
|
try {
|
|
85
|
-
const result = await cliClient().resolvePageFeedbackComment(
|
|
86
|
-
return { resolved: true, ticket_id:
|
|
88
|
+
const result = await cliClient().resolvePageFeedbackComment(ticket.inboxTicketId, commentId, callerNodeId);
|
|
89
|
+
return { resolved: true, ticket_id: ticket.requestId, comment_id: commentId, open_count: result.feedback.open_count };
|
|
87
90
|
}
|
|
88
91
|
catch (e) {
|
|
89
92
|
if (e instanceof ApiError && e.status === 409 && e.code === 'comment_already_resolved') {
|
|
90
|
-
return { resolved: false, ticket_id:
|
|
93
|
+
return { resolved: false, ticket_id: ticket.requestId, comment_id: commentId, reason: 'comment_already_resolved' };
|
|
91
94
|
}
|
|
92
95
|
rethrowAsCliError(e);
|
|
93
96
|
}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import { envNodeId } from '../../shared/env.js';
|
|
2
2
|
import { defineLeaf } from '../../core/command.js';
|
|
3
3
|
import { InputError } from '../../core/io.js';
|
|
4
|
-
import { cliClient } from '../api-client.js';
|
|
4
|
+
import { cliClient, rethrowAsCliError } from '../api-client.js';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
7
|
import { tmpdir } from 'node:os';
|
|
9
8
|
import { resolvePageComponents } from '../../core/config.js';
|
|
10
9
|
import { markStopSignal } from '../../core/runtime/stop-signals.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { preparePage, publishPage, readTicketResult } from '../../core/human/tickets.js';
|
|
14
|
-
import { PageAuthoringError, parsePage } from '../../core/human/page.js';
|
|
15
|
-
import { pageManifestPath } from '../../core/human/convention.js';
|
|
10
|
+
import { preparePage } from '../../core/human/page-authoring.js';
|
|
11
|
+
import { PageAuthoringError } from '../../core/human/page.js';
|
|
16
12
|
import { isResponseBearingSlot, validateAuthoredPageManifest } from '../../core/human/page-schema.js';
|
|
17
13
|
import { display } from '../../core/termrender/display.js';
|
|
18
14
|
import { pageHelpText, resolveMaxPanes } from './shared.js';
|
|
@@ -100,18 +96,19 @@ function submitAuthoredPage(submit) {
|
|
|
100
96
|
});
|
|
101
97
|
}
|
|
102
98
|
}
|
|
103
|
-
/** The page a `--replace` names, read for the
|
|
104
|
-
function replacedPage(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
error: 'unknown_page',
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
/** The page a `--replace` names, read through crtrd for the routing it retains. */
|
|
100
|
+
async function replacedPage(requestId) {
|
|
101
|
+
try {
|
|
102
|
+
const request = await cliClient().getHumanRequest(requestId);
|
|
103
|
+
if (request.kind !== 'page')
|
|
104
|
+
throw new InputError({ error: 'unknown_page', message: `no page ${requestId} on this canvas`, field: 'replace', next: 'Pass the page_id a previous send returned, or omit --replace to raise a new page.' });
|
|
105
|
+
return (await cliClient().getHumanInboxPage(request.inbox_ticket_id)).page;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof InputError)
|
|
109
|
+
throw error;
|
|
110
|
+
rethrowAsCliError(error, 'Pass the page_id a previous send returned, or omit --replace to raise a new page.');
|
|
113
111
|
}
|
|
114
|
-
return parsePage(dir);
|
|
115
112
|
}
|
|
116
113
|
function writePipedPage(document) {
|
|
117
114
|
const dir = mkdtempSync(join(tmpdir(), 'crtr-human-page-'));
|
|
@@ -184,14 +181,14 @@ export function humanSend(pageSurface = false) {
|
|
|
184
181
|
{ name: 'dir', type: 'string', required: true, constraint: pageHelp.dirConstraint },
|
|
185
182
|
{ name: 'placement', type: 'inline | panel', required: true, constraint: 'Resolved page placement.' },
|
|
186
183
|
{ name: 'inbox', type: 'boolean', required: true, constraint: 'Whether the page is projected into the inbox.' },
|
|
187
|
-
{ name: 'reply', type: 'boolean', required: true, constraint: 'Whether completion is
|
|
184
|
+
{ name: 'reply', type: 'boolean', required: true, constraint: 'Whether completion is delivered to the asking request identity. Not a flag: true exactly when the page has response-bearing components.' },
|
|
188
185
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'What happens next; never instructs the caller to poll.' },
|
|
189
186
|
],
|
|
190
187
|
outputKind: 'object',
|
|
191
188
|
effects: [
|
|
192
|
-
'Writes one durable page
|
|
189
|
+
'Writes one durable page request. Inbox projection follows the delivery flags, except from a terminal node, which always projects; completion delivery follows the page\'s own components.',
|
|
193
190
|
"A display-only page's inbox entry is a notice, not a task: it clears itself once the person has actually seen the page, wherever they see it.",
|
|
194
|
-
'A reply-bearing page
|
|
191
|
+
'A reply-bearing page records the asking node on the request and delivers completion directly to that node.',
|
|
195
192
|
'--replace rewrites source in place while retaining delivery and emitted time; open surfaces reload it without creating another page.',
|
|
196
193
|
],
|
|
197
194
|
},
|
|
@@ -207,11 +204,12 @@ export function humanSend(pageSurface = false) {
|
|
|
207
204
|
}
|
|
208
205
|
try {
|
|
209
206
|
if (replacePage !== undefined) {
|
|
210
|
-
const prior = replacedPage(replacePage);
|
|
207
|
+
const prior = await replacedPage(replacePage);
|
|
211
208
|
// The human's submit wins the race with a revision: once the ticket has
|
|
212
209
|
// any terminal outcome, replacement is refused, and that refusal is the
|
|
213
210
|
// reviser's signal that the ticket is closed.
|
|
214
|
-
|
|
211
|
+
const existing = await cliClient().getHumanRequest(replacePage);
|
|
212
|
+
if (existing.state !== 'pending') {
|
|
215
213
|
throw new InputError({
|
|
216
214
|
error: 'page_settled',
|
|
217
215
|
message: `page ${replacePage} already has a terminal outcome`,
|
|
@@ -223,8 +221,8 @@ export function humanSend(pageSurface = false) {
|
|
|
223
221
|
// time stay the original sender's, so the prior source is inherited
|
|
224
222
|
// verbatim — a fork revising the page must not repoint its provenance.
|
|
225
223
|
const revised = submitAuthoredPage(() => preparePage({ sourceFile, source: prior.source, productKinds: pageProductKinds(), delivery: prior.delivery }));
|
|
226
|
-
const
|
|
227
|
-
return { page_id: replacePage, dir, ...prior.delivery, follow_up: 'Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged.' };
|
|
224
|
+
const updated = await cliClient().replaceHumanRequest(replacePage, { page: { dialect: revised.manifest.dialect, source: revised.document } });
|
|
225
|
+
return { page_id: replacePage, dir: updated.request_dir, ...prior.delivery, follow_up: 'Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged.' };
|
|
228
226
|
}
|
|
229
227
|
const { source, lifecycle } = await sender();
|
|
230
228
|
// A terminal node finishes and its transcript stops being read, so the
|
|
@@ -247,8 +245,14 @@ export function humanSend(pageSurface = false) {
|
|
|
247
245
|
const delivery = deliveryFrom(input, responseBearing, lifecycle);
|
|
248
246
|
prepared.manifest = submitAuthoredPage(() => validateAuthoredPageManifest({ ...prepared.manifest, delivery }, productKinds));
|
|
249
247
|
if (!delivery.reply) {
|
|
250
|
-
const
|
|
251
|
-
|
|
248
|
+
const created = await cliClient().createHumanRequest({
|
|
249
|
+
page: { dialect: prepared.manifest.dialect, source: prepared.document },
|
|
250
|
+
delivery,
|
|
251
|
+
source: prepared.manifest.source,
|
|
252
|
+
requester_node_id: null,
|
|
253
|
+
creator_cwd: process.cwd(),
|
|
254
|
+
});
|
|
255
|
+
const dir = created.request_dir;
|
|
252
256
|
// A page with no reply route carries no response-bearing components, so what the
|
|
253
257
|
// follow-up must get right is where the person will meet it and how it
|
|
254
258
|
// settles: an inline page is already in this conversation, and its
|
|
@@ -262,16 +266,21 @@ export function humanSend(pageSurface = false) {
|
|
|
262
266
|
: delivery.inbox
|
|
263
267
|
? 'Queued in the inbox; it opens in the panel and waits until dismissed.'
|
|
264
268
|
: 'Published as passive page content with no inbox or response lifecycle.';
|
|
265
|
-
return { page_id:
|
|
269
|
+
return { page_id: created.request_id, dir, ...delivery, follow_up: followUp };
|
|
266
270
|
}
|
|
267
271
|
const originNodeId = askingNode();
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
if (originNodeId === null || originNodeId === '') {
|
|
273
|
+
throw new InputError({ error: 'no_requester', message: 'a response-bearing page must be sent from a crouter node', field: 'page', next: 'Run `crtr human send` from the node that should receive the answer.' });
|
|
274
|
+
}
|
|
275
|
+
const created = await cliClient().createHumanRequest({
|
|
276
|
+
page: { dialect: prepared.manifest.dialect, source: prepared.document },
|
|
277
|
+
delivery,
|
|
278
|
+
source: prepared.manifest.source,
|
|
279
|
+
requester_node_id: originNodeId,
|
|
280
|
+
creator_cwd: process.cwd(),
|
|
281
|
+
});
|
|
282
|
+
markStopSignal(originNodeId, 'requestedHumanReply');
|
|
283
|
+
return { page_id: created.request_id, dir: created.request_dir, ...delivery, follow_up: queuedFollowUp() };
|
|
275
284
|
}
|
|
276
285
|
finally {
|
|
277
286
|
if (tempDir !== undefined)
|
|
@@ -1,38 +1,26 @@
|
|
|
1
1
|
import { envNodeId } from '../../shared/env.js';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
2
|
import { ApiError } from '../../api/index.js';
|
|
4
3
|
import { defineLeaf } from '../../core/command.js';
|
|
5
|
-
import { ticketDir } from '../../core/human/root.js';
|
|
6
|
-
import { scanInbox } from '../../core/human/scan.js';
|
|
7
|
-
import { pageManifestPath, isResolved, readTicketReplyRoute, reviewPath } from '../../core/human/convention.js';
|
|
8
4
|
import { InputError } from '../../core/io.js';
|
|
9
5
|
import { paginate } from '../../core/pagination.js';
|
|
10
6
|
import { cliClient, rethrowAsCliError } from '../api-client.js';
|
|
11
|
-
import { filterVisibleReviewTickets } from '../../clients/inbox/review-visibility.js';
|
|
12
|
-
function toRef(n) {
|
|
13
|
-
return { node_id: n.node_id, name: n.name, cwd: n.cwd, parent: n.parent, status: n.status };
|
|
14
|
-
}
|
|
15
|
-
/** The canvas roster, fetched ONCE per leaf run through the API and threaded
|
|
16
|
-
* into the sync provenance helpers below (the API surface is async, so the
|
|
17
|
-
* fetch is hoisted to the leaf and the helpers read the pre-fetched
|
|
18
|
-
* array/map). */
|
|
19
7
|
async function fetchNodeRefs() {
|
|
20
|
-
return (await cliClient().listNodes()).map(
|
|
8
|
+
return (await cliClient().listNodes()).map((node) => ({ node_id: node.node_id, name: node.name, cwd: node.cwd, parent: node.parent, status: node.status }));
|
|
21
9
|
}
|
|
22
10
|
function resolveConversation(asking, nodes) {
|
|
23
11
|
if (asking === undefined)
|
|
24
12
|
return undefined;
|
|
25
|
-
const byId = new Map(nodes.map((
|
|
26
|
-
let
|
|
13
|
+
const byId = new Map(nodes.map((node) => [node.node_id, node]));
|
|
14
|
+
let current = asking;
|
|
27
15
|
const seen = new Set();
|
|
28
|
-
while (
|
|
29
|
-
seen.add(
|
|
30
|
-
const parent = byId.get(
|
|
16
|
+
while (current.parent !== null && !seen.has(current.node_id)) {
|
|
17
|
+
seen.add(current.node_id);
|
|
18
|
+
const parent = byId.get(current.parent);
|
|
31
19
|
if (parent === undefined)
|
|
32
20
|
break;
|
|
33
|
-
|
|
21
|
+
current = parent;
|
|
34
22
|
}
|
|
35
|
-
return
|
|
23
|
+
return current;
|
|
36
24
|
}
|
|
37
25
|
function readableSourceLabel(source) {
|
|
38
26
|
const sessionName = source?.sessionName?.trim();
|
|
@@ -41,55 +29,39 @@ function readableSourceLabel(source) {
|
|
|
41
29
|
const askedBy = source?.askedBy?.trim();
|
|
42
30
|
return askedBy === '' ? undefined : askedBy;
|
|
43
31
|
}
|
|
44
|
-
function askingNodeFor(jobId, byId) {
|
|
45
|
-
const bridge = byId.get(jobId);
|
|
46
|
-
if (bridge?.parent !== undefined && bridge.parent !== null)
|
|
47
|
-
return byId.get(bridge.parent) ?? bridge;
|
|
48
|
-
return bridge;
|
|
49
|
-
}
|
|
50
|
-
/** A missing reply route is the complete representation of a standalone page;
|
|
51
|
-
* a route whose bridge disappeared is an orphaned reply-bearing ticket. */
|
|
52
|
-
function replyBridgeId(dir) {
|
|
53
|
-
return readTicketReplyRoute(dir)?.bridge_node_id ?? null;
|
|
54
|
-
}
|
|
55
32
|
function summarizeTicket(summary, nodes, byId) {
|
|
56
|
-
const
|
|
57
|
-
// A reply-bearing ticket whose bridge was reaped is stale and absent from the
|
|
58
|
-
// canvas view. A standalone page has no bridge by construction.
|
|
59
|
-
if (bridgeNodeId !== null && !byId.has(bridgeNodeId))
|
|
60
|
-
return null;
|
|
61
|
-
const asking = bridgeNodeId === null ? undefined : askingNodeFor(bridgeNodeId, byId);
|
|
33
|
+
const asking = summary.source.nodeId === undefined ? undefined : byId.get(summary.source.nodeId);
|
|
62
34
|
const conversation = resolveConversation(asking, nodes);
|
|
63
35
|
const sourceLabel = readableSourceLabel(summary.source);
|
|
64
36
|
return {
|
|
65
|
-
ticket_id: summary.
|
|
66
|
-
|
|
67
|
-
dir: summary.dir,
|
|
37
|
+
ticket_id: summary.request_id,
|
|
38
|
+
dir: summary.request_dir,
|
|
68
39
|
title: summary.title,
|
|
69
|
-
subtitle: summary.subtitle,
|
|
40
|
+
subtitle: summary.subtitle ?? '',
|
|
70
41
|
kind: summary.kind,
|
|
71
|
-
...(summary.kind === 'page' ? { emitted_at: summary.
|
|
72
|
-
...(
|
|
73
|
-
...(
|
|
74
|
-
...(
|
|
75
|
-
...(conversation !== undefined ? { conversation_id: conversation.node_id, conversation_title: conversation.name } : {}),
|
|
42
|
+
...(summary.kind === 'page' ? { emitted_at: summary.emitted_at } : { blocked_since: summary.blocked_since }),
|
|
43
|
+
...(sourceLabel === undefined ? {} : { source_label: sourceLabel }),
|
|
44
|
+
...(asking === undefined ? {} : { asking_node_id: asking.node_id, asking_node_name: asking.name, cwd: asking.cwd }),
|
|
45
|
+
...(conversation === undefined ? {} : { conversation_id: conversation.node_id, conversation_title: conversation.name }),
|
|
76
46
|
};
|
|
77
47
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
48
|
+
async function requireInteraction(ticketId) {
|
|
49
|
+
try {
|
|
50
|
+
const request = await cliClient().getHumanRequest(ticketId);
|
|
51
|
+
return { request_id: request.request_id, kind: request.kind };
|
|
82
52
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (!(error instanceof ApiError) || error.status !== 404)
|
|
55
|
+
rethrowAsCliError(error);
|
|
56
|
+
}
|
|
57
|
+
const ticket = (await cliClient().listHumanInbox()).tickets.find((item) => item.ticket_id === ticketId);
|
|
58
|
+
if (ticket === undefined)
|
|
59
|
+
throw new InputError({ error: 'not_found', message: `no interaction ticket: ${ticketId}`, field: 'ticket_id', next: 'Pass a ticket_id from `crtr human list`.' });
|
|
60
|
+
return { request_id: ticket.request_id, kind: ticket.kind };
|
|
88
61
|
}
|
|
89
|
-
function
|
|
90
|
-
return error instanceof ApiError && error.status === 409 && error.code === '
|
|
62
|
+
function isAlreadySettled(error) {
|
|
63
|
+
return error instanceof ApiError && error.status === 409 && (error.code === 'already_settled' || error.code === 'review_terminal_conflict');
|
|
91
64
|
}
|
|
92
|
-
// list (read-only, paginated)
|
|
93
65
|
export const humanList = defineLeaf({
|
|
94
66
|
name: 'list',
|
|
95
67
|
description: 'enumerate pending interactions',
|
|
@@ -102,7 +74,7 @@ export const humanList = defineLeaf({
|
|
|
102
74
|
{ kind: 'flag', name: 'cursor', type: 'string', required: false, constraint: "Opaque token from a previous response's next_cursor. Omit on first call." },
|
|
103
75
|
],
|
|
104
76
|
output: [
|
|
105
|
-
{ name: 'items', type: 'object[]', required: true, constraint: 'Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with
|
|
77
|
+
{ name: 'items', type: 'object[]', required: true, constraint: 'Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with claim_owner, source_label, asking_node_id/name, conversation_id/title. Oldest first.' },
|
|
106
78
|
{ name: 'next_cursor', type: 'string | null', required: true, constraint: 'Pass on the next call to continue. null means no more items.' },
|
|
107
79
|
{ name: 'total', type: 'integer | null', required: true, constraint: 'Total pending interactions.' },
|
|
108
80
|
],
|
|
@@ -110,47 +82,35 @@ export const humanList = defineLeaf({
|
|
|
110
82
|
effects: ['None. Read-only.'],
|
|
111
83
|
},
|
|
112
84
|
run: async (input) => {
|
|
113
|
-
const
|
|
114
|
-
const limit = Math.min(Math.max(1, limitRaw), 100);
|
|
115
|
-
const cursor = input['cursor'];
|
|
116
|
-
const { tickets } = await cliClient().listHumanInbox();
|
|
117
|
-
const raw = filterVisibleReviewTickets(scanInbox(), new Set(tickets.map((ticket) => ticket.ticket_id)));
|
|
85
|
+
const limit = Math.min(Math.max(1, input['limit']), 100);
|
|
118
86
|
const nodes = await fetchNodeRefs();
|
|
119
|
-
const byId = new Map(nodes.map((
|
|
120
|
-
const items =
|
|
121
|
-
.
|
|
122
|
-
const
|
|
123
|
-
return
|
|
124
|
-
})
|
|
125
|
-
.sort((a, b) => {
|
|
126
|
-
const ka = `${a.emitted_at ?? a.blocked_since}|${a.ticket_id}`;
|
|
127
|
-
const kb = `${b.emitted_at ?? b.blocked_since}|${b.ticket_id}`;
|
|
128
|
-
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
87
|
+
const byId = new Map(nodes.map((node) => [node.node_id, node]));
|
|
88
|
+
const items = (await cliClient().listHumanInbox()).tickets.map((summary) => summarizeTicket(summary, nodes, byId)).sort((a, b) => {
|
|
89
|
+
const left = `${a.emitted_at ?? a.blocked_since}|${a.ticket_id}`;
|
|
90
|
+
const right = `${b.emitted_at ?? b.blocked_since}|${b.ticket_id}`;
|
|
91
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
129
92
|
});
|
|
130
|
-
const page = paginate(items, { limit, cursor }, {
|
|
93
|
+
const page = paginate(items, { limit, cursor: input['cursor'] }, {
|
|
131
94
|
defaultLimit: 20,
|
|
132
95
|
maxLimit: 100,
|
|
133
|
-
keyOf: (
|
|
96
|
+
keyOf: (item) => `${item.emitted_at ?? item.blocked_since}|${item.ticket_id}`,
|
|
134
97
|
total: 'count',
|
|
135
98
|
});
|
|
136
99
|
return { items: page.items, next_cursor: page.next_cursor, total: page.total };
|
|
137
100
|
},
|
|
138
101
|
});
|
|
139
|
-
// resolve — non-interactive human answer
|
|
140
|
-
/** Decode the stdin envelope only; the daemon validates every response against the page manifest. */
|
|
141
102
|
function parseResolveBody(raw) {
|
|
142
103
|
let parsed;
|
|
143
104
|
try {
|
|
144
105
|
parsed = JSON.parse(raw);
|
|
145
106
|
}
|
|
146
|
-
catch (
|
|
147
|
-
throw new InputError({ error: 'invalid_json', message: `stdin is not valid JSON: ${String(
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new InputError({ error: 'invalid_json', message: `stdin is not valid JSON: ${String(error)}`, field: 'stdin', next: 'Pass {"responses":{"<component_id>":{...}}} on stdin.' });
|
|
148
109
|
}
|
|
149
|
-
|
|
150
|
-
if (typeof responses !== 'object' || responses === null || Array.isArray(responses)) {
|
|
110
|
+
if (typeof parsed.responses !== 'object' || parsed.responses === null || Array.isArray(parsed.responses)) {
|
|
151
111
|
throw new InputError({ error: 'invalid_field', message: 'responses must be an object keyed by the id of a response-bearing component', field: 'responses', next: 'Pass {"responses":{"<component_id>":{...}}}, or {"responses":{}} to acknowledge a page with no response-bearing components.' });
|
|
152
112
|
}
|
|
153
|
-
return responses;
|
|
113
|
+
return parsed.responses;
|
|
154
114
|
}
|
|
155
115
|
export const humanResolve = defineLeaf({
|
|
156
116
|
name: 'resolve',
|
|
@@ -166,76 +126,66 @@ export const humanResolve = defineLeaf({
|
|
|
166
126
|
],
|
|
167
127
|
output: [
|
|
168
128
|
{ name: 'resolved', type: 'boolean', required: true, constraint: 'True when this call completed the page.' },
|
|
169
|
-
{ name: 'ticket_id', type: 'string', required: true, constraint: 'Interaction
|
|
170
|
-
{ name: 'reason', type: 'string', required: false, constraint: '
|
|
129
|
+
{ name: 'ticket_id', type: 'string', required: true, constraint: 'Interaction request id.' },
|
|
130
|
+
{ name: 'reason', type: 'string', required: false, constraint: 'already_settled when resolved is false.' },
|
|
171
131
|
],
|
|
172
132
|
outputKind: 'object',
|
|
173
|
-
effects: ['The daemon
|
|
133
|
+
effects: ['The daemon settles the request and directly delivers a reply-bearing completion to its asking node.'],
|
|
174
134
|
},
|
|
175
135
|
run: async (input) => {
|
|
176
136
|
const ticketId = input['ticket_id'];
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!existsSync(pageManifestPath(dir))) {
|
|
181
|
-
throw new InputError({ error: 'not_found', message: `no page for interaction: ${ticketId}`, field: 'ticket_id', next: 'Pass a pending ticket_id from `crtr human list`.' });
|
|
182
|
-
}
|
|
183
|
-
const responses = parseResolveBody(input['body']);
|
|
184
|
-
// A published result on a live bridge may be an interrupted delivery. The
|
|
185
|
-
// daemon re-drives that delivery before it returns its 409. A standalone
|
|
186
|
-
// ticket or terminal bridge has nothing left to deliver.
|
|
187
|
-
if (isResolved(dir) && (bridge === null || isTerminalBridge(bridge.status))) {
|
|
188
|
-
return { resolved: false, ticket_id: ticketId, reason: 'already_resolved' };
|
|
189
|
-
}
|
|
137
|
+
const ticket = await requireInteraction(ticketId);
|
|
138
|
+
if (ticket.kind !== 'page')
|
|
139
|
+
throw new InputError({ error: 'not_found', message: `no page for interaction: ${ticketId}`, field: 'ticket_id', next: 'Pass a pending page ticket_id from `crtr human list`.' });
|
|
190
140
|
try {
|
|
191
|
-
await cliClient().
|
|
141
|
+
await cliClient().respondHumanRequest(ticket.request_id, { responses: parseResolveBody(input['body']), actor: envNodeId() ?? 'human' });
|
|
192
142
|
}
|
|
193
|
-
catch (
|
|
194
|
-
if (
|
|
195
|
-
return { resolved: false, ticket_id: ticketId, reason: '
|
|
196
|
-
rethrowAsCliError(
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (isAlreadySettled(error))
|
|
145
|
+
return { resolved: false, ticket_id: ticketId, reason: 'already_settled' };
|
|
146
|
+
rethrowAsCliError(error);
|
|
197
147
|
}
|
|
198
148
|
return { resolved: true, ticket_id: ticketId };
|
|
199
149
|
},
|
|
200
150
|
});
|
|
201
|
-
// cancel — retract a pending page/review
|
|
202
151
|
export const humanCancel = defineLeaf({
|
|
203
152
|
name: 'cancel',
|
|
204
153
|
description: 'retract a pending page/review',
|
|
205
154
|
whenToUse: 'a page or review went stale before the user completed it',
|
|
206
155
|
help: {
|
|
207
156
|
name: 'human cancel',
|
|
208
|
-
summary: 'retract a pending page
|
|
209
|
-
guide: 'Pass the page_id returned by `human send`, or a ticket_id from `human list`. Best-effort and idempotent: if the user already completed or canceled it, canceled:false with reason "already_resolved" changes nothing. Cancellation races the user\'s submit; whichever wins is authoritative. For a reply-bearing ticket, only subscribers other than the caller receive the cancellation notice.',
|
|
157
|
+
summary: 'retract a pending page or review',
|
|
210
158
|
params: [
|
|
211
159
|
{ kind: 'positional', name: 'ticket_id', type: 'string', required: true, constraint: 'page_id returned by human send, or ticket_id returned by human list.' },
|
|
212
|
-
{ kind: 'flag', name: 'reason', type: 'string', required: false, constraint: 'Optional short note delivered to
|
|
160
|
+
{ kind: 'flag', name: 'reason', type: 'string', required: false, constraint: 'Optional short note delivered to the asking node explaining why it was retracted.' },
|
|
213
161
|
],
|
|
214
162
|
output: [
|
|
215
|
-
{ name: 'canceled', type: 'boolean', required: true, constraint: 'True when the interaction was retracted; false when
|
|
216
|
-
{ name: 'ticket_id', type: 'string', required: true, constraint: 'The interaction
|
|
217
|
-
{ name: 'reason', type: 'string', required: false, constraint: '
|
|
163
|
+
{ name: 'canceled', type: 'boolean', required: true, constraint: 'True when the interaction was retracted; false when it was already terminal.' },
|
|
164
|
+
{ name: 'ticket_id', type: 'string', required: true, constraint: 'The interaction request id.' },
|
|
165
|
+
{ name: 'reason', type: 'string', required: false, constraint: 'already_settled when canceled is false.' },
|
|
218
166
|
],
|
|
219
167
|
outputKind: 'object',
|
|
220
|
-
effects: [
|
|
221
|
-
'The daemon removes the ticket from the inbox; when it has a reply bridge, the bridge retires and subscribers other than the caller are notified that no answer is coming.',
|
|
222
|
-
],
|
|
168
|
+
effects: ['The daemon removes the request from the human inbox and directly notifies its asking node when it has one.'],
|
|
223
169
|
},
|
|
224
170
|
run: async (input) => {
|
|
225
171
|
const ticketId = input['ticket_id'];
|
|
172
|
+
const ticket = await requireInteraction(ticketId);
|
|
226
173
|
const reason = input['reason'];
|
|
227
|
-
const
|
|
174
|
+
const body = { ...(reason === undefined || reason === '' ? {} : { reason }), actor: envNodeId() ?? 'human' };
|
|
228
175
|
try {
|
|
229
|
-
|
|
176
|
+
if (ticket.kind === 'review')
|
|
177
|
+
await cliClient().cancelReview(ticket.request_id, body);
|
|
178
|
+
else
|
|
179
|
+
await cliClient().cancelHumanRequest(ticket.request_id, body);
|
|
230
180
|
}
|
|
231
|
-
catch (
|
|
232
|
-
if (
|
|
233
|
-
return { canceled: false, ticket_id: ticketId, reason: '
|
|
234
|
-
rethrowAsCliError(
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (isAlreadySettled(error))
|
|
183
|
+
return { canceled: false, ticket_id: ticketId, reason: 'already_settled' };
|
|
184
|
+
rethrowAsCliError(error);
|
|
235
185
|
}
|
|
236
186
|
return { canceled: true, ticket_id: ticketId };
|
|
237
187
|
},
|
|
238
|
-
render: (
|
|
239
|
-
? `Canceled human interaction ${
|
|
240
|
-
: `Nothing to cancel for ${
|
|
188
|
+
render: (result) => result['canceled'] === true
|
|
189
|
+
? `Canceled human interaction ${result['ticket_id']} — it left the human inbox.`
|
|
190
|
+
: `Nothing to cancel for ${result['ticket_id']} — ${result['reason'] ?? 'nothing to cancel'}.`,
|
|
241
191
|
});
|