@north-light/crouter 0.3.226 → 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 +37 -9
- package/dist/daemon/api/bridge.d.ts +35 -4
- package/dist/daemon/api/bridge.js +50 -8
- 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,21 +1,28 @@
|
|
|
1
1
|
// Run with: node --import tsx/esm --test src/pi-extensions/__tests__/canvas-context-intro.test.ts
|
|
2
2
|
//
|
|
3
|
-
// A forked crouter branch receives
|
|
4
|
-
//
|
|
5
|
-
// the exact node-id stamp, independent of the bearings text.
|
|
3
|
+
// A forked crouter branch receives compact node bearings only with persisted
|
|
4
|
+
// transcript provenance. Persona drift is delivered at the provider gate.
|
|
6
5
|
import { test, before, after, beforeEach, afterEach } from 'node:test';
|
|
7
6
|
import assert from 'node:assert/strict';
|
|
8
|
-
import { existsSync, mkdtempSync, mkdirSync, rmSync } from 'node:fs';
|
|
7
|
+
import { existsSync, mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
8
|
import { tmpdir } from 'node:os';
|
|
10
9
|
import { join } from 'node:path';
|
|
11
10
|
import { registerCanvasContextIntro, CONTEXT_INTRO_CUSTOM_TYPE, } from '../canvas-context-intro.js';
|
|
11
|
+
import { registerCanvasDocSubstrate } from '../canvas-doc-substrate.js';
|
|
12
12
|
import { buildContextBearings as buildContextIntro } from '../../core/runtime/bearings.js';
|
|
13
|
-
import {
|
|
13
|
+
import { installPersonaTransitionGate } from '../../core/runtime/broker-persona-guidance.js';
|
|
14
|
+
import { createNode, deleteNode, getNode, updateNode } from '../../core/canvas/canvas.js';
|
|
14
15
|
import { apiSocketPath } from '../../core/canvas/paths.js';
|
|
15
16
|
import { createApiServer } from '../../daemon/api/server.js';
|
|
16
17
|
import { closeDb } from '../../core/canvas/db.js';
|
|
18
|
+
import { resetScopeCache } from '../../core/scope.js';
|
|
19
|
+
import { clearSessionCache } from '../../core/substrate/session-cache.js';
|
|
20
|
+
import { emptyContextExposureState, exposureTarget, initializeForkContextExposure, loadContextExposureState, registerExposure, saveContextExposureState, } from '../../core/substrate/injected-store.js';
|
|
17
21
|
let home;
|
|
22
|
+
let origCwd;
|
|
23
|
+
let origHome;
|
|
18
24
|
let origNode;
|
|
25
|
+
let origProfile;
|
|
19
26
|
let server;
|
|
20
27
|
async function waitForSocket(path) {
|
|
21
28
|
for (let i = 0; i < 200; i++) {
|
|
@@ -45,6 +52,7 @@ function introEntry(content) {
|
|
|
45
52
|
}
|
|
46
53
|
function makeFakePi() {
|
|
47
54
|
let handler;
|
|
55
|
+
let branch = [];
|
|
48
56
|
return {
|
|
49
57
|
sent: [],
|
|
50
58
|
on(event, h) {
|
|
@@ -53,41 +61,74 @@ function makeFakePi() {
|
|
|
53
61
|
},
|
|
54
62
|
sendMessage(m) {
|
|
55
63
|
this.sent.push(m);
|
|
64
|
+
branch.push({ type: 'custom_message', ...m });
|
|
56
65
|
},
|
|
57
66
|
registerMessageRenderer() {
|
|
58
67
|
/* display-only; irrelevant here */
|
|
59
68
|
},
|
|
60
|
-
async fireSessionStart(entries) {
|
|
61
|
-
|
|
69
|
+
async fireSessionStart(entries, reason) {
|
|
70
|
+
branch = entries;
|
|
71
|
+
await handler?.({ reason }, {
|
|
72
|
+
sessionManager: {
|
|
73
|
+
getEntries: () => branch,
|
|
74
|
+
getBranch: () => branch,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
62
77
|
},
|
|
63
78
|
};
|
|
64
79
|
}
|
|
65
80
|
before(() => {
|
|
81
|
+
origCwd = process.cwd();
|
|
82
|
+
origHome = process.env['HOME'];
|
|
66
83
|
origNode = process.env['CRTR_NODE_ID'];
|
|
84
|
+
origProfile = process.env['CRTR_PROFILE_ID'];
|
|
67
85
|
});
|
|
68
86
|
beforeEach(async () => {
|
|
69
87
|
closeDb();
|
|
70
88
|
if (home)
|
|
71
89
|
rmSync(home, { recursive: true, force: true });
|
|
72
90
|
home = mkdtempSync(join(tmpdir(), 'crtr-context-intro-'));
|
|
91
|
+
const userHome = join(home, 'user');
|
|
92
|
+
const cwd = join(userHome, 'work');
|
|
93
|
+
mkdirSync(cwd, { recursive: true });
|
|
94
|
+
process.chdir(cwd);
|
|
95
|
+
process.env['HOME'] = userHome;
|
|
73
96
|
process.env['CRTR_HOME'] = home;
|
|
74
97
|
delete process.env['CRTR_NODE_ID'];
|
|
75
|
-
|
|
98
|
+
delete process.env['CRTR_PROFILE_ID'];
|
|
99
|
+
resetScopeCache();
|
|
100
|
+
clearSessionCache();
|
|
76
101
|
server = createApiServer();
|
|
77
102
|
await waitForSocket(apiSocketPath());
|
|
78
103
|
});
|
|
79
104
|
afterEach(async () => {
|
|
80
|
-
|
|
105
|
+
try {
|
|
106
|
+
await server.close();
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
process.chdir(origCwd);
|
|
110
|
+
}
|
|
81
111
|
});
|
|
82
112
|
after(() => {
|
|
113
|
+
process.chdir(origCwd);
|
|
83
114
|
closeDb();
|
|
84
115
|
if (home)
|
|
85
116
|
rmSync(home, { recursive: true, force: true });
|
|
117
|
+
if (origHome === undefined)
|
|
118
|
+
delete process.env['HOME'];
|
|
119
|
+
else
|
|
120
|
+
process.env['HOME'] = origHome;
|
|
121
|
+
resetScopeCache();
|
|
122
|
+
clearSessionCache();
|
|
86
123
|
delete process.env['CRTR_HOME'];
|
|
87
124
|
if (origNode === undefined)
|
|
88
125
|
delete process.env['CRTR_NODE_ID'];
|
|
89
126
|
else
|
|
90
127
|
process.env['CRTR_NODE_ID'] = origNode;
|
|
128
|
+
if (origProfile === undefined)
|
|
129
|
+
delete process.env['CRTR_PROFILE_ID'];
|
|
130
|
+
else
|
|
131
|
+
process.env['CRTR_PROFILE_ID'] = origProfile;
|
|
91
132
|
});
|
|
92
133
|
test('session_start appends compact fork bearings after an inherited source baseline', async () => {
|
|
93
134
|
createNode(node('srcA'));
|
|
@@ -95,6 +136,16 @@ test('session_start appends compact fork bearings after an inherited source base
|
|
|
95
136
|
// A fork boots with the SOURCE's whole conversation copied in — including the
|
|
96
137
|
// source's bearings intro entry (which names srcA, not forkB).
|
|
97
138
|
const inheritedSourceBlock = introEntry(buildContextIntro('srcA'));
|
|
139
|
+
const sourceExposure = emptyContextExposureState();
|
|
140
|
+
registerExposure(exposureTarget(sourceExposure, 'system'), 'source-system-only', 'content');
|
|
141
|
+
registerExposure(exposureTarget(sourceExposure, 'transcript'), 'inherited-source-document', 'content');
|
|
142
|
+
saveContextExposureState('srcA', sourceExposure);
|
|
143
|
+
initializeForkContextExposure('forkB', 'srcA');
|
|
144
|
+
deleteNode('srcA');
|
|
145
|
+
initializeForkContextExposure('forkB', null);
|
|
146
|
+
const forkExposure = loadContextExposureState('forkB');
|
|
147
|
+
assert.deepEqual(forkExposure.exposures.get('inherited-source-document'), { transcript: 3 }, 'a pre-bind retry preserves the transcript exposure seed after its source disappears');
|
|
148
|
+
assert.equal(forkExposure.exposures.has('source-system-only'), false, 'a fork never inherits source system exposure');
|
|
98
149
|
process.env['CRTR_NODE_ID'] = 'forkB';
|
|
99
150
|
const pi = makeFakePi();
|
|
100
151
|
registerCanvasContextIntro(pi);
|
|
@@ -104,15 +155,172 @@ test('session_start appends compact fork bearings after an inherited source base
|
|
|
104
155
|
assert.equal(pi.sent[0].customType, CONTEXT_INTRO_CUSTOM_TYPE);
|
|
105
156
|
assert.match(pi.sent[0].content, /You are node forkB/, 'injected bearings name the fork, not the source');
|
|
106
157
|
assert.match(pi.sent[0].content, /new FORK of srcA/, 'injected bearings carry the fork callout');
|
|
107
|
-
assert.doesNotMatch(pi.sent[0].content, /<project_context>/, 'the
|
|
158
|
+
assert.doesNotMatch(pi.sent[0].content, /<project_context>/, 'inherited crouter bearings stay compact even after the source row is gone');
|
|
108
159
|
assert.equal(pi.sent[0].details?.nodeId, 'forkB', 'the compact update carries the fork node stamp');
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
160
|
+
// An inherited crouter block without a recoverable transcript seed cannot
|
|
161
|
+
// prove what that branch contains, so the destination starts from scratch.
|
|
162
|
+
createNode(node('unknownF', { fork_from: '/tmp/unknown-session.jsonl' }));
|
|
163
|
+
const unknownPreState = loadContextExposureState('unknownF');
|
|
164
|
+
assert.equal(unknownPreState.exposures.size, 0);
|
|
165
|
+
assert.equal(unknownPreState.preferenceSnapshot, null);
|
|
166
|
+
process.env['CRTR_NODE_ID'] = 'unknownF';
|
|
167
|
+
const unknownPi = makeFakePi();
|
|
168
|
+
registerCanvasContextIntro(unknownPi);
|
|
169
|
+
await unknownPi.fireSessionStart([inheritedSourceBlock]);
|
|
170
|
+
assert.match(unknownPi.sent[0].content, /<project_context>/, 'unknown inherited provenance receives the full baseline');
|
|
171
|
+
});
|
|
172
|
+
test('root /new leaves baseline assembly to the replacement broker', async () => {
|
|
173
|
+
const oldPersona = { kind: 'general', mode: 'base', lifecycle: 'resident' };
|
|
174
|
+
createNode(node('root-new', {
|
|
175
|
+
parent: null,
|
|
176
|
+
kind: 'developer',
|
|
177
|
+
lifecycle: 'resident',
|
|
178
|
+
persona_ack: oldPersona,
|
|
179
|
+
}));
|
|
180
|
+
const exposure = emptyContextExposureState();
|
|
181
|
+
exposure.preferenceSnapshot = 'ORIGINAL-SNAPSHOT';
|
|
182
|
+
registerExposure(exposureTarget(exposure, 'transcript'), 'original-transcript', 'content');
|
|
183
|
+
saveContextExposureState('root-new', exposure);
|
|
184
|
+
process.env['CRTR_NODE_ID'] = 'root-new';
|
|
185
|
+
const pi = makeFakePi();
|
|
186
|
+
registerCanvasContextIntro(pi);
|
|
187
|
+
await pi.fireSessionStart([], 'new');
|
|
188
|
+
assert.equal(pi.sent.length, 0, 'the provisional in-process branch receives no baseline');
|
|
189
|
+
assert.equal(loadContextExposureState('root-new').preferenceSnapshot, 'ORIGINAL-SNAPSHOT');
|
|
190
|
+
assert.deepEqual(getNode('root-new')?.persona_ack, oldPersona, 'the replacement launch still owns the pending transition');
|
|
191
|
+
});
|
|
192
|
+
test('child /new commits its baseline before ack and the provider gate delivers strict-resume drift', async () => {
|
|
193
|
+
const previousProfile = process.env['CRTR_PROFILE_ID'];
|
|
194
|
+
const userHome = join(home, 'user');
|
|
195
|
+
const cwd = join(userHome, 'work');
|
|
196
|
+
const userMemory = join(userHome, '.crouter', 'memory');
|
|
197
|
+
const projectMemory = join(cwd, '.crouter', 'memory');
|
|
198
|
+
mkdirSync(userMemory, { recursive: true });
|
|
199
|
+
mkdirSync(projectMemory, { recursive: true });
|
|
200
|
+
const preferencePath = join(userMemory, 'new-context-preference.md');
|
|
201
|
+
const knowledgePath = join(userMemory, 'new-context-knowledge.md');
|
|
202
|
+
const workspacePath = join(projectMemory, 'INDEX.md');
|
|
203
|
+
const transitionPath = join(userMemory, 'developer-transition.md');
|
|
204
|
+
writeFileSync(preferencePath, '---\nkind: preference\nwhen-and-why-to-read: New context preference.\nsurfaces:\n - {on: boot, at: content}\n---\nSYSTEM-PREFERENCE-NEW-CONTEXT\n');
|
|
205
|
+
writeFileSync(knowledgePath, '---\nkind: knowledge\nwhen-and-why-to-read: New context knowledge.\nsurfaces:\n - {on: boot, at: content}\n---\nBEARINGS-KNOWLEDGE-NEW-CONTEXT\n');
|
|
206
|
+
writeFileSync(workspacePath, '---\nnamespace: new-context-workspace\nkind: knowledge\nwhen-and-why-to-read: New context workspace.\nsurfaces:\n - {on: workspace-open, at: content}\n---\nBEARINGS-WORKSPACE-NEW-CONTEXT\n');
|
|
207
|
+
writeFileSync(transitionPath, '---\nkind: knowledge\nwhen-and-why-to-read: Developer transition knowledge.\ngate: {kind: developer}\nsurfaces:\n - {on: boot, at: content}\n---\nSTRICT-RESUME-DEVELOPER-DELTA\n');
|
|
208
|
+
delete process.env['CRTR_PROFILE_ID'];
|
|
209
|
+
resetScopeCache();
|
|
210
|
+
clearSessionCache();
|
|
211
|
+
try {
|
|
212
|
+
createNode(node('new-parent', { cwd, lifecycle: 'resident' }));
|
|
213
|
+
const oldPersona = { kind: 'general', mode: 'base', lifecycle: 'terminal' };
|
|
214
|
+
const currentPersona = { kind: 'review', mode: 'base', lifecycle: 'terminal' };
|
|
215
|
+
createNode(node('new-child', {
|
|
216
|
+
cwd,
|
|
217
|
+
parent: 'new-parent',
|
|
218
|
+
kind: currentPersona.kind,
|
|
219
|
+
persona_ack: oldPersona,
|
|
220
|
+
}));
|
|
221
|
+
mkdirSync(join(home, 'nodes', 'new-child'), { recursive: true });
|
|
222
|
+
process.env['CRTR_NODE_ID'] = 'new-child';
|
|
223
|
+
const sessionStartHandlers = [];
|
|
224
|
+
const sent = [];
|
|
225
|
+
const branch = [];
|
|
226
|
+
let ackWhenBearingsWereSent;
|
|
227
|
+
const pi = {
|
|
228
|
+
on(event, handler) {
|
|
229
|
+
if (event === 'session_start')
|
|
230
|
+
sessionStartHandlers.push(handler);
|
|
231
|
+
},
|
|
232
|
+
sendMessage(message) {
|
|
233
|
+
sent.push(message);
|
|
234
|
+
branch.push({ type: 'custom_message', ...message });
|
|
235
|
+
if (message.customType === CONTEXT_INTRO_CUSTOM_TYPE) {
|
|
236
|
+
ackWhenBearingsWereSent = getNode('new-child')?.persona_ack;
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
registerMessageRenderer() { },
|
|
240
|
+
};
|
|
241
|
+
registerCanvasContextIntro(pi);
|
|
242
|
+
registerCanvasDocSubstrate(pi);
|
|
243
|
+
const ctx = { sessionManager: { getEntries: () => branch, getBranch: () => branch } };
|
|
244
|
+
for (const handler of sessionStartHandlers)
|
|
245
|
+
await handler({ reason: 'new' }, ctx);
|
|
246
|
+
assert.equal(sent.length, 1, 'the fresh child branch receives one bearings message and no transition delta');
|
|
247
|
+
assert.match(sent[0].content, /BEARINGS-KNOWLEDGE-NEW-CONTEXT/);
|
|
248
|
+
assert.match(sent[0].content, /BEARINGS-WORKSPACE-NEW-CONTEXT/);
|
|
249
|
+
assert.deepEqual(ackWhenBearingsWereSent, oldPersona, 'the old persona remains acknowledged until the baseline is complete');
|
|
250
|
+
assert.deepEqual(getNode('new-child')?.persona_ack, currentPersona);
|
|
251
|
+
const state = loadContextExposureState('new-child');
|
|
252
|
+
assert.match(state.preferenceSnapshot ?? '', /SYSTEM-PREFERENCE-NEW-CONTEXT/);
|
|
253
|
+
assert.deepEqual(state.exposures.get(realpathSync(preferencePath)), { system: 3 });
|
|
254
|
+
assert.deepEqual(state.exposures.get(realpathSync(knowledgePath)), { transcript: 3 });
|
|
255
|
+
assert.deepEqual(state.exposures.get(realpathSync(workspacePath)), { transcript: 3 });
|
|
256
|
+
assert.equal(state.exposures.has(realpathSync(transitionPath)), false);
|
|
257
|
+
const frozenSnapshot = state.preferenceSnapshot;
|
|
258
|
+
updateNode('new-child', { kind: 'developer' });
|
|
259
|
+
for (const handler of sessionStartHandlers)
|
|
260
|
+
await handler({ reason: 'startup' }, ctx);
|
|
261
|
+
assert.equal(sent.length, 1, 'session_start preserves the branch and leaves pending drift to the provider gate');
|
|
262
|
+
assert.deepEqual(getNode('new-child')?.persona_ack, currentPersona);
|
|
263
|
+
const order = [];
|
|
264
|
+
const activeMessages = [];
|
|
265
|
+
let latestTransformed = [];
|
|
266
|
+
let mutateDuringAppend = false;
|
|
267
|
+
const providerSession = {
|
|
268
|
+
agent: {
|
|
269
|
+
state: { messages: activeMessages },
|
|
270
|
+
transformContext: async (messages) => {
|
|
271
|
+
order.push('previous-transform');
|
|
272
|
+
latestTransformed = [...messages];
|
|
273
|
+
return latestTransformed;
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
sessionManager: {
|
|
277
|
+
appendCustomMessageEntry(customType, content, display, details) {
|
|
278
|
+
order.push('append-transition');
|
|
279
|
+
branch.push({ type: 'custom_message', customType, content, display, details });
|
|
280
|
+
if (mutateDuringAppend)
|
|
281
|
+
updateNode('new-child', { kind: 'review' });
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
installPersonaTransitionGate('new-child', providerSession);
|
|
286
|
+
const transformed = await providerSession.agent.transformContext([{ role: 'user', content: 'continue' }]);
|
|
287
|
+
assert.deepEqual(order, ['previous-transform', 'append-transition'], 'Pi transforms context before crouter appends the transition');
|
|
288
|
+
assert.equal(branch.at(-1)?.customType, 'crtr-persona-transition');
|
|
289
|
+
assert.match(branch.at(-1)?.content ?? '', /STRICT-RESUME-DEVELOPER-DELTA/);
|
|
290
|
+
assert.equal(activeMessages.at(-1)?.['customType'], 'crtr-persona-transition');
|
|
291
|
+
assert.equal(transformed.at(-1)?.['customType'], 'crtr-persona-transition');
|
|
292
|
+
assert.deepEqual(getNode('new-child')?.persona_ack, {
|
|
293
|
+
kind: 'developer',
|
|
294
|
+
mode: 'base',
|
|
295
|
+
lifecycle: 'terminal',
|
|
296
|
+
});
|
|
297
|
+
const resumedState = loadContextExposureState('new-child');
|
|
298
|
+
assert.equal(resumedState.preferenceSnapshot, frozenSnapshot, 'strict resume preserves the frozen system snapshot byte-for-byte');
|
|
299
|
+
assert.deepEqual(resumedState.exposures.get(realpathSync(transitionPath)), { transcript: 3 });
|
|
300
|
+
updateNode('new-child', { mode: 'orchestrator' });
|
|
301
|
+
mutateDuringAppend = true;
|
|
302
|
+
order.length = 0;
|
|
303
|
+
const branchBeforeRace = branch.length;
|
|
304
|
+
const activeBeforeRace = activeMessages.length;
|
|
305
|
+
await providerSession.agent.transformContext([{ role: 'user', content: 'race' }]);
|
|
306
|
+
assert.deepEqual(order, ['previous-transform', 'append-transition', 'append-transition']);
|
|
307
|
+
assert.equal(branch.length, branchBeforeRace + 2, 'the stale transition is followed by current guidance');
|
|
308
|
+
assert.equal(activeMessages.length, activeBeforeRace + 2, 'the active agent transcript keeps both materialized cards');
|
|
309
|
+
assert.equal(latestTransformed.at(-1)?.['customType'], 'crtr-persona-transition', 'the provider context ends at the current persona');
|
|
310
|
+
assert.deepEqual(getNode('new-child')?.persona_ack, {
|
|
311
|
+
kind: 'review',
|
|
312
|
+
mode: 'orchestrator',
|
|
313
|
+
lifecycle: 'terminal',
|
|
314
|
+
}, 'the corrective transition acknowledges the current persona');
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
resetScopeCache();
|
|
318
|
+
clearSessionCache();
|
|
319
|
+
if (previousProfile === undefined)
|
|
320
|
+
delete process.env['CRTR_PROFILE_ID'];
|
|
321
|
+
else
|
|
322
|
+
process.env['CRTR_PROFILE_ID'] = previousProfile;
|
|
323
|
+
}
|
|
116
324
|
});
|
|
117
325
|
test('resume idempotency uses the exact details stamp independent of block text', async () => {
|
|
118
326
|
createNode(node('stampD'));
|
|
@@ -62,12 +62,20 @@ async function waitForRequestMentioning(id) {
|
|
|
62
62
|
before(async () => {
|
|
63
63
|
home = mkdtempSync(join(tmpdir(), 'crtr-goal-capture-'));
|
|
64
64
|
process.env['CRTR_HOME'] = home;
|
|
65
|
-
// A stub daemon: it records the request and
|
|
66
|
-
// fast (and silently, as it must) without any model call.
|
|
65
|
+
// A stub daemon: it records the request and answers a typed error, so the
|
|
66
|
+
// naming path fails fast (and silently, as it must) without any model call.
|
|
67
|
+
// It must ANSWER rather than hang up — the api client reads a mid-request
|
|
68
|
+
// hangup as a daemon handover and rides it out with a 10s /healthz poll,
|
|
69
|
+
// which kept this process alive for ten seconds after the test had passed.
|
|
67
70
|
socket = createServer((connection) => {
|
|
68
71
|
connection.on('data', (chunk) => {
|
|
69
72
|
requests.push(chunk.toString('utf8'));
|
|
70
|
-
|
|
73
|
+
const body = JSON.stringify({ error: { code: 'internal', message: 'stub daemon' } });
|
|
74
|
+
connection.end('HTTP/1.1 500 Internal Server Error\r\n'
|
|
75
|
+
+ 'Content-Type: application/json\r\n'
|
|
76
|
+
+ `Content-Length: ${Buffer.byteLength(body)}\r\n`
|
|
77
|
+
+ 'Connection: close\r\n\r\n'
|
|
78
|
+
+ body);
|
|
71
79
|
});
|
|
72
80
|
});
|
|
73
81
|
await new Promise((resolve) => socket.listen(apiSocketPath(), resolve));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CONTEXT_INTRO_CUSTOM_TYPE } from '../shared/generated-context.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type ExposureTarget } from '../core/substrate/injected-store.js';
|
|
3
3
|
/** The `customType` stamped on the injected session message. Used both to write
|
|
4
4
|
* the entry and to detect it on resume (the idempotency guard). Defined beside
|
|
5
5
|
* the block builder in `core/runtime/bearings.ts` because the warm-pool claim's
|
|
@@ -69,7 +69,7 @@ interface PiLike {
|
|
|
69
69
|
}
|
|
70
70
|
/** Build the full bearings baseline for a conversation without inherited
|
|
71
71
|
* crouter context. Exported for testing. */
|
|
72
|
-
export declare function buildContextIntro(nodeId: string,
|
|
72
|
+
export declare function buildContextIntro(nodeId: string, target?: ExposureTarget): Promise<string>;
|
|
73
73
|
/** Build the compact node-scoped update for a fork whose copied branch already
|
|
74
74
|
* carries a full crouter baseline. Exported for testing. */
|
|
75
75
|
export declare function buildForkContextIntro(nodeId: string): Promise<string>;
|
|
@@ -82,8 +82,6 @@ export declare function buildForkContextIntro(nodeId: string): Promise<string>;
|
|
|
82
82
|
export declare function renderContextMessage(message: RenderedMessageLike, options: {
|
|
83
83
|
expanded?: boolean;
|
|
84
84
|
}, theme: ThemeLike): ComponentLike;
|
|
85
|
-
/** Register
|
|
86
|
-
* baseline, forked crouter branches receive one compact update, and resumes
|
|
87
|
-
* whose active branch already carries this node's stamp receive nothing. */
|
|
85
|
+
/** Register new-context baseline assembly; a strict resume changes nothing. */
|
|
88
86
|
export declare function registerCanvasContextIntro(pi: PiLike): void;
|
|
89
87
|
export default registerCanvasContextIntro;
|
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
// Loaded into every canvas node's pi process via the node's launch.extensions
|
|
4
4
|
// list. INERT when CRTR_NODE_ID is absent (plain pi session or legacy job agent).
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// copied history and immediately before its new mandate. The prose lives in
|
|
10
|
-
// core/runtime/bearings.ts.
|
|
6
|
+
// At session_start, before the first response, this assembles the context-owned
|
|
7
|
+
// memory state. A fresh branch receives full bearings, while a copied crouter
|
|
8
|
+
// branch with known transcript provenance receives compact node bearings.
|
|
11
9
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// whose active branch carries another node's bearings receives only a compact
|
|
16
|
-
// node-scoped update; a fork of a non-crouter session receives the full baseline.
|
|
10
|
+
// The active branch owns the idempotency stamp. A new cycle has no current-node
|
|
11
|
+
// stamp and receives a new baseline. The preference snapshot and persona
|
|
12
|
+
// acknowledgment persist only after Pi materializes that baseline.
|
|
17
13
|
//
|
|
18
14
|
// COLLAPSED BY DEFAULT: a `registerMessageRenderer` keyed to our customType
|
|
19
15
|
// renders the block as a single one-line stub; the full body only appears when
|
|
@@ -28,9 +24,11 @@
|
|
|
28
24
|
// crouter's own tsc build without a dep on the pi packages.
|
|
29
25
|
import { CONTEXT_INTRO_CUSTOM_TYPE, formatCard, parseCard } from '../shared/generated-context.js';
|
|
30
26
|
import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
31
|
-
import { buildContextBearingsFromState, buildForkBearingsFromState, buildProjectContextBlockForBroker } from '../core/runtime/broker-extension-render.js';
|
|
27
|
+
import { buildContextBearingsFromState, buildForkBearingsFromState, buildProjectContextBlockForBroker, renderPreferencesFromState, } from '../core/runtime/broker-extension-render.js';
|
|
28
|
+
import { acknowledgePersonaBaseline } from '../core/runtime/broker-persona-guidance.js';
|
|
32
29
|
import { situationalContextEnvelope } from '../core/runtime/situational-context.js';
|
|
33
|
-
import {
|
|
30
|
+
import { clearContextExposureState, cloneTranscriptExposureState, emptyContextExposureState, exposureTarget, freezePreferenceSnapshot, hasTranscriptExposure, loadContextExposureState, replaceContextExposureState, saveContextExposureState, sharedContextExposureState, } from '../core/substrate/injected-store.js';
|
|
31
|
+
import { clearSessionCache } from '../core/substrate/session-cache.js';
|
|
34
32
|
import { truncateToWidth } from './truncate.js';
|
|
35
33
|
/** The `customType` stamped on the injected session message. Used both to write
|
|
36
34
|
* the entry and to detect it on resume (the idempotency guard). Defined beside
|
|
@@ -42,9 +40,9 @@ export { CONTEXT_INTRO_CUSTOM_TYPE };
|
|
|
42
40
|
// ---------------------------------------------------------------------------
|
|
43
41
|
/** Build the full bearings baseline for a conversation without inherited
|
|
44
42
|
* crouter context. Exported for testing. */
|
|
45
|
-
export async function buildContextIntro(nodeId,
|
|
43
|
+
export async function buildContextIntro(nodeId, target) {
|
|
46
44
|
const state = await brokerExtensionState(nodeId);
|
|
47
|
-
return buildContextBearingsFromState(state, buildProjectContextBlockForBroker(state.node.cwd), situationalContextEnvelope(nodeId) ?? '',
|
|
45
|
+
return buildContextBearingsFromState(state, buildProjectContextBlockForBroker(state.node.cwd), situationalContextEnvelope(nodeId) ?? '', target);
|
|
48
46
|
}
|
|
49
47
|
/** Build the compact node-scoped update for a fork whose copied branch already
|
|
50
48
|
* carries a full crouter baseline. Exported for testing. */
|
|
@@ -79,6 +77,11 @@ function entryText(e) {
|
|
|
79
77
|
}
|
|
80
78
|
return '';
|
|
81
79
|
}
|
|
80
|
+
function activeBranch(ctx) {
|
|
81
|
+
return typeof ctx.sessionManager.getBranch === 'function'
|
|
82
|
+
? ctx.sessionManager.getBranch()
|
|
83
|
+
: ctx.sessionManager.getEntries();
|
|
84
|
+
}
|
|
82
85
|
/** Hard-wrap a single logical line to `width` columns (content carries no ANSI).
|
|
83
86
|
* Code-point aware so wide-string slicing never splits a surrogate pair; the
|
|
84
87
|
* bearings prose is plain text, so code-point count == visible columns. */
|
|
@@ -127,60 +130,61 @@ export function renderContextMessage(message, options, theme) {
|
|
|
127
130
|
// ---------------------------------------------------------------------------
|
|
128
131
|
// Extension
|
|
129
132
|
// ---------------------------------------------------------------------------
|
|
130
|
-
/** Register
|
|
131
|
-
* baseline, forked crouter branches receive one compact update, and resumes
|
|
132
|
-
* whose active branch already carries this node's stamp receive nothing. */
|
|
133
|
+
/** Register new-context baseline assembly; a strict resume changes nothing. */
|
|
133
134
|
export function registerCanvasContextIntro(pi) {
|
|
134
135
|
// Collapse the block to a one-liner until the user expands tool output (Ctrl+O).
|
|
135
136
|
// Harmless to register outside TUI mode (it's only consulted while rendering).
|
|
136
137
|
pi.registerMessageRenderer(CONTEXT_INTRO_CUSTOM_TYPE, renderContextMessage);
|
|
137
|
-
pi.on('session_start', async (
|
|
138
|
+
pi.on('session_start', async (event, ctx) => {
|
|
138
139
|
const nodeId = process.env['CRTR_NODE_ID'];
|
|
139
140
|
if (nodeId === undefined || nodeId.trim() === '')
|
|
140
141
|
return;
|
|
142
|
+
const reason = event?.reason;
|
|
141
143
|
// Warm spares have no claimant yet. Their claim path delivers the full
|
|
142
144
|
// baseline after assigning the real persona, profile, and working directory.
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
state = await brokerExtensionState(nodeId);
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
145
|
+
const state = await brokerExtensionState(nodeId);
|
|
150
146
|
if (state.warm_spare)
|
|
151
147
|
return;
|
|
148
|
+
// A root's in-process /new branch is provisional: stophook has already asked
|
|
149
|
+
// crtrd to relaunch it. The replacement broker owns the new baseline.
|
|
150
|
+
if (reason === 'new' && state.node.parent === null)
|
|
151
|
+
return;
|
|
152
|
+
if (reason === 'new') {
|
|
153
|
+
clearContextExposureState(nodeId);
|
|
154
|
+
clearSessionCache();
|
|
155
|
+
}
|
|
152
156
|
// The active branch, rather than the whole session file, is the context the
|
|
153
157
|
// next turn sees. A new cycle therefore receives its own current baseline.
|
|
154
|
-
const
|
|
155
|
-
? ctx.sessionManager.getBranch()
|
|
156
|
-
: ctx.sessionManager.getEntries();
|
|
157
|
-
const contextEntries = scan.filter((e) => e.type === 'custom_message' && e.customType === CONTEXT_INTRO_CUSTOM_TYPE);
|
|
158
|
+
const contextEntries = activeBranch(ctx).filter((e) => e.type === 'custom_message' && e.customType === CONTEXT_INTRO_CUSTOM_TYPE);
|
|
158
159
|
const ours = contextEntries.some((e) => e.details?.nodeId === nodeId || entryText(e).includes(nodeId));
|
|
159
160
|
if (ours)
|
|
160
161
|
return;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// the same Set instance, so a front door delivered here never re-delivers
|
|
168
|
-
// on a later read beneath its store. The fork branch renders no workspace
|
|
169
|
-
// docs and needs no set.
|
|
170
|
-
const seen = inherited && forked ? undefined : sharedInjectedDocs(nodeId);
|
|
171
|
-
const content = inherited && forked
|
|
162
|
+
const persisted = loadContextExposureState(nodeId);
|
|
163
|
+
const compact = contextEntries.length > 0 && hasTranscriptExposure(persisted);
|
|
164
|
+
const next = compact
|
|
165
|
+
? cloneTranscriptExposureState(persisted)
|
|
166
|
+
: emptyContextExposureState();
|
|
167
|
+
const content = compact
|
|
172
168
|
? buildForkBearingsFromState(state, situationalContextEnvelope(nodeId) ?? '')
|
|
173
|
-
: buildContextBearingsFromState(state, buildProjectContextBlockForBroker(state.node.cwd), situationalContextEnvelope(nodeId) ?? '',
|
|
169
|
+
: buildContextBearingsFromState(state, buildProjectContextBlockForBroker(state.node.cwd), situationalContextEnvelope(nodeId) ?? '', exposureTarget(next, 'transcript'));
|
|
170
|
+
freezePreferenceSnapshot(next, renderPreferencesFromState(state, exposureTarget(next, 'system')));
|
|
171
|
+
const messageContent = formatCard('bearings', {}, content);
|
|
174
172
|
pi.sendMessage({
|
|
175
173
|
customType: CONTEXT_INTRO_CUSTOM_TYPE,
|
|
176
|
-
content:
|
|
174
|
+
content: messageContent,
|
|
177
175
|
display: true,
|
|
178
176
|
details: { nodeId },
|
|
179
177
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
const delivered = activeBranch(ctx).some((entry) => entry.type === 'custom_message'
|
|
179
|
+
&& entry.customType === CONTEXT_INTRO_CUSTOM_TYPE
|
|
180
|
+
&& entry.details?.nodeId === nodeId
|
|
181
|
+
&& entryText(entry) === messageContent);
|
|
182
|
+
if (!delivered)
|
|
183
|
+
throw new Error(`Pi did not materialize the context baseline for ${nodeId}`);
|
|
184
|
+
const live = sharedContextExposureState(nodeId);
|
|
185
|
+
replaceContextExposureState(live, next);
|
|
186
|
+
saveContextExposureState(nodeId, live);
|
|
187
|
+
await acknowledgePersonaBaseline(state);
|
|
184
188
|
});
|
|
185
189
|
}
|
|
186
190
|
export default registerCanvasContextIntro;
|
|
@@ -31,15 +31,8 @@ type ToolResultResultLike = {
|
|
|
31
31
|
} | void;
|
|
32
32
|
interface PiLike {
|
|
33
33
|
on(event: 'before_agent_start', handler: (event: BeforeAgentStartEventLike) => BeforeAgentStartResultLike | void | Promise<BeforeAgentStartResultLike | void>): void;
|
|
34
|
-
on(event: 'session_start', handler: (event: unknown, ctx: unknown) => void): void;
|
|
34
|
+
on(event: 'session_start', handler: (event: unknown, ctx: unknown) => void | Promise<void>): void;
|
|
35
35
|
on(event: 'tool_result', handler: (event: ToolResultEventLike, ctx: ToolResultCtxLike) => ToolResultResultLike | Promise<ToolResultResultLike>): void;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Register the document substrate's pi hooks on `pi`.
|
|
39
|
-
*
|
|
40
|
-
* Returns immediately (inert) when CRTR_NODE_ID is absent — a non-canvas pi
|
|
41
|
-
* session gets no hooks. Each handler is independently wrapped so a failure
|
|
42
|
-
* degrades to a no-op (boot proceeds, the read returns) rather than bricking.
|
|
43
|
-
*/
|
|
44
37
|
export declare function registerCanvasDocSubstrate(pi: PiLike): void;
|
|
45
38
|
export default registerCanvasDocSubstrate;
|