@north-light/crouter 0.3.197 → 0.3.199
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +19 -19
- package/dist/api/client.js +29 -37
- package/dist/api/dto/broker-ops.d.ts +3 -1
- package/dist/api/dto/broker.d.ts +13 -2
- package/dist/api/dto/broker.js +4 -4
- package/dist/api/dto/human.d.ts +6 -5
- package/dist/api/dto/inbox.d.ts +126 -33
- package/dist/api/dto/nodes.d.ts +8 -2
- package/dist/api/dto/profiles.d.ts +23 -0
- package/dist/api/dto/profiles.js +2 -2
- package/dist/api/dto/worktree.d.ts +3 -5
- package/dist/api/routes.d.ts +3 -5
- package/dist/api/routes.js +4 -6
- package/dist/builtin-memory/00-runtime-base.md +8 -8
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +6 -6
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +2 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -1
- package/dist/builtin-memory/insights/capture.md +2 -2
- package/dist/builtin-memory/insights/init.md +3 -3
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +3 -3
- package/dist/builtin-memory/internal/plugins.md +59 -2
- package/dist/builtin-memory/internal/storage-tiers.md +3 -1
- package/dist/builtin-memory/spec/roadmap.md +2 -2
- package/dist/builtin-memory/testing.md +1 -1
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +1 -1
- package/dist/clients/attach/chrome/bash-jobs.js +3 -3
- package/dist/clients/attach/render/chat-view.d.ts +29 -9
- package/dist/clients/attach/render/chat-view.js +64 -35
- package/dist/clients/attach/render/markdown-source.js +0 -6
- package/dist/clients/attach/render/page-block.d.ts +34 -0
- package/dist/clients/attach/render/page-block.js +226 -0
- package/dist/clients/attach/render/tool-calls.js +16 -2
- package/dist/clients/attach/session/whip.d.ts +2 -0
- package/dist/clients/attach/session/whip.js +2 -0
- package/dist/clients/attach/viewer.js +1419 -982
- package/dist/clients/conversation/projection.js +18 -3
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +9 -6
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +13 -14
- package/dist/clients/inbox/controller.js +10 -16
- package/dist/clients/inbox/page-adapter.d.ts +3 -4
- package/dist/clients/inbox/page-adapter.js +3 -4
- package/dist/clients/inbox/resolve.d.ts +1 -1
- package/dist/clients/inbox/resolve.js +3 -3
- package/dist/clients/inbox/review/document-surface.js +3 -1
- package/dist/clients/inbox/review/state.d.ts +2 -1
- package/dist/clients/inbox/review/state.js +5 -16
- package/dist/clients/inbox/tui/input.js +37 -19
- package/dist/clients/inbox/tui/panel.js +5 -31
- package/dist/clients/inbox/tui/render.d.ts +11 -0
- package/dist/clients/inbox/tui/render.js +101 -36
- package/dist/clients/inbox/tui/slots.d.ts +4 -4
- package/dist/clients/inbox/tui/slots.js +19 -44
- package/dist/clients/inbox/tui/types.d.ts +6 -10
- package/dist/clients/inbox/tui.js +2 -2
- package/dist/commands/__tests__/human.test.js +118 -24
- package/dist/commands/__tests__/node-message.test.js +14 -0
- package/dist/commands/attention.js +4 -4
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-snapshot.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/human/{doc.d.ts → components.d.ts} +1 -1
- package/dist/commands/human/components.js +77 -0
- package/dist/commands/human/feedback.d.ts +2 -0
- package/dist/commands/human/feedback.js +108 -0
- package/dist/commands/human/prompts.d.ts +17 -8
- package/dist/commands/human/prompts.js +217 -226
- package/dist/commands/human/queue.js +57 -88
- package/dist/commands/human/shared.d.ts +4 -15
- package/dist/commands/human/shared.js +19 -61
- package/dist/commands/human.js +32 -19
- package/dist/commands/memory/origin.js +2 -2
- package/dist/commands/node/bash.js +4 -4
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +2 -2
- package/dist/commands/node-worktree.js +8 -8
- package/dist/commands/pkg/browse.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +129 -10
- package/dist/commands/profile/delete.js +41 -15
- package/dist/commands/profile.js +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/focus.js +29 -14
- package/dist/commands/surface-edit.js +3 -3
- package/dist/commands/surface-inbox.d.ts +2 -0
- package/dist/commands/{human/inbox.js → surface-inbox.js} +14 -18
- package/dist/commands/surface.js +2 -1
- package/dist/commands/sys/doctor.js +60 -3
- package/dist/commands/sys/feedback.js +79 -25
- package/dist/commands/sys/settings.js +1 -1
- package/dist/core/__tests__/cron-broker-capacity.test.js +1 -1
- package/dist/core/__tests__/daemon-boot.test.js +7 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +20 -36
- package/dist/core/__tests__/extension-abort.test.js +5 -2
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +13 -2
- package/dist/core/__tests__/fixtures/fake-engine.js +47 -3
- package/dist/core/__tests__/human-cancel-guard.test.js +31 -10
- package/dist/core/__tests__/human-deliver.test.js +41 -45
- package/dist/core/__tests__/human-node-not-supervised.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +157 -0
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +13 -11
- package/dist/core/__tests__/seam/dormancy-release.test.js +103 -4
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +2 -2
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +12 -16
- package/dist/core/__tests__/serial/live-mutation.test.js +1 -1
- package/dist/core/__tests__/serial/tmux-surface.test.js +4 -2
- package/dist/core/__tests__/serial/worktree.test.js +24 -198
- package/dist/core/__tests__/session-cycles.test.js +22 -0
- package/dist/core/__tests__/stop-guard.test.js +4 -4
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +19 -4
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +10 -0
- package/dist/core/canvas/__tests__/attention.test.js +8 -5
- package/dist/core/canvas/__tests__/render-remote.test.js +5 -5
- package/dist/core/canvas/attention.js +8 -6
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +21 -3
- package/dist/core/canvas/browse/model.d.ts +8 -2
- package/dist/core/canvas/browse/model.js +11 -4
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +5 -1
- package/dist/core/canvas/canvas.d.ts +33 -0
- package/dist/core/canvas/canvas.js +92 -0
- package/dist/core/canvas/crons.d.ts +9 -0
- package/dist/core/canvas/crons.js +51 -0
- package/dist/core/canvas/node-order.d.ts +22 -1
- package/dist/core/canvas/node-order.js +26 -1
- package/dist/core/canvas/render-source.d.ts +5 -0
- package/dist/core/canvas/render-source.js +1 -0
- package/dist/core/canvas/types.d.ts +9 -3
- package/dist/core/command-plugins/bundle.d.ts +6 -1
- package/dist/core/command-plugins/bundle.js +26 -13
- package/dist/core/command.js +8 -12
- package/dist/core/config.d.ts +47 -1
- package/dist/core/config.js +138 -4
- package/dist/core/help.d.ts +3 -0
- package/dist/core/help.js +1 -1
- package/dist/core/human/__tests__/page-catalog.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-catalog.test.js +20 -0
- package/dist/core/human/__tests__/page-tickets.test.js +68 -34
- package/dist/core/human/__tests__/page.test.js +60 -119
- package/dist/core/human/__tests__/serial/inbox-core.test.js +6 -16
- package/dist/core/human/answer-text.d.ts +3 -0
- package/dist/core/human/answer-text.js +98 -0
- package/dist/core/human/answer.d.ts +53 -0
- package/dist/core/human/answer.js +119 -0
- package/dist/core/human/component-docs.d.ts +18 -0
- package/dist/core/human/component-docs.js +434 -0
- package/dist/core/human/convention.d.ts +17 -3
- package/dist/core/human/convention.js +36 -5
- package/dist/core/human/feedback-companion.d.ts +20 -0
- package/dist/core/human/feedback-companion.js +98 -0
- package/dist/core/human/feedback.d.ts +65 -0
- package/dist/core/human/feedback.js +116 -0
- package/dist/core/human/page-catalog.d.ts +56 -5
- package/dist/core/human/page-catalog.js +151 -22
- package/dist/core/human/page-errors.d.ts +4 -0
- package/dist/core/human/page-errors.js +7 -0
- package/dist/core/human/page-eval.d.ts +20 -0
- package/dist/core/human/page-eval.js +123 -0
- package/dist/core/human/page-schema.d.ts +48 -116
- package/dist/core/human/page-schema.js +100 -72
- package/dist/core/human/page-synth.d.ts +2 -4
- package/dist/core/human/page-synth.js +20 -7
- package/dist/core/human/page.d.ts +13 -21
- package/dist/core/human/page.js +147 -391
- package/dist/core/human/review-schema.d.ts +1 -0
- package/dist/core/human/review-schema.js +1 -1
- package/dist/core/human/root.d.ts +6 -2
- package/dist/core/human/root.js +12 -4
- package/dist/core/human/scan.d.ts +3 -3
- package/dist/core/human/scan.js +21 -21
- package/dist/core/human/summary.d.ts +2 -6
- package/dist/core/human/summary.js +3 -59
- package/dist/core/human/tickets.d.ts +22 -15
- package/dist/core/human/tickets.js +90 -28
- package/dist/core/human/types.d.ts +14 -2
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/preview-registry.js +8 -10
- package/dist/core/profiles/default-binding.d.ts +4 -0
- package/dist/core/profiles/default-binding.js +24 -0
- package/dist/core/profiles/deletion-reservation.d.ts +8 -0
- package/dist/core/profiles/deletion-reservation.js +38 -0
- package/dist/core/profiles/manifest.d.ts +3 -0
- package/dist/core/profiles/manifest.js +12 -0
- package/dist/core/profiles/select.js +2 -2
- package/dist/core/review/realize.js +12 -4
- package/dist/core/review/store.d.ts +6 -0
- package/dist/core/review/store.js +31 -0
- package/dist/core/runtime/bearings.d.ts +0 -4
- package/dist/core/runtime/bearings.js +4 -62
- package/dist/core/runtime/bin-contributions.d.ts +44 -0
- package/dist/core/runtime/bin-contributions.js +186 -0
- package/dist/core/runtime/boot-root.js +6 -1
- package/dist/core/runtime/broker/client-registry.d.ts +4 -0
- package/dist/core/runtime/broker/client-registry.js +13 -1
- package/dist/core/runtime/broker/extension-abort.d.ts +1 -1
- package/dist/core/runtime/broker/extension-abort.js +4 -2
- package/dist/core/runtime/broker/fault-retry.js +4 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +3 -1
- package/dist/core/runtime/broker/frame-dispatch.js +5 -6
- package/dist/core/runtime/broker/read-ops.d.ts +1 -1
- package/dist/core/runtime/broker/read-ops.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +6 -1
- package/dist/core/runtime/broker-extension-render.js +19 -0
- package/dist/core/runtime/broker-protocol.d.ts +14 -0
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +25 -12
- package/dist/core/runtime/busy.d.ts +4 -3
- package/dist/core/runtime/busy.js +4 -3
- package/dist/core/runtime/front-door-env.d.ts +5 -0
- package/dist/core/runtime/front-door-env.js +16 -0
- package/dist/core/runtime/front-door.d.ts +1 -5
- package/dist/core/runtime/front-door.js +2 -5
- package/dist/core/runtime/host.d.ts +1 -1
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/invocation.js +10 -0
- package/dist/core/runtime/kickoff.js +34 -0
- package/dist/core/runtime/launch.d.ts +2 -6
- package/dist/core/runtime/node-read.js +4 -1
- package/dist/core/runtime/nodes.js +2 -0
- package/dist/core/runtime/promote.d.ts +1 -0
- package/dist/core/runtime/promote.js +3 -1
- package/dist/core/runtime/session-cycles.d.ts +6 -0
- package/dist/core/runtime/session-cycles.js +15 -1
- package/dist/core/runtime/session-visibility.d.ts +17 -8
- package/dist/core/runtime/session-visibility.js +19 -10
- package/dist/core/runtime/spawn-env.d.ts +9 -1
- package/dist/core/runtime/spawn-env.js +62 -2
- package/dist/core/runtime/spawn.js +4 -4
- package/dist/core/runtime/stop-guard.d.ts +2 -2
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/stop-signals.d.ts +1 -1
- package/dist/core/runtime/stop-signals.js +2 -2
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/session-model/session-state.d.ts +4 -3
- package/dist/core/session-model/session-state.js +14 -1
- package/dist/core/user-settings.d.ts +36 -2
- package/dist/core/user-settings.js +56 -14
- package/dist/core/worktree.d.ts +15 -45
- package/dist/core/worktree.js +59 -261
- package/dist/daemon/api/__tests__/seam/profile-delete.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +386 -0
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +9 -5
- package/dist/daemon/api/handlers/feedback-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/feedback-comments.js +207 -0
- package/dist/daemon/api/handlers/human.js +24 -20
- package/dist/daemon/api/handlers/inbox.d.ts +10 -0
- package/dist/daemon/api/handlers/inbox.js +48 -109
- package/dist/daemon/api/handlers/profiles.js +11 -29
- package/dist/daemon/api/map.d.ts +6 -1
- package/dist/daemon/api/map.js +23 -0
- package/dist/daemon/api/server.js +3 -1
- package/dist/daemon/companion-retire.d.ts +2 -0
- package/dist/daemon/companion-retire.js +33 -0
- package/dist/daemon/cron-run.js +29 -17
- package/dist/daemon/crtrd.js +1 -1
- package/dist/daemon/fleet.d.ts +22 -10
- package/dist/daemon/fleet.js +43 -24
- package/dist/daemon/human/finish.d.ts +12 -6
- package/dist/daemon/human/finish.js +134 -54
- package/dist/daemon/human/sweep.js +9 -3
- package/dist/daemon/profile-delete.d.ts +7 -0
- package/dist/daemon/profile-delete.js +306 -0
- package/dist/daemon/reconcilers/broker-supervision.js +14 -9
- package/dist/daemon/reconcilers/dormant-inbox.js +11 -6
- package/dist/daemon/reconcilers/live-obligation.d.ts +21 -1
- package/dist/daemon/reconcilers/live-obligation.js +30 -2
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +28 -1
- package/dist/daemon/review/deliver.js +3 -3
- package/dist/daemon/review/finish.d.ts +0 -2
- package/dist/daemon/review/finish.js +1 -29
- package/dist/daemon/review/sweep.js +12 -1
- package/dist/pi-extensions/__tests__/canvas-bash-valve.test.js +56 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-bash-valve.d.ts +9 -1
- package/dist/pi-extensions/canvas-bash-valve.js +31 -13
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/dist/pi-extensions/canvas-review-boundary.d.ts +4 -0
- package/dist/pi-extensions/canvas-review-boundary.js +18 -9
- package/dist/pi-extensions/canvas-stophook.js +4 -4
- package/dist/shared/generated-context.js +2 -2
- package/dist/types.d.ts +61 -5
- package/dist/types.js +8 -2
- package/package.json +4 -4
- package/runtime.lock.json +5 -33
- package/scripts/install-runtime.mjs +19 -24
- package/dist/builtin-memory/init.md +0 -38
- package/dist/builtin-memory/plan.md +0 -19
- package/dist/builtin-memory/spec.md +0 -19
- package/dist/clients/attach/render/html-markdown.d.ts +0 -13
- package/dist/clients/attach/render/html-markdown.js +0 -206
- package/dist/commands/human/doc.js +0 -144
- package/dist/commands/human/inbox.d.ts +0 -2
- package/dist/core/human/__tests__/page-render.test.js +0 -49
- package/dist/core/human/markdown-html.d.ts +0 -8
- package/dist/core/human/markdown-html.js +0 -581
- package/dist/core/human/page-render.d.ts +0 -3
- package/dist/core/human/page-render.js +0 -131
- package/dist/pages/bundle.css +0 -1
- package/dist/pages/bundle.js +0 -1347
- package/dist/pages/comments.d.ts +0 -43
- package/dist/pages/comments.js +0 -125
- package/dist/pages/elements/cards.d.ts +0 -17
- package/dist/pages/elements/cards.js +0 -802
- package/dist/pages/elements/chart.js +0 -648
- package/dist/pages/elements/options.d.ts +0 -21
- package/dist/pages/elements/options.js +0 -629
- package/dist/pages/elements/pages.d.ts +0 -24
- package/dist/pages/elements/pages.js +0 -355
- package/dist/pages/elements/slot.d.ts +0 -21
- package/dist/pages/elements/slot.js +0 -105
- package/dist/pages/elements/table.d.ts +0 -19
- package/dist/pages/elements/table.js +0 -935
- package/dist/pages/elements/text.d.ts +0 -25
- package/dist/pages/elements/text.js +0 -968
- package/dist/pages/entry.d.ts +0 -8
- package/dist/pages/entry.js +0 -8
- package/dist/pages/host.d.ts +0 -158
- package/dist/pages/host.js +0 -182
- package/dist/pages/register.d.ts +0 -2
- package/dist/pages/register.js +0 -3
- package/dist/pages/slot-config.d.ts +0 -36
- package/dist/pages/slot-config.js +0 -50
- package/dist/pages/types.d.ts +0 -98
- package/dist/pages/types.js +0 -23
- /package/dist/{core/human/__tests__/page-render.test.d.ts → commands/__tests__/node-message.test.d.ts} +0 -0
- /package/dist/{pages/elements/chart.d.ts → core/__tests__/plugin-page-components.test.d.ts} +0 -0
|
@@ -24,12 +24,12 @@ import { Container, Loader, Spacer, Text } from '@earendil-works/pi-tui';
|
|
|
24
24
|
import { AssistantMessageComponent, BashExecutionComponent, BranchSummaryMessageComponent, CompactionSummaryMessageComponent, CustomMessageComponent, parseSkillBlock, SkillInvocationMessageComponent, ToolExecutionComponent, UserMessageComponent, } from '@earendil-works/pi-coding-agent';
|
|
25
25
|
import { attachMarkdownTheme } from '../config.js';
|
|
26
26
|
import { ContextMessageComponent } from './context-message.js';
|
|
27
|
+
import { detectInlinePageResult, PageBlockComponent } from './page-block.js';
|
|
27
28
|
import { CRTR_OUTPUT_CUSTOM_TYPE, CrtrOutputMessageComponent, createCrtrBashToolDefinition, } from './crtr-output.js';
|
|
28
|
-
import { CRTR_CYCLE_DIVIDER_CUSTOM_TYPE } from '../../../core/runtime/session-cycles.js';
|
|
29
|
+
import { CRTR_CYCLE_DIVIDER_CUSTOM_TYPE, endedByAbort } from '../../../core/runtime/session-cycles.js';
|
|
29
30
|
import { generatedContextExpandedBody, generatedContextPresentation, isRuntimeRestartContinuation, } from '../../../shared/generated-context.js';
|
|
30
31
|
import { assistantVisibleText, isTrueUserMessage, } from '../../../shared/tool-groups.js';
|
|
31
32
|
import { transformDiagramFences } from './diagram.js';
|
|
32
|
-
import { transformHtmlMarkdown } from './html-markdown.js';
|
|
33
33
|
import { styleAttachAgentMarkdown, styleAttachMarkdownSource, styleAttachMessageMarkdown, styleAttachSummaryMarkdown, } from './markdown-source.js';
|
|
34
34
|
import { createCrtrEditToolDefinition } from './edit-diff.js';
|
|
35
35
|
import { GroupActivityRecorder } from './group-activity.js';
|
|
@@ -267,6 +267,11 @@ export class ChatView {
|
|
|
267
267
|
* a nested container while condensed history is being built, so the same
|
|
268
268
|
* message-rendering path serves both regions. */
|
|
269
269
|
appendTarget = this.historyContainer;
|
|
270
|
+
/** Inline page blocks placed in this transcript, by page id — one block per
|
|
271
|
+
* page, so a `--replace` revision refreshes the block already on screen. */
|
|
272
|
+
pageBlocks = new Map();
|
|
273
|
+
inlinePageBlocks;
|
|
274
|
+
pageOpenHint;
|
|
270
275
|
/** Trailing cycles kept at full fidelity (config `live_cycles`). */
|
|
271
276
|
liveCycles;
|
|
272
277
|
/** Messages kept from a condensed cycle (config `condensed_history`). */
|
|
@@ -305,27 +310,13 @@ export class ChatView {
|
|
|
305
310
|
recapPalette;
|
|
306
311
|
/** Bold accent for the expanded crtr-context label. */
|
|
307
312
|
labelStyle = (s) => `\x1b[1m${s}\x1b[22m`;
|
|
308
|
-
/** Undefined honors each `<details open>` attribute until Ctrl+O establishes
|
|
309
|
-
* one viewer-wide disclosure state. */
|
|
310
|
-
detailsExpandedOverride;
|
|
311
313
|
/** Viewer-local Markdown projection, handed to pi's message components so
|
|
312
314
|
* THEY apply it at render time with the exact content width. The viewer keeps
|
|
313
315
|
* no projected copy: the components hold canonical source, and each
|
|
314
|
-
* transformer reads live viewer state
|
|
315
|
-
*
|
|
316
|
-
* Markdown. */
|
|
316
|
+
* transformer reads live viewer state on every call. Diagrams apply only to
|
|
317
|
+
* assistant-authored Markdown. */
|
|
317
318
|
markdownTransformers = [
|
|
318
319
|
(markdown, { messageType, availableWidth }) => (messageType === 'assistant' ? transformDiagramFences(markdown, availableWidth) : markdown),
|
|
319
|
-
(markdown, { messageType }) => (messageType === 'assistant'
|
|
320
|
-
? transformHtmlMarkdown(markdown, {
|
|
321
|
-
detailsExpanded: this.detailsExpandedOverride,
|
|
322
|
-
styles: {
|
|
323
|
-
kbd: (text) => this.recapPalette.active(this.recapPalette.bold(`‹${text}›`)),
|
|
324
|
-
mark: (text) => `\x1b[7m${this.recapPalette.bold(this.recapPalette.accent(text))}\x1b[27m`,
|
|
325
|
-
hint: this.dimStyle,
|
|
326
|
-
},
|
|
327
|
-
})
|
|
328
|
-
: markdown),
|
|
329
320
|
(markdown, { messageType }) => (messageType === 'user' ? styleAttachMarkdownSource(markdown) : styleAttachAgentMarkdown(markdown)),
|
|
330
321
|
];
|
|
331
322
|
/** The assistant message component currently being streamed (between
|
|
@@ -394,6 +385,8 @@ export class ChatView {
|
|
|
394
385
|
this.condensedHistory = opts.condensedHistory ?? readCondensedHistory();
|
|
395
386
|
this.onFooterEvent = opts.onFooterEvent;
|
|
396
387
|
this.onActivityChange = opts.onActivityChange;
|
|
388
|
+
this.inlinePageBlocks = opts.inlinePageBlocks ?? false;
|
|
389
|
+
this.pageOpenHint = opts.pageOpenHint ?? (() => 'crtr human list');
|
|
397
390
|
this.recapPalette = opts.palette ?? defaultAttachPalette;
|
|
398
391
|
this.spinnerStyle = this.recapPalette.active;
|
|
399
392
|
this.dimStyle = this.recapPalette.muted;
|
|
@@ -497,9 +490,7 @@ export class ChatView {
|
|
|
497
490
|
const hasProse = assistantVisibleText(message).trim() !== '';
|
|
498
491
|
const toolCalls = this.assistantToolCalls(message);
|
|
499
492
|
const endedWithFailure = message.stopReason === 'aborted' || message.stopReason === 'error';
|
|
500
|
-
const terminalErrorMessage = message.
|
|
501
|
-
? 'Interrupted'
|
|
502
|
-
: (message.errorMessage ?? 'Error');
|
|
493
|
+
const terminalErrorMessage = endedByAbort(message) ? 'Interrupted' : (message.errorMessage ?? 'Error');
|
|
503
494
|
// A terminal assistant failure remains visible as its own boundary. Settle
|
|
504
495
|
// any interrupted calls so the completed span before it can still recap.
|
|
505
496
|
if (endedWithFailure) {
|
|
@@ -554,6 +545,7 @@ export class ChatView {
|
|
|
554
545
|
this.historyContainer.markDirty(component);
|
|
555
546
|
this.settleGroupTool(component, message, message.isError === true);
|
|
556
547
|
renderedPendingTools.delete(message.toolCallId);
|
|
548
|
+
this.maybeAppendPageBlock(message);
|
|
557
549
|
}
|
|
558
550
|
}
|
|
559
551
|
else {
|
|
@@ -674,8 +666,7 @@ export class ChatView {
|
|
|
674
666
|
// `setToolsExpanded` / `toggleThinkingBlockVisibility`.
|
|
675
667
|
// -------------------------------------------------------------------------
|
|
676
668
|
/** Ctrl+O prefers summaries when the user enabled both folding and summary
|
|
677
|
-
* generation.
|
|
678
|
-
* sections, so the transcript has one reveal-more action rather than two. */
|
|
669
|
+
* generation. */
|
|
679
670
|
toggleToolsExpanded() {
|
|
680
671
|
let result;
|
|
681
672
|
if (this.summarizeToolCalls && this.foldSettledTools) {
|
|
@@ -687,11 +678,6 @@ export class ChatView {
|
|
|
687
678
|
this.toolOutputExpanded = !this.toolOutputExpanded;
|
|
688
679
|
result = { kind: 'output', expanded: this.toolOutputExpanded };
|
|
689
680
|
}
|
|
690
|
-
this.detailsExpandedOverride = this.toolOutputExpanded;
|
|
691
|
-
// The disclosure state lives in the transformer closure, so the transcript
|
|
692
|
-
// only has to drop its projections: pi's message components rebuild their
|
|
693
|
-
// Markdown on invalidate() and re-run the transformer on the next render.
|
|
694
|
-
this.historyContainer.invalidate();
|
|
695
681
|
this.applyToolDisplay();
|
|
696
682
|
return result;
|
|
697
683
|
}
|
|
@@ -850,15 +836,21 @@ export class ChatView {
|
|
|
850
836
|
if (this.streamingComponent) {
|
|
851
837
|
const stopReason = event.message.stopReason;
|
|
852
838
|
let errorMessage = event.message.errorMessage;
|
|
853
|
-
// An aborted turn may be a user interrupt or a lifecycle interruption
|
|
854
|
-
//
|
|
855
|
-
//
|
|
856
|
-
|
|
839
|
+
// An aborted turn may be a user interrupt or a lifecycle interruption
|
|
840
|
+
// (a refresh-yield aborts its own turn). Providers spell it several
|
|
841
|
+
// ways — including `stopReason: 'error'` carrying a bare AbortError,
|
|
842
|
+
// which would otherwise render as a red `Error: This operation was
|
|
843
|
+
// aborted` for a perfectly healthy yield — so override it outright
|
|
844
|
+
// with one plain, human label.
|
|
845
|
+
if (endedByAbort(event.message)) {
|
|
857
846
|
errorMessage = 'Interrupted';
|
|
858
847
|
// Surface the abort on the assistant bubble itself, mirroring pi
|
|
859
848
|
// (interactive-mode.js:2280 sets streamingMessage.errorMessage before
|
|
860
|
-
// updateContent) so the rendered message shows the annotation.
|
|
849
|
+
// updateContent) so the rendered message shows the annotation. The
|
|
850
|
+
// stopReason moves with it: pi's component reserves its red
|
|
851
|
+
// `Error: …` line for stopReason 'error', and an abort is not one.
|
|
861
852
|
event.message.errorMessage = errorMessage;
|
|
853
|
+
event.message.stopReason = 'aborted';
|
|
862
854
|
}
|
|
863
855
|
this.streamingComponent.updateContent(event.message);
|
|
864
856
|
if (stopReason === 'aborted' || stopReason === 'error') {
|
|
@@ -926,6 +918,7 @@ export class ChatView {
|
|
|
926
918
|
this.settleGroupTool(component, result, event.isError);
|
|
927
919
|
this.applyToolDisplay();
|
|
928
920
|
this.historyContainer.markDirty(component);
|
|
921
|
+
this.maybeAppendPageBlock(result);
|
|
929
922
|
}
|
|
930
923
|
break;
|
|
931
924
|
}
|
|
@@ -1162,10 +1155,16 @@ export class ChatView {
|
|
|
1162
1155
|
// -------------------------------------------------------------------------
|
|
1163
1156
|
// Helpers
|
|
1164
1157
|
// -------------------------------------------------------------------------
|
|
1165
|
-
/** Render an assistant reply plus its initially-hidden boundary separator.
|
|
1158
|
+
/** Render an assistant reply plus its initially-hidden boundary separator.
|
|
1159
|
+
* A turn that ended on its own abort is rendered as the interrupt it is,
|
|
1160
|
+
* matching the live seam above, so re-reading the transcript later never
|
|
1161
|
+
* turns a healthy interrupt into a red `Error:` line. */
|
|
1166
1162
|
appendAssistantMessage(message, groupMember = false) {
|
|
1167
1163
|
const component = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel, this.markdownTransformers);
|
|
1168
|
-
|
|
1164
|
+
const rendered = endedByAbort(message)
|
|
1165
|
+
? { ...message, stopReason: 'aborted', errorMessage: 'Interrupted' }
|
|
1166
|
+
: message;
|
|
1167
|
+
component.updateContent(rendered);
|
|
1169
1168
|
this.appendAssistantPart(component, groupMember);
|
|
1170
1169
|
this.registerCopyBlock(component, 'assistant', assistantVisibleText(message));
|
|
1171
1170
|
const separator = this.appendAssistantSeparator(groupMember);
|
|
@@ -1421,8 +1420,38 @@ export class ChatView {
|
|
|
1421
1420
|
this.copyBlockRecords.length = 0;
|
|
1422
1421
|
this.copyIndex.clear();
|
|
1423
1422
|
this.transcriptFilePaths.length = 0;
|
|
1423
|
+
this.pageBlocks.clear();
|
|
1424
1424
|
this.historyContainer.clear();
|
|
1425
1425
|
}
|
|
1426
|
+
/** Place the transcript block for an inline page a `human send` just raised.
|
|
1427
|
+
* The block is a plain history child, NOT a tool-group member: collapsing the
|
|
1428
|
+
* tool group folds the command away and leaves the page itself on screen, the
|
|
1429
|
+
* way assistant prose stays. Condensed history is skipped deliberately — old
|
|
1430
|
+
* cycles keep only prose and cycle seams. */
|
|
1431
|
+
maybeAppendPageBlock(result) {
|
|
1432
|
+
if (!this.inlinePageBlocks || this.appendTarget !== this.historyContainer)
|
|
1433
|
+
return;
|
|
1434
|
+
const page = detectInlinePageResult(result);
|
|
1435
|
+
if (page === undefined || this.pageBlocks.has(page.pageId))
|
|
1436
|
+
return;
|
|
1437
|
+
const block = new PageBlockComponent(page.pageId, page.dir, this.pageOpenHint);
|
|
1438
|
+
this.pageBlocks.set(page.pageId, block);
|
|
1439
|
+
this.append(block);
|
|
1440
|
+
}
|
|
1441
|
+
/** Re-read every placed page from disk. Driven by the viewer's ticket-activity
|
|
1442
|
+
* watcher, so an answer, a cancellation, or a `--replace` revision lands on
|
|
1443
|
+
* the block already anchored in the transcript. */
|
|
1444
|
+
refreshPageBlocks() {
|
|
1445
|
+
let changed = false;
|
|
1446
|
+
for (const block of this.pageBlocks.values()) {
|
|
1447
|
+
if (!block.refresh())
|
|
1448
|
+
continue;
|
|
1449
|
+
changed = true;
|
|
1450
|
+
this.historyContainer.markDirty(block);
|
|
1451
|
+
}
|
|
1452
|
+
if (changed)
|
|
1453
|
+
this.tui.requestRender();
|
|
1454
|
+
}
|
|
1426
1455
|
makeLoader(message) {
|
|
1427
1456
|
// Loader updates itself on an interval. Its TUI callback has to evict the
|
|
1428
1457
|
// status block's cached frame before requesting the next render.
|
|
@@ -30,10 +30,6 @@ const FENCE_OPEN = /^\s{0,3}(`{3,}|~{3,})/;
|
|
|
30
30
|
* a literal `>` stays untouched rather than being mis-split. */
|
|
31
31
|
const XML_TAG_LINE = /^([ \t]{0,3})(<\/?)([A-Za-z][\w.:-]*)((?:[ \t]+[^<>]*?)?)(\/?)>[ \t]*$/;
|
|
32
32
|
const XML_ATTRIBUTE = /([^\s=]+)(?:=("[^"]*"|'[^']*'|[^\s]*))?/g;
|
|
33
|
-
/** Tags the HTML allowlist owns (see html-markdown.ts). A `<details>` block
|
|
34
|
-
* that reaches this pass is one that failed validation there and is meant to
|
|
35
|
-
* stay literal. */
|
|
36
|
-
const HTML_ALLOWLIST_TAGS = new Set(['details', 'summary', 'kbd', 'mark']);
|
|
37
33
|
// A constant-lightness ramp from warm yellow to cool blue, with saturation
|
|
38
34
|
// deliberately reduced at each step so the hierarchy recedes rather than
|
|
39
35
|
// becoming a tour of unrelated accent colors.
|
|
@@ -85,8 +81,6 @@ function styleXmlTagLine(line) {
|
|
|
85
81
|
if (!match)
|
|
86
82
|
return undefined;
|
|
87
83
|
const [, indent, open, name, attributes, selfClose] = match;
|
|
88
|
-
if (HTML_ALLOWLIST_TAGS.has(name.toLowerCase()))
|
|
89
|
-
return undefined;
|
|
90
84
|
return indent
|
|
91
85
|
+ paint(XML_PUNCTUATION, open)
|
|
92
86
|
+ paint(XML_NAME, name)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Component } from '@earendil-works/pi-tui';
|
|
2
|
+
export interface InlinePageRef {
|
|
3
|
+
pageId: string;
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The inline page a `human send` tool result announces, or undefined for every
|
|
8
|
+
* other result. Read from the structured CLI record the canvas-preview extension
|
|
9
|
+
* attaches (`details.crtrPreview`), which is present on both live tool events and
|
|
10
|
+
* rebuilt snapshot messages — never from the rendered bash text.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectInlinePageResult(result: unknown): InlinePageRef | undefined;
|
|
13
|
+
export declare class PageBlockComponent implements Component {
|
|
14
|
+
readonly pageId: string;
|
|
15
|
+
private readonly dir;
|
|
16
|
+
private readonly openHint;
|
|
17
|
+
private expanded;
|
|
18
|
+
private generation;
|
|
19
|
+
private stamp;
|
|
20
|
+
private snapshot;
|
|
21
|
+
private cache;
|
|
22
|
+
constructor(pageId: string, dir: string, openHint: () => string);
|
|
23
|
+
/** Re-read the ticket when its request or its result changed on disk. */
|
|
24
|
+
refresh(): boolean;
|
|
25
|
+
/** Ctrl+O global expansion (ChatView.isExpandable duck-type). */
|
|
26
|
+
setExpanded(expanded: boolean): void;
|
|
27
|
+
render(width: number): string[];
|
|
28
|
+
invalidate(): void;
|
|
29
|
+
private diskStamp;
|
|
30
|
+
private reload;
|
|
31
|
+
private compose;
|
|
32
|
+
private bodyLines;
|
|
33
|
+
private footerLine;
|
|
34
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// render/page-block.ts — the transcript home for an inline page.
|
|
2
|
+
//
|
|
3
|
+
// `crtr human send --placement inline` puts a page in THIS conversation, and in
|
|
4
|
+
// crouter the conversation is the attach viewer's transcript. So the page gets a
|
|
5
|
+
// durable block anchored where it was sent, exactly as the assistant's prose is
|
|
6
|
+
// anchored: the inbox stays purely the queue (a page sent `--no-inbox` has no
|
|
7
|
+
// queue entry at all and would otherwise be invisible here), while this block is
|
|
8
|
+
// what the person actually reads.
|
|
9
|
+
//
|
|
10
|
+
// The block renders from the ticket directory on disk, never from the tool
|
|
11
|
+
// result text, so `--replace` revisions and the answer that settles a page both
|
|
12
|
+
// land on the already-placed block when `refresh()` is driven by the viewer's
|
|
13
|
+
// inbox-activity watcher.
|
|
14
|
+
import { statSync } from 'node:fs';
|
|
15
|
+
import { parsePage } from '../../../core/human/page.js';
|
|
16
|
+
import { pageManifestPath, responsePath } from '../../../core/human/convention.js';
|
|
17
|
+
import { pageTicketState, readTicketResult } from '../../../core/human/tickets.js';
|
|
18
|
+
import { renderMarkdownBlockAwareLines } from '../../../core/termrender/termrender.js';
|
|
19
|
+
import { describeSlot, tableMarkdown, terminalAnswerable } from '../../inbox/tui/slots.js';
|
|
20
|
+
import { BOLD, CYAN, DIM, GREEN, RESET, YELLOW, sanitize, singleLine, truncate, wrap } from '../../inbox/tui/ansi.js';
|
|
21
|
+
/** Rows of page body shown while the block is folded; Ctrl+O expands it. */
|
|
22
|
+
const FOLDED_BODY_LINES = 16;
|
|
23
|
+
/**
|
|
24
|
+
* The inline page a `human send` tool result announces, or undefined for every
|
|
25
|
+
* other result. Read from the structured CLI record the canvas-preview extension
|
|
26
|
+
* attaches (`details.crtrPreview`), which is present on both live tool events and
|
|
27
|
+
* rebuilt snapshot messages — never from the rendered bash text.
|
|
28
|
+
*/
|
|
29
|
+
export function detectInlinePageResult(result) {
|
|
30
|
+
if (result === null || typeof result !== 'object')
|
|
31
|
+
return undefined;
|
|
32
|
+
const details = result['details'];
|
|
33
|
+
if (details === null || typeof details !== 'object')
|
|
34
|
+
return undefined;
|
|
35
|
+
const preview = details['crtrPreview'];
|
|
36
|
+
if (preview === null || typeof preview !== 'object')
|
|
37
|
+
return undefined;
|
|
38
|
+
const record = preview;
|
|
39
|
+
if (record['path'] !== 'human send')
|
|
40
|
+
return undefined;
|
|
41
|
+
const payload = record['result'];
|
|
42
|
+
if (payload === null || typeof payload !== 'object')
|
|
43
|
+
return undefined;
|
|
44
|
+
const fields = payload;
|
|
45
|
+
const pageId = fields['page_id'];
|
|
46
|
+
const dir = fields['dir'];
|
|
47
|
+
if (typeof pageId !== 'string' || typeof dir !== 'string')
|
|
48
|
+
return undefined;
|
|
49
|
+
if (fields['placement'] !== 'inline')
|
|
50
|
+
return undefined;
|
|
51
|
+
return { pageId, dir };
|
|
52
|
+
}
|
|
53
|
+
function mtimeOf(path) {
|
|
54
|
+
try {
|
|
55
|
+
return String(statSync(path).mtimeMs);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return '-';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class PageBlockComponent {
|
|
62
|
+
pageId;
|
|
63
|
+
dir;
|
|
64
|
+
openHint;
|
|
65
|
+
expanded = false;
|
|
66
|
+
generation = 0;
|
|
67
|
+
stamp = '';
|
|
68
|
+
snapshot;
|
|
69
|
+
cache;
|
|
70
|
+
constructor(pageId, dir, openHint) {
|
|
71
|
+
this.pageId = pageId;
|
|
72
|
+
this.dir = dir;
|
|
73
|
+
this.openHint = openHint;
|
|
74
|
+
this.reload();
|
|
75
|
+
}
|
|
76
|
+
/** Re-read the ticket when its request or its result changed on disk. */
|
|
77
|
+
refresh() {
|
|
78
|
+
if (this.diskStamp() === this.stamp)
|
|
79
|
+
return false;
|
|
80
|
+
this.reload();
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/** Ctrl+O global expansion (ChatView.isExpandable duck-type). */
|
|
84
|
+
setExpanded(expanded) {
|
|
85
|
+
if (expanded === this.expanded)
|
|
86
|
+
return;
|
|
87
|
+
this.expanded = expanded;
|
|
88
|
+
this.cache = undefined;
|
|
89
|
+
}
|
|
90
|
+
render(width) {
|
|
91
|
+
const w = width > 0 ? width : 80;
|
|
92
|
+
const cached = this.cache;
|
|
93
|
+
if (cached !== undefined && cached.width === w && cached.generation === this.generation && cached.expanded === this.expanded) {
|
|
94
|
+
return cached.lines;
|
|
95
|
+
}
|
|
96
|
+
const lines = this.compose(w);
|
|
97
|
+
this.cache = { width: w, generation: this.generation, expanded: this.expanded, lines };
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
invalidate() {
|
|
101
|
+
this.cache = undefined;
|
|
102
|
+
}
|
|
103
|
+
diskStamp() {
|
|
104
|
+
return `${mtimeOf(pageManifestPath(this.dir))}|${mtimeOf(responsePath(this.dir))}`;
|
|
105
|
+
}
|
|
106
|
+
reload() {
|
|
107
|
+
this.stamp = this.diskStamp();
|
|
108
|
+
this.generation++;
|
|
109
|
+
this.cache = undefined;
|
|
110
|
+
try {
|
|
111
|
+
const manifest = parsePage(this.dir);
|
|
112
|
+
const state = pageTicketState(this.dir);
|
|
113
|
+
const digest = state === 'resolved' ? readTicketResult(this.dir) : null;
|
|
114
|
+
this.snapshot = {
|
|
115
|
+
manifest,
|
|
116
|
+
state,
|
|
117
|
+
...(digest?.kind === 'page' ? { answerDigest: digest.summary.replace(/\s+/g, ' ').trim() } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// A removed or half-written ticket keeps whatever was last read; a block
|
|
122
|
+
// that never read one renders its unavailable line instead.
|
|
123
|
+
if (this.snapshot === undefined)
|
|
124
|
+
this.snapshot = undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
compose(width) {
|
|
128
|
+
const snapshot = this.snapshot;
|
|
129
|
+
if (snapshot === undefined)
|
|
130
|
+
return ['', ` ${DIM}[page] no longer available${RESET}`, ''];
|
|
131
|
+
const maxW = Math.min(Math.max(20, width - 4), 100);
|
|
132
|
+
const out = ['', ` ${BOLD}${CYAN}${truncate(sanitize(singleLine(snapshot.manifest.title)), maxW)}${RESET}`];
|
|
133
|
+
if (snapshot.manifest.subtitle) {
|
|
134
|
+
for (const line of wrap(sanitize(snapshot.manifest.subtitle), maxW))
|
|
135
|
+
out.push(` ${DIM}${line}${RESET}`);
|
|
136
|
+
}
|
|
137
|
+
const body = this.bodyLines(snapshot.manifest, maxW, Math.max(20, width - 4));
|
|
138
|
+
if (body.length > 0) {
|
|
139
|
+
out.push('');
|
|
140
|
+
const shown = this.expanded || body.length <= FOLDED_BODY_LINES ? body : body.slice(0, FOLDED_BODY_LINES);
|
|
141
|
+
for (const line of shown)
|
|
142
|
+
out.push(` ${line}`);
|
|
143
|
+
if (shown.length < body.length)
|
|
144
|
+
out.push(` ${DIM}… +${body.length - shown.length} lines — ctrl+o to expand${RESET}`);
|
|
145
|
+
}
|
|
146
|
+
const footer = this.footerLine(snapshot, maxW);
|
|
147
|
+
if (footer !== undefined)
|
|
148
|
+
out.push('', ` ${footer}`);
|
|
149
|
+
out.push('');
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
bodyLines(manifest, maxW, paneW) {
|
|
153
|
+
const md = (text) => renderMarkdownBlockAwareLines(text, maxW, paneW);
|
|
154
|
+
if (manifest.dialect === 'html')
|
|
155
|
+
return [`${DIM}HTML page — opens in a browser surface${RESET}`];
|
|
156
|
+
// Display components (Card, Alert, the rest of the shadcn set) are page markup,
|
|
157
|
+
// not manifest slots, so a page built only from them keeps nothing here a
|
|
158
|
+
// terminal could draw. Say so rather than paint a title over an empty body.
|
|
159
|
+
if (manifest.slots.length === 0)
|
|
160
|
+
return [`${DIM}display-only page — its content renders in Northlight${RESET}`];
|
|
161
|
+
const lines = [];
|
|
162
|
+
let priorStep = -1;
|
|
163
|
+
for (const slot of manifest.slots) {
|
|
164
|
+
if (manifest.steps > 1 && slot.step !== priorStep) {
|
|
165
|
+
if (lines.length > 0)
|
|
166
|
+
lines.push('');
|
|
167
|
+
lines.push(`${BOLD}Step ${slot.step + 1}/${manifest.steps}${RESET}`);
|
|
168
|
+
priorStep = slot.step;
|
|
169
|
+
}
|
|
170
|
+
if (lines.length > 0)
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push(`${BOLD}${truncate(sanitize(singleLine(describeSlot(slot))), maxW)}${RESET}`);
|
|
173
|
+
const body = slot.config.body;
|
|
174
|
+
if (typeof body === 'string' && body !== '')
|
|
175
|
+
lines.push(...md(body));
|
|
176
|
+
if (!terminalAnswerable(slot)) {
|
|
177
|
+
lines.push(`${DIM}${sanitize(slot.kind)} — open in Northlight to answer this${RESET}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (slot.kind === 'text') {
|
|
181
|
+
lines.push(...md(slot.config.initialText));
|
|
182
|
+
}
|
|
183
|
+
else if (slot.kind === 'table') {
|
|
184
|
+
lines.push(...md(tableMarkdown(slot.config)));
|
|
185
|
+
}
|
|
186
|
+
else if (slot.kind === 'cards') {
|
|
187
|
+
for (const card of slot.config.cards ?? []) {
|
|
188
|
+
lines.push(` • ${truncate(sanitize(singleLine(card.subtitle === undefined ? card.title : `${card.title} — ${card.subtitle}`)), Math.max(10, maxW - 4))}`);
|
|
189
|
+
if (card.body !== undefined)
|
|
190
|
+
for (const line of wrap(sanitize(card.body), Math.max(10, maxW - 6)))
|
|
191
|
+
lines.push(` ${DIM}${line}${RESET}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (slot.kind === 'options') {
|
|
195
|
+
for (const option of slot.config.options) {
|
|
196
|
+
lines.push(` • ${truncate(sanitize(singleLine(option.label)), Math.max(10, maxW - 4))}`);
|
|
197
|
+
if (option.description !== undefined)
|
|
198
|
+
for (const line of wrap(sanitize(option.description), Math.max(10, maxW - 6)))
|
|
199
|
+
lines.push(` ${DIM}${line}${RESET}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (slot.kind === 'chart') {
|
|
203
|
+
const config = slot.config;
|
|
204
|
+
const title = config.title ?? `${config.x ?? ''} vs ${(config.y ?? []).map((y) => y.label ?? y.field).join(', ')}`;
|
|
205
|
+
lines.push(`${DIM}chart — ${sanitize(config.kind ?? 'chart')}: ${sanitize(title)}${RESET}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return lines;
|
|
209
|
+
}
|
|
210
|
+
footerLine(snapshot, maxW) {
|
|
211
|
+
switch (snapshot.state) {
|
|
212
|
+
case 'pending':
|
|
213
|
+
return snapshot.manifest.delivery.reply
|
|
214
|
+
? `${YELLOW}▸ awaiting your answer${RESET} ${DIM}— ${sanitize(this.openHint())}${RESET}`
|
|
215
|
+
: `${DIM}◦ queued in your inbox — ${sanitize(this.openHint())}${RESET}`;
|
|
216
|
+
case 'resolved':
|
|
217
|
+
return snapshot.answerDigest === undefined
|
|
218
|
+
? `${GREEN}✓ answered${RESET}`
|
|
219
|
+
: `${GREEN}✓ answered${RESET} ${DIM}— ${truncate(sanitize(snapshot.answerDigest), Math.max(10, maxW - 12))}${RESET}`;
|
|
220
|
+
case 'canceled':
|
|
221
|
+
return `${DIM}⊘ canceled${RESET}`;
|
|
222
|
+
case 'passive':
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -15,6 +15,7 @@ import { Text, getCapabilities, hyperlink, truncateToWidth, visibleWidth, wrapTe
|
|
|
15
15
|
import { homedir } from 'node:os';
|
|
16
16
|
import { isAbsolute, resolve } from 'node:path';
|
|
17
17
|
import { pathToFileURL } from 'node:url';
|
|
18
|
+
import { normalizeBashJobPurpose } from '../../../core/bash-jobs.js';
|
|
18
19
|
import { TOOL_ICON } from '../../../core/preview-registry.js';
|
|
19
20
|
function str(value) {
|
|
20
21
|
return typeof value === 'string' ? value : '';
|
|
@@ -216,18 +217,25 @@ function styleCommandLine(line, theme) {
|
|
|
216
217
|
* there is more. Expanded (ctrl+o) it prints every line of the command, wrapped
|
|
217
218
|
* (never clipped — expanded is the raw truth) into the pane with continuations
|
|
218
219
|
* hanging under the icon. A line wider than the pane would desync pi-tui's line
|
|
219
|
-
* accounting against the terminal's own wrapping and garble the frame.
|
|
220
|
+
* accounting against the terminal's own wrapping and garble the frame.
|
|
221
|
+
*
|
|
222
|
+
* FOLDED, a call that supplied a `purpose` shows that clause INSTEAD of the
|
|
223
|
+
* command: the fold is a dense index of what the turn did, and the agent's own
|
|
224
|
+
* words for why it ran this read better there than `rg -n … | head -30`. The
|
|
225
|
+
* command itself is never lost — unfolding (or ctrl+o) shows it verbatim. */
|
|
220
226
|
class BashCallComponent {
|
|
221
227
|
command = '';
|
|
228
|
+
purpose;
|
|
222
229
|
expanded = false;
|
|
223
230
|
timeout;
|
|
224
231
|
theme;
|
|
225
232
|
/** Nothing is cached across paints (render recomputes from the fields), so
|
|
226
233
|
* pi's cache-drop hook has nothing to do here. */
|
|
227
234
|
invalidate() { }
|
|
228
|
-
update(theme, command, expanded, timeout) {
|
|
235
|
+
update(theme, command, purpose, expanded, timeout) {
|
|
229
236
|
this.theme = theme;
|
|
230
237
|
this.command = command;
|
|
238
|
+
this.purpose = purpose;
|
|
231
239
|
this.expanded = expanded;
|
|
232
240
|
this.timeout = timeout;
|
|
233
241
|
}
|
|
@@ -236,6 +244,9 @@ class BashCallComponent {
|
|
|
236
244
|
if (!theme)
|
|
237
245
|
return [];
|
|
238
246
|
const icon = theme.fg('accent', TOOL_ICON.bash);
|
|
247
|
+
if (this.purpose !== undefined) {
|
|
248
|
+
return [truncateToWidth(`${icon} ${theme.fg('toolOutput', this.purpose)}`, width, '…')];
|
|
249
|
+
}
|
|
239
250
|
const lines = this.command.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
|
|
240
251
|
const suffix = this.timeout ? theme.fg('muted', ` (timeout ${this.timeout}s)`) : '';
|
|
241
252
|
if (lines.length === 0)
|
|
@@ -286,6 +297,9 @@ export function createCrtrPlainBashToolDefinition(cwd, fold = new FoldedToolCall
|
|
|
286
297
|
}
|
|
287
298
|
const component = context.lastComponent instanceof BashCallComponent ? context.lastComponent : new BashCallComponent();
|
|
288
299
|
component.update(theme, str(a?.command),
|
|
300
|
+
// The purpose stands in for the command only while folded; it is normalized
|
|
301
|
+
// here because it is model-supplied text painted straight into the frame.
|
|
302
|
+
fold.folded ? normalizeBashJobPurpose(a?.purpose) ?? undefined : undefined,
|
|
289
303
|
// Folded wins over ctrl+o expansion: a folded bash is always its one-line form.
|
|
290
304
|
Boolean(context.expanded) && !fold.folded, typeof a?.timeout === 'number' ? a.timeout : undefined);
|
|
291
305
|
return fold.capture(component);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type Component, type TUI } from '@earendil-works/pi-tui';
|
|
2
2
|
import type { AttachPalette } from '../config.js';
|
|
3
|
+
/** The fixed line the plain whip sends — not drawn from `whip_messages`. */
|
|
4
|
+
export declare const GO_FASTER_WHIP_MESSAGE = "Go faster";
|
|
3
5
|
/** A random line, never the one the previous whip used. */
|
|
4
6
|
export declare function randomWhipMessage(messages: readonly string[]): string;
|
|
5
7
|
/** A 20-frame, 12-row header animation that gives the focused agent a playful nudge. */
|
|
@@ -306,6 +306,8 @@ const WHIP_FRAMES = [
|
|
|
306
306
|
""
|
|
307
307
|
]
|
|
308
308
|
];
|
|
309
|
+
/** The fixed line the plain whip sends — not drawn from `whip_messages`. */
|
|
310
|
+
export const GO_FASTER_WHIP_MESSAGE = 'Go faster';
|
|
309
311
|
let lastMessage;
|
|
310
312
|
/** A random line, never the one the previous whip used. */
|
|
311
313
|
export function randomWhipMessage(messages) {
|