@north-light/crouter 0.3.231 → 0.3.233
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 +3 -1
- package/dist/api/client.js +4 -0
- package/dist/api/dto/canvas.d.ts +10 -0
- package/dist/api/dto/common.d.ts +1 -1
- package/dist/api/dto/health.d.ts +2 -1
- package/dist/api/dto/lifecycle.d.ts +3 -4
- package/dist/api/dto/messages.d.ts +5 -4
- package/dist/api/dto/nodes.d.ts +2 -0
- package/dist/api/dto/profiles.d.ts +5 -0
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -0
- package/dist/builtin-memory/00-runtime-base/00-authoring.md +8 -0
- package/dist/builtin-memory/00-runtime-base/01-escalation.md +1 -1
- package/dist/builtin-memory/01-spine/00-has-manager.md +1 -1
- package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +5 -0
- package/dist/builtin-memory/02-turn-lifecycle/02-resident.md +5 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +2 -2
- package/dist/builtin-memory/insights/capture.md +3 -2
- package/dist/builtin-memory/internal/memory-loading.md +4 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +6 -1
- package/dist/clients/attach/render/diagram.js +13 -5
- package/dist/clients/attach/render/page-block.d.ts +0 -1
- package/dist/clients/attach/render/page-block.js +4 -57
- package/dist/clients/attach/viewer.js +570 -563
- package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +9 -0
- package/dist/clients/inbox/__tests__/integration/mount-panel.test.js +62 -1
- package/dist/clients/inbox/controller.d.ts +10 -0
- package/dist/clients/inbox/controller.js +56 -12
- package/dist/clients/inbox/tui/input.js +38 -10
- package/dist/clients/inbox/tui/page-body.d.ts +10 -0
- package/dist/clients/inbox/tui/page-body.js +66 -0
- package/dist/clients/inbox/tui/panel.js +6 -4
- package/dist/clients/inbox/tui/render.js +89 -17
- package/dist/clients/inbox/tui/types.d.ts +4 -4
- package/dist/commands/__tests__/human.test.js +18 -3
- package/dist/commands/__tests__/node-message.test.js +3 -3
- package/dist/commands/api-client.js +1 -7
- package/dist/commands/canvas-config.js +6 -14
- package/dist/commands/canvas-use.js +4 -6
- package/dist/commands/cron.js +16 -22
- package/dist/commands/human/prompts.d.ts +1 -1
- package/dist/commands/human/prompts.js +118 -112
- package/dist/commands/human/request.js +13 -14
- package/dist/commands/human/review.js +3 -4
- package/dist/commands/human/shared.d.ts +6 -0
- package/dist/commands/human/shared.js +43 -5
- package/dist/commands/human.js +1 -1
- package/dist/commands/memory/delete.js +4 -6
- package/dist/commands/memory/edit.js +0 -4
- package/dist/commands/memory/move.js +3 -5
- package/dist/commands/memory/shared.d.ts +1 -1
- package/dist/commands/memory/shared.js +11 -7
- package/dist/commands/memory/write.js +60 -29
- package/dist/commands/memory.js +1 -1
- package/dist/commands/node/bash.js +6 -9
- package/dist/commands/node/create.js +89 -22
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +30 -27
- package/dist/commands/node/message.js +24 -45
- package/dist/commands/node/subscription.js +6 -15
- package/dist/commands/node/wait.js +2 -3
- package/dist/commands/node-lifecycle-revive.js +1 -12
- package/dist/commands/pkg/browse/actions.js +2 -3
- package/dist/commands/pkg/market-manage.js +2 -5
- package/dist/commands/pkg/plugin-manage.js +7 -8
- package/dist/commands/profile/default.js +5 -5
- package/dist/commands/profile/delete.js +1 -1
- package/dist/commands/profile/env.js +9 -15
- package/dist/commands/profile/kind.js +3 -7
- package/dist/commands/profile/meta.js +3 -5
- package/dist/commands/profile/new.js +0 -6
- package/dist/commands/profile/pause.js +4 -8
- package/dist/commands/profile/project.js +5 -9
- package/dist/commands/profile/rename.js +3 -7
- package/dist/commands/profile/show.js +3 -3
- package/dist/commands/profile.js +4 -3
- package/dist/commands/surface-tmux-spread.js +1 -3
- package/dist/commands/sys/config.js +3 -4
- package/dist/commands/sys/support/prepare.js +8 -4
- package/dist/commands/sys/support/submit.js +2 -3
- package/dist/commands/sys/sync-deps.js +1 -9
- package/dist/commands/sys/sync-project-guidance.js +1 -7
- package/dist/commands/sys/sync-skills.js +1 -11
- package/dist/core/__tests__/cron-node-sink-parked-root.test.d.ts +1 -0
- package/dist/core/__tests__/cron-node-sink-parked-root.test.js +147 -0
- package/dist/core/__tests__/history-inbox.test.js +11 -1
- package/dist/core/__tests__/human-deliver.test.js +2 -1
- package/dist/core/__tests__/integration/command-plugins.test.js +0 -1
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -1
- package/dist/core/__tests__/lifecycle.test.js +30 -2
- package/dist/core/__tests__/revive-parked-fresh.test.d.ts +1 -0
- package/dist/core/__tests__/revive-parked-fresh.test.js +109 -0
- package/dist/core/__tests__/seam/dormancy-release.test.js +32 -5
- package/dist/core/canvas/attention.d.ts +2 -0
- package/dist/core/canvas/attention.js +25 -18
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +7 -1
- package/dist/core/canvas/history.js +20 -2
- package/dist/core/canvas/types.d.ts +1 -1
- package/dist/core/command.js +33 -8
- package/dist/core/help.d.ts +28 -2
- package/dist/core/help.js +46 -10
- package/dist/core/human/__tests__/page-html-markdown.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-html-markdown.test.js +48 -0
- package/dist/core/human/component-docs.js +4 -4
- package/dist/core/human/page-html-markdown.d.ts +8 -0
- package/dist/core/human/page-html-markdown.js +260 -0
- package/dist/core/memory/lint.d.ts +15 -0
- package/dist/core/memory/lint.js +150 -90
- package/dist/core/profiles/__tests__/fuzzy-match.test.d.ts +1 -0
- package/dist/core/profiles/__tests__/fuzzy-match.test.js +51 -0
- package/dist/core/profiles/fuzzy-match.d.ts +19 -0
- package/dist/core/profiles/fuzzy-match.js +92 -0
- package/dist/core/profiles/manifest.d.ts +14 -7
- package/dist/core/profiles/manifest.js +62 -12
- package/dist/core/profiles/select.d.ts +3 -1
- package/dist/core/profiles/select.js +5 -3
- package/dist/core/profiles/state-block.js +4 -3
- package/dist/core/runtime/boot-root.d.ts +3 -2
- package/dist/core/runtime/canvas-extensions.d.ts +7 -1
- package/dist/core/runtime/canvas-extensions.js +8 -1
- package/dist/core/runtime/lifecycle.d.ts +11 -2
- package/dist/core/runtime/lifecycle.js +15 -2
- package/dist/core/runtime/model-selection.d.ts +4 -0
- package/dist/core/runtime/model-selection.js +5 -0
- package/dist/core/runtime/nodes.js +5 -0
- package/dist/core/runtime/reopen.d.ts +6 -0
- package/dist/core/runtime/reopen.js +12 -1
- package/dist/core/runtime/revive.d.ts +6 -0
- package/dist/core/runtime/revive.js +22 -2
- package/dist/core/runtime/spawn.d.ts +5 -2
- package/dist/core/runtime/spawn.js +18 -32
- package/dist/core/runtime/structured-output.d.ts +6 -0
- package/dist/core/runtime/structured-output.js +6 -0
- package/dist/core/substrate/__tests__/surface-match-command.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-command.test.js +89 -0
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +92 -0
- package/dist/core/substrate/frontmatter-validation.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +6 -0
- package/dist/core/substrate/injected-store.js +24 -0
- package/dist/core/substrate/on-read.d.ts +17 -1
- package/dist/core/substrate/on-read.js +38 -3
- package/dist/core/substrate/schema.d.ts +3 -3
- package/dist/core/substrate/schema.js +4 -4
- package/dist/core/substrate/surface-match.d.ts +19 -0
- package/dist/core/substrate/surface-match.js +224 -12
- package/dist/core/termrender/version.d.ts +1 -1
- package/dist/core/termrender/version.js +1 -1
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/__tests__/broker-settle-park.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +102 -0
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +188 -0
- package/dist/daemon/api/__tests__/node-create-description.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/node-create-description.test.js +83 -0
- package/dist/daemon/api/__tests__/profile-metadata-route.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/profile-metadata-route.test.js +92 -0
- package/dist/daemon/api/__tests__/reopen-delivery.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +173 -0
- package/dist/daemon/api/handlers/broker-ops.js +21 -0
- package/dist/daemon/api/handlers/canvas.js +10 -0
- package/dist/daemon/api/handlers/messages.js +25 -16
- package/dist/daemon/api/handlers/nodes.js +5 -0
- package/dist/daemon/api/handlers/profiles.js +22 -1
- package/dist/daemon/cron-run.js +19 -1
- package/dist/daemon/manage.d.ts +16 -1
- package/dist/daemon/manage.js +20 -1
- package/dist/daemon/park-pending.d.ts +13 -0
- package/dist/daemon/park-pending.js +42 -0
- package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -0
- package/dist/daemon/reconcilers/broker-supervision.js +139 -21
- package/dist/daemon/reconcilers/live-obligation.d.ts +10 -4
- package/dist/daemon/reconcilers/live-obligation.js +7 -3
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +0 -4
- package/dist/daemon/reconcilers/storage-maintenance.js +1 -33
- package/dist/pi-extensions/__tests__/pre-command-gate.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +220 -0
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +14 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +75 -2
- package/dist/pi-extensions/canvas-prompt-scrub.d.ts +13 -0
- package/dist/pi-extensions/canvas-prompt-scrub.js +53 -0
- package/dist/shared/generated-context.d.ts +7 -0
- package/dist/shared/generated-context.js +11 -0
- package/package.json +4 -4
- package/runtime.lock.json +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/strip-skills-docs.ts +0 -47
package/dist/api/client.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { FocusDTO, RegisterFocusRequest, SetFocusPaneRequest } from './dto/
|
|
|
9
9
|
import { type ArmCronRequest, type CancelCronQuery, type CronDTO, type CronRunDTO, type CronScopeQuery, type CronShowDTO, type ListCronsQuery, type PokeCronsResult } from './dto/crons.js';
|
|
10
10
|
import type { NodeConfigPatch } from './dto/config.js';
|
|
11
11
|
import type { AttachEnsureRequest, AttachEnsureResultDTO } from './dto/attach.js';
|
|
12
|
-
import type { DeleteProfileRequest, DeleteProfileResultDTO, EnsureProfileRequest, ProfileDTO } from './dto/profiles.js';
|
|
12
|
+
import type { DeleteProfileRequest, DeleteProfileResultDTO, EnsureProfileRequest, ProfileDTO, UpdateProfileMetadataRequest } from './dto/profiles.js';
|
|
13
13
|
import type { FilePeekDTO } from './dto/files.js';
|
|
14
14
|
import type { MemoryDocRefDTO } from './dto/memory.js';
|
|
15
15
|
import type { ChatInventoryDTO, ProspectiveChatInventoryDTO, ProspectiveChatInventoryQuery } from './dto/chat-inventory.js';
|
|
@@ -174,6 +174,8 @@ export declare class CrtrClient {
|
|
|
174
174
|
ensureProfile(name: string, req?: EnsureProfileRequest): Promise<ProfileDTO>;
|
|
175
175
|
listProfiles(): Promise<ProfileDTO[]>;
|
|
176
176
|
getProfile(name: string): Promise<ProfileDTO>;
|
|
177
|
+
/** Merge and remove entries in a profile's metadata map. */
|
|
178
|
+
updateProfileMetadata(name: string, req: UpdateProfileMetadataRequest): Promise<ProfileDTO>;
|
|
177
179
|
/** Force-delete or detach one profile by exact id or unique name. */
|
|
178
180
|
deleteProfile(name: string, req: DeleteProfileRequest): Promise<DeleteProfileResultDTO>;
|
|
179
181
|
listModelAuth(): Promise<ModelAuthListDTO>;
|
package/dist/api/client.js
CHANGED
|
@@ -309,6 +309,10 @@ export class CrtrClient {
|
|
|
309
309
|
getProfile(name) {
|
|
310
310
|
return this.request('GET', routes.profile(name));
|
|
311
311
|
}
|
|
312
|
+
/** Merge and remove entries in a profile's metadata map. */
|
|
313
|
+
updateProfileMetadata(name, req) {
|
|
314
|
+
return this.request('PATCH', routes.profileMetadata(name), req);
|
|
315
|
+
}
|
|
312
316
|
/** Force-delete or detach one profile by exact id or unique name. */
|
|
313
317
|
deleteProfile(name, req) {
|
|
314
318
|
return this.request('DELETE', routes.profile(name), req);
|
package/dist/api/dto/canvas.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface AttentionItemDTO {
|
|
|
19
19
|
cwd: string;
|
|
20
20
|
/** Number of open tickets on this node. */
|
|
21
21
|
count: number;
|
|
22
|
+
/** Open ticket titles for this node, newest first — one per counted ticket. */
|
|
23
|
+
subjects: string[];
|
|
22
24
|
}
|
|
23
25
|
/** `GET /v1/canvas/attention` result. */
|
|
24
26
|
export interface AttentionDTO {
|
|
@@ -142,6 +144,10 @@ export interface HistoryReadResultDTO {
|
|
|
142
144
|
export interface SnapshotNodeDTO {
|
|
143
145
|
node_id: NodeIdDTO;
|
|
144
146
|
name: string;
|
|
147
|
+
/** Caller-supplied node title, kept separate from the enriched full label. */
|
|
148
|
+
title: string;
|
|
149
|
+
/** Caller-supplied node brief, or null when absent. */
|
|
150
|
+
description: string | null;
|
|
145
151
|
kind: string;
|
|
146
152
|
mode: string;
|
|
147
153
|
lifecycle?: string;
|
|
@@ -161,6 +167,10 @@ export interface SnapshotNodeDTO {
|
|
|
161
167
|
last_activity?: IsoTime;
|
|
162
168
|
ctx_tokens?: number;
|
|
163
169
|
streaming: boolean;
|
|
170
|
+
/** Number of active background bash jobs, reported only for a live broker. */
|
|
171
|
+
jobs_active: number;
|
|
172
|
+
/** Controller node this node is durably waiting for, or null. */
|
|
173
|
+
waiting_for: string | null;
|
|
164
174
|
/** The node's active fault projection (structurally a `Fault`), or null. Kept
|
|
165
175
|
* as a loose object to keep this DTO free of a `core` type import. */
|
|
166
176
|
hanging: Record<string, unknown> | null;
|
package/dist/api/dto/common.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type LifecycleDTO = 'terminal' | 'resident';
|
|
|
18
18
|
/** Execution mode of a node (mirrors the runtime `Mode` union). */
|
|
19
19
|
export type ModeDTO = 'base' | 'orchestrator';
|
|
20
20
|
/** Why a node last stopped (mirrors the runtime `ExitIntent` union). */
|
|
21
|
-
export type ExitIntentDTO = 'done' | 'refresh' | 'idle-release' | null;
|
|
21
|
+
export type ExitIntentDTO = 'done' | 'refresh' | 'idle-release' | 'parked' | null;
|
|
22
22
|
/** Inbox urgency tier for a delivered message (mirrors feed/inbox `InboxTier`). */
|
|
23
23
|
export type InboxTierDTO = 'critical' | 'urgent' | 'normal' | 'deferred';
|
|
24
24
|
/** Whether a node id is a safe single filesystem path segment. Re-declared here
|
package/dist/api/dto/health.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ export interface HealthDTO {
|
|
|
8
8
|
brokers_reconciled: boolean;
|
|
9
9
|
}
|
|
10
10
|
/** `GET /v1/status` — daemon status snapshot. Backs `crtr sys daemon status`
|
|
11
|
-
* state and the `crtr canvas dashboard` header.
|
|
11
|
+
* state and the `crtr canvas dashboard` header. `runtime_version` is also
|
|
12
|
+
* checked by Core's minimum-runtime capability gate. */
|
|
12
13
|
export interface StatusDTO {
|
|
13
14
|
daemon_pid: number;
|
|
14
15
|
tick_interval_ms: number;
|
|
@@ -4,10 +4,9 @@ export interface ReviveRequest {
|
|
|
4
4
|
/** Resume the saved conversation (true) vs a fresh launch (false). */
|
|
5
5
|
resume?: boolean;
|
|
6
6
|
/** Clear the finalization latch before reviving (the `--reopen` gate). The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* (`not_finalized`). */
|
|
7
|
+
* revive doorway accepts it only for a finalized node (`not_finalized`
|
|
8
|
+
* otherwise); without it, a finalized node is rejected (`node_finalized`).
|
|
9
|
+
* Message delivery has its own wider reopen behavior. */
|
|
11
10
|
reopen?: boolean;
|
|
12
11
|
/** Wake provenance: the cron whose run drove this revive (`CRTR_CRON_ID`,
|
|
13
12
|
* set by the daemon in every cron run's environment and forwarded by the
|
|
@@ -19,8 +19,9 @@ export interface SendMessageRequest {
|
|
|
19
19
|
from?: NodeIdDTO | null;
|
|
20
20
|
/** Revive with no inbox entry (`--fresh` → `reviveNode({ resume: false })`). */
|
|
21
21
|
fresh?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
22
|
+
/** Before delivery, make the target resident and clear its finalization latch
|
|
23
|
+
* when present (`--reopen`). Accepted for both durable and interactive
|
|
24
|
+
* delivery; `--fresh` retains its separate finalized-only gate. */
|
|
24
25
|
reopen?: boolean;
|
|
25
26
|
/** Hidden ambient context upserted onto the target's sidecar as a
|
|
26
27
|
* `situational` card, never visible chat (`--situational-context`).
|
|
@@ -42,8 +43,8 @@ export interface SendMessageRequest {
|
|
|
42
43
|
* on its one serialized frame loop — the same ordering a tmux viewer gets)
|
|
43
44
|
* instead of the durable inbox; a dormant or mid-revive target falls back to
|
|
44
45
|
* the durable inbox + revive (watcher delivers post-boot). Plain immediate
|
|
45
|
-
* body only — rejected with fresh/
|
|
46
|
-
*
|
|
46
|
+
* body only — rejected with fresh/situational_context/output_schema or tier
|
|
47
|
+
* 'deferred'. Runtime cards and reopen ARE accepted: a
|
|
47
48
|
* card-bearing send is an ordinary human send that happens to carry context,
|
|
48
49
|
* and the live deliver frame places the cards ahead of the body in one turn.
|
|
49
50
|
* Absent → durable inbox (unchanged). */
|
package/dist/api/dto/nodes.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface CreateNodeRequest {
|
|
|
33
33
|
pin_cwd?: string;
|
|
34
34
|
/** Display name (tmux window + resume picker). Defaults to the kind. */
|
|
35
35
|
name?: string;
|
|
36
|
+
/** Caller-supplied short description, preserved against automatic naming. */
|
|
37
|
+
description?: string;
|
|
36
38
|
parent?: NodeIdDTO | null;
|
|
37
39
|
root?: boolean;
|
|
38
40
|
/** Lifecycle of an independent root. Defaults to `resident`; use `terminal`
|
|
@@ -23,6 +23,11 @@ export interface EnsureProfileRequest {
|
|
|
23
23
|
* launched under the profile as `CRTR_PROFILE_META_<KEY>` env. */
|
|
24
24
|
metadata?: Record<string, string>;
|
|
25
25
|
}
|
|
26
|
+
/** `PATCH /v1/profiles/{name}/metadata` body. Merges `set` and removes `unset`. */
|
|
27
|
+
export interface UpdateProfileMetadataRequest {
|
|
28
|
+
set?: Record<string, string>;
|
|
29
|
+
unset?: string[];
|
|
30
|
+
}
|
|
26
31
|
/** `DELETE /v1/profiles/{name}` body. Destructive deletion is never implicit. */
|
|
27
32
|
export interface DeleteProfileRequest {
|
|
28
33
|
force: boolean;
|
package/dist/api/routes.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export declare const routes: {
|
|
|
93
93
|
readonly humanRequestCancel: (requestId: string) => string;
|
|
94
94
|
readonly profiles: () => string;
|
|
95
95
|
readonly profile: (name: string) => string;
|
|
96
|
+
readonly profileMetadata: (name: string) => string;
|
|
96
97
|
readonly modelAuths: () => string;
|
|
97
98
|
readonly modelAuth: (provider: string) => string;
|
|
98
99
|
readonly filePeek: () => string;
|
package/dist/api/routes.js
CHANGED
|
@@ -124,6 +124,7 @@ export const routes = {
|
|
|
124
124
|
// Profiles (deletion is daemon-owned because it crosses canvas state)
|
|
125
125
|
profiles: () => `${V}/profiles`,
|
|
126
126
|
profile: (name) => `${V}/profiles/${name}`,
|
|
127
|
+
profileMetadata: (name) => `${V}/profiles/${name}/metadata`,
|
|
127
128
|
// Model auth
|
|
128
129
|
modelAuths: () => `${V}/model-auth`,
|
|
129
130
|
modelAuth: (provider) => `${V}/model-auth/${provider}`,
|
|
@@ -6,6 +6,8 @@ rationale: >-
|
|
|
6
6
|
|
|
7
7
|
"Say what actually happens" exists because an approval request called a root a person had created an "attended root" — an invented category with no referent in the product, which forced Silas to halt the decision and ask what the term meant (2026-07-28). Agents coin taxonomies to compress a distinction; the reader pays by decoding a word that names nothing real.
|
|
8
8
|
|
|
9
|
+
"Ground what you report" and "Acting" exist because the Fable 5 model guidance (adopted 2026-08-22, Silas) showed that an explicit audit-against-tool-results instruction nearly eliminates fabricated status reports, and that un-steered models take unrequested actions and over-deliberate; both sections track the official wording closely on purpose — its density and operational triggers are the quality bar.
|
|
10
|
+
|
|
9
11
|
The Mermaid line exists because the viewer's inline diagram affordance is otherwise invisible to an agent working from ordinary Markdown defaults.
|
|
10
12
|
|
|
11
13
|
An "Identity" section is deliberately absent, and the artifacts section carries no paths. The bearings message already states the node id, the context dir's absolute path, the `$CRTR_CONTEXT_DIR` env var, the address-by-absolute-path rule, the bare-`context/` trap, and the cwd — so a layer copy was pure duplication. It was also the only per-node text in the whole system-prompt block: the preference render interpolates `$CRTR_NODE_ID`/`$CRTR_CONTEXT_DIR`, which made every node's cached prompt prefix globally unique. Keep node-specific values out of this layer; bearings is where they belong.
|
|
@@ -24,6 +26,12 @@ An artifact you write to your context dir is shared by pointer: whatever carries
|
|
|
24
26
|
## Living documents
|
|
25
27
|
Every doc you keep — artifact, plan, findings, memory — is a living statement of what is true *now*, never a log of how it got that way. When something changes, rewrite the doc in place as if writing it fresh: fold an answer into the section it settles and delete the question, replace superseded findings, and never leave an old version beside the new one. Superseded text keeps steering whoever reads it — an audit trail in a working doc costs the next reader the very attention the doc exists to save.
|
|
26
28
|
|
|
29
|
+
## Ground what you report
|
|
30
|
+
Before reporting progress, audit each claim against a tool result from this session. Report only work you can point to evidence for; if something is not yet verified, say so explicitly. Report outcomes faithfully: if tests fail, say so with the output; if a step was skipped, say that; when something is done and verified, state it plainly without hedging.
|
|
31
|
+
|
|
32
|
+
## Acting
|
|
33
|
+
When you have enough information to act, act — do not re-derive facts already established, re-litigate a decision already made, or narrate options you will not pursue; when weighing a choice, give a recommendation, not a survey. When whoever tasked you is describing a problem or thinking out loud rather than requesting a change, the deliverable is your assessment — report your findings and stop. Before a command that changes system state (a restart, a delete, a config edit), check that the evidence supports that specific action — a signal that pattern-matches a known failure may have a different cause.
|
|
34
|
+
|
|
27
35
|
## Say what actually happens
|
|
28
36
|
Everything you write — replies, reports, approval requests, artifacts, memory docs, comments — describes systems in concrete, existing product terms: the real command, the real event, the actual cause. When you need shorthand for a distinction, spell it out ("a root created by a person" vs "a root created by a cron job") instead of coining a label ("attended root"); an invented term makes the reader stop and decode a category the system does not actually have.
|
|
29
37
|
|
|
@@ -8,7 +8,7 @@ surfaces:
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## When blocked, want feedback, or need the user
|
|
11
|
-
|
|
11
|
+
Ask the user only when the work genuinely requires them: intent that stays ambiguous after you have dug, a tradeoff that is genuinely theirs, a real scope or direction change, a destructive or irreversible action, or sign-off on finished work. Settle everything the code, a tool, or a delegate can settle first — a whole goal costs a handful of asks, not a stream. Ask through the human inbox (`crtr human send -h`), then end your turn: a question posed as prose in a reply or report pings nobody, while an ask lands on their screen and its answer wakes you.
|
|
12
12
|
|
|
13
13
|
## When crtr itself misbehaves
|
|
14
14
|
A `crtr` command that errors unexpectedly, hangs, churns, double-spawns, or contradicts its own `-h` is a harness bug — don't silently work around it. Run `crtr sys feedback` to report it (`-h` for how), then continue.
|
|
@@ -8,7 +8,7 @@ surfaces:
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## Reporting up (the feed)
|
|
11
|
-
You report to whoever subscribes to you (usually your parent). They see your output ONLY through explicit pushes (`crtr push -h`) — nothing is sent automatically when you stop, so narrating progress in your turn reaches no one.
|
|
11
|
+
You report to whoever subscribes to you (usually your parent). They see your output ONLY through explicit pushes (`crtr push -h`) — nothing is sent automatically when you stop, so narrating progress in your turn reaches no one. Lead a push with the outcome — what happened or what you found — then the detail and artifact paths.
|
|
12
12
|
|
|
13
13
|
## Escalating
|
|
14
14
|
If the work is bigger or different than your task implies, say so in a push to your managers rather than silently expanding scope.
|
|
@@ -4,6 +4,8 @@ when-and-why-to-read: When a node has no immediate action because it awaits an e
|
|
|
4
4
|
rationale: >-
|
|
5
5
|
"Waiting is a way to end a turn" lived in its own ungated all-node doc until 2026-07-28. Same gate, same audience, never independently readable — so the split bought no routing and cost a stub cross-reference in runtime-base pointing at a section spliced a few hundred tokens later. It now has its own sort position as the turn-lifecycle preamble; split it again only when a section needs its own sort position, incident rationale, or independent deletion/re-gate decision.
|
|
6
6
|
|
|
7
|
+
"Never end on a promise" exists because deep into long sessions models occasionally end a turn on a text-only statement of intent without the corresponding tool call; the check-your-last-paragraph form is the operational trigger the Fable 5 guidance validated (adopted 2026-08-22, Silas).
|
|
8
|
+
|
|
7
9
|
Yield applies to every node regardless of mode; promotion does not, because only the mode layers own that boundary — 04-base-worker for when a base node should promote, the kernel for how an orchestrator uses promotion — so restating it in the universal layer duplicated the base-worker text for an audience that includes nodes it does not apply to.
|
|
8
10
|
lint-ignore: length
|
|
9
11
|
surfaces:
|
|
@@ -19,6 +21,9 @@ When your goal is sound but your next step is blocked on something that has not
|
|
|
19
21
|
- **For waits the runtime already knows — a child's report or the reply to your own human page — just stop.** Go dormant; the runtime wakes you when it lands. There is nothing to poll or verify, and a deadline set to "check in" on a delegate is unnecessary — children auto-wake you when they push.
|
|
20
22
|
- **Schedule a wake yourself only when nothing can push to you** — recurring or scheduled standing work, or polling an external the spine can't deliver (CI, a deploy, a clock). Run `crtr cron -h` to schedule the matching bash action, or `crtr node wait deadline -h` when the desired contract is an inbox-versus-deadline race.
|
|
21
23
|
|
|
24
|
+
## Never end on a promise
|
|
25
|
+
Before you stop, check your last paragraph. If it is a plan, a list of next steps, or a promise about work you have not done ("I'll now…"), that is not an ending — do that work now with tool calls. End your turn only when the work is complete, you are waiting, or you are blocked on input only a person can provide.
|
|
26
|
+
|
|
22
27
|
## Yield for a fresh window
|
|
23
28
|
When your context is filling but the mandate isn't done, yield: you revive fresh as the same node with the same mandate, carrying a note to your future self.
|
|
24
29
|
|
|
@@ -8,9 +8,11 @@ surfaces:
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## Communicating with the user
|
|
11
|
-
- Respond to what was actually said—don
|
|
12
|
-
- Lead with
|
|
13
|
-
-
|
|
11
|
+
- Respond to what was actually said — don't invent questions, concerns, or agreement.
|
|
12
|
+
- Lead with the outcome: your first sentence answers "what happened" or "what did you find" — the thing they would ask for if they said "just give me the TLDR." Supporting detail and reasoning come after.
|
|
13
|
+
- Keep replies short by being selective about what you include — drop details that don't change what the reader would do next — not by compressing into fragments, arrow chains, or coined shorthand. Readable beats short.
|
|
14
|
+
- After a long working stretch, your reply is their first look at any of it: write it as a re-grounding, not a continuation of your working thread, and leave the vocabulary you built up while working behind unless you re-introduce it.
|
|
15
|
+
- Use plain, proportionate language — cut praise, clichés, faux urgency, and repetition.
|
|
14
16
|
|
|
15
17
|
## How you end
|
|
16
18
|
You are **resident** and interactable: you are never forced to submit a final result. Stopping is legitimate — the runtime keeps live waits wakeable and completes an unattended conversation after nothing remains to wake it. Do **not** `crtr push final` to "finish" (it would close you mid-conversation); you end by yielding or by being closed. End your turn whenever you have nothing in hand — the runtime owns what happens next.
|
|
@@ -5,7 +5,7 @@ gate: {mode: orchestrator}
|
|
|
5
5
|
rationale: >-
|
|
6
6
|
Two observed orchestration failures set this kernel's stopping rules. A sole-writer feature lane produced a 5-deep 1:1 developer/orchestrator chain by repeatedly delegating the whole assignment; separately, the kernel's “idle capacity,” “maximum agents,” and “when in doubt, more rigor” objective helped produce review-only subtrees as large as 87 nodes and five levels deep. Coordination must optimize new evidence toward the goal rather than node count or process length.
|
|
7
7
|
|
|
8
|
-
Waiting guidance is deliberately absent: 02-turn-lifecycle/00-ending-a-turn owns waiting for every node, including the auto-wake on a child's report, so a kernel copy only duplicated it. Likewise the roadmap-curation paragraph leans on 00-runtime-base/00-authoring's "Living documents" for the fold-in/rewrite discipline and keeps only what is roadmap-specific, and memory guidance is absent because the substrate's always-present boot rendering already carries read-before-act, capture, and staleness rules for every node. Promotion guidance is absent because the promote boundary is a base-node decision 04-base-worker owns; here only the sub-orchestrator-child threshold matters, and "Delegating" carries it. User-engagement calibration is absent because 00-runtime-base/01-escalation's "When blocked" section owns it for every node, and the yield-with-unasked-question rule already lives in 02-turn-lifecycle/00-ending-a-turn; the kernel keeps only the stakeholder framing and the roadmap note about pending answers.
|
|
8
|
+
Prompt-writing guidance (outcome framing, intent context, artifact paths, report shape) is deliberately absent because `node new -h` owns it — the forced read at the spawn moment for every spawner, base and orchestrator alike; the kernel keeps only decomposition and cross-child routing. Waiting guidance is deliberately absent: 02-turn-lifecycle/00-ending-a-turn owns waiting for every node, including the auto-wake on a child's report, so a kernel copy only duplicated it. Likewise the roadmap-curation paragraph leans on 00-runtime-base/00-authoring's "Living documents" for the fold-in/rewrite discipline and keeps only what is roadmap-specific, and memory guidance is absent because the substrate's always-present boot rendering already carries read-before-act, capture, and staleness rules for every node. Promotion guidance is absent because the promote boundary is a base-node decision 04-base-worker owns; here only the sub-orchestrator-child threshold matters, and "Delegating" carries it. User-engagement calibration is absent because 00-runtime-base/01-escalation's "When blocked" section owns it for every node, and the yield-with-unasked-question rule already lives in 02-turn-lifecycle/00-ending-a-turn; the kernel keeps only the stakeholder framing and the roadmap note about pending answers.
|
|
9
9
|
lint-ignore: length
|
|
10
10
|
surfaces:
|
|
11
11
|
- on: boot
|
|
@@ -62,7 +62,7 @@ Then advance. Reshape the phases themselves only when reality invalidates the pl
|
|
|
62
62
|
|
|
63
63
|
## Delegating
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
How to write a child's prompt lives on `crtr node new -h`, read at the moment you spawn; this kernel owns the split and the management. Break the goal into units each small enough for one child to finish well in one window, and route findings across children: you are the relay point for everything your children report up, so when a child's task depends on an explore report, design, or spec an earlier child produced, hand that artifact into the new task. When a bounded unit itself contains enough independent work for worthwhile parallelism, create it directly as a sub-orchestrator (`crtr node new --kind <kind> --mode orchestrator`); when it is sequential, assign it to a base child that can yield across windows rather than relying on promotion.
|
|
66
66
|
|
|
67
67
|
Every child's task must be a proper subset of your goal. Handing your whole remaining assignment to one child is recursion, not delegation — it recreates you one window poorer, and repeated once per window it builds a chain of managers with no worker. When a unit cannot be split and cannot run in parallel — a sole-writer implementation, one tightly interlocked surface — it is yours: work it hands-on and yield to continue across windows; hands-on work on an unsplittable unit is orchestration done right, not a lost plot.
|
|
68
68
|
|
|
@@ -26,7 +26,8 @@ Write a small Markdown artifact under this node's absolute context directory and
|
|
|
26
26
|
```markdown
|
|
27
27
|
# Proposed insight: <short principle name>
|
|
28
28
|
|
|
29
|
-
- **destination:** <canonical name (scope) — existing doc to rewrite, new flat doc, or link into a listener or router>
|
|
29
|
+
- **destination:** <canonical name (scope) — existing doc to rewrite, new flat doc, or link into a listener or router — with the neighbouring listing, so the placement is judgeable without a lookup>
|
|
30
|
+
- **visibility:** <the gate and surfaces the doc will carry, or none, and why that reach is right>
|
|
30
31
|
- **source:** <the episode in one line, quoting the user's pivotal words>
|
|
31
32
|
|
|
32
33
|
## Proposed durable truth
|
|
@@ -35,7 +36,7 @@ Write a small Markdown artifact under this node's absolute context directory and
|
|
|
35
36
|
- <Another plausible formulation only when the deeper principle is uncertain.>
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
Quote only enough of the user's words in `source` for them to judge the inference. Candidate bullets are hypotheses, not polished prose: several alternatives are allowed when the deeper principle is uncertain; do not pad, explain every bullet, or hide uncertainty in a long essay.
|
|
39
|
+
`destination` and `visibility` are proposals to approve, never questions to ask: decide both and state the reason. Quote only enough of the user's words in `source` for them to judge the inference. Candidate bullets are hypotheses, not polished prose: several alternatives are allowed when the deeper principle is uncertain; do not pad, explain every bullet, or hide uncertainty in a long essay.
|
|
39
40
|
|
|
40
41
|
The review is asynchronous. Continue unrelated work or wait dormant, but do not finish while this review is outstanding. An approval settles only this candidate; do not recapture it as a new insight unless the user introduces a genuinely separate principle.
|
|
41
42
|
|
|
@@ -9,7 +9,7 @@ surfaces:
|
|
|
9
9
|
|
|
10
10
|
# How memory loads
|
|
11
11
|
|
|
12
|
-
Every memory doc declares its own delivery in frontmatter `surfaces` entries; the runtime never guesses. Delivery is
|
|
12
|
+
Every memory doc declares its own delivery in frontmatter `surfaces` entries; the runtime never guesses. Delivery is six events, one rung per entry, document and entry gates, and structural ordering. The authoring contract (flags, routing-line craft) is `crtr memory write -h`; which scope to write to is `internal/agent-shaping`; physical paths are `internal/storage-tiers`. This doc is the mechanics between those: what actually fires, when, and in what order.
|
|
13
13
|
|
|
14
14
|
## Surfaces entries
|
|
15
15
|
|
|
@@ -19,7 +19,8 @@ A doc with no `surfaces` does exactly one thing: appears in its directory's list
|
|
|
19
19
|
- **workspace-open** — first-message context when cwd/profile mounts the doc's project store. Project stores only.
|
|
20
20
|
- **read** — a `read` tool call returned a matching file: path globs vs the file's absolute path and basename, `./`-anchored globs vs its path relative to the store's owning repo dir, `match-frontmatter` predicates over the read file's own YAML frontmatter.
|
|
21
21
|
- **memory-read** — a `crtr memory read` resolved a matching doc: name globs vs its canonical name, `./` anchored to this doc's canonical routing anchor (a collapsed directory document anchors at its own directory name).
|
|
22
|
-
- **command** — a matching shell command ran: globs vs the whole command string, `*` crossing `/`. Delivery is post-execution — right for "you are now in this territory
|
|
22
|
+
- **command** — a matching shell command ran: globs vs the whole command string, `*` crossing `/`. Delivery is post-execution — right for "you are now in this territory"; when the point is "don't run this at all," use `pre-command` instead.
|
|
23
|
+
- **pre-command** — a matching `bash` command is about to run. When the memory has not been read yet, the command does not execute — the memory comes back as the tool result instead, and the agent re-issues the command, which then runs. Use it to guarantee a memory is seen before a sensitive action is taken. Same globs as `command`, matched at command position; `match` is required. A guardrail against the faithful-but-uninformed action, not an enforcement boundary.
|
|
23
24
|
|
|
24
25
|
Nothing positional fires from where a doc happens to sit on disk — only from its declared entries and its listing.
|
|
25
26
|
|
|
@@ -49,7 +50,7 @@ At boot/first-message assembly the runtime mounts: builtin docs, the user store
|
|
|
49
50
|
|
|
50
51
|
Each project the selected profile has a relationship with carries a `memory` value — `none`, `name`, `preview`, or `content` — and that value is the maximum rung anything in that project's stores delivers at boot and workspace-open, whatever directory the node is working in. It only lowers: an entry authored below the maximum delivers at its authored rung. `none` contributes nothing to either automatic event, so a `none` project cannot shadow a same-named doc from a wider scope — that wider doc becomes the winner. A project store the selected profile has no relationship with delivers exactly what it authored.
|
|
51
52
|
|
|
52
|
-
The maximum reaches those two events and nothing else. Read, memory-read, and command entries fire at their authored rungs; directory listings, `crtr memory read`, `crtr memory find`, config resolution, and plugin discovery all see the full corpus. An explicit `crtr memory read` is itself a content delivery, so reading a capped or document-gated doc returns its whole body and records content — the upgrade path for a doc the automatic events disclosed only by name or preview. Entry gates apply to companion docs routed by the `memory-read` event, not to the deliberate read or its listings.
|
|
53
|
+
The maximum reaches those two events and nothing else. Read, memory-read, command, and pre-command entries fire at their authored rungs; directory listings, `crtr memory read`, `crtr memory find`, config resolution, and plugin discovery all see the full corpus. An explicit `crtr memory read` is itself a content delivery, so reading a capped or document-gated doc returns its whole body and records content — the upgrade path for a doc the automatic events disclosed only by name or preview. Entry gates apply to companion docs routed by the `memory-read` event, not to the deliberate read or its listings.
|
|
53
54
|
|
|
54
55
|
A workspace's front door is an ordinary doc carrying the entry pair `{on: workspace-open, at: content}` + `{on: read, match: "./**", at: content}` — the operating guide loads when that workspace mounts or its files are read, not in every boot catalog. `crtr memory lint` requires exactly one workspace-open content doc per profile-managed project store. Multiple mounted roots render broad-to-specific.
|
|
55
56
|
|
|
@@ -38,7 +38,12 @@ directory's `node_modules/`.
|
|
|
38
38
|
| `sysprompt-window.ts` | Registers `/sysprompt`, which runs `crtr sys sysprompt --window` without injecting the prompt into context. |
|
|
39
39
|
| `frontmatter-rules/` | Injects `.pi/rules/*.md` whose `when:` frontmatter matches a read markdown file. `.claude/rules` are migrated into substrate docs with read-glob `surfaces` entries via `crtr sys sync project-guidance`. Needs the `yaml` dep. See the `pi-frontmatter-rules` skill. |
|
|
40
40
|
| `statusline.ts` | Custom status line. |
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
Removing pi's self-referential prompt text (the harness identity sentence and
|
|
43
|
+
the pi documentation block) is NOT done here. It runs as a canvas extension, so
|
|
44
|
+
it is ordered ahead of every installed package — including the Claude
|
|
45
|
+
subscription adapter, which otherwise cuts the node's own persona out of the
|
|
46
|
+
system prompt along with the docs block.
|
|
42
47
|
|
|
43
48
|
## Notes
|
|
44
49
|
|
|
@@ -6,11 +6,19 @@
|
|
|
6
6
|
// viewer shows the process rather than source syntax.
|
|
7
7
|
//
|
|
8
8
|
// How it stays simple:
|
|
9
|
-
// •
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
9
|
+
// • The rendered lines are spliced back into markdown inside a plain ``` code
|
|
10
|
+
// fence, and pi's Markdown shows a fence body VERBATIM — no re-wrapping, and
|
|
11
|
+
// ANSI escapes pass through to the terminal unchanged. That pass-through is
|
|
12
|
+
// what the splice depends on, because mermaid output is NOT escape-free:
|
|
13
|
+
// since termrender 4.12.9 an emphasis tag in a node or edge label (`<b>`,
|
|
14
|
+
// `<strong>`, `<i>`, `<em>`) renders as a real ANSI run. Verified on screen
|
|
15
|
+
// — an emphasized label arrives bold, with its box borders still aligned.
|
|
16
|
+
// • So treat these lines as STYLED text, not plain text. termrender already
|
|
17
|
+
// sized every box off the visible glyphs, so anything here that measures,
|
|
18
|
+
// truncates, or pads a rendered line must be escape-aware or it will
|
|
19
|
+
// silently reintroduce the misalignment termrender just removed. The
|
|
20
|
+
// trailing-whitespace trim below is safe only because termrender closes a
|
|
21
|
+
// run before the cell padding that follows it.
|
|
14
22
|
// • `renderMarkdown` (crouter's sole org-wide termrender binding) is
|
|
15
23
|
// synchronous and memoized, so this is a plain string→string transform with
|
|
16
24
|
// no async swap or component lifecycle.
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
import { readFileSync, statSync } from 'node:fs';
|
|
15
15
|
import { pageAwaitsResponse } from '../../../core/human/page-schema.js';
|
|
16
16
|
import { projectPageDisplayMarkdown } from '../../../core/human/page-markdown.js';
|
|
17
|
+
import { projectHtmlDocumentMarkdown } from '../../../core/human/page-html-markdown.js';
|
|
17
18
|
import { parsePage } from '../../../core/human/page.js';
|
|
18
19
|
import { pageManifestPath, pagePath, responsePath } from '../../../core/human/convention.js';
|
|
19
20
|
import { pageTicketState, readTicketResult } from '../../../core/human/tickets.js';
|
|
20
|
-
import {
|
|
21
|
-
import { describeSlot, tableMarkdown, terminalAnswerable } from '../../inbox/tui/slots.js';
|
|
21
|
+
import { pageBodyLines } from '../../inbox/tui/page-body.js';
|
|
22
22
|
import { BOLD, CYAN, DIM, GREEN, RESET, YELLOW, sanitize, singleLine, truncate, wrap } from '../../inbox/tui/ansi.js';
|
|
23
23
|
/** Rows of page body shown while the block is folded; Ctrl+O expands it. */
|
|
24
24
|
const FOLDED_BODY_LINES = 16;
|
|
@@ -113,7 +113,7 @@ export class PageBlockComponent {
|
|
|
113
113
|
const manifest = parsePage(this.dir);
|
|
114
114
|
const displayMarkdown = manifest.dialect === 'jsx'
|
|
115
115
|
? projectPageDisplayMarkdown(readFileSync(pagePath(this.dir, 'jsx'), 'utf8'), manifest)
|
|
116
|
-
: '';
|
|
116
|
+
: projectHtmlDocumentMarkdown(readFileSync(pagePath(this.dir, 'html'), 'utf8'));
|
|
117
117
|
const state = pageTicketState(this.dir);
|
|
118
118
|
const digest = state === 'resolved' ? readTicketResult(this.dir) : null;
|
|
119
119
|
this.snapshot = {
|
|
@@ -140,7 +140,7 @@ export class PageBlockComponent {
|
|
|
140
140
|
for (const line of wrap(sanitize(snapshot.manifest.subtitle), maxW))
|
|
141
141
|
out.push(` ${DIM}${line}${RESET}`);
|
|
142
142
|
}
|
|
143
|
-
const body =
|
|
143
|
+
const body = pageBodyLines(snapshot.manifest, snapshot.displayMarkdown, maxW, Math.max(20, width - 4));
|
|
144
144
|
if (body.length > 0) {
|
|
145
145
|
out.push('');
|
|
146
146
|
const shown = this.expanded || body.length <= FOLDED_BODY_LINES ? body : body.slice(0, FOLDED_BODY_LINES);
|
|
@@ -155,59 +155,6 @@ export class PageBlockComponent {
|
|
|
155
155
|
out.push('');
|
|
156
156
|
return out;
|
|
157
157
|
}
|
|
158
|
-
bodyLines(manifest, displayMarkdown, maxW, paneW) {
|
|
159
|
-
const md = (text) => renderMarkdownBlockAwareLines(text, maxW, paneW);
|
|
160
|
-
if (manifest.dialect === 'html')
|
|
161
|
-
return [`${DIM}HTML page — opens in a browser surface${RESET}`];
|
|
162
|
-
const lines = displayMarkdown === '' ? [] : md(displayMarkdown);
|
|
163
|
-
let priorStep = -1;
|
|
164
|
-
for (const slot of manifest.slots) {
|
|
165
|
-
if (manifest.steps > 1 && slot.step !== priorStep) {
|
|
166
|
-
if (lines.length > 0)
|
|
167
|
-
lines.push('');
|
|
168
|
-
lines.push(`${BOLD}Step ${slot.step + 1}/${manifest.steps}${RESET}`);
|
|
169
|
-
priorStep = slot.step;
|
|
170
|
-
}
|
|
171
|
-
if (lines.length > 0)
|
|
172
|
-
lines.push('');
|
|
173
|
-
lines.push(`${BOLD}${truncate(sanitize(singleLine(describeSlot(slot))), maxW)}${RESET}`);
|
|
174
|
-
const body = slot.config.body;
|
|
175
|
-
if (typeof body === 'string' && body !== '')
|
|
176
|
-
lines.push(...md(body));
|
|
177
|
-
if (!terminalAnswerable(slot)) {
|
|
178
|
-
lines.push(`${DIM}${sanitize(slot.kind)} — open in Northlight to answer this${RESET}`);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (slot.kind === 'text') {
|
|
182
|
-
lines.push(...md(slot.config.initialText));
|
|
183
|
-
}
|
|
184
|
-
else if (slot.kind === 'table') {
|
|
185
|
-
lines.push(...md(tableMarkdown(slot.config)));
|
|
186
|
-
}
|
|
187
|
-
else if (slot.kind === 'cards') {
|
|
188
|
-
for (const card of slot.config.cards ?? []) {
|
|
189
|
-
lines.push(` • ${truncate(sanitize(singleLine(card.subtitle === undefined ? card.title : `${card.title} — ${card.subtitle}`)), Math.max(10, maxW - 4))}`);
|
|
190
|
-
if (card.body !== undefined)
|
|
191
|
-
for (const line of wrap(sanitize(card.body), Math.max(10, maxW - 6)))
|
|
192
|
-
lines.push(` ${DIM}${line}${RESET}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else if (slot.kind === 'options') {
|
|
196
|
-
for (const option of slot.config.options) {
|
|
197
|
-
lines.push(` • ${truncate(sanitize(singleLine(option.label)), Math.max(10, maxW - 4))}`);
|
|
198
|
-
if (option.description !== undefined)
|
|
199
|
-
for (const line of wrap(sanitize(option.description), Math.max(10, maxW - 6)))
|
|
200
|
-
lines.push(` ${DIM}${line}${RESET}`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
else if (slot.kind === 'chart') {
|
|
204
|
-
const config = slot.config;
|
|
205
|
-
const title = config.title ?? `${config.x ?? ''} vs ${(config.y ?? []).map((y) => y.label ?? y.field).join(', ')}`;
|
|
206
|
-
lines.push(`${DIM}chart — ${sanitize(config.kind ?? 'chart')}: ${sanitize(title)}${RESET}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return lines;
|
|
210
|
-
}
|
|
211
158
|
footerLine(snapshot, maxW) {
|
|
212
159
|
switch (snapshot.state) {
|
|
213
160
|
case 'pending':
|