@north-light/crouter 0.3.195 → 0.3.196
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/clients/attach/render/assistant-message.d.ts +1 -1
- package/dist/clients/attach/render/assistant-message.js +3 -2
- package/dist/clients/attach/render/chat-view.d.ts +7 -17
- package/dist/clients/attach/render/chat-view.js +42 -57
- package/dist/clients/attach/render/diagram.d.ts +0 -10
- package/dist/clients/attach/render/diagram.js +0 -32
- package/dist/clients/attach/render/html-markdown.d.ts +0 -4
- package/dist/clients/attach/render/html-markdown.js +0 -22
- package/dist/clients/attach/render/markdown-source.d.ts +5 -0
- package/dist/clients/attach/render/markdown-source.js +9 -7
- package/dist/clients/attach/viewer.js +389 -389
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
|
@@ -6,7 +6,7 @@ export declare class ViewerAssistantMessageComponent extends AssistantMessageCom
|
|
|
6
6
|
/** The stripped copy last handed to the base class, for identity comparison. */
|
|
7
7
|
private stripped;
|
|
8
8
|
private hidden;
|
|
9
|
-
constructor(message: AssistantMessageLike | undefined, hideThinkingBlock?: boolean, markdownTheme?: ConstructorParameters<typeof AssistantMessageComponent>[2], hiddenThinkingLabel?: string);
|
|
9
|
+
constructor(message: AssistantMessageLike | undefined, hideThinkingBlock?: boolean, markdownTheme?: ConstructorParameters<typeof AssistantMessageComponent>[2], hiddenThinkingLabel?: string, markdownTransformers?: ConstructorParameters<typeof AssistantMessageComponent>[5]);
|
|
10
10
|
updateContent(message: AssistantMessageLike): void;
|
|
11
11
|
setHideThinkingBlock(hide: boolean): void;
|
|
12
12
|
}
|
|
@@ -23,9 +23,10 @@ export class ViewerAssistantMessageComponent extends AssistantMessageComponent {
|
|
|
23
23
|
/** The stripped copy last handed to the base class, for identity comparison. */
|
|
24
24
|
stripped;
|
|
25
25
|
hidden;
|
|
26
|
-
constructor(message, hideThinkingBlock = false, markdownTheme, hiddenThinkingLabel) {
|
|
26
|
+
constructor(message, hideThinkingBlock = false, markdownTheme, hiddenThinkingLabel, markdownTransformers) {
|
|
27
27
|
// Defer content to our own updateContent so stripping applies to it too.
|
|
28
|
-
|
|
28
|
+
// `undefined` outputPad keeps the base class's own default.
|
|
29
|
+
super(undefined, hideThinkingBlock, markdownTheme, hiddenThinkingLabel, undefined, markdownTransformers);
|
|
29
30
|
this.hidden = hideThinkingBlock;
|
|
30
31
|
if (message)
|
|
31
32
|
this.updateContent(message);
|
|
@@ -111,13 +111,16 @@ export declare class ChatView {
|
|
|
111
111
|
private readonly recapPalette;
|
|
112
112
|
/** Bold accent for the expanded crtr-context label. */
|
|
113
113
|
private readonly labelStyle;
|
|
114
|
-
/** Raw broker messages retained beside their viewer components so a local
|
|
115
|
-
* Ctrl+O disclosure toggle can re-project settled Markdown without touching
|
|
116
|
-
* persisted/copyable source. Includes condensed-history components. */
|
|
117
|
-
private readonly assistantSources;
|
|
118
114
|
/** Undefined honors each `<details open>` attribute until Ctrl+O establishes
|
|
119
115
|
* one viewer-wide disclosure state. */
|
|
120
116
|
private detailsExpandedOverride;
|
|
117
|
+
/** Viewer-local Markdown projection, handed to pi's message components so
|
|
118
|
+
* THEY apply it at render time with the exact content width. The viewer keeps
|
|
119
|
+
* no projected copy: the components hold canonical source, and each
|
|
120
|
+
* transformer reads live viewer state (disclosure, palette) on every call.
|
|
121
|
+
* Diagrams and the HTML allowlist apply only to assistant-authored
|
|
122
|
+
* Markdown. */
|
|
123
|
+
private readonly markdownTransformers;
|
|
121
124
|
/** The assistant message component currently being streamed (between
|
|
122
125
|
* message_start and message_end for an assistant turn). */
|
|
123
126
|
private streamingComponent;
|
|
@@ -268,19 +271,6 @@ export declare class ChatView {
|
|
|
268
271
|
/** Append conversation content above the independent live status area — or into
|
|
269
272
|
* the nested container while condensed history is under construction. */
|
|
270
273
|
private append;
|
|
271
|
-
/** Project one raw assistant message through every attach-local Markdown
|
|
272
|
-
* transform. This is the sole pipeline used by snapshots, streaming, settled
|
|
273
|
-
* messages, and Ctrl+O re-renders. */
|
|
274
|
-
private renderAssistantMessage;
|
|
275
|
-
/** Retain canonical source and update one stable transcript component with its
|
|
276
|
-
* viewer-local projection. */
|
|
277
|
-
private updateAssistantComponent;
|
|
278
|
-
/** Re-project every assistant component in place after a disclosure toggle. */
|
|
279
|
-
private rerenderAssistantMessages;
|
|
280
|
-
/** Approximate render width for inline mermaid diagrams. Only affects
|
|
281
|
-
* termrender's trailing padding (stripped in the transform), so the terminal
|
|
282
|
-
* column count is close enough without threading the live pane width here. */
|
|
283
|
-
private renderWidth;
|
|
284
274
|
/** Number of rendered conversation components in the container currently being
|
|
285
275
|
* appended to (live history or the nested condensed region). */
|
|
286
276
|
private chatChildCount;
|
|
@@ -28,9 +28,9 @@ import { CRTR_OUTPUT_CUSTOM_TYPE, CrtrOutputMessageComponent, createCrtrBashTool
|
|
|
28
28
|
import { CRTR_CYCLE_DIVIDER_CUSTOM_TYPE } from '../../../core/runtime/session-cycles.js';
|
|
29
29
|
import { generatedContextExpandedBody, generatedContextPresentation, isRuntimeRestartContinuation, } from '../../../shared/generated-context.js';
|
|
30
30
|
import { assistantVisibleText, isTrueUserMessage, } from '../../../shared/tool-groups.js';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import { styleAttachMarkdownSource, styleAttachMessageMarkdown, styleAttachSummaryMarkdown, } from './markdown-source.js';
|
|
31
|
+
import { transformDiagramFences } from './diagram.js';
|
|
32
|
+
import { transformHtmlMarkdown } from './html-markdown.js';
|
|
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';
|
|
36
36
|
import { GroupRecapComponent } from './group-recap.js';
|
|
@@ -305,13 +305,29 @@ export class ChatView {
|
|
|
305
305
|
recapPalette;
|
|
306
306
|
/** Bold accent for the expanded crtr-context label. */
|
|
307
307
|
labelStyle = (s) => `\x1b[1m${s}\x1b[22m`;
|
|
308
|
-
/** Raw broker messages retained beside their viewer components so a local
|
|
309
|
-
* Ctrl+O disclosure toggle can re-project settled Markdown without touching
|
|
310
|
-
* persisted/copyable source. Includes condensed-history components. */
|
|
311
|
-
assistantSources = new Map();
|
|
312
308
|
/** Undefined honors each `<details open>` attribute until Ctrl+O establishes
|
|
313
309
|
* one viewer-wide disclosure state. */
|
|
314
310
|
detailsExpandedOverride;
|
|
311
|
+
/** Viewer-local Markdown projection, handed to pi's message components so
|
|
312
|
+
* THEY apply it at render time with the exact content width. The viewer keeps
|
|
313
|
+
* no projected copy: the components hold canonical source, and each
|
|
314
|
+
* transformer reads live viewer state (disclosure, palette) on every call.
|
|
315
|
+
* Diagrams and the HTML allowlist apply only to assistant-authored
|
|
316
|
+
* Markdown. */
|
|
317
|
+
markdownTransformers = [
|
|
318
|
+
(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
|
+
(markdown, { messageType }) => (messageType === 'user' ? styleAttachMarkdownSource(markdown) : styleAttachAgentMarkdown(markdown)),
|
|
330
|
+
];
|
|
315
331
|
/** The assistant message component currently being streamed (between
|
|
316
332
|
* message_start and message_end for an assistant turn). */
|
|
317
333
|
streamingComponent;
|
|
@@ -499,11 +515,11 @@ export class ChatView {
|
|
|
499
515
|
this.closeOpenGroup();
|
|
500
516
|
let separator;
|
|
501
517
|
if (isStreamingTail) {
|
|
502
|
-
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
518
|
+
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel, this.markdownTransformers);
|
|
503
519
|
this.appendAssistantPart(this.streamingComponent, !hasProse);
|
|
504
520
|
this.registerCopyBlock(this.streamingComponent, 'assistant', '');
|
|
505
521
|
this.streamingWasGroupMember = !hasProse;
|
|
506
|
-
this.
|
|
522
|
+
this.streamingComponent.updateContent(message);
|
|
507
523
|
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(message));
|
|
508
524
|
separator = this.streamingToolSeparator = this.appendAssistantSeparator(!hasProse);
|
|
509
525
|
}
|
|
@@ -638,7 +654,6 @@ export class ChatView {
|
|
|
638
654
|
if (this.streamingComponent) {
|
|
639
655
|
this.historyContainer.removeChild(this.streamingComponent);
|
|
640
656
|
this.unregisterCopyBlock(this.streamingComponent);
|
|
641
|
-
this.assistantSources.delete(this.streamingComponent);
|
|
642
657
|
this.streamingComponent = undefined;
|
|
643
658
|
}
|
|
644
659
|
this.streamingToolSeparator = undefined;
|
|
@@ -673,7 +688,10 @@ export class ChatView {
|
|
|
673
688
|
result = { kind: 'output', expanded: this.toolOutputExpanded };
|
|
674
689
|
}
|
|
675
690
|
this.detailsExpandedOverride = this.toolOutputExpanded;
|
|
676
|
-
|
|
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();
|
|
677
695
|
this.applyToolDisplay();
|
|
678
696
|
return result;
|
|
679
697
|
}
|
|
@@ -772,11 +790,11 @@ export class ChatView {
|
|
|
772
790
|
const hasProse = assistantVisibleText(event.message).trim() !== '';
|
|
773
791
|
if (hasProse)
|
|
774
792
|
this.closeOpenGroup();
|
|
775
|
-
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
793
|
+
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel, this.markdownTransformers);
|
|
776
794
|
this.streamingWasGroupMember = !hasProse;
|
|
777
795
|
this.appendAssistantPart(this.streamingComponent, !hasProse);
|
|
778
796
|
this.registerCopyBlock(this.streamingComponent, 'assistant', '');
|
|
779
|
-
this.
|
|
797
|
+
this.streamingComponent.updateContent(event.message);
|
|
780
798
|
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(event.message));
|
|
781
799
|
this.streamingToolSeparator = this.appendAssistantSeparator(!hasProse);
|
|
782
800
|
this.setAssistantSeparatorContent(this.streamingToolSeparator, event.message);
|
|
@@ -786,10 +804,10 @@ export class ChatView {
|
|
|
786
804
|
case 'message_update': {
|
|
787
805
|
if (this.streamingComponent && event.message.role === 'assistant') {
|
|
788
806
|
this.promoteStreamingAssistantToBoundary(event.message);
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
//
|
|
792
|
-
this.
|
|
807
|
+
// Incomplete Mermaid fences and HTML pairs stay literal until their
|
|
808
|
+
// closing marker arrives; the transformers upgrade them on the render
|
|
809
|
+
// that follows, with no separate mid-stream path.
|
|
810
|
+
this.streamingComponent.updateContent(event.message);
|
|
793
811
|
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(event.message));
|
|
794
812
|
this.historyContainer.markDirty(this.streamingComponent);
|
|
795
813
|
const toolCalls = this.assistantToolCalls(event.message);
|
|
@@ -842,8 +860,7 @@ export class ChatView {
|
|
|
842
860
|
// updateContent) so the rendered message shows the annotation.
|
|
843
861
|
event.message.errorMessage = errorMessage;
|
|
844
862
|
}
|
|
845
|
-
|
|
846
|
-
this.updateAssistantComponent(this.streamingComponent, event.message);
|
|
863
|
+
this.streamingComponent.updateContent(event.message);
|
|
847
864
|
if (stopReason === 'aborted' || stopReason === 'error') {
|
|
848
865
|
const text = errorMessage ?? 'Error';
|
|
849
866
|
for (const component of this.pendingTools.values()) {
|
|
@@ -1111,18 +1128,20 @@ export class ChatView {
|
|
|
1111
1128
|
this.appendUserPart(new Spacer(1), !isTrueUserMessage(message));
|
|
1112
1129
|
const skillBlock = parseSkillBlock(textContent);
|
|
1113
1130
|
if (skillBlock) {
|
|
1131
|
+
// SkillInvocationMessageComponent has no transformer seam, so its body
|
|
1132
|
+
// is still styled before construction.
|
|
1114
1133
|
const component = new SkillInvocationMessageComponent({ ...skillBlock, content: styleAttachMarkdownSource(skillBlock.content) }, attachMarkdownTheme());
|
|
1115
1134
|
component.setExpanded(this.toolOutputExpanded);
|
|
1116
1135
|
this.appendUserPart(component, !isTrueUserMessage(message));
|
|
1117
1136
|
this.registerCopyBlock(component, 'user', textContent);
|
|
1118
1137
|
if (skillBlock.userMessage) {
|
|
1119
|
-
const userMessage = new UserMessageComponent(
|
|
1138
|
+
const userMessage = new UserMessageComponent(skillBlock.userMessage, attachMarkdownTheme(), undefined, this.markdownTransformers);
|
|
1120
1139
|
this.appendUserPart(userMessage, !isTrueUserMessage(message));
|
|
1121
1140
|
this.registerCopyBlock(userMessage, 'user', skillBlock.userMessage);
|
|
1122
1141
|
}
|
|
1123
1142
|
}
|
|
1124
1143
|
else {
|
|
1125
|
-
const component = new UserMessageComponent(
|
|
1144
|
+
const component = new UserMessageComponent(textContent, attachMarkdownTheme(), undefined, this.markdownTransformers);
|
|
1126
1145
|
this.appendUserPart(component, !isTrueUserMessage(message));
|
|
1127
1146
|
this.registerCopyBlock(component, 'user', textContent);
|
|
1128
1147
|
}
|
|
@@ -1145,8 +1164,8 @@ export class ChatView {
|
|
|
1145
1164
|
// -------------------------------------------------------------------------
|
|
1146
1165
|
/** Render an assistant reply plus its initially-hidden boundary separator. */
|
|
1147
1166
|
appendAssistantMessage(message, groupMember = false) {
|
|
1148
|
-
const component = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
1149
|
-
|
|
1167
|
+
const component = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel, this.markdownTransformers);
|
|
1168
|
+
component.updateContent(message);
|
|
1150
1169
|
this.appendAssistantPart(component, groupMember);
|
|
1151
1170
|
this.registerCopyBlock(component, 'assistant', assistantVisibleText(message));
|
|
1152
1171
|
const separator = this.appendAssistantSeparator(groupMember);
|
|
@@ -1380,39 +1399,6 @@ export class ChatView {
|
|
|
1380
1399
|
append(child) {
|
|
1381
1400
|
this.appendTarget.addChild(child);
|
|
1382
1401
|
}
|
|
1383
|
-
/** Project one raw assistant message through every attach-local Markdown
|
|
1384
|
-
* transform. This is the sole pipeline used by snapshots, streaming, settled
|
|
1385
|
-
* messages, and Ctrl+O re-renders. */
|
|
1386
|
-
renderAssistantMessage(message) {
|
|
1387
|
-
const withDiagrams = transformMessageDiagrams(message, this.renderWidth());
|
|
1388
|
-
const withHtml = transformMessageHtml(withDiagrams, {
|
|
1389
|
-
detailsExpanded: this.detailsExpandedOverride,
|
|
1390
|
-
styles: {
|
|
1391
|
-
kbd: (text) => this.recapPalette.active(this.recapPalette.bold(`‹${text}›`)),
|
|
1392
|
-
mark: (text) => `\x1b[7m${this.recapPalette.bold(this.recapPalette.accent(text))}\x1b[27m`,
|
|
1393
|
-
hint: this.dimStyle,
|
|
1394
|
-
},
|
|
1395
|
-
});
|
|
1396
|
-
return styleAttachMessageMarkdown(withHtml);
|
|
1397
|
-
}
|
|
1398
|
-
/** Retain canonical source and update one stable transcript component with its
|
|
1399
|
-
* viewer-local projection. */
|
|
1400
|
-
updateAssistantComponent(component, message) {
|
|
1401
|
-
this.assistantSources.set(component, message);
|
|
1402
|
-
component.updateContent(this.renderAssistantMessage(message));
|
|
1403
|
-
}
|
|
1404
|
-
/** Re-project every assistant component in place after a disclosure toggle. */
|
|
1405
|
-
rerenderAssistantMessages() {
|
|
1406
|
-
for (const [component, source] of this.assistantSources) {
|
|
1407
|
-
component.updateContent(this.renderAssistantMessage(source));
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
/** Approximate render width for inline mermaid diagrams. Only affects
|
|
1411
|
-
* termrender's trailing padding (stripped in the transform), so the terminal
|
|
1412
|
-
* column count is close enough without threading the live pane width here. */
|
|
1413
|
-
renderWidth() {
|
|
1414
|
-
return Math.max(40, process.stdout.columns ?? 80);
|
|
1415
|
-
}
|
|
1416
1402
|
/** Number of rendered conversation components in the container currently being
|
|
1417
1403
|
* appended to (live history or the nested condensed region). */
|
|
1418
1404
|
chatChildCount() {
|
|
@@ -1426,7 +1412,6 @@ export class ChatView {
|
|
|
1426
1412
|
this.streamingToolSeparator = undefined;
|
|
1427
1413
|
this.streamingWasGroupMember = false;
|
|
1428
1414
|
this.foldedToolSeparatorContents.clear();
|
|
1429
|
-
this.assistantSources.clear();
|
|
1430
1415
|
this.groups.length = 0;
|
|
1431
1416
|
this.openGroup = undefined;
|
|
1432
1417
|
this.groupSummaries.clear();
|
|
@@ -2,13 +2,3 @@
|
|
|
2
2
|
export declare function textHasDiagram(text: string): boolean;
|
|
3
3
|
/** Replace every supported complete fence in `text` with its rendered diagram. */
|
|
4
4
|
export declare function transformDiagramFences(text: string, width: number): string;
|
|
5
|
-
/** True when any text part of an assistant message carries a supported fence. */
|
|
6
|
-
export declare function messageHasDiagram(message: {
|
|
7
|
-
content?: unknown;
|
|
8
|
-
}): boolean;
|
|
9
|
-
/** Return a shallow clone of an assistant message with every supported fence
|
|
10
|
-
* rendered inline. Non-text parts (thinking, toolCall) pass through untouched,
|
|
11
|
-
* and a message without a supported fence keeps the same reference. */
|
|
12
|
-
export declare function transformMessageDiagrams<T extends {
|
|
13
|
-
content?: unknown;
|
|
14
|
-
}>(message: T, width: number): T;
|
|
@@ -101,35 +101,3 @@ export function transformDiagramFences(text, width) {
|
|
|
101
101
|
return source === null ? whole : renderDiagram(whole, source, width);
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
/** True when any text part of an assistant message carries a supported fence. */
|
|
105
|
-
export function messageHasDiagram(message) {
|
|
106
|
-
const content = message.content;
|
|
107
|
-
if (typeof content === 'string')
|
|
108
|
-
return textHasDiagram(content);
|
|
109
|
-
if (!Array.isArray(content))
|
|
110
|
-
return false;
|
|
111
|
-
return content.some((p) => p?.type === 'text' && textHasDiagram(p.text ?? ''));
|
|
112
|
-
}
|
|
113
|
-
/** Return a shallow clone of an assistant message with every supported fence
|
|
114
|
-
* rendered inline. Non-text parts (thinking, toolCall) pass through untouched,
|
|
115
|
-
* and a message without a supported fence keeps the same reference. */
|
|
116
|
-
export function transformMessageDiagrams(message, width) {
|
|
117
|
-
const content = message.content;
|
|
118
|
-
if (typeof content === 'string') {
|
|
119
|
-
if (!textHasDiagram(content))
|
|
120
|
-
return message;
|
|
121
|
-
return { ...message, content: transformDiagramFences(content, width) };
|
|
122
|
-
}
|
|
123
|
-
if (!Array.isArray(content) || !messageHasDiagram(message))
|
|
124
|
-
return message;
|
|
125
|
-
return {
|
|
126
|
-
...message,
|
|
127
|
-
content: content.map((part) => {
|
|
128
|
-
const p = part;
|
|
129
|
-
if (p?.type === 'text' && typeof p.text === 'string' && textHasDiagram(p.text)) {
|
|
130
|
-
return { ...p, text: transformDiagramFences(p.text, width) };
|
|
131
|
-
}
|
|
132
|
-
return part;
|
|
133
|
-
}),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
@@ -11,7 +11,3 @@ export interface HtmlMarkdownOptions {
|
|
|
11
11
|
}
|
|
12
12
|
/** Transform the strict supported HTML subset in one Markdown string. */
|
|
13
13
|
export declare function transformHtmlMarkdown(source: string, options: HtmlMarkdownOptions): string;
|
|
14
|
-
/** Copy an assistant message for terminal rendering, enhancing only text parts. */
|
|
15
|
-
export declare function transformMessageHtml<T extends {
|
|
16
|
-
content?: unknown;
|
|
17
|
-
}>(message: T, options: HtmlMarkdownOptions): T;
|
|
@@ -204,25 +204,3 @@ export function transformHtmlMarkdown(source, options) {
|
|
|
204
204
|
out += transformInlineHtml(masked.slice(cursor), options.styles);
|
|
205
205
|
return protectedMarkdown.restore(out);
|
|
206
206
|
}
|
|
207
|
-
/** Copy an assistant message for terminal rendering, enhancing only text parts. */
|
|
208
|
-
export function transformMessageHtml(message, options) {
|
|
209
|
-
const content = message.content;
|
|
210
|
-
if (typeof content === 'string') {
|
|
211
|
-
const transformed = transformHtmlMarkdown(content, options);
|
|
212
|
-
return transformed === content ? message : { ...message, content: transformed };
|
|
213
|
-
}
|
|
214
|
-
if (!Array.isArray(content))
|
|
215
|
-
return message;
|
|
216
|
-
let changed = false;
|
|
217
|
-
const next = content.map((part) => {
|
|
218
|
-
const candidate = part;
|
|
219
|
-
if (candidate?.type !== 'text' || typeof candidate.text !== 'string')
|
|
220
|
-
return part;
|
|
221
|
-
const transformed = transformHtmlMarkdown(candidate.text, options);
|
|
222
|
-
if (transformed === candidate.text)
|
|
223
|
-
return part;
|
|
224
|
-
changed = true;
|
|
225
|
-
return { ...candidate, text: transformed };
|
|
226
|
-
});
|
|
227
|
-
return changed ? { ...message, content: next } : message;
|
|
228
|
-
}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* tag lines. Fenced examples remain literal Markdown, never decorated
|
|
3
3
|
* terminal output. */
|
|
4
4
|
export declare function styleAttachMarkdownSource(markdown: string): string;
|
|
5
|
+
/** Every source-text transform the viewer applies to AGENT-authored Markdown,
|
|
6
|
+
* in one place: heading and XML syntax, then glyph expansion. A human's own
|
|
7
|
+
* message keeps `styleAttachMarkdownSource` alone — the `U+XXXX` escape hatch
|
|
8
|
+
* exists for agents that cannot type a Nerd Font character. */
|
|
9
|
+
export declare function styleAttachAgentMarkdown(markdown: string): string;
|
|
5
10
|
/** Copy a pi message for terminal rendering and decorate only Markdown-bearing
|
|
6
11
|
* text/thinking blocks. The broker's source event is never mutated. */
|
|
7
12
|
export declare function styleAttachMessageMarkdown<T>(message: T): T;
|
|
@@ -127,9 +127,11 @@ export function styleAttachMarkdownSource(markdown) {
|
|
|
127
127
|
}
|
|
128
128
|
return out.join('\n');
|
|
129
129
|
}
|
|
130
|
-
/** Every source-text transform the viewer applies to
|
|
131
|
-
* in one place: heading and XML syntax, then glyph expansion.
|
|
132
|
-
|
|
130
|
+
/** Every source-text transform the viewer applies to AGENT-authored Markdown,
|
|
131
|
+
* in one place: heading and XML syntax, then glyph expansion. A human's own
|
|
132
|
+
* message keeps `styleAttachMarkdownSource` alone — the `U+XXXX` escape hatch
|
|
133
|
+
* exists for agents that cannot type a Nerd Font character. */
|
|
134
|
+
export function styleAttachAgentMarkdown(markdown) {
|
|
133
135
|
return expandGlyphCodepoints(styleAttachMarkdownSource(markdown));
|
|
134
136
|
}
|
|
135
137
|
/** Copy a pi message for terminal rendering and decorate only Markdown-bearing
|
|
@@ -139,7 +141,7 @@ export function styleAttachMessageMarkdown(message) {
|
|
|
139
141
|
return message;
|
|
140
142
|
const candidate = message;
|
|
141
143
|
if (typeof candidate.content === 'string') {
|
|
142
|
-
return { ...candidate, content:
|
|
144
|
+
return { ...candidate, content: styleAttachAgentMarkdown(candidate.content) };
|
|
143
145
|
}
|
|
144
146
|
if (!Array.isArray(candidate.content))
|
|
145
147
|
return message;
|
|
@@ -150,10 +152,10 @@ export function styleAttachMessageMarkdown(message) {
|
|
|
150
152
|
return part;
|
|
151
153
|
const block = part;
|
|
152
154
|
if (block.type === 'text' && typeof block.text === 'string') {
|
|
153
|
-
return { ...block, text:
|
|
155
|
+
return { ...block, text: styleAttachAgentMarkdown(block.text) };
|
|
154
156
|
}
|
|
155
157
|
if (block.type === 'thinking' && typeof block.thinking === 'string') {
|
|
156
|
-
return { ...block, thinking:
|
|
158
|
+
return { ...block, thinking: styleAttachAgentMarkdown(block.thinking) };
|
|
157
159
|
}
|
|
158
160
|
return part;
|
|
159
161
|
}),
|
|
@@ -161,5 +163,5 @@ export function styleAttachMessageMarkdown(message) {
|
|
|
161
163
|
}
|
|
162
164
|
/** Copy an expanded pi summary without changing its persisted source. */
|
|
163
165
|
export function styleAttachSummaryMarkdown(message) {
|
|
164
|
-
return { ...message, summary:
|
|
166
|
+
return { ...message, summary: styleAttachAgentMarkdown(message.summary) };
|
|
165
167
|
}
|