@north-light/crouter 0.3.262 → 0.3.269

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.
Files changed (150) hide show
  1. package/dist/api/client.d.ts +5 -1
  2. package/dist/api/client.js +6 -0
  3. package/dist/api/dto/canvas.d.ts +53 -2
  4. package/dist/api/dto/health.d.ts +2 -0
  5. package/dist/api/dto/nodes.d.ts +6 -2
  6. package/dist/api/routes.d.ts +1 -0
  7. package/dist/api/routes.js +1 -0
  8. package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +2 -2
  9. package/dist/builtin-memory/internal/memory-loading.md +3 -3
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +50 -0
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +46 -6
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +10 -5
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  14. package/dist/clients/attach/input/controller.d.ts +4 -0
  15. package/dist/clients/attach/input/controller.js +8 -0
  16. package/dist/clients/attach/session/bindings.d.ts +3 -0
  17. package/dist/clients/attach/session/bindings.js +7 -0
  18. package/dist/clients/attach/session/keys.d.ts +2 -0
  19. package/dist/clients/attach/session/keys.js +3 -3
  20. package/dist/clients/attach/session/whip-achievements.d.ts +4 -0
  21. package/dist/clients/attach/session/whip-achievements.js +145 -0
  22. package/dist/clients/attach/slash/dispatch.js +5 -3
  23. package/dist/clients/attach/viewer.js +668 -667
  24. package/dist/commands/canvas-history/grep.js +3 -22
  25. package/dist/commands/canvas-history/read.js +5 -3
  26. package/dist/commands/canvas-history/search.js +3 -22
  27. package/dist/commands/canvas-history/shared.d.ts +8 -0
  28. package/dist/commands/canvas-history/shared.js +64 -1
  29. package/dist/commands/canvas-history/stats.d.ts +1 -0
  30. package/dist/commands/canvas-history/stats.js +68 -0
  31. package/dist/commands/canvas-history.js +4 -3
  32. package/dist/commands/memory/read.js +15 -38
  33. package/dist/commands/memory/shared.d.ts +1 -1
  34. package/dist/commands/memory/shared.js +1 -1
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/sys/config.js +19 -2
  37. package/dist/commands/sys/context/admin/docs-panel.d.ts +3 -3
  38. package/dist/commands/sys/context/admin/model.d.ts +7 -2
  39. package/dist/commands/sys/context/admin/model.js +7 -1
  40. package/dist/commands/sys/context/admin/read-view.d.ts +5 -5
  41. package/dist/commands/sys/context/admin/read-view.js +9 -4
  42. package/dist/commands/sys/context/resolve.d.ts +32 -3
  43. package/dist/commands/sys/context/resolve.js +74 -25
  44. package/dist/commands/sys/panels/models-panel.d.ts +28 -5
  45. package/dist/commands/sys/panels/models-panel.js +560 -75
  46. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +1 -1
  47. package/dist/core/__tests__/integration/revive.test.js +4 -2
  48. package/dist/core/__tests__/model-pin-durability.test.js +25 -1
  49. package/dist/core/__tests__/model-routes-config.test.d.ts +1 -0
  50. package/dist/core/__tests__/model-routes-config.test.js +142 -0
  51. package/dist/core/__tests__/on-read-nested-store.test.js +8 -0
  52. package/dist/core/__tests__/profile-project-memory-delivery.test.js +264 -1
  53. package/dist/core/__tests__/relaunch-root.test.js +17 -0
  54. package/dist/core/__tests__/seam/broker-provider-retry.test.js +21 -6
  55. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +19 -2
  56. package/dist/core/broker-client/__tests__/transport-relay.test.js +3 -1
  57. package/dist/core/broker-client/transport-relay.js +5 -1
  58. package/dist/core/canvas/__tests__/history-transcript.test.d.ts +1 -0
  59. package/dist/core/canvas/__tests__/history-transcript.test.js +102 -0
  60. package/dist/core/canvas/browse/app.d.ts +1 -1
  61. package/dist/core/canvas/browse/app.js +13 -10
  62. package/dist/core/canvas/browse/model.js +16 -1
  63. package/dist/core/canvas/browse/render.d.ts +23 -3
  64. package/dist/core/canvas/browse/render.js +429 -64
  65. package/dist/core/canvas/canvas.js +12 -8
  66. package/dist/core/canvas/extensions.d.ts +1 -1
  67. package/dist/core/canvas/extensions.js +7 -1
  68. package/dist/core/canvas/history.d.ts +36 -1
  69. package/dist/core/canvas/history.js +328 -7
  70. package/dist/core/canvas/labels.d.ts +2 -2
  71. package/dist/core/canvas/migrations.js +32 -0
  72. package/dist/core/canvas/node-recap.d.ts +23 -0
  73. package/dist/core/canvas/node-recap.js +67 -0
  74. package/dist/core/canvas/render-source.d.ts +34 -2
  75. package/dist/core/canvas/render-source.js +83 -24
  76. package/dist/core/canvas/types.d.ts +9 -0
  77. package/dist/core/config.d.ts +19 -2
  78. package/dist/core/config.js +174 -51
  79. package/dist/core/keybindings/catalog.d.ts +2 -2
  80. package/dist/core/keybindings/catalog.js +11 -8
  81. package/dist/core/model-routes.js +11 -4
  82. package/dist/core/runtime/broker/auth-reload.js +4 -1
  83. package/dist/core/runtime/broker/fault-retry.js +7 -2
  84. package/dist/core/runtime/broker/frame-client.js +4 -0
  85. package/dist/core/runtime/broker/frame-dispatch.js +7 -0
  86. package/dist/core/runtime/broker/inbox.d.ts +12 -2
  87. package/dist/core/runtime/broker/inbox.js +68 -38
  88. package/dist/core/runtime/broker-protocol.d.ts +6 -0
  89. package/dist/core/runtime/broker.js +14 -0
  90. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  91. package/dist/core/runtime/canvas-extensions.js +2 -0
  92. package/dist/core/runtime/interactive-deliver.d.ts +8 -2
  93. package/dist/core/runtime/interactive-deliver.js +12 -3
  94. package/dist/core/runtime/launch-target.js +1 -1
  95. package/dist/core/runtime/launch.d.ts +12 -6
  96. package/dist/core/runtime/launch.js +19 -17
  97. package/dist/core/runtime/model-swap.d.ts +5 -4
  98. package/dist/core/runtime/model-swap.js +7 -5
  99. package/dist/core/runtime/promote.js +2 -1
  100. package/dist/core/runtime/reset.js +4 -2
  101. package/dist/core/runtime/revive.js +22 -0
  102. package/dist/core/runtime/spawn.d.ts +3 -3
  103. package/dist/core/runtime/spawn.js +20 -3
  104. package/dist/core/runtime/stamp/channel.d.ts +18 -0
  105. package/dist/core/runtime/stamp/channel.js +74 -0
  106. package/dist/core/runtime/stamp/protocol.d.ts +86 -0
  107. package/dist/core/runtime/stamp/protocol.js +113 -0
  108. package/dist/core/runtime/tmux-driver.d.ts +11 -1
  109. package/dist/core/runtime/tmux-driver.js +27 -11
  110. package/dist/core/self-update.js +2 -5
  111. package/dist/core/substrate/listings.d.ts +1 -1
  112. package/dist/core/substrate/listings.js +6 -2
  113. package/dist/core/substrate/on-read.d.ts +61 -5
  114. package/dist/core/substrate/on-read.js +129 -22
  115. package/dist/core/substrate/render.d.ts +3 -2
  116. package/dist/core/substrate/render.js +133 -38
  117. package/dist/core/tui/draw.d.ts +1 -0
  118. package/dist/core/tui/draw.js +5 -1
  119. package/dist/core/tui/markdown.d.ts +17 -0
  120. package/dist/core/tui/markdown.js +407 -0
  121. package/dist/core/user-settings.d.ts +4 -0
  122. package/dist/core/user-settings.js +1 -0
  123. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -0
  124. package/dist/daemon/api/__tests__/seam/api-server.test.js +34 -0
  125. package/dist/daemon/api/bridge.d.ts +3 -2
  126. package/dist/daemon/api/bridge.js +34 -9
  127. package/dist/daemon/api/handlers/canvas.js +80 -18
  128. package/dist/daemon/api/handlers/health.js +17 -12
  129. package/dist/daemon/api/handlers/messages.js +12 -1
  130. package/dist/daemon/api/handlers/nodes.js +10 -3
  131. package/dist/daemon/api/map.js +2 -0
  132. package/dist/daemon/messaging/node-message.js +15 -1
  133. package/dist/daemon/reconcilers/broker-supervision.js +26 -9
  134. package/dist/daemon/reconcilers/live-obligation.d.ts +8 -1
  135. package/dist/daemon/reconcilers/live-obligation.js +17 -0
  136. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +2 -2
  137. package/dist/pi-extensions/canvas-inbox-watcher.js +27 -2
  138. package/dist/pi-extensions/canvas-recap.js +4 -0
  139. package/dist/pi-extensions/canvas-stamp.d.ts +14 -0
  140. package/dist/pi-extensions/canvas-stamp.js +77 -0
  141. package/dist/pi-extensions/canvas-stophook.js +3 -0
  142. package/dist/shared/__tests__/env-boundary.test.js +4 -2
  143. package/dist/shared/crtr-version.d.ts +2 -0
  144. package/dist/shared/crtr-version.js +18 -0
  145. package/dist/shared/env.d.ts +10 -0
  146. package/dist/shared/env.js +19 -4
  147. package/dist/types.d.ts +14 -2
  148. package/dist/types.js +4 -0
  149. package/package.json +1 -1
  150. package/runtime.lock.json +2 -2
