@north-light/crouter 0.3.197 → 0.3.199
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +19 -19
- package/dist/api/client.js +29 -37
- package/dist/api/dto/broker-ops.d.ts +3 -1
- package/dist/api/dto/broker.d.ts +13 -2
- package/dist/api/dto/broker.js +4 -4
- package/dist/api/dto/human.d.ts +6 -5
- package/dist/api/dto/inbox.d.ts +126 -33
- package/dist/api/dto/nodes.d.ts +8 -2
- package/dist/api/dto/profiles.d.ts +23 -0
- package/dist/api/dto/profiles.js +2 -2
- package/dist/api/dto/worktree.d.ts +3 -5
- package/dist/api/routes.d.ts +3 -5
- package/dist/api/routes.js +4 -6
- package/dist/builtin-memory/00-runtime-base.md +8 -8
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +6 -6
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +2 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -1
- package/dist/builtin-memory/insights/capture.md +2 -2
- package/dist/builtin-memory/insights/init.md +3 -3
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +3 -3
- package/dist/builtin-memory/internal/plugins.md +59 -2
- package/dist/builtin-memory/internal/storage-tiers.md +3 -1
- package/dist/builtin-memory/spec/roadmap.md +2 -2
- package/dist/builtin-memory/testing.md +1 -1
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +1 -1
- package/dist/clients/attach/chrome/bash-jobs.js +3 -3
- package/dist/clients/attach/render/chat-view.d.ts +29 -9
- package/dist/clients/attach/render/chat-view.js +64 -35
- package/dist/clients/attach/render/markdown-source.js +0 -6
- package/dist/clients/attach/render/page-block.d.ts +34 -0
- package/dist/clients/attach/render/page-block.js +226 -0
- package/dist/clients/attach/render/tool-calls.js +16 -2
- package/dist/clients/attach/session/whip.d.ts +2 -0
- package/dist/clients/attach/session/whip.js +2 -0
- package/dist/clients/attach/viewer.js +1419 -982
- package/dist/clients/conversation/projection.js +18 -3
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +9 -6
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +13 -14
- package/dist/clients/inbox/controller.js +10 -16
- package/dist/clients/inbox/page-adapter.d.ts +3 -4
- package/dist/clients/inbox/page-adapter.js +3 -4
- package/dist/clients/inbox/resolve.d.ts +1 -1
- package/dist/clients/inbox/resolve.js +3 -3
- package/dist/clients/inbox/review/document-surface.js +3 -1
- package/dist/clients/inbox/review/state.d.ts +2 -1
- package/dist/clients/inbox/review/state.js +5 -16
- package/dist/clients/inbox/tui/input.js +37 -19
- package/dist/clients/inbox/tui/panel.js +5 -31
- package/dist/clients/inbox/tui/render.d.ts +11 -0
- package/dist/clients/inbox/tui/render.js +101 -36
- package/dist/clients/inbox/tui/slots.d.ts +4 -4
- package/dist/clients/inbox/tui/slots.js +19 -44
- package/dist/clients/inbox/tui/types.d.ts +6 -10
- package/dist/clients/inbox/tui.js +2 -2
- package/dist/commands/__tests__/human.test.js +118 -24
- package/dist/commands/__tests__/node-message.test.js +14 -0
- package/dist/commands/attention.js +4 -4
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-snapshot.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/human/{doc.d.ts → components.d.ts} +1 -1
- package/dist/commands/human/components.js +77 -0
- package/dist/commands/human/feedback.d.ts +2 -0
- package/dist/commands/human/feedback.js +108 -0
- package/dist/commands/human/prompts.d.ts +17 -8
- package/dist/commands/human/prompts.js +217 -226
- package/dist/commands/human/queue.js +57 -88
- package/dist/commands/human/shared.d.ts +4 -15
- package/dist/commands/human/shared.js +19 -61
- package/dist/commands/human.js +32 -19
- package/dist/commands/memory/origin.js +2 -2
- package/dist/commands/node/bash.js +4 -4
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +2 -2
- package/dist/commands/node-worktree.js +8 -8
- package/dist/commands/pkg/browse.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +129 -10
- package/dist/commands/profile/delete.js +41 -15
- package/dist/commands/profile.js +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/focus.js +29 -14
- package/dist/commands/surface-edit.js +3 -3
- package/dist/commands/surface-inbox.d.ts +2 -0
- package/dist/commands/{human/inbox.js → surface-inbox.js} +14 -18
- package/dist/commands/surface.js +2 -1
- package/dist/commands/sys/doctor.js +60 -3
- package/dist/commands/sys/feedback.js +79 -25
- package/dist/commands/sys/settings.js +1 -1
- package/dist/core/__tests__/cron-broker-capacity.test.js +1 -1
- package/dist/core/__tests__/daemon-boot.test.js +7 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +20 -36
- package/dist/core/__tests__/extension-abort.test.js +5 -2
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +13 -2
- package/dist/core/__tests__/fixtures/fake-engine.js +47 -3
- package/dist/core/__tests__/human-cancel-guard.test.js +31 -10
- package/dist/core/__tests__/human-deliver.test.js +41 -45
- package/dist/core/__tests__/human-node-not-supervised.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +157 -0
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +13 -11
- package/dist/core/__tests__/seam/dormancy-release.test.js +103 -4
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +2 -2
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +12 -16
- package/dist/core/__tests__/serial/live-mutation.test.js +1 -1
- package/dist/core/__tests__/serial/tmux-surface.test.js +4 -2
- package/dist/core/__tests__/serial/worktree.test.js +24 -198
- package/dist/core/__tests__/session-cycles.test.js +22 -0
- package/dist/core/__tests__/stop-guard.test.js +4 -4
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +19 -4
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +10 -0
- package/dist/core/canvas/__tests__/attention.test.js +8 -5
- package/dist/core/canvas/__tests__/render-remote.test.js +5 -5
- package/dist/core/canvas/attention.js +8 -6
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +21 -3
- package/dist/core/canvas/browse/model.d.ts +8 -2
- package/dist/core/canvas/browse/model.js +11 -4
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +5 -1
- package/dist/core/canvas/canvas.d.ts +33 -0
- package/dist/core/canvas/canvas.js +92 -0
- package/dist/core/canvas/crons.d.ts +9 -0
- package/dist/core/canvas/crons.js +51 -0
- package/dist/core/canvas/node-order.d.ts +22 -1
- package/dist/core/canvas/node-order.js +26 -1
- package/dist/core/canvas/render-source.d.ts +5 -0
- package/dist/core/canvas/render-source.js +1 -0
- package/dist/core/canvas/types.d.ts +9 -3
- package/dist/core/command-plugins/bundle.d.ts +6 -1
- package/dist/core/command-plugins/bundle.js +26 -13
- package/dist/core/command.js +8 -12
- package/dist/core/config.d.ts +47 -1
- package/dist/core/config.js +138 -4
- package/dist/core/help.d.ts +3 -0
- package/dist/core/help.js +1 -1
- package/dist/core/human/__tests__/page-catalog.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-catalog.test.js +20 -0
- package/dist/core/human/__tests__/page-tickets.test.js +68 -34
- package/dist/core/human/__tests__/page.test.js +60 -119
- package/dist/core/human/__tests__/serial/inbox-core.test.js +6 -16
- package/dist/core/human/answer-text.d.ts +3 -0
- package/dist/core/human/answer-text.js +98 -0
- package/dist/core/human/answer.d.ts +53 -0
- package/dist/core/human/answer.js +119 -0
- package/dist/core/human/component-docs.d.ts +18 -0
- package/dist/core/human/component-docs.js +434 -0
- package/dist/core/human/convention.d.ts +17 -3
- package/dist/core/human/convention.js +36 -5
- package/dist/core/human/feedback-companion.d.ts +20 -0
- package/dist/core/human/feedback-companion.js +98 -0
- package/dist/core/human/feedback.d.ts +65 -0
- package/dist/core/human/feedback.js +116 -0
- package/dist/core/human/page-catalog.d.ts +56 -5
- package/dist/core/human/page-catalog.js +151 -22
- package/dist/core/human/page-errors.d.ts +4 -0
- package/dist/core/human/page-errors.js +7 -0
- package/dist/core/human/page-eval.d.ts +20 -0
- package/dist/core/human/page-eval.js +123 -0
- package/dist/core/human/page-schema.d.ts +48 -116
- package/dist/core/human/page-schema.js +100 -72
- package/dist/core/human/page-synth.d.ts +2 -4
- package/dist/core/human/page-synth.js +20 -7
- package/dist/core/human/page.d.ts +13 -21
- package/dist/core/human/page.js +147 -391
- package/dist/core/human/review-schema.d.ts +1 -0
- package/dist/core/human/review-schema.js +1 -1
- package/dist/core/human/root.d.ts +6 -2
- package/dist/core/human/root.js +12 -4
- package/dist/core/human/scan.d.ts +3 -3
- package/dist/core/human/scan.js +21 -21
- package/dist/core/human/summary.d.ts +2 -6
- package/dist/core/human/summary.js +3 -59
- package/dist/core/human/tickets.d.ts +22 -15
- package/dist/core/human/tickets.js +90 -28
- package/dist/core/human/types.d.ts +14 -2
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/preview-registry.js +8 -10
- package/dist/core/profiles/default-binding.d.ts +4 -0
- package/dist/core/profiles/default-binding.js +24 -0
- package/dist/core/profiles/deletion-reservation.d.ts +8 -0
- package/dist/core/profiles/deletion-reservation.js +38 -0
- package/dist/core/profiles/manifest.d.ts +3 -0
- package/dist/core/profiles/manifest.js +12 -0
- package/dist/core/profiles/select.js +2 -2
- package/dist/core/review/realize.js +12 -4
- package/dist/core/review/store.d.ts +6 -0
- package/dist/core/review/store.js +31 -0
- package/dist/core/runtime/bearings.d.ts +0 -4
- package/dist/core/runtime/bearings.js +4 -62
- package/dist/core/runtime/bin-contributions.d.ts +44 -0
- package/dist/core/runtime/bin-contributions.js +186 -0
- package/dist/core/runtime/boot-root.js +6 -1
- package/dist/core/runtime/broker/client-registry.d.ts +4 -0
- package/dist/core/runtime/broker/client-registry.js +13 -1
- package/dist/core/runtime/broker/extension-abort.d.ts +1 -1
- package/dist/core/runtime/broker/extension-abort.js +4 -2
- package/dist/core/runtime/broker/fault-retry.js +4 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +3 -1
- package/dist/core/runtime/broker/frame-dispatch.js +5 -6
- package/dist/core/runtime/broker/read-ops.d.ts +1 -1
- package/dist/core/runtime/broker/read-ops.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +6 -1
- package/dist/core/runtime/broker-extension-render.js +19 -0
- package/dist/core/runtime/broker-protocol.d.ts +14 -0
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +25 -12
- package/dist/core/runtime/busy.d.ts +4 -3
- package/dist/core/runtime/busy.js +4 -3
- package/dist/core/runtime/front-door-env.d.ts +5 -0
- package/dist/core/runtime/front-door-env.js +16 -0
- package/dist/core/runtime/front-door.d.ts +1 -5
- package/dist/core/runtime/front-door.js +2 -5
- package/dist/core/runtime/host.d.ts +1 -1
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/invocation.js +10 -0
- package/dist/core/runtime/kickoff.js +34 -0
- package/dist/core/runtime/launch.d.ts +2 -6
- package/dist/core/runtime/node-read.js +4 -1
- package/dist/core/runtime/nodes.js +2 -0
- package/dist/core/runtime/promote.d.ts +1 -0
- package/dist/core/runtime/promote.js +3 -1
- package/dist/core/runtime/session-cycles.d.ts +6 -0
- package/dist/core/runtime/session-cycles.js +15 -1
- package/dist/core/runtime/session-visibility.d.ts +17 -8
- package/dist/core/runtime/session-visibility.js +19 -10
- package/dist/core/runtime/spawn-env.d.ts +9 -1
- package/dist/core/runtime/spawn-env.js +62 -2
- package/dist/core/runtime/spawn.js +4 -4
- package/dist/core/runtime/stop-guard.d.ts +2 -2
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/stop-signals.d.ts +1 -1
- package/dist/core/runtime/stop-signals.js +2 -2
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/session-model/session-state.d.ts +4 -3
- package/dist/core/session-model/session-state.js +14 -1
- package/dist/core/user-settings.d.ts +36 -2
- package/dist/core/user-settings.js +56 -14
- package/dist/core/worktree.d.ts +15 -45
- package/dist/core/worktree.js +59 -261
- package/dist/daemon/api/__tests__/seam/profile-delete.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +386 -0
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +9 -5
- package/dist/daemon/api/handlers/feedback-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/feedback-comments.js +207 -0
- package/dist/daemon/api/handlers/human.js +24 -20
- package/dist/daemon/api/handlers/inbox.d.ts +10 -0
- package/dist/daemon/api/handlers/inbox.js +48 -109
- package/dist/daemon/api/handlers/profiles.js +11 -29
- package/dist/daemon/api/map.d.ts +6 -1
- package/dist/daemon/api/map.js +23 -0
- package/dist/daemon/api/server.js +3 -1
- package/dist/daemon/companion-retire.d.ts +2 -0
- package/dist/daemon/companion-retire.js +33 -0
- package/dist/daemon/cron-run.js +29 -17
- package/dist/daemon/crtrd.js +1 -1
- package/dist/daemon/fleet.d.ts +22 -10
- package/dist/daemon/fleet.js +43 -24
- package/dist/daemon/human/finish.d.ts +12 -6
- package/dist/daemon/human/finish.js +134 -54
- package/dist/daemon/human/sweep.js +9 -3
- package/dist/daemon/profile-delete.d.ts +7 -0
- package/dist/daemon/profile-delete.js +306 -0
- package/dist/daemon/reconcilers/broker-supervision.js +14 -9
- package/dist/daemon/reconcilers/dormant-inbox.js +11 -6
- package/dist/daemon/reconcilers/live-obligation.d.ts +21 -1
- package/dist/daemon/reconcilers/live-obligation.js +30 -2
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +28 -1
- package/dist/daemon/review/deliver.js +3 -3
- package/dist/daemon/review/finish.d.ts +0 -2
- package/dist/daemon/review/finish.js +1 -29
- package/dist/daemon/review/sweep.js +12 -1
- package/dist/pi-extensions/__tests__/canvas-bash-valve.test.js +56 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-bash-valve.d.ts +9 -1
- package/dist/pi-extensions/canvas-bash-valve.js +31 -13
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/dist/pi-extensions/canvas-review-boundary.d.ts +4 -0
- package/dist/pi-extensions/canvas-review-boundary.js +18 -9
- package/dist/pi-extensions/canvas-stophook.js +4 -4
- package/dist/shared/generated-context.js +2 -2
- package/dist/types.d.ts +61 -5
- package/dist/types.js +8 -2
- package/package.json +4 -4
- package/runtime.lock.json +5 -33
- package/scripts/install-runtime.mjs +19 -24
- package/dist/builtin-memory/init.md +0 -38
- package/dist/builtin-memory/plan.md +0 -19
- package/dist/builtin-memory/spec.md +0 -19
- package/dist/clients/attach/render/html-markdown.d.ts +0 -13
- package/dist/clients/attach/render/html-markdown.js +0 -206
- package/dist/commands/human/doc.js +0 -144
- package/dist/commands/human/inbox.d.ts +0 -2
- package/dist/core/human/__tests__/page-render.test.js +0 -49
- package/dist/core/human/markdown-html.d.ts +0 -8
- package/dist/core/human/markdown-html.js +0 -581
- package/dist/core/human/page-render.d.ts +0 -3
- package/dist/core/human/page-render.js +0 -131
- package/dist/pages/bundle.css +0 -1
- package/dist/pages/bundle.js +0 -1347
- package/dist/pages/comments.d.ts +0 -43
- package/dist/pages/comments.js +0 -125
- package/dist/pages/elements/cards.d.ts +0 -17
- package/dist/pages/elements/cards.js +0 -802
- package/dist/pages/elements/chart.js +0 -648
- package/dist/pages/elements/options.d.ts +0 -21
- package/dist/pages/elements/options.js +0 -629
- package/dist/pages/elements/pages.d.ts +0 -24
- package/dist/pages/elements/pages.js +0 -355
- package/dist/pages/elements/slot.d.ts +0 -21
- package/dist/pages/elements/slot.js +0 -105
- package/dist/pages/elements/table.d.ts +0 -19
- package/dist/pages/elements/table.js +0 -935
- package/dist/pages/elements/text.d.ts +0 -25
- package/dist/pages/elements/text.js +0 -968
- package/dist/pages/entry.d.ts +0 -8
- package/dist/pages/entry.js +0 -8
- package/dist/pages/host.d.ts +0 -158
- package/dist/pages/host.js +0 -182
- package/dist/pages/register.d.ts +0 -2
- package/dist/pages/register.js +0 -3
- package/dist/pages/slot-config.d.ts +0 -36
- package/dist/pages/slot-config.js +0 -50
- package/dist/pages/types.d.ts +0 -98
- package/dist/pages/types.js +0 -23
- /package/dist/{core/human/__tests__/page-render.test.d.ts → commands/__tests__/node-message.test.d.ts} +0 -0
- /package/dist/{pages/elements/chart.d.ts → core/__tests__/plugin-page-components.test.d.ts} +0 -0
|
@@ -14,7 +14,7 @@ import { clearFault, readFault } from '../fault.js';
|
|
|
14
14
|
import { isManagedProvider, readSubscriptionPool, } from '../../subscription-state.js';
|
|
15
15
|
import { SessionListCache } from '../session-list-cache.js';
|
|
16
16
|
import { sessionListCachePath } from '../../canvas/paths.js';
|
|
17
|
-
import {
|
|
17
|
+
import { isReviewBoundary } from '../session-visibility.js';
|
|
18
18
|
import { isUnknownModel, parseModelSpec } from '../model-selection.js';
|
|
19
19
|
import { buildCommandList, buildGetTreeData, buildListMemoryRefsData, buildListModelsData, buildScopedModelsData, buildSessionsData, buildSettingsData, modelKey, } from './read-ops.js';
|
|
20
20
|
export const REVIEW_BOUNDARY_NAVIGATION_CODE = 'review_boundary_navigation';
|
|
@@ -279,7 +279,7 @@ export function stripHiddenEntriesFromJsonlExport(filePath) {
|
|
|
279
279
|
}
|
|
280
280
|
/** Owns frame-local input state while dispatching through explicit broker collaborators. */
|
|
281
281
|
export function createFrameDispatchContext(deps) {
|
|
282
|
-
const { nodeId, cfg, registry, toolGroups, projection, rebind,
|
|
282
|
+
const { nodeId, cfg, registry, toolGroups, projection, rebind, boundaryReviewId, pendingDialogs, sendWelcome, replayExtraPendingDialogsTo, reWelcomeAll, disposeAndExit, persistModelChoice, broadcastModelChanged, registryOf, formatModelSpec, formatExactModelSpec, resolveLaunchModel, } = deps;
|
|
283
283
|
const currentSession = () => rebind.session();
|
|
284
284
|
const currentServices = () => rebind.services();
|
|
285
285
|
let kickoffPending;
|
|
@@ -657,11 +657,10 @@ export function createFrameDispatchContext(deps) {
|
|
|
657
657
|
// pass no id (unchanged behavior).
|
|
658
658
|
...(id !== undefined ? { id } : {}),
|
|
659
659
|
});
|
|
660
|
-
const isPreBoundaryReviewEntry = (entryId) =>
|
|
660
|
+
const isPreBoundaryReviewEntry = (entryId) => boundaryReviewId !== undefined &&
|
|
661
661
|
!currentSession()
|
|
662
662
|
.sessionManager.getBranch(entryId)
|
|
663
|
-
.some((entry) => entry
|
|
664
|
-
entry.customType === REVIEW_BOUNDARY_CUSTOM_TYPE);
|
|
663
|
+
.some((entry) => isReviewBoundary(entry, boundaryReviewId));
|
|
665
664
|
const reviewBoundaryRefusalTo = (client, targetId) => {
|
|
666
665
|
const refusal = new ReviewBoundaryNavigationError(targetId);
|
|
667
666
|
registry.sendFrame(client, {
|
|
@@ -1112,7 +1111,7 @@ export function createFrameDispatchContext(deps) {
|
|
|
1112
1111
|
}
|
|
1113
1112
|
case 'get_tree': {
|
|
1114
1113
|
try {
|
|
1115
|
-
registry.sendFrame(client, buildGetTreeData(frame.id, currentSession(),
|
|
1114
|
+
registry.sendFrame(client, buildGetTreeData(frame.id, currentSession(), boundaryReviewId));
|
|
1116
1115
|
}
|
|
1117
1116
|
catch (err) {
|
|
1118
1117
|
engineErrorTo(client, frame.id)(err);
|
|
@@ -18,6 +18,6 @@ export declare function buildCommandList(session: BrokerSession): CommandDisclos
|
|
|
18
18
|
export declare function buildListMemoryRefsData(id: string, refs: RefMeta[], engineLeadingCommandNames: string[]): ListMemoryRefsData;
|
|
19
19
|
export declare function buildListModelsData(id: string, session: BrokerSession): ListModelsData;
|
|
20
20
|
export declare function buildScopedModelsData(id: string, session: BrokerSession): ListScopedModelsData;
|
|
21
|
-
export declare function buildGetTreeData(id: string, session: BrokerSession,
|
|
21
|
+
export declare function buildGetTreeData(id: string, session: BrokerSession, boundaryReviewId: string | undefined): GetTreeData;
|
|
22
22
|
export declare function buildSettingsData(id: string, session: BrokerSession): GetSettingsData;
|
|
23
23
|
export declare function buildSessionsData(id: string, scope: 'cwd' | 'all', session: BrokerSession, cache: SessionListCache): Promise<ListSessionsData>;
|
|
@@ -67,9 +67,9 @@ export function buildScopedModelsData(id, session) {
|
|
|
67
67
|
enabledModelIds: session.settingsManager.getEnabledModels() ?? null,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
export function buildGetTreeData(id, session,
|
|
70
|
+
export function buildGetTreeData(id, session, boundaryReviewId) {
|
|
71
71
|
const manager = session.sessionManager;
|
|
72
|
-
const tree =
|
|
72
|
+
const tree = boundaryReviewId !== undefined ? boundaryRootedTree(manager.getTree(), boundaryReviewId) : manager.getTree();
|
|
73
73
|
const visibleEntryIds = new Set();
|
|
74
74
|
const collectEntryIds = (nodes) => {
|
|
75
75
|
for (const node of nodes) {
|
|
@@ -56,7 +56,11 @@ export class RebindDriver {
|
|
|
56
56
|
await candidateSession.bindExtensions({
|
|
57
57
|
uiContext: this.deps.uiContext(),
|
|
58
58
|
mode: 'rpc',
|
|
59
|
-
abortHandler: () => abortSessionFromExtension(candidateSession)
|
|
59
|
+
abortHandler: () => abortSessionFromExtension(candidateSession, () => {
|
|
60
|
+
const generation = this.installed;
|
|
61
|
+
if (generation?.session === candidateSession)
|
|
62
|
+
generation.stagedRefreshAbort = true;
|
|
63
|
+
}),
|
|
60
64
|
shutdownHandler: () => this.requestShutdown(),
|
|
61
65
|
});
|
|
62
66
|
if (ticket !== this.latestRebindRequest)
|
|
@@ -92,6 +96,7 @@ export class RebindDriver {
|
|
|
92
96
|
fallback: { blocked: false },
|
|
93
97
|
stagedProviderAgentEnd: null,
|
|
94
98
|
stagedWatchdogAbort: false,
|
|
99
|
+
stagedRefreshAbort: false,
|
|
95
100
|
stagedOverflowFailure: null,
|
|
96
101
|
executingTools: new Set(),
|
|
97
102
|
};
|
|
@@ -114,10 +114,29 @@ function worktreeNote(node) {
|
|
|
114
114
|
return '';
|
|
115
115
|
return `Branch: \`${worktree.branch}\`\nWorktree: \`${worktree.path}\`\nBase: \`${worktree.base_ref}\` @ \`${worktree.base_sha}\`\nCommit work here. Land it with \`crtr node worktree close\` before finishing. Auto-drop applies only when the worktree is clean (no uncommitted changes) and nothing remains to land (zero commits ahead of the base, or its commits are already contained in the base); unlanded commits (commits not already contained in the base) or uncommitted changes require an explicit close/landing decision.`;
|
|
116
116
|
}
|
|
117
|
+
/** The companion note — rendered ONLY when a node carries its immutable
|
|
118
|
+
* companion binding. Names the target (the document under review, or the page
|
|
119
|
+
* the feedback is about), and for a page-feedback companion carries the whole
|
|
120
|
+
* behavioral contract — the companion is an ordinary fork of the sending node,
|
|
121
|
+
* so this note is the only place it learns the role. */
|
|
117
122
|
function reviewNote(node) {
|
|
118
123
|
const review = node.review_binding;
|
|
119
124
|
if (review === null || review === undefined)
|
|
120
125
|
return '';
|
|
126
|
+
if (review.kind === 'page_feedback') {
|
|
127
|
+
const ticketId = review.review_id;
|
|
128
|
+
const targetFile = review.target_file;
|
|
129
|
+
return (`You exist for one page's feedback conversation — ticket \`${ticketId}\`, page source \`${targetFile}\`. ` +
|
|
130
|
+
`Think with the person about this one page and answer in a few plain sentences from the context you already carry. ` +
|
|
131
|
+
`Revise the page in place with \`crtr human send --replace ${ticketId} --page ${targetFile}\` — it swaps in on their screen immediately — and briefly say what changed; a silent swap leaves them guessing. ` +
|
|
132
|
+
`After dealing with an open feedback item — by revising the page or answering it here — resolve it with \`crtr human feedback resolve <comment_id>\`; ` +
|
|
133
|
+
`\`crtr human feedback list ${ticketId}\` shows each item's id and status. ` +
|
|
134
|
+
`Never author feedback or resolve an item before dealing with it — a sent comment is immutable, and resolution is your report that the work is done. ` +
|
|
135
|
+
`Asked to act beyond this page, give your advice and point the person at submitting it: the submitted answer is what reaches the task's owner, while work done here is invisible to it and ends when the ticket settles. ` +
|
|
136
|
+
`When a question needs real exploration, delegate it to a child node and keep talking while it runs. ` +
|
|
137
|
+
`The inherited conversation belongs to the origin node, not you — its task stays with it; carry only the context needed for this page. ` +
|
|
138
|
+
`The person's visible transcript begins at the boundary marker above; everything earlier is context they cannot see — never treat it as shared ground; when they ask about it, answer plainly in your own words rather than replaying it.`);
|
|
139
|
+
}
|
|
121
140
|
return `You exist for one human review of \`${review.target_file}\`. The inherited conversation belongs to the origin node, not you — carry only the context needed for this review. The person's visible transcript begins at the boundary marker above; everything earlier is context they cannot see.`;
|
|
122
141
|
}
|
|
123
142
|
export function buildContextBearingsFromState(state, projectContext, situational = '', seen) {
|
|
@@ -46,6 +46,20 @@ export interface BrokerSnapshot {
|
|
|
46
46
|
}>;
|
|
47
47
|
title: string | undefined;
|
|
48
48
|
};
|
|
49
|
+
/** The engine's UNRUN queue — the steer/follow-up texts a writable client
|
|
50
|
+
* sent mid-turn that have not started yet (pi's `_steeringMessages` +
|
|
51
|
+
* `_followUpMessages`, read non-destructively). `state.pendingMessageCount`
|
|
52
|
+
* is the same queue's SIZE; without the texts a (re)attaching viewer can say
|
|
53
|
+
* how many messages are waiting but not which, so it cannot render the
|
|
54
|
+
* queued rows it renders from `queue_update` while attached — the welcome
|
|
55
|
+
* would no longer fully determine viewer state.
|
|
56
|
+
*
|
|
57
|
+
* Ingest defensively: a broker pinned to a pre-`queued` runtime generation is
|
|
58
|
+
* still live and sends `welcome` without it. */
|
|
59
|
+
queued?: {
|
|
60
|
+
steering: string[];
|
|
61
|
+
followUp: string[];
|
|
62
|
+
};
|
|
49
63
|
/** Broker-owned summaries keyed by the first tool-call id in each group.
|
|
50
64
|
* Structured objects with bullets and filesDeleted.
|
|
51
65
|
* Optional so snapshots from an older live runtime remain readable. */
|
|
@@ -29,7 +29,7 @@ interface PendingDialog {
|
|
|
29
29
|
* bash children. No-op before the session is built or after a clean dispose. */
|
|
30
30
|
export declare function disposeActiveSession(): void;
|
|
31
31
|
export declare function runBroker(nodeId: string, startupAt?: bigint, startupTs?: string): Promise<void>;
|
|
32
|
-
export declare function snapshotMessages(session: CreateAgentSessionResult['session'],
|
|
32
|
+
export declare function snapshotMessages(session: CreateAgentSessionResult['session'], boundaryReviewId?: string): BrokerSnapshot['messages'];
|
|
33
33
|
export declare function buildBrokerSession(engine: BrokerEngine, cfg: BrokerSdkConfig): Promise<{
|
|
34
34
|
session: CreateAgentSessionResult['session'];
|
|
35
35
|
services: AgentSessionServices;
|
|
@@ -120,7 +120,8 @@ export function disposeActiveSession() {
|
|
|
120
120
|
// ---------------------------------------------------------------------------
|
|
121
121
|
export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), startupTs = new Date().toISOString()) {
|
|
122
122
|
// A clean-abort certificate belongs only to the dead broker that wrote it.
|
|
123
|
-
// Fleet classification consumes it
|
|
123
|
+
// Fleet classification consumes it as proof that the dead broker had a turn
|
|
124
|
+
// in flight even though Pi's settled abort already cleared the busy marker.
|
|
124
125
|
clearCleanAbort(nodeId);
|
|
125
126
|
// The canonical broker event source (job/log.jsonl) is bound before any other
|
|
126
127
|
// work in this function — no path/env/state setup may precede it, and the
|
|
@@ -215,8 +216,11 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
215
216
|
let { session, services, resuming, runtime } = await buildBrokerSession(engine, cfg);
|
|
216
217
|
// Review identity is immutable for the broker's lifetime. Snapshot producers
|
|
217
218
|
// close over this one construction-time classification rather than re-reading
|
|
218
|
-
// canvas metadata for every welcome or session replacement.
|
|
219
|
-
|
|
219
|
+
// canvas metadata for every welcome or session replacement. The BOUND review
|
|
220
|
+
// id (not a mere boolean) is what selects this companion's own boundary
|
|
221
|
+
// marker — a fork of another companion inherits that companion's earlier
|
|
222
|
+
// marker, and slicing at any marker would reveal its transcript.
|
|
223
|
+
const boundaryReviewId = (await brokerExtensionState(nodeId)).node.review_binding?.review_id;
|
|
220
224
|
// M3: name the boot session as the process-fatal disposal target immediately —
|
|
221
225
|
// enqueueRebind reassigns this on every later replacement, but without this
|
|
222
226
|
// initial assignment `activeSession` stays null (and disposeActiveSession() a
|
|
@@ -354,7 +358,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
354
358
|
const buildSnapshot = () => {
|
|
355
359
|
const liveSession = rebind.session();
|
|
356
360
|
return ({
|
|
357
|
-
messages: snapshotMessages(liveSession,
|
|
361
|
+
messages: snapshotMessages(liveSession, boundaryReviewId),
|
|
358
362
|
stats: liveSession.getSessionStats(),
|
|
359
363
|
// pi's `get_state` (rpc-mode.js), field for field, from the same getters.
|
|
360
364
|
// The ONE divergence: a model-less session (undefined OR the SDK
|
|
@@ -381,6 +385,14 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
381
385
|
},
|
|
382
386
|
toolGroupSummaries: toolGroups.summaries(),
|
|
383
387
|
workingActivity: projection.currentWorkingActivity(),
|
|
388
|
+
// The UNRUN queue's texts, read non-destructively (`clearQueue()` would
|
|
389
|
+
// consume them). Same two arrays a live `queue_update` carries, so a client
|
|
390
|
+
// that attaches mid-queue lands on the state its next `queue_update` would
|
|
391
|
+
// have produced.
|
|
392
|
+
queued: {
|
|
393
|
+
steering: [...liveSession.getSteeringMessages()],
|
|
394
|
+
followUp: [...liveSession.getFollowUpMessages()],
|
|
395
|
+
},
|
|
384
396
|
});
|
|
385
397
|
};
|
|
386
398
|
// Send a client its catch-up snapshot. welcome.pending_dialog carries a single
|
|
@@ -508,7 +520,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
508
520
|
rebind.setRebindSession();
|
|
509
521
|
await rebind.enqueueRebind(session, services);
|
|
510
522
|
const frameDispatch = createFrameDispatchContext({
|
|
511
|
-
nodeId, cfg, registry, toolGroups, projection, rebind,
|
|
523
|
+
nodeId, cfg, registry, toolGroups, projection, rebind, boundaryReviewId,
|
|
512
524
|
pendingDialogs, sendWelcome, replayExtraPendingDialogsTo, reWelcomeAll, disposeAndExit,
|
|
513
525
|
persistModelChoice, broadcastModelChanged, registryOf, formatModelSpec,
|
|
514
526
|
formatExactModelSpec, resolveLaunchModel,
|
|
@@ -594,9 +606,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
594
606
|
// A daemon fleet bounce gives a busy Pi a short, bounded chance to persist
|
|
595
607
|
// its aborted turn before teardown. `session.abort()` resolves only after Pi
|
|
596
608
|
// reaches idle (and thus after its message_end persistence); only then may we
|
|
597
|
-
// leave the durable clean-abort certificate that
|
|
598
|
-
//
|
|
599
|
-
//
|
|
609
|
+
// leave the durable clean-abort certificate that preserves the otherwise
|
|
610
|
+
// cleared evidence that this broker had a turn in flight. A timeout/rejection
|
|
611
|
+
// deliberately leaves no certificate: the surviving busy marker carries the
|
|
612
|
+
// same interrupted-turn fact through the fleet's shared SIGKILL deadline.
|
|
600
613
|
const ABORT_BEFORE_EXIT_MS = 2_500;
|
|
601
614
|
let signalExitInFlight = false;
|
|
602
615
|
const abortAndExit = async (reason) => {
|
|
@@ -633,8 +646,8 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
633
646
|
}
|
|
634
647
|
else if (frameDispatch.pendingPromptDispatches() > 0 || rebind.isAgentRunActive()) {
|
|
635
648
|
// The engine never reached an abortable persisted turn in the budget.
|
|
636
|
-
// Preserve the
|
|
637
|
-
//
|
|
649
|
+
// Preserve the dirty interrupted-turn witness so recovery resumes the
|
|
650
|
+
// saved transcript and explicitly re-drives the stopped work.
|
|
638
651
|
markBusy(nodeId);
|
|
639
652
|
}
|
|
640
653
|
}
|
|
@@ -671,13 +684,13 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
671
684
|
// conversation instead of just the post-yield window; single-cycle sessions and
|
|
672
685
|
// the fake-engine fixture (no tree accessors) fall through to plain
|
|
673
686
|
// `buildSessionContext().messages` unchanged.
|
|
674
|
-
export function snapshotMessages(session,
|
|
687
|
+
export function snapshotMessages(session, boundaryReviewId) {
|
|
675
688
|
// Cast: the real SDK's `SessionEntry.data` is `unknown` (untyped custom-entry
|
|
676
689
|
// payload), narrower than `CycleSessionManagerLike`'s structural read of the
|
|
677
690
|
// `crtr-cycle` marker's `{cycle, fromLeaf}` shape — the fields we read off it
|
|
678
691
|
// are ones ONLY this module's own `appendCustomEntry` call (above) ever writes.
|
|
679
692
|
const messages = cycleAwareMessages(session.sessionManager);
|
|
680
|
-
return visibleMessages(messages, {
|
|
693
|
+
return visibleMessages(messages, { boundaryReviewId });
|
|
681
694
|
}
|
|
682
695
|
// ---------------------------------------------------------------------------
|
|
683
696
|
// resolveLaunchModel — the SOLE launch model-selection policy, module-scoped
|
|
@@ -4,12 +4,13 @@ export declare function markBusy(nodeId: string): void;
|
|
|
4
4
|
/** Clear the mid-run marker after Pi settles, however it routed. Best-effort. */
|
|
5
5
|
export declare function clearBusy(nodeId: string): void;
|
|
6
6
|
/** Certify that this broker aborted a busy turn and Pi persisted its settled
|
|
7
|
-
* aborted entry before teardown.
|
|
8
|
-
*
|
|
7
|
+
* aborted entry before teardown. Settling clears the busy marker, so the daemon
|
|
8
|
+
* consumes this durable certificate as the surviving interrupted-turn witness
|
|
9
|
+
* and resumes with a continuation. A dirty death instead leaves `busy` behind. */
|
|
9
10
|
export declare function markCleanAbort(nodeId: string): void;
|
|
10
11
|
/** Remove a consumed clean-abort certificate as a broker boots. */
|
|
11
12
|
export declare function clearCleanAbort(nodeId: string): void;
|
|
12
|
-
/** Whether the dead broker left a persisted clean-abort
|
|
13
|
+
/** Whether the dead broker left a persisted clean-abort turn witness. */
|
|
13
14
|
export declare function hasCleanAbort(nodeId: string): boolean;
|
|
14
15
|
/** Is the node currently inside a turn? AND this with `pidAlive` at the call
|
|
15
16
|
* site — a stale marker from a crashed pi is harmless because the dead pid
|
|
@@ -44,8 +44,9 @@ export function clearBusy(nodeId) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
/** Certify that this broker aborted a busy turn and Pi persisted its settled
|
|
47
|
-
* aborted entry before teardown.
|
|
48
|
-
*
|
|
47
|
+
* aborted entry before teardown. Settling clears the busy marker, so the daemon
|
|
48
|
+
* consumes this durable certificate as the surviving interrupted-turn witness
|
|
49
|
+
* and resumes with a continuation. A dirty death instead leaves `busy` behind. */
|
|
49
50
|
export function markCleanAbort(nodeId) {
|
|
50
51
|
try {
|
|
51
52
|
mkdirSync(jobDir(nodeId), { recursive: true });
|
|
@@ -64,7 +65,7 @@ export function clearCleanAbort(nodeId) {
|
|
|
64
65
|
/* best-effort */
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
|
-
/** Whether the dead broker left a persisted clean-abort
|
|
68
|
+
/** Whether the dead broker left a persisted clean-abort turn witness. */
|
|
68
69
|
export function hasCleanAbort(nodeId) {
|
|
69
70
|
return existsSync(cleanAbortPath(nodeId));
|
|
70
71
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Env marker set on every pi the front door boots. Its presence means we are
|
|
2
|
+
* already inside a front-door-booted root, so a nested front-door launch must
|
|
3
|
+
* be refused — otherwise a removed/renamed subcommand that a child pi re-runs
|
|
4
|
+
* (e.g. `crtr node -h`) fork-bombs pi until the machine must be rebooted. */
|
|
5
|
+
export declare const FRONT_DOOR_ENV = "CRTR_FRONT_DOOR";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// The front-door recursion-guard env name, alone in a dependency-free module.
|
|
2
|
+
//
|
|
3
|
+
// `front-door.ts` owns the boot path and reaches the canvas through the
|
|
4
|
+
// dynamic imports inside `maybeBootRoot`; every module that only needs the
|
|
5
|
+
// MARKER NAME must not inherit that reach. The shipped broker extensions are
|
|
6
|
+
// gated on their static import graph, which cannot tell a lazily-imported boot
|
|
7
|
+
// dependency from an eagerly-loaded one, so importing the constant from
|
|
8
|
+
// `front-door.js` reads as an extension reaching canvas.db.
|
|
9
|
+
//
|
|
10
|
+
// `front-door.ts` re-exports this, so `FRONT_DOOR_ENV` stays importable from
|
|
11
|
+
// either module and every pi-booting path keeps setting the same marker.
|
|
12
|
+
/** Env marker set on every pi the front door boots. Its presence means we are
|
|
13
|
+
* already inside a front-door-booted root, so a nested front-door launch must
|
|
14
|
+
* be refused — otherwise a removed/renamed subcommand that a child pi re-runs
|
|
15
|
+
* (e.g. `crtr node -h`) fork-bombs pi until the machine must be rebooted. */
|
|
16
|
+
export const FRONT_DOOR_ENV = 'CRTR_FRONT_DOOR';
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* already inside a front-door-booted root, so a nested front-door launch must
|
|
3
|
-
* be refused — otherwise a removed/renamed subcommand that a child pi re-runs
|
|
4
|
-
* (e.g. `crtr node -h`) fork-bombs pi until the machine must be rebooted. */
|
|
5
|
-
export declare const FRONT_DOOR_ENV = "CRTR_FRONT_DOOR";
|
|
1
|
+
export { FRONT_DOOR_ENV } from './front-door-env.js';
|
|
6
2
|
/** If this invocation is a front-door (root) launch, boot it and never return.
|
|
7
3
|
* Returns false when it's a recognized subcommand / help / unknown token (let
|
|
8
4
|
* the dispatcher handle it — for unknown tokens it errors cleanly).
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { existsSync, statSync } from 'node:fs';
|
|
22
22
|
import { resolve as resolvePath } from 'node:path';
|
|
23
23
|
import { SUBTREE_NAMES } from '../../build-root.js';
|
|
24
|
+
import { FRONT_DOOR_ENV } from './front-door-env.js';
|
|
24
25
|
// NOTE: the boot-path deps — ./boot-root.js (bootRoot), ./resume-root.js (resumeRoot),
|
|
25
26
|
// ./package-health.js (missingPackageWarning + piAgentDir) — are DYNAMICALLY
|
|
26
27
|
// imported inside maybeBootRoot, NOT statically here. `cli.ts` calls
|
|
@@ -89,11 +90,7 @@ function parseRootArgs(tokens) {
|
|
|
89
90
|
const prompt = positionals.length > 0 ? positionals.join(' ') : undefined;
|
|
90
91
|
return { cwd, prompt, name, kind, profile, pickProfile, resume };
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
-
* already inside a front-door-booted root, so a nested front-door launch must
|
|
94
|
-
* be refused — otherwise a removed/renamed subcommand that a child pi re-runs
|
|
95
|
-
* (e.g. `crtr node -h`) fork-bombs pi until the machine must be rebooted. */
|
|
96
|
-
export const FRONT_DOOR_ENV = 'CRTR_FRONT_DOOR';
|
|
93
|
+
export { FRONT_DOOR_ENV } from './front-door-env.js';
|
|
97
94
|
/** If this invocation is a front-door (root) launch, boot it and never return.
|
|
98
95
|
* Returns false when it's a recognized subcommand / help / unknown token (let
|
|
99
96
|
* the dispatcher handle it — for unknown tokens it errors cleanly).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type NodeRow } from '../canvas/index.js';
|
|
2
|
-
import type { PiInvocation } from './
|
|
2
|
+
import type { PiInvocation } from './invocation.js';
|
|
3
3
|
/** View-side launch hints the broker host needs beyond the PiInvocation. The
|
|
4
4
|
* broker uses `cwd` + `resuming`; `name` is carried for the node's display. */
|
|
5
5
|
export interface LaunchPlacement {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The broker-launch recipe shape, alone in a dependency-free module.
|
|
2
|
+
//
|
|
3
|
+
// `launch.ts` builds a PiInvocation and therefore reaches canvas state; the
|
|
4
|
+
// modules that only need to READ the recipe shape (spawn-env, host) must not.
|
|
5
|
+
// A type-only import is erased at compile time, but the shipped broker
|
|
6
|
+
// extensions are gated on their STATIC import graph — the gate cannot tell an
|
|
7
|
+
// erased edge from a live one, so an extension that reaches spawn-env would
|
|
8
|
+
// read as reaching canvas.db through launch.ts. Keeping the interface here
|
|
9
|
+
// makes the graph say what the runtime already does.
|
|
10
|
+
export {};
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, readdirSync, } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { contextDir, reportsDir, getNode, subscriptionsOf, subscribersOf, } from '../canvas/index.js';
|
|
23
|
+
import { activeBackgroundBashJobs, bashJobCommandLine, formatBashElapsed } from '../bash-jobs.js';
|
|
23
24
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
24
25
|
import { buildWakeBearings } from './bearings.js';
|
|
25
26
|
import { personaDrift, commitPersonaAck } from './persona.js';
|
|
@@ -205,6 +206,33 @@ function feedBlock(nodeId, unreadDigest) {
|
|
|
205
206
|
}
|
|
206
207
|
return `<feed>\n${lines.join('\n')}\n</feed>`;
|
|
207
208
|
}
|
|
209
|
+
/** One-line label for a job: its purpose when the caller gave one, else its real
|
|
210
|
+
* command line, clipped so a long pipeline stays scannable. */
|
|
211
|
+
function jobLabel(purpose, command) {
|
|
212
|
+
const raw = (purpose ?? bashJobCommandLine(command)).trim() || '(no command)';
|
|
213
|
+
return raw.length > 120 ? `${raw.slice(0, 117)}…` : raw;
|
|
214
|
+
}
|
|
215
|
+
/** Live file-backed background bash jobs this node owns. A fresh revive has no
|
|
216
|
+
* memory of handing anything off, so without this block the node cannot tell a
|
|
217
|
+
* command it started is still running: it can neither read the log nor decide
|
|
218
|
+
* whether to wait, and may relaunch the same work. Returns null — and the
|
|
219
|
+
* kickoff omits the block entirely — when nothing is live. */
|
|
220
|
+
function backgroundJobsBlock(nodeId) {
|
|
221
|
+
const jobs = activeBackgroundBashJobs(contextDir(nodeId));
|
|
222
|
+
if (jobs.length === 0)
|
|
223
|
+
return null;
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
const one = jobs.length === 1;
|
|
226
|
+
const lines = [
|
|
227
|
+
`${jobs.length} background bash job${one ? '' : 's'} you started ${one ? 'is' : 'are'} STILL RUNNING, detached from any turn and unaffected by your refresh. ` +
|
|
228
|
+
`Each one sends you a message when it exits — that wake is automatic, so do not poll it and do not relaunch the same command.`,
|
|
229
|
+
];
|
|
230
|
+
for (const job of jobs) {
|
|
231
|
+
lines.push(` - ${job.jobId} — ${jobLabel(job.purpose, job.command)} · running ${formatBashElapsed(now - job.startedAtMs)} · log ${job.logPath}`);
|
|
232
|
+
}
|
|
233
|
+
lines.push('', 'Read output so far: `tail -n 50 <log>` (or `crtr node bash list --tail 20`). Abandon one: `crtr node bash kill <job-id>`.');
|
|
234
|
+
return `<background-jobs>\n${lines.join('\n')}\n</background-jobs>`;
|
|
235
|
+
}
|
|
208
236
|
// ---------------------------------------------------------------------------
|
|
209
237
|
// buildReviveKickoff — assemble the full fresh-revive first message.
|
|
210
238
|
// ---------------------------------------------------------------------------
|
|
@@ -257,6 +285,12 @@ export function buildReviveKickoff(meta, bearings, wakeReason) {
|
|
|
257
285
|
// revive (a new pi process boot) and re-injects buildContextIntro(), which
|
|
258
286
|
// already carries the sidecar as a hidden sibling of <crtr-bearings>; that seam
|
|
259
287
|
// covers this path, so duplicating it into the visible prompt would leak it.
|
|
288
|
+
// Live background bash jobs sit beside the context-dir listing: both describe
|
|
289
|
+
// this node's OWN in-flight state, and both precede <feed>, which is about the
|
|
290
|
+
// other nodes it awaits. Absent when nothing is running.
|
|
291
|
+
const jobsBlock = backgroundJobsBlock(nodeId);
|
|
292
|
+
if (jobsBlock !== null)
|
|
293
|
+
parts.push(jobsBlock);
|
|
260
294
|
parts.push(feedBlock(nodeId, bearings.unreadDigest));
|
|
261
295
|
parts.push(bearings.yieldMsg !== null
|
|
262
296
|
? `<yield-message>\n${bearings.yieldMsg.trim()}\n</yield-message>`
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PiInvocation } from './invocation.js';
|
|
1
2
|
export { CANVAS_EXTENSIONS, CANVAS_STOPHOOK_PATH, CANVAS_INBOX_WATCHER_PATH, CANVAS_RECAP_PATH, CANVAS_GOAL_CAPTURE_PATH, CANVAS_PASSIVE_CONTEXT_PATH, CANVAS_CONTEXT_INTRO_PATH, CANVAS_TOOL_GUIDE_PATH, CANVAS_DOC_SUBSTRATE_PATH, CANVAS_STRUCTURED_OUTPUT_PATH, CANVAS_BASH_VALVE_PATH, } from './canvas-extensions.js';
|
|
2
3
|
import type { NodeMeta, LaunchSpec, Mode, Lifecycle } from '../canvas/index.js';
|
|
3
4
|
import type { ModelProvider, ModelStrength, ScopeConfig } from '../../types.js';
|
|
@@ -104,12 +105,7 @@ export declare function buildLaunchSpec(kind: string, mode: Mode, opts: {
|
|
|
104
105
|
launch: LaunchSpec;
|
|
105
106
|
lifecycle: 'terminal' | 'resident';
|
|
106
107
|
};
|
|
107
|
-
export
|
|
108
|
-
/** argv after the `pi` binary. */
|
|
109
|
-
argv: string[];
|
|
110
|
-
/** env to merge into the process. */
|
|
111
|
-
env: Record<string, string>;
|
|
112
|
-
}
|
|
108
|
+
export type { PiInvocation } from './invocation.js';
|
|
113
109
|
/** Construct the pi invocation for a node.
|
|
114
110
|
* - fresh start: pass `prompt` (the node's first user message), no resume.
|
|
115
111
|
* - fork start: pass `forkFrom` (absolute .jsonl path or partial uuid) to `--fork`
|
|
@@ -152,7 +152,7 @@ export async function readNodeSnapshot(nodeId) {
|
|
|
152
152
|
copyFileSync(sessionFile, copy);
|
|
153
153
|
const manager = SessionManager.open(copy);
|
|
154
154
|
const context = manager.buildSessionContext();
|
|
155
|
-
const messages = visibleMessages(cycleAwareMessages(manager), {
|
|
155
|
+
const messages = visibleMessages(cycleAwareMessages(manager), { boundaryReviewId: node.review_binding?.review_id });
|
|
156
156
|
const sessionId = manager.getSessionId();
|
|
157
157
|
const sessionName = manager.getSessionName();
|
|
158
158
|
const snapshot = {
|
|
@@ -179,6 +179,9 @@ export async function readNodeSnapshot(nodeId) {
|
|
|
179
179
|
// process state (last setStatus/setWidget/setTitle) and is not persisted,
|
|
180
180
|
// so an offline read has none.
|
|
181
181
|
display: { statuses: {}, widgets: {}, title: undefined },
|
|
182
|
+
// The unrun queue lives in the engine's memory; a dormant node has no
|
|
183
|
+
// engine, so nothing is waiting to run.
|
|
184
|
+
queued: { steering: [], followUp: [] },
|
|
182
185
|
// Sidecar summaries survive broker exit so a dormant transcript receives
|
|
183
186
|
// the same completed-group data as a live welcome snapshot. Malformed or
|
|
184
187
|
// absent sidecars are deliberately ignored: tool calls remain visible.
|
|
@@ -19,6 +19,7 @@ import { memoryDir } from './memory.js';
|
|
|
19
19
|
import { resolveInstallId } from '../canvas/install-id.js';
|
|
20
20
|
import { canvasDbPath } from '../canvas/paths.js';
|
|
21
21
|
import { usage } from '../errors.js';
|
|
22
|
+
import { assertProfileAvailableForBirth } from '../profiles/deletion-reservation.js';
|
|
22
23
|
// crtrd sets this once at boot (setInstallId) and it wins unconditionally —
|
|
23
24
|
// a daemon process only ever serves one canvas home for its whole lifetime,
|
|
24
25
|
// so no keying is needed there. Every OTHER in-process caller (tests,
|
|
@@ -259,6 +260,7 @@ export function spawnNode(opts) {
|
|
|
259
260
|
if (parent !== null && getNode(parent) === null) {
|
|
260
261
|
throw new Error(`cannot spawn under unknown parent node: ${parent}`);
|
|
261
262
|
}
|
|
263
|
+
assertProfileAvailableForBirth(meta.profile_id ?? null);
|
|
262
264
|
createNode(meta);
|
|
263
265
|
// Create the node-local memory directory so substrate docs can be written
|
|
264
266
|
// directly into it without a separate mkdir. Skip for ephemeral human-bridge
|
|
@@ -19,6 +19,7 @@ export declare function reshapeNode(nodeId: string, opts?: {
|
|
|
19
19
|
model?: string;
|
|
20
20
|
mode?: Mode;
|
|
21
21
|
lifecycle?: Lifecycle;
|
|
22
|
+
profileId?: string | null;
|
|
22
23
|
}, beforePersist?: () => void): NodeMeta;
|
|
23
24
|
/** Promote a node to an orchestrator (mode→orchestrator), optionally
|
|
24
25
|
* specializing its kind (e.g. a `general` worker becoming a
|
|
@@ -42,6 +42,7 @@ export function reshapeNode(nodeId, opts = {}, beforePersist) {
|
|
|
42
42
|
const targetModel = opts.model ?? node.model_override ?? undefined;
|
|
43
43
|
const targetMode = opts.mode ?? node.mode;
|
|
44
44
|
const targetLifecycle = opts.lifecycle ?? node.lifecycle;
|
|
45
|
+
const targetProfileId = opts.profileId === undefined ? node.profile_id : opts.profileId;
|
|
45
46
|
// Rewrite the launch spec to the target kind/mode's persona so the *next*
|
|
46
47
|
// revive comes back as this exact shape (polymorph stage 2). nodeEnv reads
|
|
47
48
|
// meta.{kind,mode}, so CRTR_KIND/CRTR_MODE flip immediately for the live
|
|
@@ -66,7 +67,7 @@ export function reshapeNode(nodeId, opts = {}, beforePersist) {
|
|
|
66
67
|
// Resolve kind/ladder config against the NODE's own cwd + profile, not
|
|
67
68
|
// this process's ambient scope.
|
|
68
69
|
cwd: node.cwd,
|
|
69
|
-
profileId:
|
|
70
|
+
profileId: targetProfileId,
|
|
70
71
|
});
|
|
71
72
|
// Promotion seeds after its target launch has been built but before the
|
|
72
73
|
// mutation is persisted, preserving the original promote() failure order.
|
|
@@ -81,6 +82,7 @@ export function reshapeNode(nodeId, opts = {}, beforePersist) {
|
|
|
81
82
|
kind: targetKind,
|
|
82
83
|
mode: targetMode,
|
|
83
84
|
lifecycle: targetLifecycle,
|
|
85
|
+
profile_id: targetProfileId,
|
|
84
86
|
launch,
|
|
85
87
|
...(carriesDefaultHandle ? { name: targetKind } : {}),
|
|
86
88
|
// Persist a newly-chosen tier so it is durable across future revives; omit
|
|
@@ -20,6 +20,12 @@ export interface CycleSessionManagerLike {
|
|
|
20
20
|
* path root-first. */
|
|
21
21
|
getBranch?(fromId?: string): SessionEntry[];
|
|
22
22
|
}
|
|
23
|
+
/** True when an assistant turn ended because its own abort fired — an ESC, a
|
|
24
|
+
* critical-tier interrupt, or the self-abort a refresh-yield performs. */
|
|
25
|
+
export declare function endedByAbort(message: {
|
|
26
|
+
stopReason?: string;
|
|
27
|
+
errorMessage?: string;
|
|
28
|
+
}): boolean;
|
|
23
29
|
/** A refresh-yield deliberately aborts its own turn before the next cycle is
|
|
24
30
|
* rooted. Preserve that assistant message (including tool calls and prose), but
|
|
25
31
|
* omit the failure annotation: the cycle divider is the truthful visible seam. */
|
|
@@ -32,11 +32,25 @@ function cycleDivider(cycle, timestamp) {
|
|
|
32
32
|
timestamp: new Date(timestamp).getTime(),
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
/** Text a provider attaches when the request's own AbortSignal fired. Pi's
|
|
36
|
+
* providers stamp `stopReason: 'aborted'` when they observe the signal
|
|
37
|
+
* themselves, but an abort that lands while the next request is starting
|
|
38
|
+
* reaches their catch as a bare AbortError instead, and is recorded as
|
|
39
|
+
* `stopReason: 'error'` carrying this text. Both spellings are the same healthy
|
|
40
|
+
* event — a turn that stopped because something asked it to stop. */
|
|
41
|
+
const ABORT_ERROR_TEXT = /\b(?:request|operation) was aborted\b|\bthe user aborted\b/i;
|
|
42
|
+
/** True when an assistant turn ended because its own abort fired — an ESC, a
|
|
43
|
+
* critical-tier interrupt, or the self-abort a refresh-yield performs. */
|
|
44
|
+
export function endedByAbort(message) {
|
|
45
|
+
if (message.stopReason === 'aborted')
|
|
46
|
+
return true;
|
|
47
|
+
return message.stopReason === 'error' && ABORT_ERROR_TEXT.test(message.errorMessage ?? '');
|
|
48
|
+
}
|
|
35
49
|
/** A refresh-yield deliberately aborts its own turn before the next cycle is
|
|
36
50
|
* rooted. Preserve that assistant message (including tool calls and prose), but
|
|
37
51
|
* omit the failure annotation: the cycle divider is the truthful visible seam. */
|
|
38
52
|
export function withoutYieldAbort(message) {
|
|
39
|
-
if (message.role !== 'assistant' || message
|
|
53
|
+
if (message.role !== 'assistant' || !endedByAbort(message))
|
|
40
54
|
return message;
|
|
41
55
|
const { stopReason: _stopReason, errorMessage: _errorMessage, ...visible } = message;
|
|
42
56
|
return visible;
|
|
@@ -12,18 +12,23 @@ export declare function isReviewBoundary(entry: unknown, reviewId?: string): boo
|
|
|
12
12
|
/**
|
|
13
13
|
* Project snapshot messages to the visible window for a review companion.
|
|
14
14
|
*
|
|
15
|
+
* A companion passes its binding's review id so the slice starts at ITS OWN
|
|
16
|
+
* boundary marker: a node forked from another companion inherits that
|
|
17
|
+
* companion's earlier marker, and slicing at the first marker of any review
|
|
18
|
+
* would reveal the origin companion's transcript.
|
|
19
|
+
*
|
|
15
20
|
* Contract (in evaluation order):
|
|
16
|
-
* 1.
|
|
17
|
-
*
|
|
18
|
-
* the person.
|
|
19
|
-
* 2. Not a
|
|
20
|
-
* nodes are not filtered.
|
|
21
|
-
* 3.
|
|
21
|
+
* 1. This binding's marker present (any marker when the node has no binding) →
|
|
22
|
+
* slice from the **first** occurrence (marker included) to the end.
|
|
23
|
+
* Everything before the marker is inherited context, hidden from the person.
|
|
24
|
+
* 2. Not a companion (`boundaryReviewId` undefined) → return input unchanged
|
|
25
|
+
* (same reference). Ordinary nodes are not filtered.
|
|
26
|
+
* 3. Bound but no matching marker → a leading compaction summary is replaced
|
|
22
27
|
* with a synthetic boundary while its retained tail remains visible. Every
|
|
23
28
|
* other markerless shape fails closed with the synthetic boundary alone.
|
|
24
29
|
*/
|
|
25
30
|
export declare function visibleMessages(messages: BrokerSnapshot['messages'], opts: {
|
|
26
|
-
|
|
31
|
+
boundaryReviewId: string | undefined;
|
|
27
32
|
}): BrokerSnapshot['messages'];
|
|
28
33
|
/**
|
|
29
34
|
* Project a session tree to the review companion's visible transcript boundary.
|
|
@@ -33,5 +38,9 @@ export declare function visibleMessages(messages: BrokerSnapshot['messages'], op
|
|
|
33
38
|
* boundary is crossed, its complete descendant subtree remains navigable. An
|
|
34
39
|
* absent boundary produces an empty tree, so picker previews never disclose an
|
|
35
40
|
* inherited entry before a mutation guard can reject its selection.
|
|
41
|
+
*
|
|
42
|
+
* `boundaryReviewId` selects only this binding's marker as a root, so a
|
|
43
|
+
* companion forked from another companion never navigates into the inherited
|
|
44
|
+
* subtree rooted at the origin's earlier marker.
|
|
36
45
|
*/
|
|
37
|
-
export declare function boundaryRootedTree(tree: readonly WireSessionTreeNode[]): WireSessionTreeNode[];
|
|
46
|
+
export declare function boundaryRootedTree(tree: readonly WireSessionTreeNode[], boundaryReviewId?: string): WireSessionTreeNode[];
|