@north-light/crouter 0.3.227 → 0.3.228
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 +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
- package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
- package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
- package/dist/builtin-memory/design/guide.md +38 -16
- package/dist/builtin-memory/design/roadmap.md +4 -4
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-memory/plan/guide.md +53 -0
- package/dist/builtin-memory/plan/roadmap.md +10 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
// Broker-local persona transition guidance. Canvas state is supplied by crtrd;
|
|
2
|
-
// this module reads only the node's own roadmap and durable memory files.
|
|
3
|
-
import { resolveMemoryDoc } from '../memory-resolver.js';
|
|
4
1
|
import { contextDir } from '../canvas/paths.js';
|
|
2
|
+
import { emitEvent } from '../events/emit.js';
|
|
3
|
+
import { brokerExtensionState, commitBrokerPersonaAck } from './broker/daemon-ops.js';
|
|
5
4
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
return '';
|
|
12
|
-
}
|
|
13
|
-
}
|
|
5
|
+
import { buildSubPersonaMenu, renderConfigChangeDelta } from '../substrate/render.js';
|
|
6
|
+
import { cloneContextExposureState, contextExposureStatesEqual, exposureTarget, loadContextExposureState, mergeContextExposureState, replaceContextExposureState, saveContextExposureState, sharedContextExposureState, } from '../substrate/injected-store.js';
|
|
7
|
+
import { formatCard } from '../../shared/generated-context.js';
|
|
14
8
|
function orchestratorContextNote(nodeId) {
|
|
15
9
|
return `Because you persist across refresh cycles, your context directory (${contextDir(nodeId)}) is also where a future cycle of you resumes the work — keep the working notes and decisions a refreshed you would need there, alongside the docs you share with the nodes you spawn.`;
|
|
16
10
|
}
|
|
17
11
|
function orchestrationGuidance(node) {
|
|
18
|
-
const kernel = loadMemoryBody('orchestration-kernel');
|
|
19
12
|
const roadmap = readRoadmap(node.node_id) ?? '(no roadmap yet)';
|
|
20
13
|
const rmPath = roadmapPath(node.node_id);
|
|
21
14
|
return [
|
|
22
15
|
`You are now a ${node.kind.toUpperCase()} ORCHESTRATOR. Your scarce resource is your own context window.`,
|
|
23
16
|
'Your job is to manage context and delegate — not to do the goal yourself.',
|
|
24
17
|
'',
|
|
25
|
-
|
|
26
|
-
'',
|
|
27
|
-
`Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach above. Current contents:`,
|
|
18
|
+
`Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach below. Current contents:`,
|
|
28
19
|
'',
|
|
29
20
|
roadmap,
|
|
30
21
|
'',
|
|
@@ -38,21 +29,125 @@ function orchestrationGuidance(node) {
|
|
|
38
29
|
function baseModeGuidance() {
|
|
39
30
|
return 'You are HANDS-ON again — base mode. Do the work yourself in THIS window and finish it here; stop delegating by default. You no longer drive a roadmap, so `crtr node yield` is not your exit. Spawn a child only for a cleanly separable unit, never as your first move.';
|
|
40
31
|
}
|
|
41
|
-
|
|
32
|
+
function subPersonaGuidance(from, to) {
|
|
33
|
+
if (from.kind === to.kind)
|
|
34
|
+
return '';
|
|
35
|
+
const before = buildSubPersonaMenu(from.kind);
|
|
36
|
+
const after = buildSubPersonaMenu(to.kind);
|
|
37
|
+
if (before === after)
|
|
38
|
+
return '';
|
|
39
|
+
return after === ''
|
|
40
|
+
? `The ${to.kind} kind has no specialist sub-personas available.`
|
|
41
|
+
: after;
|
|
42
|
+
}
|
|
43
|
+
function transitionGuidance(state, from, to, contextExposure) {
|
|
42
44
|
const sections = [];
|
|
43
45
|
if (from.mode !== to.mode) {
|
|
44
|
-
sections.push(to.mode === 'orchestrator' ? orchestrationGuidance(node) : baseModeGuidance());
|
|
45
|
-
}
|
|
46
|
-
if (from.lifecycle !== to.lifecycle) {
|
|
47
|
-
sections.push(loadMemoryBody(to.lifecycle === 'resident' ? 'turn-lifecycle/resident' : 'turn-lifecycle/terminal'));
|
|
46
|
+
sections.push(to.mode === 'orchestrator' ? orchestrationGuidance(state.node) : baseModeGuidance());
|
|
48
47
|
}
|
|
49
|
-
|
|
48
|
+
sections.push(subPersonaGuidance(from, to));
|
|
49
|
+
sections.push(renderConfigChangeDelta(state.subject, state.node.node_id, exposureTarget(contextExposure, 'transcript')));
|
|
50
|
+
return sections.filter((section) => section !== '').join('\n\n');
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const to = { mode: node.mode, lifecycle: node.lifecycle };
|
|
54
|
-
const
|
|
55
|
-
|
|
52
|
+
export function personaDriftFromBrokerState(state) {
|
|
53
|
+
const node = state.node;
|
|
54
|
+
const to = { kind: node.kind, mode: node.mode, lifecycle: node.lifecycle };
|
|
55
|
+
const ack = node.persona_ack;
|
|
56
|
+
const from = ack === undefined
|
|
57
|
+
? to
|
|
58
|
+
: { kind: ack.kind ?? node.kind, mode: ack.mode, lifecycle: ack.lifecycle };
|
|
59
|
+
if (from.kind === to.kind && from.mode === to.mode && from.lifecycle === to.lifecycle)
|
|
56
60
|
return null;
|
|
57
|
-
return { from, to
|
|
61
|
+
return { from, to };
|
|
62
|
+
}
|
|
63
|
+
/** A new context's current snapshots make a rank delta unnecessary. */
|
|
64
|
+
export async function acknowledgePersonaBaseline(state) {
|
|
65
|
+
const drift = personaDriftFromBrokerState(state);
|
|
66
|
+
if (drift === null)
|
|
67
|
+
return false;
|
|
68
|
+
const result = await commitBrokerPersonaAck(state.node.node_id, drift);
|
|
69
|
+
if (!result.applied)
|
|
70
|
+
throw new Error(`persona baseline acknowledgment was rejected for ${state.node.node_id}`);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
/** Append pending transitions until the delivered persona matches current canvas state. */
|
|
74
|
+
export async function deliverPendingPersonaTransition(initialState, deliver) {
|
|
75
|
+
const nodeId = initialState.node.node_id;
|
|
76
|
+
const live = sharedContextExposureState(nodeId);
|
|
77
|
+
const persisted = loadContextExposureState(nodeId);
|
|
78
|
+
mergeContextExposureState(live, persisted);
|
|
79
|
+
if (live.preferenceSnapshot === null) {
|
|
80
|
+
throw new Error(`context preference snapshot is missing for ${nodeId}`);
|
|
81
|
+
}
|
|
82
|
+
if (!contextExposureStatesEqual(live, persisted))
|
|
83
|
+
saveContextExposureState(nodeId, live);
|
|
84
|
+
let state = initialState;
|
|
85
|
+
let daemonDrift = personaDriftFromBrokerState(state);
|
|
86
|
+
if (daemonDrift === null)
|
|
87
|
+
return false;
|
|
88
|
+
const next = cloneContextExposureState(live);
|
|
89
|
+
let represented = daemonDrift.from;
|
|
90
|
+
for (;;) {
|
|
91
|
+
const current = {
|
|
92
|
+
kind: state.node.kind,
|
|
93
|
+
mode: state.node.mode,
|
|
94
|
+
lifecycle: state.node.lifecycle,
|
|
95
|
+
};
|
|
96
|
+
if (represented.kind !== current.kind
|
|
97
|
+
|| represented.mode !== current.mode
|
|
98
|
+
|| represented.lifecycle !== current.lifecycle) {
|
|
99
|
+
const guidance = transitionGuidance(state, represented, current, next);
|
|
100
|
+
if (guidance !== '')
|
|
101
|
+
await deliver(formatCard('persona-transition', {}, guidance));
|
|
102
|
+
represented = current;
|
|
103
|
+
replaceContextExposureState(live, next);
|
|
104
|
+
saveContextExposureState(nodeId, live);
|
|
105
|
+
}
|
|
106
|
+
daemonDrift = personaDriftFromBrokerState(state);
|
|
107
|
+
if (daemonDrift === null)
|
|
108
|
+
return true;
|
|
109
|
+
const result = await commitBrokerPersonaAck(nodeId, daemonDrift);
|
|
110
|
+
if (result.applied)
|
|
111
|
+
return true;
|
|
112
|
+
state = await brokerExtensionState(nodeId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Gate each provider request on durable delivery of the current persona delta. */
|
|
116
|
+
export function installPersonaTransitionGate(nodeId, session) {
|
|
117
|
+
const { agent } = session;
|
|
118
|
+
const previousTransform = agent.transformContext;
|
|
119
|
+
agent.transformContext = async (messages, signal) => {
|
|
120
|
+
const transformed = previousTransform === undefined
|
|
121
|
+
? messages
|
|
122
|
+
: await previousTransform(messages, signal);
|
|
123
|
+
try {
|
|
124
|
+
const state = await brokerExtensionState(nodeId);
|
|
125
|
+
await deliverPendingPersonaTransition(state, (content) => {
|
|
126
|
+
const details = { nodeId };
|
|
127
|
+
const message = {
|
|
128
|
+
role: 'custom',
|
|
129
|
+
customType: 'crtr-persona-transition',
|
|
130
|
+
content,
|
|
131
|
+
display: true,
|
|
132
|
+
details,
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
};
|
|
135
|
+
session.sessionManager.appendCustomMessageEntry('crtr-persona-transition', content, true, details);
|
|
136
|
+
const active = agent.state.messages;
|
|
137
|
+
for (const context of new Set([active, messages, transformed]))
|
|
138
|
+
context.push(message);
|
|
139
|
+
});
|
|
140
|
+
return transformed;
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
emitEvent({
|
|
144
|
+
event: 'broker.daemon.operation.failed',
|
|
145
|
+
level: 'error',
|
|
146
|
+
node_id: nodeId,
|
|
147
|
+
error,
|
|
148
|
+
fields: { operation: 'persona_transition' },
|
|
149
|
+
});
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
58
153
|
}
|
|
@@ -15,10 +15,22 @@ export interface LiveCustomMessage {
|
|
|
15
15
|
* warm claim it is fatal: a node wearing the wrong bearings is worse than a
|
|
16
16
|
* slow one, so the spare is abandoned and the create goes cold). */
|
|
17
17
|
export declare function deliverCustomMessageLive(nodeId: string, message: LiveCustomMessage): Promise<void>;
|
|
18
|
-
/** Build and deliver `nodeId`'s bearings into its live session
|
|
19
|
-
*
|
|
20
|
-
* the
|
|
21
|
-
*
|
|
18
|
+
/** Build and deliver `nodeId`'s bearings into its live session, and freeze the
|
|
19
|
+
* preference snapshot that goes with them. Called by the warm-pool claim once
|
|
20
|
+
* the row already wears the claimant's kind/mode/name, so the identity
|
|
21
|
+
* assertion, the knowledge catalog, the `<project_context>` environment
|
|
22
|
+
* snapshot, and the preference block are all built for the node being handed
|
|
23
|
+
* out.
|
|
24
|
+
*
|
|
25
|
+
* The snapshot is frozen HERE for the same reason bearings are built here: a
|
|
26
|
+
* spare boots with no claimant, so `canvas-context-intro` skips warm spares
|
|
27
|
+
* and the block would otherwise assert the SPARE's preferences. Claim is the
|
|
28
|
+
* first moment the real persona, profile, and cwd exist, and it is the last
|
|
29
|
+
* moment before the claimant's first turn — which reaches the provider through
|
|
30
|
+
* the persona gate, and which need not be a typed prompt. A slash command
|
|
31
|
+
* opens its turn through `sendMessage({ triggerTurn: true })`, and Pi emits
|
|
32
|
+
* `before_agent_start` only on the typed-prompt path, so the broker's own
|
|
33
|
+
* freeze in `canvas-doc-substrate` never runs for it.
|
|
22
34
|
*
|
|
23
35
|
* The `details.nodeId` stamp is what the context-intro extension's idempotency
|
|
24
36
|
* guard matches on, so a LATER revive of this node sees its own bearings
|
|
@@ -21,7 +21,9 @@ import { getNode } from '../canvas/index.js';
|
|
|
21
21
|
import { oneShotControllerRequest } from './broker-request.js';
|
|
22
22
|
import { isBrokerLive } from './model-swap.js';
|
|
23
23
|
import { buildContextBearings, CONTEXT_INTRO_CUSTOM_TYPE } from './bearings.js';
|
|
24
|
-
import {
|
|
24
|
+
import { renderPreferencesForNodeSubject } from './broker-extension-render.js';
|
|
25
|
+
import { assembleNodeSubject } from '../substrate/subject.js';
|
|
26
|
+
import { exposureTarget, freezePreferenceSnapshot, loadContextExposureState, saveContextExposureState, } from '../substrate/injected-store.js';
|
|
25
27
|
import { formatCard } from '../../shared/generated-context.js';
|
|
26
28
|
/** Deliver `message` into `nodeId`'s live session. Resolves without doing
|
|
27
29
|
* anything when the node has no live broker. Rejects when a live broker cannot
|
|
@@ -48,27 +50,39 @@ export async function deliverCustomMessageLive(nodeId, message) {
|
|
|
48
50
|
result: () => undefined,
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
|
-
/** Build and deliver `nodeId`'s bearings into its live session
|
|
52
|
-
*
|
|
53
|
-
* the
|
|
54
|
-
*
|
|
53
|
+
/** Build and deliver `nodeId`'s bearings into its live session, and freeze the
|
|
54
|
+
* preference snapshot that goes with them. Called by the warm-pool claim once
|
|
55
|
+
* the row already wears the claimant's kind/mode/name, so the identity
|
|
56
|
+
* assertion, the knowledge catalog, the `<project_context>` environment
|
|
57
|
+
* snapshot, and the preference block are all built for the node being handed
|
|
58
|
+
* out.
|
|
59
|
+
*
|
|
60
|
+
* The snapshot is frozen HERE for the same reason bearings are built here: a
|
|
61
|
+
* spare boots with no claimant, so `canvas-context-intro` skips warm spares
|
|
62
|
+
* and the block would otherwise assert the SPARE's preferences. Claim is the
|
|
63
|
+
* first moment the real persona, profile, and cwd exist, and it is the last
|
|
64
|
+
* moment before the claimant's first turn — which reaches the provider through
|
|
65
|
+
* the persona gate, and which need not be a typed prompt. A slash command
|
|
66
|
+
* opens its turn through `sendMessage({ triggerTurn: true })`, and Pi emits
|
|
67
|
+
* `before_agent_start` only on the typed-prompt path, so the broker's own
|
|
68
|
+
* freeze in `canvas-doc-substrate` never runs for it.
|
|
55
69
|
*
|
|
56
70
|
* The `details.nodeId` stamp is what the context-intro extension's idempotency
|
|
57
71
|
* guard matches on, so a LATER revive of this node sees its own bearings
|
|
58
72
|
* already on the branch and does not inject a second copy. */
|
|
59
73
|
export async function deliverBearingsLive(nodeId) {
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
// the on-disk set before each render, so docs delivered here never
|
|
65
|
-
// re-deliver on a later read. Plain load/save (not the process-shared
|
|
66
|
-
// cache): the daemon is long-lived and serves many nodes.
|
|
67
|
-
const seen = loadInjectedDocs(nodeId);
|
|
74
|
+
// Claim runs in the daemon while the spare's Pi process is already alive.
|
|
75
|
+
// Persist transcript registrations after Pi accepts the bearings so the
|
|
76
|
+
// broker merges the same context state before later event deliveries.
|
|
77
|
+
const contextExposure = loadContextExposureState(nodeId);
|
|
68
78
|
await deliverCustomMessageLive(nodeId, {
|
|
69
79
|
customType: CONTEXT_INTRO_CUSTOM_TYPE,
|
|
70
|
-
content: formatCard('bearings', {}, buildContextBearings(nodeId,
|
|
80
|
+
content: formatCard('bearings', {}, buildContextBearings(nodeId, exposureTarget(contextExposure, 'transcript'))),
|
|
71
81
|
details: { nodeId },
|
|
72
82
|
});
|
|
73
|
-
|
|
83
|
+
const subject = assembleNodeSubject(nodeId);
|
|
84
|
+
if (subject === null)
|
|
85
|
+
throw new Error(`deliverBearingsLive: no document subject for ${nodeId}`);
|
|
86
|
+
freezePreferenceSnapshot(contextExposure, renderPreferencesForNodeSubject(nodeId, subject, exposureTarget(contextExposure, 'system')));
|
|
87
|
+
saveContextExposureState(nodeId, contextExposure);
|
|
74
88
|
}
|
|
@@ -32,12 +32,12 @@ export interface ReviveBearings {
|
|
|
32
32
|
/** Coalesced digest of unread reports, or null when the feed was empty. The
|
|
33
33
|
* cursor now records the last physically covered entry. */
|
|
34
34
|
unreadDigest: string | null;
|
|
35
|
-
/** Persona-transition
|
|
36
|
-
*
|
|
35
|
+
/** Persona-transition framing to surface when the node's role changed while
|
|
36
|
+
* it was away. The drift remains pending until context-intro completes. */
|
|
37
37
|
driftGuidance: string | null;
|
|
38
38
|
}
|
|
39
39
|
/** Capture revive bearings for `meta`: read the pending yield note, advance the
|
|
40
|
-
* feed cursor past unread reports, and capture
|
|
40
|
+
* feed cursor past unread reports, and capture external persona framing.
|
|
41
41
|
* Revive paths call it once, then pass the result to the pure kickoff builder. */
|
|
42
42
|
export declare function drainBearings(meta: NodeMeta): ReviveBearings;
|
|
43
43
|
/** Assemble the auto-injected first message for a FRESH revive of `meta` from its
|
|
@@ -24,7 +24,7 @@ import { contextDir, reportsDir, getNode, subscriptionsOf, subscribersOf, } from
|
|
|
24
24
|
import { activeBackgroundBashJobs, bashJobCommandLine, formatBashElapsed } from '../bash-jobs.js';
|
|
25
25
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
26
26
|
import { buildWakeBearings } from './bearings.js';
|
|
27
|
-
import { personaDrift
|
|
27
|
+
import { personaDrift } from './persona.js';
|
|
28
28
|
import { readInboxSince, readCursor, writeCursor, coalesce, } from '../feed/inbox.js';
|
|
29
29
|
import { formatCard } from '../../shared/generated-context.js';
|
|
30
30
|
/** The runtime-restart continuation exactly as delivered. `revive.ts` injects
|
|
@@ -102,7 +102,7 @@ export function listContextDir(nodeId) {
|
|
|
102
102
|
return readdirSync(dir).sort();
|
|
103
103
|
}
|
|
104
104
|
/** Capture revive bearings for `meta`: read the pending yield note, advance the
|
|
105
|
-
* feed cursor past unread reports, and capture
|
|
105
|
+
* feed cursor past unread reports, and capture external persona framing.
|
|
106
106
|
* Revive paths call it once, then pass the result to the pure kickoff builder. */
|
|
107
107
|
export function drainBearings(meta) {
|
|
108
108
|
const nodeId = meta.node_id;
|
|
@@ -119,15 +119,10 @@ export function drainBearings(meta) {
|
|
|
119
119
|
// join back into this block's body; the kickoff is one whole envelope.
|
|
120
120
|
unreadDigest = coalesce(entries).map((delivery) => delivery.text).join('\n\n');
|
|
121
121
|
}
|
|
122
|
-
// Capture + commit any external persona drift (the second of the two delivery
|
|
123
|
-
// sites). Committing the ack here is the mutation; the guidance is surfaced by
|
|
124
|
-
// the pure builder from this captured value.
|
|
125
122
|
const drift = personaDrift(nodeId);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
commitPersonaAck(nodeId, drift.to);
|
|
130
|
-
}
|
|
123
|
+
const driftGuidance = drift?.guidance === undefined || drift.guidance === ''
|
|
124
|
+
? null
|
|
125
|
+
: drift.guidance;
|
|
131
126
|
return { yieldMsg, unreadDigest, driftGuidance };
|
|
132
127
|
}
|
|
133
128
|
// Fresh-revive catch-up bug: on a refresh-yield (resume:false) the old
|
|
@@ -309,12 +304,9 @@ export function buildReviveKickoff(meta, bearings, wakeReason) {
|
|
|
309
304
|
: 'If there is work to do, perform it. Otherwise end your turn — you are working ' +
|
|
310
305
|
'directly with the user, so stay available and continue the conversation when they ' +
|
|
311
306
|
'write back.');
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// captured the guidance for its new persona and committed the ack (the second
|
|
316
|
-
// and only other delivery site); we just surface it. A clean fresh revive has
|
|
317
|
-
// no drift, so this is empty unless a real external change happened.
|
|
307
|
+
// Non-document framing for a persona change made while the node was away.
|
|
308
|
+
// The replacement context's current snapshots carry all eligible documents;
|
|
309
|
+
// context-intro acknowledges the transition after those snapshots persist.
|
|
318
310
|
if (bearings.driftGuidance !== null) {
|
|
319
311
|
parts.push(`<persona-transition>\nYour role was changed while you were away. ${bearings.driftGuidance}\n</persona-transition>`);
|
|
320
312
|
}
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
// writes both fields in ONE atomic statement (on the WAL'd row setters) so the
|
|
8
8
|
// two can never disagree.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// parallel, legible state machines instead of scattered enactment.
|
|
10
|
+
// Persona modules own context framing; this file owns which status/intent move
|
|
11
|
+
// is legal. The two state machines stay independent.
|
|
13
12
|
//
|
|
14
13
|
// Crash-safety invariant: flip status to a non-supervised value + clear intent
|
|
15
14
|
// BEFORE killing the host (tmux pane or headless broker) — the daemon only ever
|
|
@@ -65,11 +65,10 @@ export interface SpawnNodeOpts {
|
|
|
65
65
|
* `parent` — e.g. an independent root (parent=null) still records its
|
|
66
66
|
* spawner. Defaults to `parent`. */
|
|
67
67
|
spawnedBy?: string | null;
|
|
68
|
-
/**
|
|
69
|
-
* node id / session path / pi session uuid). Persisted to `meta.fork_from` so
|
|
70
|
-
* the boot intro can re-assert this node's OWN identity over the source's
|
|
71
|
-
* copied-in conversation. Omit for a fresh node. */
|
|
68
|
+
/** Raw `--fork-from` operand retained for identity framing and audit. */
|
|
72
69
|
forkFrom?: string | null;
|
|
70
|
+
/** Resolved absolute source session file retained for pre-bind retries. */
|
|
71
|
+
forkSourceFile?: string | null;
|
|
73
72
|
/** Suppress the spawn-time parent→child ACTIVE subscription. Default true
|
|
74
73
|
* (every existing caller keeps its edge). False gives ordinary canvas
|
|
75
74
|
* placement with no wake path back to the parent. */
|
|
@@ -237,14 +237,13 @@ export function spawnNode(opts) {
|
|
|
237
237
|
kind: opts.kind,
|
|
238
238
|
mode,
|
|
239
239
|
lifecycle,
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
// persona injector sees no drift on its first turn boundary.
|
|
243
|
-
persona_ack: { mode, lifecycle },
|
|
240
|
+
// Birth snapshots already represent this persona, so no transition is pending.
|
|
241
|
+
persona_ack: { kind: opts.kind, mode, lifecycle },
|
|
244
242
|
status: 'active',
|
|
245
243
|
parent,
|
|
246
244
|
spawned_by: spawnedBy,
|
|
247
245
|
fork_from: opts.forkFrom,
|
|
246
|
+
fork_source_file: opts.forkSourceFile,
|
|
248
247
|
review_binding: opts.reviewBinding,
|
|
249
248
|
model_override: opts.modelOverride ?? null,
|
|
250
249
|
passive_default: opts.passiveDefault ?? false,
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { type Mode, type Lifecycle } from '../canvas/index.js';
|
|
2
|
-
/** The
|
|
2
|
+
/** The three-axis persona transition key. */
|
|
3
3
|
export interface Persona {
|
|
4
|
+
kind: string;
|
|
4
5
|
mode: Mode;
|
|
5
6
|
lifecycle: Lifecycle;
|
|
6
7
|
}
|
|
7
8
|
export interface PersonaDriftResult {
|
|
8
9
|
from: Persona;
|
|
9
10
|
to: Persona;
|
|
10
|
-
/**
|
|
11
|
+
/** Fresh-context framing for `to`; its snapshots carry document context. */
|
|
11
12
|
guidance: string;
|
|
12
13
|
}
|
|
13
|
-
/** Build the
|
|
14
|
-
* Concatenates the relevant section per changed axis (both when both changed).
|
|
15
|
-
* Pure read of the node's roadmap/memory for the base→orchestrator case. */
|
|
14
|
+
/** Build only the non-document framing for a fresh-context transition. */
|
|
16
15
|
export declare function transitionGuidance(nodeId: string, from: Persona, to: Persona): string;
|
|
17
|
-
/** Compare
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* guidance, then `commitPersonaAck`s the new state. An unset `persona_ack`
|
|
21
|
-
* (legacy node) defaults to the current persona, so it reads as no drift and
|
|
22
|
-
* never fabricates spurious guidance. */
|
|
16
|
+
/** Compare the live persona with the last acknowledged context delivery.
|
|
17
|
+
* `guidance` contains only fresh-context framing; current snapshots supply its
|
|
18
|
+
* documents. An absent or pre-kind acknowledgment cannot fabricate drift. */
|
|
23
19
|
export declare function personaDrift(nodeId: string): PersonaDriftResult | null;
|
|
24
|
-
/** Commit the persona state
|
|
20
|
+
/** Commit the persona state whose context delivery is complete. */
|
|
25
21
|
export declare function commitPersonaAck(nodeId: string, to: Persona): void;
|
|
@@ -1,75 +1,25 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// • mode — base (hands-on; yields mode-preservingly when another window is needed) ↔ orchestrator
|
|
5
|
-
// (delegates, holds a roadmap, survives refresh cycles + yields)
|
|
6
|
-
// • lifecycle — terminal (owes a final up the spine, reaps when done) ↔
|
|
7
|
-
// resident (interactable, never forced to submit; unattended
|
|
8
|
-
// obligation-free conversations complete after the daemon grace)
|
|
9
|
-
//
|
|
10
|
-
// Whenever EITHER axis changes from the value the node was last GIVEN guidance
|
|
11
|
-
// for, the node must be prompt-injected with guidance for its new state —
|
|
12
|
-
// automatically, here, not by each state-changing command. Commands just call
|
|
13
|
-
// `updateNode({ mode|lifecycle })`; this module is the single source of the
|
|
14
|
-
// transition prose, delivered from exactly two sites:
|
|
15
|
-
// • the stophook turn_end hook (self-changes this turn + external changes
|
|
16
|
-
// while the node is active), and
|
|
17
|
-
// • the revive kickoff (external changes made while the node was dormant).
|
|
18
|
-
//
|
|
19
|
-
// The `persona_ack` meta field records the last {mode,lifecycle} the node was
|
|
20
|
-
// given guidance for (born equal to its initial persona at spawn, so a fresh
|
|
21
|
-
// worker never gets spurious guidance). `personaDrift` compares live meta to it;
|
|
22
|
-
// the caller delivers the guidance, then commits the ack.
|
|
1
|
+
// Persona drift and the non-document framing carried by a fresh-revive kickoff.
|
|
2
|
+
// The new context's system and bearings snapshots supply its memory baseline;
|
|
3
|
+
// canvas-context-intro acknowledges the drift after both snapshots persist.
|
|
23
4
|
import { getNode, updateNode } from '../canvas/index.js';
|
|
24
|
-
import { resolveMemoryDoc } from '../memory-resolver.js';
|
|
25
5
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
26
6
|
import { orchestratorContextNote } from './bearings.js';
|
|
27
7
|
// ---------------------------------------------------------------------------
|
|
28
|
-
//
|
|
29
|
-
// promote.ts so the injector is the one place that builds it.
|
|
8
|
+
// Base → orchestrator guidance
|
|
30
9
|
// ---------------------------------------------------------------------------
|
|
31
|
-
/**
|
|
32
|
-
* if it can't be resolved. The single source for the lifecycle/spine/
|
|
33
|
-
* orchestration-kernel fragments this injector re-delivers on a transition —
|
|
34
|
-
* the SAME docs under `src/builtin-memory/`, so static
|
|
35
|
-
* (system-prompt splice) and transition prose can never drift. */
|
|
36
|
-
function loadMemoryBody(name) {
|
|
37
|
-
try {
|
|
38
|
-
return resolveMemoryDoc(name).body.trim();
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return '';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/** The base→orchestrator guidance dump, specialized to the node's kind: the
|
|
45
|
-
* shared orchestration-kernel doc + the roadmap scaffold the node must author +
|
|
46
|
-
* the orchestrator context-dir framing + a short pointer at the substrate
|
|
47
|
-
* memory flow (promotion re-informs of nothing about the stores, it only adds
|
|
48
|
-
* the across-cycles relevance). The node is now a delegator whose scarce
|
|
49
|
-
* resource is its own context window. Roadmap-shaping guidance is just another orchestrator-gated memory
|
|
50
|
-
* doc the node already gets via the boot-render splice (kinds/<kind>/orchestrator
|
|
51
|
-
* gated `{kind, mode: orchestrator}`). (Lifecycle is left to its own section —
|
|
52
|
-
* promotion does not force resident, so this never asserts residency.) */
|
|
10
|
+
/** Fresh-context framing for a base → orchestrator change. */
|
|
53
11
|
function orchestrationGuidance(nodeId, kind) {
|
|
54
|
-
const kernel = loadMemoryBody('orchestration-kernel');
|
|
55
12
|
const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
|
|
56
13
|
const rmPath = roadmapPath(nodeId);
|
|
57
14
|
const parts = [
|
|
58
15
|
`You are now a ${kind.toUpperCase()} ORCHESTRATOR. Your scarce resource is your own context window.`,
|
|
59
16
|
'Your job is to manage context and delegate — not to do the goal yourself.',
|
|
60
|
-
'',
|
|
61
|
-
kernel,
|
|
62
17
|
];
|
|
63
|
-
parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using
|
|
64
|
-
// The orchestrator framing for the context dir — the missing guidance a
|
|
65
|
-
// promoted node never got at spawn (it spawned as a base worker). Same note
|
|
66
|
-
// a born-orchestrator gets in its <crtr-bearings> block.
|
|
67
|
-
orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your bearings at boot. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
|
|
18
|
+
parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using your orchestration protocol. Current contents:`, '', roadmap, '', orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your bearings at boot. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
|
|
68
19
|
return parts.join('\n');
|
|
69
20
|
}
|
|
70
21
|
// ---------------------------------------------------------------------------
|
|
71
|
-
//
|
|
72
|
-
// agent (decision-first; one well-placed "don't").
|
|
22
|
+
// Orchestrator → base guidance
|
|
73
23
|
// ---------------------------------------------------------------------------
|
|
74
24
|
/** orchestrator → base (demote): hands-on again, finish in-window. */
|
|
75
25
|
function baseModeGuidance() {
|
|
@@ -77,57 +27,35 @@ function baseModeGuidance() {
|
|
|
77
27
|
'stop delegating by default. You no longer drive a roadmap, so `crtr node yield` is not your exit. ' +
|
|
78
28
|
'Spawn a child only for a cleanly separable unit, never as your first move.');
|
|
79
29
|
}
|
|
80
|
-
|
|
81
|
-
// static system prompt at birth — so both load from the one source
|
|
82
|
-
// (`turn-lifecycle/{terminal,resident}.md`) and can never drift. The flip
|
|
83
|
-
// only re-delivers that fragment as the node's new-state steer.
|
|
84
|
-
/** terminal → resident: interactable, never forced to submit. */
|
|
85
|
-
function residentLifecycleGuidance() {
|
|
86
|
-
return loadMemoryBody('turn-lifecycle/resident');
|
|
87
|
-
}
|
|
88
|
-
/** resident → terminal: owes a final, reaps when done. */
|
|
89
|
-
function terminalLifecycleGuidance() {
|
|
90
|
-
return loadMemoryBody('turn-lifecycle/terminal');
|
|
91
|
-
}
|
|
92
|
-
/** Build the injected transition prompt for a `from → to` persona change.
|
|
93
|
-
* Concatenates the relevant section per changed axis (both when both changed).
|
|
94
|
-
* Pure read of the node's roadmap/memory for the base→orchestrator case. */
|
|
30
|
+
/** Build only the non-document framing for a fresh-context transition. */
|
|
95
31
|
export function transitionGuidance(nodeId, from, to) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
else {
|
|
103
|
-
sections.push(baseModeGuidance());
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (from.lifecycle !== to.lifecycle) {
|
|
107
|
-
sections.push(to.lifecycle === 'resident' ? residentLifecycleGuidance() : terminalLifecycleGuidance());
|
|
108
|
-
}
|
|
109
|
-
return sections.join('\n\n');
|
|
32
|
+
if (from.mode === to.mode)
|
|
33
|
+
return '';
|
|
34
|
+
if (to.mode === 'base')
|
|
35
|
+
return baseModeGuidance();
|
|
36
|
+
const node = getNode(nodeId);
|
|
37
|
+
return orchestrationGuidance(nodeId, node?.kind ?? 'general');
|
|
110
38
|
}
|
|
111
39
|
// ---------------------------------------------------------------------------
|
|
112
40
|
// Detector + ack commit.
|
|
113
41
|
// ---------------------------------------------------------------------------
|
|
114
|
-
/** Compare
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* guidance, then `commitPersonaAck`s the new state. An unset `persona_ack`
|
|
118
|
-
* (legacy node) defaults to the current persona, so it reads as no drift and
|
|
119
|
-
* never fabricates spurious guidance. */
|
|
42
|
+
/** Compare the live persona with the last acknowledged context delivery.
|
|
43
|
+
* `guidance` contains only fresh-context framing; current snapshots supply its
|
|
44
|
+
* documents. An absent or pre-kind acknowledgment cannot fabricate drift. */
|
|
120
45
|
export function personaDrift(nodeId) {
|
|
121
46
|
const meta = getNode(nodeId);
|
|
122
47
|
if (meta === null)
|
|
123
48
|
return null;
|
|
124
|
-
const to = { mode: meta.mode, lifecycle: meta.lifecycle };
|
|
125
|
-
const
|
|
126
|
-
|
|
49
|
+
const to = { kind: meta.kind, mode: meta.mode, lifecycle: meta.lifecycle };
|
|
50
|
+
const ack = meta.persona_ack;
|
|
51
|
+
const from = ack === undefined
|
|
52
|
+
? to
|
|
53
|
+
: { kind: ack.kind ?? meta.kind, mode: ack.mode, lifecycle: ack.lifecycle };
|
|
54
|
+
if (from.kind === to.kind && from.mode === to.mode && from.lifecycle === to.lifecycle)
|
|
127
55
|
return null;
|
|
128
56
|
return { from, to, guidance: transitionGuidance(nodeId, from, to) };
|
|
129
57
|
}
|
|
130
|
-
/** Commit the persona state
|
|
58
|
+
/** Commit the persona state whose context delivery is complete. */
|
|
131
59
|
export function commitPersonaAck(nodeId, to) {
|
|
132
60
|
updateNode(nodeId, { persona_ack: to });
|
|
133
61
|
}
|
|
@@ -30,8 +30,8 @@ export declare function reshapeNodeAsync(nodeId: string, opts?: ReshapeOptions,
|
|
|
30
30
|
* `developer.orchestrator`) and optionally also making it resident. Idempotent:
|
|
31
31
|
* re-promoting just rewrites the spec. Seeds a roadmap SCAFFOLD if absent (a
|
|
32
32
|
* boss with no map is a failure mode) — no goal is forced here; authoring the
|
|
33
|
-
* goal + roadmap is the node's next act.
|
|
34
|
-
*
|
|
33
|
+
* goal + roadmap is the node's next act. Pending context delivery is owned by
|
|
34
|
+
* the broker provider gate, not this command. */
|
|
35
35
|
export declare function promote(nodeId: string, opts?: {
|
|
36
36
|
kind?: string;
|
|
37
37
|
resident?: boolean;
|