@@ -4,6 +4,7 @@
4
4
  import { resolve as resolvePath } from 'node:path';
5
5
  import { realpathOrSelf } from '../../../../core/fs-utils.js';
6
6
  import { planDelivery, } from '../../../../core/substrate/plan.js';
7
+ import { assembleEventAccounting, effectivePlan, } from '../resolve.js';
7
8
  import { RUNGS, SURFACE_EVENTS, previewLine, rungRank, } from '../../../../core/substrate/schema.js';
8
9
  import { scopeForCwd, profileNameFor } from '../../../../core/substrate/subject-fields.js';
9
10
  import { resolveMemoryDocForTarget } from '../../../../core/memory-resolver.js';
@@ -71,7 +72,12 @@ export class PlanSet {
71
72
  const held = this.plans.get(event);
72
73
  if (held !== undefined)
73
74
  return held;
74
- const computed = planDelivery(subjectOf(this.snapshot), targetOf(this.snapshot), event, this.payload(event));
75
+ const subject = subjectOf(this.snapshot);
76
+ const target = targetOf(this.snapshot);
77
+ const payload = this.payload(event);
78
+ const direct = planDelivery(subject, target, event, payload);
79
+ const rendered = assembleEventAccounting({ subject, target, nodeMissing: false }, event, payload);
80
+ const computed = effectivePlan(direct, rendered.accounting.deliveries, payload);
75
81
  this.plans.set(event, computed);
76
82
  return computed;
77
83
  }
@@ -1,5 +1,5 @@
1
1
  import { type Rung, type SurfaceEvent } from '../../../../core/substrate/schema.js';
2
- import type { DeliveryRecord } from '../../../../core/substrate/plan.js';
2
+ import type { EffectiveDeliveryRecord } from '../resolve.js';
3
3
  import type { NodeConfigSubject } from '../../../../core/substrate/subject-fields.js';
4
4
  /** The profile project entry whose `memory` rung ceilings this document's
5
5
  * store, or null when no project governs it. */
@@ -11,7 +11,7 @@ export interface CapOwner {
11
11
  * the sibling events' records, and the two facts the shell resolves. */
12
12
  export interface DossierContext {
13
13
  event: SurfaceEvent;
14
- records: Map<SurfaceEvent, DeliveryRecord>;
14
+ records: Map<SurfaceEvent, EffectiveDeliveryRecord>;
15
15
  capOwner: CapOwner | null;
16
16
  readOnly: boolean;
17
17
  /** The configuration every record here was planned against — what a failed
@@ -20,12 +20,12 @@ export interface DossierContext {
20
20
  }
21
21
  /** The resting card, and — when `bodyOpen` — the document's own text beneath
22
22
  * it in place of the affordance line that names it. */
23
- export declare function renderDossierRead(record: DeliveryRecord, context: DossierContext, width: number, bodyOpen: boolean): string[];
23
+ export declare function renderDossierRead(record: EffectiveDeliveryRecord, context: DossierContext, width: number, bodyOpen: boolean): string[];
24
24
  /** Identity and routing — what the edit face keeps overhead of its field list,
25
25
  * because they are the context a field is being edited against. */
26
- export declare function renderDossierHeader(record: DeliveryRecord, context: DossierContext, width: number): string[];
26
+ export declare function renderDossierHeader(record: EffectiveDeliveryRecord, context: DossierContext, width: number): string[];
27
27
  /** Does it load here, what does it cost, and — only when there is one — what
28
28
  * interfered. The three questions the old verdict, cost table and event grid
29
29
  * each answered a piece of. */
30
- export declare function renderDossierDelivery(record: DeliveryRecord, context: DossierContext, width: number): string[];
30
+ export declare function renderDossierDelivery(record: EffectiveDeliveryRecord, context: DossierContext, width: number): string[];
31
31
  export declare function bodyLineCount(body: string): number;
@@ -25,11 +25,13 @@ const INDENT = ' ';
25
25
  export function renderDossierRead(record, context, width, bodyOpen) {
26
26
  const inner = Math.max(1, width - INDENT.length);
27
27
  const out = renderDossierHeader(record, context, width);
28
- if (record.doc.shortForm.trim() !== '') {
29
- out.push('');
30
- out.push(...indent(wrapped(record.doc.shortForm, inner)));
31
- }
32
28
  out.push(...renderDossierDelivery(record, context, width));
29
+ // The gist sits against the body it summarizes: last of the card, directly
30
+ // above the affordance that opens the text it stands in for.
31
+ const gist = record.doc.shortForm.trim();
32
+ if (gist !== '') {
33
+ out.push('', theme.fg('dim', `${INDENT}SHORT FORM`), ...indent(wrapped(gist, inner)));
34
+ }
33
35
  const lines = bodyLineCount(record.doc.body);
34
36
  if (bodyOpen) {
35
37
  out.push('');
@@ -139,6 +141,9 @@ export function renderDossierDelivery(record, context, width) {
139
141
  ? `capped to ${record.cappedRung} by a profile ceiling on this store`
140
142
  : `capped to ${record.cappedRung} by the ${context.capOwner.memory} ceiling on ${context.capOwner.path}`);
141
143
  }
144
+ if (record.deliveryCause !== undefined) {
145
+ out.push(...dim(record.deliveryCause, width));
146
+ }
142
147
  if (record.shadowedBy !== null) {
143
148
  out.push(...indent(wrapped(`shadowed by the ${record.shadowedBy.scope} copy at ${record.shadowedBy.path}`, inner)).map((line) => theme.fg('muted', line)));
144
149
  }
@@ -1,6 +1,7 @@
1
- import { type MemoryTarget } from '../../../core/memory-resolver.js';
2
- import { type DeliveryPayload, type DeliveryRecord, type GateOutcome } from '../../../core/substrate/plan.js';
1
+ import { type MemoryDoc, type MemoryTarget } from '../../../core/memory-resolver.js';
2
+ import { type DeliveryPayload, type DeliveryPlan, type DeliveryRecord, type GateOutcome } from '../../../core/substrate/plan.js';
3
3
  import { type GateClause } from '../../../core/substrate/gate-explain.js';
4
+ import { deliveryAccounting } from '../../../core/substrate/on-read.js';
4
5
  import { type Rung, type SurfaceEntry, type SurfaceEvent } from '../../../core/substrate/schema.js';
5
6
  import { type NodeConfigSubject } from '../../../core/substrate/subject-fields.js';
6
7
  export declare const resolveLeaf: import("../../../core/command.js").LeafDef;
@@ -26,6 +27,10 @@ export interface PayloadFlags {
26
27
  export declare function payloadFor(event: SurfaceEvent, flags: PayloadFlags, target: MemoryTarget): DeliveryPayload;
27
28
  export declare function subjectSummary(subject: NodeConfigSubject): Record<string, unknown>;
28
29
  export declare function optionalString(value: unknown): string | null;
30
+ export interface EffectiveDeliveryRecord extends DeliveryRecord {
31
+ /** Present when the renderer raised this doc beyond its direct event plan. */
32
+ deliveryCause?: string;
33
+ }
29
34
  export interface DocRow {
30
35
  name: string;
31
36
  kind: string;
@@ -40,13 +45,24 @@ export interface DocRow {
40
45
  doc_gate: string;
41
46
  entry_gate: string | null;
42
47
  matched_entry: string | null;
48
+ delivery_cause?: string;
43
49
  excluded?: string;
44
50
  }
51
+ /** Overlay the production renderer's actual document deliveries onto direct
52
+ * planner records. The plan still supplies gates, caps, and exclusion detail;
53
+ * only an attachment that genuinely rendered may raise its final rung. */
54
+ export declare function effectivePlan(plan: DeliveryPlan, deliveries: ReadonlyMap<string, {
55
+ rung: Rung;
56
+ cause?: string;
57
+ source?: MemoryDoc;
58
+ }>, payload?: DeliveryPayload): DeliveryPlan & {
59
+ docs: EffectiveDeliveryRecord[];
60
+ };
45
61
  /** A document delivers when it won its canonical name AND resolved to a rung
46
62
  * above the no-delivery floor — boot keeps rung-`none` winners in the plan so
47
63
  * the catalog can still count them. */
48
64
  export declare function delivers(record: DeliveryRecord): boolean;
49
- export declare function docRow(record: DeliveryRecord, includeAll: boolean, subject: NodeConfigSubject | null): DocRow;
65
+ export declare function docRow(record: EffectiveDeliveryRecord, includeAll: boolean, subject: NodeConfigSubject | null): DocRow;
50
66
  /** A gate verdict as one cell: `pass`, or `fail:` plus the clauses that failed
51
67
  * — the planner's generic sentence only when the explainer cannot decompose
52
68
  * the predicate. */
@@ -55,5 +71,18 @@ export declare function entryText(entry: SurfaceEntry | null): string | null;
55
71
  /** Why a considered document delivers nothing, in the order the planner applies
56
72
  * its checks — the first failing one is the answer an engineer acts on. */
57
73
  export declare function exclusionReason(record: DeliveryRecord, subject: NodeConfigSubject | null): string;
74
+ /** Every renderer resolves its corpus from the AMBIENT process (cwd plus the
75
+ * two env seams), not from a target argument, so a snapshot dialed away from
76
+ * this process is only honored by standing in its place for the call. The
77
+ * session parse cache is cleared on both sides so neither the emulated corpus
78
+ * nor the real one is served from the other's scan. */
79
+ export interface AccountedEventRender {
80
+ assembled: string;
81
+ accounting: ReturnType<typeof deliveryAccounting>;
82
+ }
83
+ /** Run the production renderer once and retain the structured document
84
+ * deliveries it recorded. Context diagnostics and the admin viewer consume
85
+ * this result rather than reconstructing attachment/companion fixpoints. */
86
+ export declare function assembleEventAccounting(snapshot: ContextSnapshot, event: SurfaceEvent, payload: DeliveryPayload): AccountedEventRender;
58
87
  export declare function describeSubject(subject: Record<string, unknown>): string;
59
88
  export declare function cell(v: unknown): string;
@@ -19,9 +19,9 @@ import { planDelivery, } from '../../../core/substrate/plan.js';
19
19
  import { explainRecordGate, formatClause } from '../../../core/substrate/gate-explain.js';
20
20
  import { emptyContextExposureState, exposureTarget } from '../../../core/substrate/injected-store.js';
21
21
  import { renderPreferencesForSubject, renderKnowledgeForSubject } from '../../../core/substrate/render.js';
22
- import { memoryReadDocBlocks, renderOnCommandDocsForSubject, renderOnReadDocsForSubject, renderPreCommandDocsForSubject, renderWorkspaceOpenDocsForSubject, } from '../../../core/substrate/on-read.js';
22
+ import { deliveryAccounting, memoryReadDocBlocks, renderOnCommandDocsForSubject, renderOnReadDocsForSubject, renderPreCommandDocsForSubject, renderWorkspaceOpenDocsForSubject, } from '../../../core/substrate/on-read.js';
23
23
  import { clearSessionCache } from '../../../core/substrate/session-cache.js';
24
- import { rungAtLeast, SURFACE_EVENTS, } from '../../../core/substrate/schema.js';
24
+ import { parseSubstrateDoc, rungAtLeast, SURFACE_EVENTS, } from '../../../core/substrate/schema.js';
25
25
  import { scopeForCwd, profileNameFor } from '../../../core/substrate/subject-fields.js';
26
26
  import { resetScopeCache } from '../../../core/scope.js';
27
27
  import { getDefaultProfileId } from '../../../core/profiles/default-binding.js';
@@ -52,7 +52,7 @@ export const resolveLeaf = defineLeaf({
52
52
  { name: 'event', type: 'string', required: false, constraint: 'The resolved event. Absent with --all-events, where every entry in `plans` carries its own.' },
53
53
  { name: 'subject', type: 'object', required: true, constraint: 'The configuration snapshot used: kind, mode, lifecycle, hasManager, profile, scope.' },
54
54
  { name: 'target', type: 'object', required: true, constraint: 'Store resolution inputs: cwd, profileId, nodeId.' },
55
- { name: 'docs', type: 'array', required: false, constraint: 'One entry per document, at `docs`/`all` granularity: name, kind, scope, store_path, authored_rung, project_memory_cap, capped_rung, final_rung, winner, shadowed_by, doc_gate, entry_gate, matched_entry, and excluded when --all includes a non-delivering document.' },
55
+ { name: 'docs', type: 'array', required: false, constraint: 'One entry per document, at `docs`/`all` granularity: name, kind, scope, store_path, authored_rung, project_memory_cap, capped_rung, final_rung, winner, shadowed_by, doc_gate, entry_gate, matched_entry, delivery_cause when the renderer raised it through a content attachment, and excluded when --all includes a non-delivering document.' },
56
56
  { name: 'counts', type: 'object', required: false, constraint: 'At `summary` granularity: {considered, delivered: {content, preview, name}, delivered_total, excluded: {<reason>: count}}.' },
57
57
  { name: 'all_ignored', type: 'boolean', required: false, constraint: 'Present only when --all is supplied with --granularity assembled; true means assembled text is unchanged because --all applies only to docs granularity.' },
58
58
  { name: 'assembled', type: 'string', required: false, constraint: 'At `assembled` granularity: the exact text the node would receive for this event.' },
@@ -188,15 +188,59 @@ async function fetchSubjectOrNull(nodeId) {
188
188
  }
189
189
  }
190
190
  function planSection(snapshot, event, payload, granularity, includeAll) {
191
- if (granularity === 'assembled') {
192
- return { event, assembled: assembleEvent(snapshot, event, payload) };
193
- }
194
- const plan = planDelivery(snapshot.subject, snapshot.target, event, payload);
191
+ const rendered = assembleEventAccounting(snapshot, event, payload);
192
+ if (granularity === 'assembled')
193
+ return { event, assembled: rendered.assembled };
194
+ const plan = effectivePlan(planDelivery(snapshot.subject, snapshot.target, event, payload), rendered.accounting.deliveries, payload);
195
195
  if (granularity === 'summary')
196
196
  return { event, counts: countsOf(plan) };
197
197
  const docs = plan.docs.filter((r) => includeAll || delivers(r)).map((r) => docRow(r, includeAll, plan.subject));
198
198
  return { event, docs };
199
199
  }
200
+ /** Overlay the production renderer's actual document deliveries onto direct
201
+ * planner records. The plan still supplies gates, caps, and exclusion detail;
202
+ * only an attachment that genuinely rendered may raise its final rung. */
203
+ export function effectivePlan(plan, deliveries, payload = {}) {
204
+ const seen = new Set();
205
+ const docs = plan.docs.map((record) => {
206
+ seen.add(realpathOrSelf(record.doc.path));
207
+ const delivered = deliveries.get(realpathOrSelf(record.doc.path));
208
+ if (delivered === undefined || !rungAtLeast(delivered.rung, record.finalRung))
209
+ return record;
210
+ const raised = delivered.rung !== record.finalRung;
211
+ return {
212
+ ...record,
213
+ // A companion has its own memory-read winner set. If it is shadowed or
214
+ // silent in the outer event plan but genuinely rendered through that
215
+ // attachment, the rendered delivery is the truth this view reports.
216
+ ...(raised ? { winner: true, shadowedBy: null } : {}),
217
+ finalRung: delivered.rung,
218
+ ...(raised && delivered.cause !== undefined ? { deliveryCause: delivered.cause } : {}),
219
+ };
220
+ });
221
+ for (const delivered of deliveries.values()) {
222
+ if (delivered.source === undefined || seen.has(realpathOrSelf(delivered.source.path)))
223
+ continue;
224
+ const doc = parseSubstrateDoc(delivered.source);
225
+ if (doc === null)
226
+ continue;
227
+ // Workspace-open deliberately plans only project stores, while its real
228
+ // listing/companion attachments answer from the full target view. Plan this
229
+ // one rendered source solely to retain normal dossier metadata, then make
230
+ // its renderer-recorded rung and cause authoritative.
231
+ const record = planDelivery(plan.subject, plan.target, plan.event, payload, [{ doc, source: delivered.source }]).docs[0];
232
+ if (record === undefined)
233
+ continue;
234
+ docs.push({
235
+ ...record,
236
+ winner: true,
237
+ shadowedBy: null,
238
+ finalRung: delivered.rung,
239
+ ...(delivered.cause === undefined ? {} : { deliveryCause: delivered.cause }),
240
+ });
241
+ }
242
+ return { ...plan, docs };
243
+ }
200
244
  /** A document delivers when it won its canonical name AND resolved to a rung
201
245
  * above the no-delivery floor — boot keeps rung-`none` winners in the plan so
202
246
  * the catalog can still count them. */
@@ -218,6 +262,7 @@ export function docRow(record, includeAll, subject) {
218
262
  doc_gate: gateText(record.docGate, explainRecordGate(record, 'doc', subject)),
219
263
  entry_gate: record.entryGate === null ? null : gateText(record.entryGate, explainRecordGate(record, 'entry', subject)),
220
264
  matched_entry: entryText(record.matchedEntry),
265
+ ...(record.deliveryCause === undefined ? {} : { delivery_cause: record.deliveryCause }),
221
266
  };
222
267
  if (includeAll && !delivers(record))
223
268
  row.excluded = exclusionReason(record, subject);
@@ -294,26 +339,23 @@ function exclusionBucket(record) {
294
339
  }
295
340
  return record.cappedRung === 'none' ? 'capped' : 'rung_none';
296
341
  }
297
- // Assembled granularity — the real renderers, so the highest-fidelity view is
298
- // byte-identical to what the node receives.
299
- /** Every renderer resolves its corpus from the AMBIENT process (cwd plus the
300
- * two env seams), not from a target argument, so a snapshot dialed away from
301
- * this process is only honored by standing in its place for the call. The
302
- * session parse cache is cleared on both sides so neither the emulated corpus
303
- * nor the real one is served from the other's scan. */
304
- function assembleEvent(snapshot, event, payload) {
342
+ /** Run the production renderer once and retain the structured document
343
+ * deliveries it recorded. Context diagnostics and the admin viewer consume
344
+ * this result rather than reconstructing attachment/companion fixpoints. */
345
+ export function assembleEventAccounting(snapshot, event, payload) {
305
346
  const restore = {
306
347
  cwd: process.cwd(),
307
348
  profileId: envProfileId(),
308
349
  nodeId: envNodeId(),
309
350
  };
351
+ const accounting = deliveryAccounting();
310
352
  clearSessionCache();
311
353
  resetScopeCache();
312
354
  try {
313
355
  process.chdir(snapshot.target.cwd);
314
356
  setEnv('CRTR_PROFILE_ID', snapshot.target.profileId ?? undefined);
315
357
  setEnv('CRTR_NODE_ID', snapshot.target.nodeId ?? undefined);
316
- return renderEvent(snapshot, event, payload);
358
+ return { assembled: renderEvent(snapshot, event, payload, accounting), accounting };
317
359
  }
318
360
  finally {
319
361
  process.chdir(restore.cwd);
@@ -329,29 +371,34 @@ function setEnv(name, value) {
329
371
  else
330
372
  process.env[name] = value;
331
373
  }
332
- function renderEvent(snapshot, event, payload) {
374
+ function renderEvent(snapshot, event, payload, accounting) {
333
375
  const { subject, target } = snapshot;
334
376
  switch (event) {
335
377
  case 'boot': {
336
- const { block } = renderPreferencesForSubject(subject);
337
- const knowledge = target.nodeId === null ? '' : renderKnowledgeForSubject(subject, target.nodeId);
378
+ const { block } = renderPreferencesForSubject(subject, undefined, accounting);
379
+ const knowledge = target.nodeId === null ? '' : renderKnowledgeForSubject(subject, target.nodeId, undefined, accounting);
338
380
  return knowledge === '' ? block : `${block}\n\n${knowledge}`;
339
381
  }
340
382
  case 'workspace-open':
341
- return renderWorkspaceOpenDocsForSubject(subject, target.cwd, target.profileId);
383
+ return renderWorkspaceOpenDocsForSubject(subject, target.cwd, target.profileId, undefined, accounting);
342
384
  case 'read':
343
- return payload.absoluteFile === undefined ? '' : renderOnReadDocsForSubject(subject, payload.absoluteFile);
385
+ return payload.absoluteFile === undefined ? '' : renderOnReadDocsForSubject(subject, payload.absoluteFile, undefined, accounting);
344
386
  case 'memory-read': {
345
387
  const name = payload.resolvedCanonicalName;
346
388
  if (name === undefined)
347
389
  return '';
348
- const blocks = memoryReadDocBlocks(subject, payload.resolvedDocPath ?? '', name, exposureTarget(emptyContextExposureState(), 'transcript'));
390
+ const blocks = memoryReadDocBlocks(payload.resolvedDocPath ?? '', name, {
391
+ subject,
392
+ target: exposureTarget(emptyContextExposureState(), 'transcript'),
393
+ listings: 'blocks',
394
+ accounting,
395
+ });
349
396
  return blocks.length === 0 ? '' : `<auto-loaded-context>\n${blocks.join('\n')}\n</auto-loaded-context>`;
350
397
  }
351
398
  case 'command':
352
- return payload.command === undefined ? '' : renderOnCommandDocsForSubject(subject, payload.command);
399
+ return payload.command === undefined ? '' : renderOnCommandDocsForSubject(subject, payload.command, undefined, accounting);
353
400
  case 'pre-command':
354
- return payload.command === undefined ? '' : renderPreCommandDocsForSubject(subject, payload.command);
401
+ return payload.command === undefined ? '' : renderPreCommandDocsForSubject(subject, payload.command, undefined, accounting);
355
402
  }
356
403
  }
357
404
  /** The read document never delivers to itself, so the plan needs its physical
@@ -403,7 +450,8 @@ function renderSection(section) {
403
450
  if (docs.length === 0)
404
451
  return 'No document delivers for this event.';
405
452
  const showExcluded = docs.some((d) => d.excluded !== undefined);
406
- const cols = ['document', 'scope', 'kind', 'authored', 'cap', 'final', 'matched', 'gate', ...(showExcluded ? ['excluded'] : [])];
453
+ const showCause = docs.some((d) => d.delivery_cause !== undefined);
454
+ const cols = ['document', 'scope', 'kind', 'authored', 'cap', 'final', 'matched', 'gate', ...(showCause ? ['cause'] : []), ...(showExcluded ? ['excluded'] : [])];
407
455
  const rows = docs.map((d) => [
408
456
  d.name,
409
457
  d.scope,
@@ -413,6 +461,7 @@ function renderSection(section) {
413
461
  d.final_rung,
414
462
  d.matched_entry ?? '—',
415
463
  d.doc_gate,
464
+ ...(showCause ? [d.delivery_cause ?? ''] : []),
416
465
  ...(showExcluded ? [d.excluded ?? ''] : []),
417
466
  ]);
418
467
  return [
@@ -10,6 +10,8 @@ export declare class ModelsPanel implements SetupPanel {
10
10
  private readonly label;
11
11
  private mode;
12
12
  private ladders;
13
+ private modelRoutes;
14
+ private legacyModelRoutes;
13
15
  private kinds;
14
16
  /** The ladder providers this panel offers — only columns whose runtime
15
17
  * provider is signed in (all of them when nobody is, so config stays
@@ -27,8 +29,11 @@ export declare class ModelsPanel implements SetupPanel {
27
29
  private modelSelector;
28
30
  private thinkingSelector;
29
31
  private pending;
30
- private pendingDim;
31
- private dimPickIndex;
32
+ private choice;
33
+ private choiceIndex;
34
+ private routeForm;
35
+ private readonly routeInput;
36
+ private pendingRouteCell;
32
37
  onChange?: () => void;
33
38
  constructor(tui: TUI, runtime: ModelRuntime, registry: ModelRegistry, matches: (id: BindingId, data: string) => boolean, label: (id: BindingId) => string);
34
39
  /** Same signed-in rule as the broker's Alt+M cycle: pool occupancy for a
@@ -38,6 +43,8 @@ export declare class ModelsPanel implements SetupPanel {
38
43
  private cycleTarget;
39
44
  private shadowingPath;
40
45
  private savedButShadowed;
46
+ private routeIds;
47
+ private routeFamilies;
41
48
  private buildRows;
42
49
  private reload;
43
50
  /** Re-read config and auth state — called when the Models tab is entered so
@@ -50,12 +57,26 @@ export declare class ModelsPanel implements SetupPanel {
50
57
  private clampColumn;
51
58
  handleInput(data: string): boolean;
52
59
  private activateCell;
53
- /** Snapshot of the mode's current provider+strength. For orchestrator this
60
+ private portableSelection;
61
+ private familyLabel;
62
+ private familyOptions;
63
+ /** Snapshot of the mode's current family+strength. For orchestrator this
54
64
  * falls back to the base `model` when no explicit override exists, so the
55
65
  * inherited pair is what an edit preserves. */
56
66
  private kindSelection;
57
67
  private openDimensionPicker;
58
- private commitDimensionPick;
68
+ private writeKindSelection;
69
+ private commitChoice;
70
+ private openRouteForm;
71
+ private closeRouteForm;
72
+ private activateRouteField;
73
+ private setRouteProvider;
74
+ private openRouteCell;
75
+ private onRouteModelPicked;
76
+ private saveRoute;
77
+ private declaringPath;
78
+ private deleteRoute;
79
+ private showModelSelector;
59
80
  private openModelPicker;
60
81
  private onModelPicked;
61
82
  private onThinkingPicked;
@@ -66,7 +87,9 @@ export declare class ModelsPanel implements SetupPanel {
66
87
  private targetKindOverride;
67
88
  footerHint(): string;
68
89
  /** Body lines only — the caller frames and windows them. */
69
- private renderDimensionPicker;
90
+ private renderChoicePicker;
91
+ private renderRouteForm;
92
+ private renderRouteDelete;
70
93
  /** Fits the panel body into `budget` rows. The wizard draws header/tabs above
71
94
  * and a footer below, so a body taller than its budget pushes the tab strip
72
95
  * off the top of the terminal — the panel must window itself instead. Chrome