@ai-matrx/chat 0.4.9 → 0.4.12
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/CHANGELOG.md +53 -0
- package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
- package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
- package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
- package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
- package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
- package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
- package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
- package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
- package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
- package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
- package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
- package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
- package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
- package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
- package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
- package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
- package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
- package/dist/agents/conversation-export/conversation-transfer.js +1 -1
- package/dist/agents/conversation-export/document-markdown.js +1 -1
- package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
- package/dist/agents/hooks/useModelControls.js +1 -1
- package/dist/agents/redux/agent-definition/converters.js +1 -1
- package/dist/agents/redux/agent-definition/slice.js +1 -1
- package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
- package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +1 -1
- package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
- package/dist/agents/redux/execution-system/thunks/process-stream.js +1 -1
- package/dist/agents/redux/execution-system/thunks/resolve-base-url.js +17 -0
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +14 -2
- package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
- package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
- package/dist/agents/routing/confidential-routing.d.ts +52 -0
- package/dist/agents/routing/confidential-routing.js +38 -0
- package/dist/agents/utils/canvasItemContext.js +1 -1
- package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
- package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
- package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
- package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
- package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
- package/dist/cx-conversation/AssistantMessage.js +1 -1
- package/dist/host/configure.js +4 -1
- package/dist/host/contract.d.ts +14 -1
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +1 -0
- package/dist/mandates/llm-params.d.ts +1 -1
- package/dist/mandates/output-contract.js +1 -1
- package/dist/mandates/service.d.ts +1 -1
- package/dist/mandates/service.js +1 -1
- package/dist/mandates/test-run-core.js +1 -1
- package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
- package/dist/surfaces/services/surface-config.service.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
- package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
- package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
- package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
- package/dist/tool-call-visualization/result-fields/shape.js +1 -1
- package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
- package/dist/tool-call-visualization/utils/human-copy.js +3 -3
- package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
- package/dist/ui/mandates-storage/provision-shapes.js +1 -1
- package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
- package/dist/ui/treatment-shape.d.ts +1 -1
- package/dist/ui/treatment-shape.js +1 -1
- package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
- package/package.json +26 -9
- package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
- package/dist/agents/hooks/useWarmAgent.js +0 -25
- package/dist/agents/utils/artifactId.d.ts +0 -34
- package/dist/agents/utils/artifactId.js +0 -15
- package/dist/ui/ShimmerText.d.ts +0 -5
- package/dist/ui/ShimmerText.js +0 -21
- package/dist/ui/addressWithoutNavigating.d.ts +0 -68
- package/dist/ui/addressWithoutNavigating.js +0 -60
- package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
- package/dist/utils/content-ir/decision-answers-ids.js +0 -6
- package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
- package/dist/utils/content-ir/envelope-parity.js +0 -32
- package/dist/utils/content-ir/instance-title.d.ts +0 -26
- package/dist/utils/content-ir/instance-title.js +0 -39
- package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
- package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
- package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
- package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
- package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
- package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
- package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
- package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
- package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
- package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
- package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
- package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
- package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
- package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
- package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
- package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
- package/dist/utils/inline-decision/decision-options.d.ts +0 -21
- package/dist/utils/inline-decision/decision-options.js +0 -32
- package/dist/utils/json.d.ts +0 -63
- package/dist/utils/json.js +0 -22
- package/dist/utils/markdown/plain-title.d.ts +0 -25
- package/dist/utils/markdown/plain-title.js +0 -51
- package/dist/utils/rendered-find.d.ts +0 -9
- package/dist/utils/rendered-find.js +0 -68
- package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
- package/dist/utils/rich-editor/core/caret-context.js +0 -73
- package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
- package/dist/utils/rich-editor/in-place/caret-handoff.js +0 -15
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
import { findCodeRanges, fenceParts } from "@ai-matrx/content-ir/source";
|
|
2
|
-
import { kindValueToMarkdown } from "../kinds/kind-value-markdown.js";
|
|
3
|
-
import { humanizeKind, plainValueMarkdown } from "../kinds/kind-markdown-utils.js";
|
|
4
|
-
import {
|
|
5
|
-
balancedEnds,
|
|
6
|
-
findBrokenKindJsonRegions,
|
|
7
|
-
findKindCarryingJsonValues,
|
|
8
|
-
frontMatterEnd
|
|
9
|
-
} from "./embedded-kind-json.js";
|
|
10
|
-
import { firstKindSlug, hasKindKey, isKindJsonText, normalizeKindSpellings, valueCarriesKind } from "./json-kind-signal.js";
|
|
11
|
-
function isRecord(value) {
|
|
12
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
-
}
|
|
14
|
-
function isKindObject(value) {
|
|
15
|
-
return isRecord(value) && typeof value.__kind === "string" && value.__kind.trim().length > 0;
|
|
16
|
-
}
|
|
17
|
-
function unfinishedKindLabel(kind) {
|
|
18
|
-
return kind ? `${humanizeKind(kind)} did not finish` : "Result did not finish";
|
|
19
|
-
}
|
|
20
|
-
const UNREADABLE_KIND_NOTE = "Structured output could not be read";
|
|
21
|
-
const KIND_SLUG_TEXT = /^[A-Za-z0-9_.:-]+$/;
|
|
22
|
-
function carriesBrokenKind(value, depth = 0) {
|
|
23
|
-
if (depth > 64 || value === null || typeof value !== "object") return false;
|
|
24
|
-
if (Array.isArray(value)) return value.some((item) => carriesBrokenKind(item, depth + 1));
|
|
25
|
-
const record = value;
|
|
26
|
-
if (Object.prototype.hasOwnProperty.call(record, "__kind")) {
|
|
27
|
-
const kind = record.__kind;
|
|
28
|
-
const shape = typeof kind === "object" && kind !== null && !Array.isArray(kind);
|
|
29
|
-
if (!shape && (typeof kind !== "string" || !KIND_SLUG_TEXT.test(kind))) return true;
|
|
30
|
-
}
|
|
31
|
-
return Object.values(record).some((item) => carriesBrokenKind(item, depth + 1));
|
|
32
|
-
}
|
|
33
|
-
function rangeCursor(ranges) {
|
|
34
|
-
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
35
|
-
let k = 0;
|
|
36
|
-
return (at) => {
|
|
37
|
-
while (k < sorted.length && sorted[k][1] <= at) k++;
|
|
38
|
-
for (let j = k; j < sorted.length && sorted[j][0] <= at; j++) if (at < sorted[j][1]) return sorted[j];
|
|
39
|
-
return void 0;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function quotedCodeRanges(text) {
|
|
43
|
-
return findCodeRanges(text).filter((range) => {
|
|
44
|
-
if (range.kind !== "fence") return true;
|
|
45
|
-
const parts = fenceParts(text.slice(range.start, range.end));
|
|
46
|
-
return !parts || !JSON_FENCE_LANGS.has(parts.opener.lang.toLowerCase());
|
|
47
|
-
}).map((range) => [range.start, range.end]);
|
|
48
|
-
}
|
|
49
|
-
function noteUnreadableKinds(text) {
|
|
50
|
-
if (!hasKindKey(text)) return text;
|
|
51
|
-
const quoted = quotedCodeRanges(text);
|
|
52
|
-
const fences = findCodeRanges(text).filter((range) => range.kind === "fence");
|
|
53
|
-
const spans = [];
|
|
54
|
-
const ends = balancedEnds(text);
|
|
55
|
-
const quotedAt = rangeCursor(quoted);
|
|
56
|
-
for (let i = 0; i < text.length; i++) {
|
|
57
|
-
const ch = text[i];
|
|
58
|
-
if (ch !== "{" && ch !== "[") continue;
|
|
59
|
-
const range = quotedAt(i);
|
|
60
|
-
if (range) {
|
|
61
|
-
i = range[1] - 1;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const end = ends[i];
|
|
65
|
-
if (end < 0) continue;
|
|
66
|
-
let value;
|
|
67
|
-
try {
|
|
68
|
-
value = JSON.parse(text.slice(i, end));
|
|
69
|
-
} catch {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (carriesBrokenKind(value)) spans.push([i, end]);
|
|
73
|
-
i = end - 1;
|
|
74
|
-
}
|
|
75
|
-
let kept = text.length;
|
|
76
|
-
let tail = "";
|
|
77
|
-
let tailHead = "";
|
|
78
|
-
for (let k = spans.length - 1; k >= 0; k--) {
|
|
79
|
-
let [start, end] = spans[k];
|
|
80
|
-
const fence = fences.find((range) => start >= range.start && end <= range.end);
|
|
81
|
-
if (fence) {
|
|
82
|
-
const parts = fenceParts(text.slice(fence.start, fence.end));
|
|
83
|
-
const inner = parts ? text.slice(fence.start, fence.end).split("\n").slice(1, parts.closed ? -1 : void 0).join("\n").trim() : "";
|
|
84
|
-
if (parts && inner === text.slice(start, end).trim()) {
|
|
85
|
-
start = fence.start;
|
|
86
|
-
end = fence.end;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const before = closeBeforeAt(text, start);
|
|
90
|
-
const following = end <= kept ? text.slice(end, kept) : "";
|
|
91
|
-
const after = end <= kept ? openAfterJoined(following, tail, tailHead) : openAfter(tail.slice(end - kept));
|
|
92
|
-
kept = before.cut;
|
|
93
|
-
tailHead = before.add + UNREADABLE_KIND_NOTE;
|
|
94
|
-
tail = tailHead + after;
|
|
95
|
-
}
|
|
96
|
-
return text.slice(0, kept) + tail;
|
|
97
|
-
}
|
|
98
|
-
function liftKindStrings(value, depth = 0) {
|
|
99
|
-
if (depth > 64) return value;
|
|
100
|
-
if (typeof value === "string") {
|
|
101
|
-
if (!isKindJsonText(value)) return value;
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(value);
|
|
104
|
-
return valueCarriesKind(parsed) ? parsed : value;
|
|
105
|
-
} catch {
|
|
106
|
-
return value;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (Array.isArray(value)) return value.map((item) => liftKindStrings(item, depth + 1));
|
|
110
|
-
if (isRecord(value)) {
|
|
111
|
-
const out = {};
|
|
112
|
-
for (const [key, item] of Object.entries(value)) out[key] = liftKindStrings(item, depth + 1);
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
117
|
-
function valueMarkdown(raw) {
|
|
118
|
-
const value = liftKindStrings(raw);
|
|
119
|
-
const nested = (child) => kindValueToMarkdown(child);
|
|
120
|
-
if (isKindObject(value)) return kindValueToMarkdown(value);
|
|
121
|
-
if (Array.isArray(value)) {
|
|
122
|
-
const blocks = [];
|
|
123
|
-
let run = [];
|
|
124
|
-
const flush = () => {
|
|
125
|
-
if (run.length) blocks.push(plainValueMarkdown(run, nested));
|
|
126
|
-
run = [];
|
|
127
|
-
};
|
|
128
|
-
for (const item of value) {
|
|
129
|
-
if (isKindObject(item)) {
|
|
130
|
-
flush();
|
|
131
|
-
blocks.push(kindValueToMarkdown(item));
|
|
132
|
-
} else if (Array.isArray(item) || isRecord(item)) {
|
|
133
|
-
if (valueCarriesKind(item)) {
|
|
134
|
-
flush();
|
|
135
|
-
blocks.push(valueMarkdown(item));
|
|
136
|
-
} else run.push(item);
|
|
137
|
-
} else run.push(item);
|
|
138
|
-
}
|
|
139
|
-
flush();
|
|
140
|
-
return blocks.filter((block) => block.trim()).join("\n\n");
|
|
141
|
-
}
|
|
142
|
-
return plainValueMarkdown(value, nested);
|
|
143
|
-
}
|
|
144
|
-
function jsonKindValueMarkdown(text) {
|
|
145
|
-
const trimmed = text.trim();
|
|
146
|
-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
147
|
-
let parsed;
|
|
148
|
-
try {
|
|
149
|
-
parsed = JSON.parse(trimmed);
|
|
150
|
-
} catch {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
return valueCarriesKind(parsed) ? valueMarkdown(parsed) : null;
|
|
154
|
-
}
|
|
155
|
-
function closeBefore(text) {
|
|
156
|
-
const { cut, add } = closeBeforeAt(text, text.length);
|
|
157
|
-
return text.slice(0, cut) + add;
|
|
158
|
-
}
|
|
159
|
-
const isLineSpace = (ch) => ch === " " || ch === " " || ch === "\r";
|
|
160
|
-
function closeBeforeAt(text, at) {
|
|
161
|
-
let cut = at;
|
|
162
|
-
while (cut > 0 && isLineSpace(text[cut - 1])) cut--;
|
|
163
|
-
if (cut === 0) return { cut, add: "" };
|
|
164
|
-
if (text[cut - 1] !== "\n") return { cut, add: "\n\n" };
|
|
165
|
-
return { cut, add: cut > 1 && text[cut - 2] === "\n" ? "" : "\n" };
|
|
166
|
-
}
|
|
167
|
-
function openAfterJoined(head, tail, tailHead) {
|
|
168
|
-
let from = 0;
|
|
169
|
-
while (from < head.length && isLineSpace(head[from])) from++;
|
|
170
|
-
const rest = head.slice(from);
|
|
171
|
-
const lead = rest + tailHead.slice(0, 4);
|
|
172
|
-
if (!lead) return "";
|
|
173
|
-
const joined = rest + tail;
|
|
174
|
-
if (lead.startsWith("\n\n") || lead.startsWith("\r\n\r\n")) return joined;
|
|
175
|
-
if (lead.startsWith("\n")) return `
|
|
176
|
-
${joined}`;
|
|
177
|
-
return `
|
|
178
|
-
|
|
179
|
-
${joined}`;
|
|
180
|
-
}
|
|
181
|
-
function openAfter(text) {
|
|
182
|
-
const out = text.replace(/^[ \t\r]+/, "");
|
|
183
|
-
if (!out) return out;
|
|
184
|
-
if (out.startsWith("\n\n") || out.startsWith("\r\n\r\n")) return out;
|
|
185
|
-
if (out.startsWith("\n")) return `
|
|
186
|
-
${out}`;
|
|
187
|
-
return `
|
|
188
|
-
|
|
189
|
-
${out}`;
|
|
190
|
-
}
|
|
191
|
-
function convertRegions(text, excludeLiteralContexts, options, leftover = (piece) => piece) {
|
|
192
|
-
const spans = findKindCarryingJsonValues(text, { excludeLiteralContexts }).map(
|
|
193
|
-
(region) => ({ start: region.start, end: region.end, md: valueMarkdown(region.value) })
|
|
194
|
-
);
|
|
195
|
-
if (options.broken === "note") {
|
|
196
|
-
for (const region of findBrokenKindJsonRegions(text, { excludeLiteralContexts })) {
|
|
197
|
-
spans.push({
|
|
198
|
-
start: region.start,
|
|
199
|
-
end: region.end,
|
|
200
|
-
md: unfinishedKindLabel(firstKindSlug(region.content))
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (spans.length === 0) return null;
|
|
205
|
-
spans.sort((a, b) => a.start - b.start);
|
|
206
|
-
const parts = [];
|
|
207
|
-
const push = (piece) => {
|
|
208
|
-
if (piece) parts.push(piece);
|
|
209
|
-
};
|
|
210
|
-
const closeBeforeParts = () => {
|
|
211
|
-
while (parts.length > 0) {
|
|
212
|
-
const last2 = parts[parts.length - 1];
|
|
213
|
-
const trimmed = last2.replace(/[ \t\r]+$/, "");
|
|
214
|
-
if (trimmed) {
|
|
215
|
-
parts[parts.length - 1] = trimmed;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
parts.pop();
|
|
219
|
-
}
|
|
220
|
-
if (parts.length === 0) return;
|
|
221
|
-
const last = parts[parts.length - 1];
|
|
222
|
-
if (last[last.length - 1] !== "\n") {
|
|
223
|
-
parts.push("\n\n");
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const before = last.length > 1 ? last[last.length - 2] : parts.length > 1 ? parts[parts.length - 2].slice(-1) : "";
|
|
227
|
-
if (before !== "\n") parts.push("\n");
|
|
228
|
-
};
|
|
229
|
-
let cursor = 0;
|
|
230
|
-
let afterBlock = false;
|
|
231
|
-
for (const span of spans) {
|
|
232
|
-
if (span.start < cursor) continue;
|
|
233
|
-
const between = leftover(text.slice(cursor, span.start));
|
|
234
|
-
push(afterBlock ? openAfter(between) : between);
|
|
235
|
-
closeBeforeParts();
|
|
236
|
-
push(span.md);
|
|
237
|
-
cursor = span.end;
|
|
238
|
-
afterBlock = true;
|
|
239
|
-
}
|
|
240
|
-
const tail = leftover(text.slice(cursor));
|
|
241
|
-
push(afterBlock ? openAfter(tail) : tail);
|
|
242
|
-
return parts.join("");
|
|
243
|
-
}
|
|
244
|
-
const JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
245
|
-
const QUOTE_PREFIX = /^(?: {0,3}> ?)*/;
|
|
246
|
-
function convertKindFences(text, options) {
|
|
247
|
-
const fences = findCodeRanges(text).filter((range) => range.kind === "fence");
|
|
248
|
-
let out = text;
|
|
249
|
-
for (let i = fences.length - 1; i >= 0; i--) {
|
|
250
|
-
const range = fences[i];
|
|
251
|
-
const raw = text.slice(range.start, range.end);
|
|
252
|
-
if (!hasKindKey(raw)) continue;
|
|
253
|
-
const parts = fenceParts(raw);
|
|
254
|
-
if (!parts || !JSON_FENCE_LANGS.has(parts.opener.lang.toLowerCase())) continue;
|
|
255
|
-
const lines = raw.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
256
|
-
const prefix = QUOTE_PREFIX.exec(lines[0] ?? "")?.[0] ?? "";
|
|
257
|
-
const strip = (line) => prefix ? line.replace(QUOTE_PREFIX, "") : line;
|
|
258
|
-
const opener = strip(lines[0] ?? "").trim();
|
|
259
|
-
const fence = parts.opener.char.repeat(parts.opener.ticks);
|
|
260
|
-
const body = lines.slice(1, parts.closed ? -1 : void 0).map(strip).join("\n");
|
|
261
|
-
const md = jsonKindValueMarkdown(body) ?? convertRegions(
|
|
262
|
-
body,
|
|
263
|
-
false,
|
|
264
|
-
options,
|
|
265
|
-
(piece) => /^[\s,[\]{}]*$/.test(piece) ? "" : `${opener}
|
|
266
|
-
${piece.trim()}
|
|
267
|
-
${fence}`
|
|
268
|
-
);
|
|
269
|
-
if (md === null) continue;
|
|
270
|
-
const block = prefix ? md.split("\n").map((line) => line ? `${prefix}${line}` : prefix.trimEnd()).join("\n") : md;
|
|
271
|
-
out = closeBefore(out.slice(0, range.start)) + block + openAfter(out.slice(range.end));
|
|
272
|
-
}
|
|
273
|
-
return out;
|
|
274
|
-
}
|
|
275
|
-
function convertKindText(text, options) {
|
|
276
|
-
text = noteUnreadableKinds(convertFrontMatterKind(text, options));
|
|
277
|
-
const whole = jsonKindValueMarkdown(text);
|
|
278
|
-
if (whole !== null) return whole;
|
|
279
|
-
let out = convertKindFences(text, options);
|
|
280
|
-
if (!hasKindKey(out)) return out;
|
|
281
|
-
out = convertRegions(out, true, options) ?? out;
|
|
282
|
-
out = convertCommentKinds(out, options);
|
|
283
|
-
return noteEscapedCutOffKind(out, options);
|
|
284
|
-
}
|
|
285
|
-
function convertFrontMatterKind(text, options) {
|
|
286
|
-
const end = frontMatterEnd(text);
|
|
287
|
-
if (end === 0 || !hasKindKey(text.slice(0, end))) return text;
|
|
288
|
-
const block = text.slice(0, end);
|
|
289
|
-
const lines = block.split("\n");
|
|
290
|
-
const body = lines.slice(1, -1).join("\n").replace(/\r$/, "");
|
|
291
|
-
const md = convertRegions(
|
|
292
|
-
body,
|
|
293
|
-
false,
|
|
294
|
-
options,
|
|
295
|
-
(piece) => /^[\s,[\]{}]*$/.test(piece) ? "" : piece.trim()
|
|
296
|
-
);
|
|
297
|
-
if (md === null) return text;
|
|
298
|
-
return md + openAfter(text.slice(end));
|
|
299
|
-
}
|
|
300
|
-
function convertCommentKinds(text, options) {
|
|
301
|
-
const quoted = quotedCodeRanges(text);
|
|
302
|
-
const comments = [...text.matchAll(/<!--[\s\S]*?(?:-->|$)/g)].filter(
|
|
303
|
-
(m) => hasKindKey(m[0]) && !quoted.some(([a, b]) => m.index >= a && m.index < b)
|
|
304
|
-
);
|
|
305
|
-
let out = text;
|
|
306
|
-
for (let i = comments.length - 1; i >= 0; i--) {
|
|
307
|
-
const m = comments[i];
|
|
308
|
-
const body = m[0].replace(/^<!--/, "").replace(/-->$/, "");
|
|
309
|
-
const md = convertRegions(
|
|
310
|
-
body,
|
|
311
|
-
false,
|
|
312
|
-
options,
|
|
313
|
-
(piece) => piece.trim() ? `<!-- ${piece.trim()} -->` : ""
|
|
314
|
-
);
|
|
315
|
-
if (md === null) continue;
|
|
316
|
-
const start = m.index;
|
|
317
|
-
const end = start + m[0].length;
|
|
318
|
-
out = closeBefore(out.slice(0, start)) + md + openAfter(out.slice(end));
|
|
319
|
-
}
|
|
320
|
-
return out;
|
|
321
|
-
}
|
|
322
|
-
const KIND_KEY_PLAIN = /(?<!\\)"__kind"\s*:/;
|
|
323
|
-
function noteEscapedCutOffKind(text, options) {
|
|
324
|
-
if (options.broken !== "note" || !hasKindKey(text) || KIND_KEY_PLAIN.test(text)) return text;
|
|
325
|
-
const quotedAt = rangeCursor(quotedCodeRanges(text));
|
|
326
|
-
const ends = balancedEnds(text);
|
|
327
|
-
for (let i = 0; i < text.length; i++) {
|
|
328
|
-
const ch = text[i];
|
|
329
|
-
if (ch !== "{" && ch !== "[") continue;
|
|
330
|
-
const range = quotedAt(i);
|
|
331
|
-
if (range) {
|
|
332
|
-
i = range[1] - 1;
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
const end = ends[i];
|
|
336
|
-
if (end >= 0) {
|
|
337
|
-
i = end - 1;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const tail = text.slice(i);
|
|
341
|
-
if (/^[{[]\s*["{[]/.test(tail) && hasKindKey(tail)) {
|
|
342
|
-
return closeBefore(text.slice(0, i)).trimEnd() + (i > 0 ? "\n\n" : "") + unfinishedKindLabel(firstKindSlug(tail));
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return text;
|
|
346
|
-
}
|
|
347
|
-
function kindTextToMarkdown(raw) {
|
|
348
|
-
if (!raw) return "";
|
|
349
|
-
const text = normalizeKindSpellings(raw);
|
|
350
|
-
if (!hasKindKey(text)) return text;
|
|
351
|
-
return convertKindText(text, { broken: "note" });
|
|
352
|
-
}
|
|
353
|
-
function owningObjectStart(text, keyIndex) {
|
|
354
|
-
const open = [];
|
|
355
|
-
let inString = false;
|
|
356
|
-
for (let i = 0; i < keyIndex; i++) {
|
|
357
|
-
const ch = text[i];
|
|
358
|
-
if (inString) {
|
|
359
|
-
if (ch === "\\") i++;
|
|
360
|
-
else if (ch === '"') inString = false;
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
if (ch === '"') inString = true;
|
|
364
|
-
else if (ch === "{") open.push(i);
|
|
365
|
-
else if (ch === "}") open.pop();
|
|
366
|
-
}
|
|
367
|
-
return open.length > 0 ? open[0] : -1;
|
|
368
|
-
}
|
|
369
|
-
function kindKeyIndex(text) {
|
|
370
|
-
if (!hasKindKey(text)) return -1;
|
|
371
|
-
let lo = 0;
|
|
372
|
-
let hi = text.length;
|
|
373
|
-
while (lo < hi) {
|
|
374
|
-
const mid = lo + hi >> 1;
|
|
375
|
-
if (hasKindKey(text.slice(0, mid))) hi = mid;
|
|
376
|
-
else lo = mid + 1;
|
|
377
|
-
}
|
|
378
|
-
let i = lo - 1;
|
|
379
|
-
while (i > 0 && /\s/.test(text[i - 1])) i--;
|
|
380
|
-
const closing = i - 1;
|
|
381
|
-
return text.lastIndexOf('"', closing - 1);
|
|
382
|
-
}
|
|
383
|
-
function kindTextPreview(raw) {
|
|
384
|
-
const text = raw ? normalizeKindSpellings(raw) : raw;
|
|
385
|
-
const md = !text ? "" : hasKindKey(text) ? convertKindText(text, { broken: "keep" }) : text;
|
|
386
|
-
const keyIndex = kindKeyIndex(md);
|
|
387
|
-
if (keyIndex < 0) return { text: md, pendingKind: null, pendingUnnamed: false };
|
|
388
|
-
const start = owningObjectStart(md, keyIndex);
|
|
389
|
-
const cutAt = start < 0 ? keyIndex : start;
|
|
390
|
-
const head = md.slice(0, cutAt).replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n?[ \t]*$/, "$1");
|
|
391
|
-
const slug = firstKindSlug(md.slice(keyIndex));
|
|
392
|
-
return { text: head.trimEnd(), pendingKind: slug, pendingUnnamed: slug === null };
|
|
393
|
-
}
|
|
394
|
-
export {
|
|
395
|
-
UNREADABLE_KIND_NOTE,
|
|
396
|
-
kindTextPreview,
|
|
397
|
-
kindTextToMarkdown,
|
|
398
|
-
unfinishedKindLabel
|
|
399
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE MARKDOWN-ESCAPED KIND (P8, round 4). A model that escapes markdown in
|
|
3
|
-
* its whole answer writes `{"\_\_kind":"flashcard\_set",…}`. No JSON reader
|
|
4
|
-
* sees a kind there (`\_` is not even a JSON escape), but the markdown
|
|
5
|
-
* renderer un-escapes it, so the reader saw `{"__kind":…}` drawn raw.
|
|
6
|
-
*
|
|
7
|
-
* THE RULE: the key `"\_\_kind"` IS the key `"__kind"` (the detector reads it
|
|
8
|
-
* in text contexts, `hasKindKey(…, { markdown: true })`), and inside the JSON
|
|
9
|
-
* object that holds it every `\_` is `_`. ONE transform, both hosts: the live
|
|
10
|
-
* accumulator pushes every delta through a `MarkdownEscapedKindJson` before
|
|
11
|
-
* its line machine; the static splitter runs `unescapeMarkdownKindJson` on the
|
|
12
|
-
* whole text first. Character-driven and chunk-invariant, so live = reload.
|
|
13
|
-
*
|
|
14
|
-
* Also here (decided, round 4): a DOUBLE-ENCODED kind — the whole answer is a
|
|
15
|
-
* JSON string literal whose value is kind JSON (`"{\"__kind\":…}"`) — reads as
|
|
16
|
-
* that kind (`decodeDoubleEncodedKindText`, whole text, settled readers only).
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* A ZERO-WIDTH character inside the key (`"__\u200Bkind"`, round 8): the
|
|
20
|
-
* screen reads `"__kind"`, so the key is rewritten as `"__kind"` before
|
|
21
|
-
* anything else reads the text, and the normal kind route (fence, standalone,
|
|
22
|
-
* prose) takes it. Only a complete key is rewritten — a zero-width character
|
|
23
|
-
* anywhere else stays. Character-driven and chunk-invariant (live = reload).
|
|
24
|
-
*/
|
|
25
|
-
export declare class ZeroWidthKindKey {
|
|
26
|
-
/** Bytes that may still become the key — held, never shown. */
|
|
27
|
-
private pending;
|
|
28
|
-
push(text: string): string;
|
|
29
|
-
flush(): string;
|
|
30
|
-
private pushChar;
|
|
31
|
-
}
|
|
32
|
-
export declare class MarkdownEscapedKindJson {
|
|
33
|
-
/** The zero-width key spelling is read first (round 8) — one transform, both hosts. */
|
|
34
|
-
private zeroWidth;
|
|
35
|
-
/** Bytes that may still become the escaped key — held, never shown. */
|
|
36
|
-
private pending;
|
|
37
|
-
/** Inside the object holding a rewritten key: bracket depth (0 = outside). */
|
|
38
|
-
private depth;
|
|
39
|
-
private inString;
|
|
40
|
-
/** A held backslash inside the object (its fate depends on the next char). */
|
|
41
|
-
private escape;
|
|
42
|
-
push(text: string): string;
|
|
43
|
-
private pushUnescape;
|
|
44
|
-
flush(): string;
|
|
45
|
-
private pushChar;
|
|
46
|
-
private insideObject;
|
|
47
|
-
}
|
|
48
|
-
/** The whole-text form, for the static splitter (identical bytes to any streaming of `source`). */
|
|
49
|
-
export declare function unescapeMarkdownKindJson(source: string): string;
|
|
50
|
-
/**
|
|
51
|
-
* A whole answer that is a JSON STRING literal holding kind JSON → that kind
|
|
52
|
-
* JSON; anything else unchanged.
|
|
53
|
-
*/
|
|
54
|
-
export declare function decodeDoubleEncodedKindText(source: string): string;
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { isKindJsonText, withoutZeroWidth } from "./json-kind-signal.js";
|
|
2
|
-
const TARGET = '"\\_\\_kind"';
|
|
3
|
-
const KEY = '"__kind"';
|
|
4
|
-
const HAS_ZERO_WIDTH = /[\u200B-\u200D\u2060\uFEFF\u00AD]/;
|
|
5
|
-
class ZeroWidthKindKey {
|
|
6
|
-
/** Bytes that may still become the key — held, never shown. */
|
|
7
|
-
pending = "";
|
|
8
|
-
push(text) {
|
|
9
|
-
let out = "";
|
|
10
|
-
for (const ch of text) out += this.pushChar(ch);
|
|
11
|
-
return out;
|
|
12
|
-
}
|
|
13
|
-
flush() {
|
|
14
|
-
const out = this.pending;
|
|
15
|
-
this.pending = "";
|
|
16
|
-
return out;
|
|
17
|
-
}
|
|
18
|
-
pushChar(ch) {
|
|
19
|
-
let candidate = this.pending + ch;
|
|
20
|
-
let out = "";
|
|
21
|
-
while (candidate && !(candidate.startsWith('"') && KEY.startsWith(withoutZeroWidth(candidate)))) {
|
|
22
|
-
const next = candidate.indexOf('"', 1);
|
|
23
|
-
if (next < 0) {
|
|
24
|
-
out += candidate;
|
|
25
|
-
candidate = "";
|
|
26
|
-
} else {
|
|
27
|
-
out += candidate.slice(0, next);
|
|
28
|
-
candidate = candidate.slice(next);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (withoutZeroWidth(candidate) === KEY) {
|
|
32
|
-
this.pending = "";
|
|
33
|
-
return out + KEY;
|
|
34
|
-
}
|
|
35
|
-
this.pending = candidate;
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
class MarkdownEscapedKindJson {
|
|
40
|
-
/** The zero-width key spelling is read first (round 8) — one transform, both hosts. */
|
|
41
|
-
zeroWidth = new ZeroWidthKindKey();
|
|
42
|
-
/** Bytes that may still become the escaped key — held, never shown. */
|
|
43
|
-
pending = "";
|
|
44
|
-
/** Inside the object holding a rewritten key: bracket depth (0 = outside). */
|
|
45
|
-
depth = 0;
|
|
46
|
-
inString = false;
|
|
47
|
-
/** A held backslash inside the object (its fate depends on the next char). */
|
|
48
|
-
escape = false;
|
|
49
|
-
push(text) {
|
|
50
|
-
return this.pushUnescape(this.zeroWidth.push(text));
|
|
51
|
-
}
|
|
52
|
-
pushUnescape(text) {
|
|
53
|
-
let out = "";
|
|
54
|
-
for (const ch of text) out += this.pushChar(ch);
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
flush() {
|
|
58
|
-
const held = this.pushUnescape(this.zeroWidth.flush());
|
|
59
|
-
const out = held + this.pending + (this.escape ? "\\" : "");
|
|
60
|
-
this.pending = "";
|
|
61
|
-
this.escape = false;
|
|
62
|
-
this.depth = 0;
|
|
63
|
-
this.inString = false;
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
pushChar(ch) {
|
|
67
|
-
if (this.depth > 0) return this.insideObject(ch);
|
|
68
|
-
let candidate = this.pending + ch;
|
|
69
|
-
let out = "";
|
|
70
|
-
while (candidate && !(candidate.startsWith('"') && TARGET.startsWith(withoutZeroWidth(candidate)))) {
|
|
71
|
-
const next = candidate.indexOf('"', 1);
|
|
72
|
-
if (next < 0) {
|
|
73
|
-
out += candidate;
|
|
74
|
-
candidate = "";
|
|
75
|
-
} else {
|
|
76
|
-
out += candidate.slice(0, next);
|
|
77
|
-
candidate = candidate.slice(next);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (withoutZeroWidth(candidate) === TARGET) {
|
|
81
|
-
this.pending = "";
|
|
82
|
-
this.depth = 1;
|
|
83
|
-
this.inString = false;
|
|
84
|
-
return out + KEY;
|
|
85
|
-
}
|
|
86
|
-
this.pending = candidate;
|
|
87
|
-
return out;
|
|
88
|
-
}
|
|
89
|
-
insideObject(ch) {
|
|
90
|
-
if (this.escape) {
|
|
91
|
-
this.escape = false;
|
|
92
|
-
return ch === "_" ? "_" : `\\${ch}`;
|
|
93
|
-
}
|
|
94
|
-
if (ch === "\\") {
|
|
95
|
-
this.escape = true;
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
98
|
-
if (this.inString) {
|
|
99
|
-
if (ch === '"') this.inString = false;
|
|
100
|
-
return ch;
|
|
101
|
-
}
|
|
102
|
-
if (ch === '"') this.inString = true;
|
|
103
|
-
else if (ch === "{" || ch === "[") this.depth += 1;
|
|
104
|
-
else if (ch === "}" || ch === "]") this.depth -= 1;
|
|
105
|
-
return ch;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function unescapeMarkdownKindJson(source) {
|
|
109
|
-
if (!source.includes(TARGET) && !HAS_ZERO_WIDTH.test(source)) return source;
|
|
110
|
-
const transform = new MarkdownEscapedKindJson();
|
|
111
|
-
return transform.push(source) + transform.flush();
|
|
112
|
-
}
|
|
113
|
-
function decodeDoubleEncodedKindText(source) {
|
|
114
|
-
const trimmed = source.trim();
|
|
115
|
-
if (!trimmed.startsWith('"') || !trimmed.endsWith('"') || !trimmed.includes('\\"__kind\\"')) return source;
|
|
116
|
-
try {
|
|
117
|
-
const inner = JSON.parse(trimmed);
|
|
118
|
-
return typeof inner === "string" && isKindJsonText(inner.trim()) ? inner.trim() : source;
|
|
119
|
-
} catch {
|
|
120
|
-
return source;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
export {
|
|
124
|
-
MarkdownEscapedKindJson,
|
|
125
|
-
ZeroWidthKindKey,
|
|
126
|
-
decodeDoubleEncodedKindText,
|
|
127
|
-
unescapeMarkdownKindJson
|
|
128
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A JSON REGION INSIDE A BLOCKQUOTE leaves the quote (V1, the never-raw law).
|
|
3
|
-
*
|
|
4
|
-
* A model sometimes quotes its whole answer — `> ```json` / `> {"__kind":…}` —
|
|
5
|
-
* and every line then starts with `>`. Neither host could see the region: the
|
|
6
|
-
* live accumulator classifies a `>` line as prose, the static splitter keeps
|
|
7
|
-
* the quote as one text block, so the kind was drawn as raw JSON inside a
|
|
8
|
-
* blockquote in chat, on reload, on public shared pages and in tool results.
|
|
9
|
-
*
|
|
10
|
-
* THE RULE: a blockquote holds prose, never a data region. A JSON-family
|
|
11
|
-
* fence (```json / ```jsonc / ```json5 / unlabelled, any case, ``` or ~~~)
|
|
12
|
-
* opening on a quoted line, or a quoted line whose JSON could be a kind (the
|
|
13
|
-
* first-key rule, `jsonKindSignal`), is LIFTED: its quote prefix is removed
|
|
14
|
-
* from every line of the region, so the region becomes an ordinary top-level
|
|
15
|
-
* fence / bare JSON object between two quotes. The quote before it renders as
|
|
16
|
-
* a quote, the region as its kind, the quote after it as a new quote. Quoted
|
|
17
|
-
* text that is not a JSON region is never touched — and a fence of another
|
|
18
|
-
* language (```ts, ```xml, ```markdown, quoted or not) is the model quoting
|
|
19
|
-
* SOURCE, so nothing inside it is lifted (the owner's ruling, 2026-09-30).
|
|
20
|
-
*
|
|
21
|
-
* ONE transform, both hosts: the live accumulator pushes every delta through
|
|
22
|
-
* a `QuotedKindLift` before its line machine; the static splitter runs
|
|
23
|
-
* `liftQuotedKindRegions` on the whole text first. The transform is
|
|
24
|
-
* chunk-invariant (decisions are taken only at points every chunking reaches
|
|
25
|
-
* with the same bytes), so live = reload byte for byte.
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* The streaming lift. `push` returns the bytes that are safe to hand on now;
|
|
29
|
-
* a line whose fate is not known yet (a quoted line that may open a JSON
|
|
30
|
-
* region) is held — never shown raw — until it is. `flush` releases what is
|
|
31
|
-
* held, as written, at the end of the stream (or a hard boundary).
|
|
32
|
-
*/
|
|
33
|
-
export declare class QuotedKindLift {
|
|
34
|
-
private mode;
|
|
35
|
-
/** The current line's bytes not yet handed on (held while undecided). */
|
|
36
|
-
private held;
|
|
37
|
-
/** The current line's bytes so far (handed on or not), as received. */
|
|
38
|
-
private line;
|
|
39
|
-
/** Whether the rest of the current line passes straight through. */
|
|
40
|
-
private passing;
|
|
41
|
-
/** Prefix stripped from the current line's handed-on bytes (passing lines). */
|
|
42
|
-
private stripping;
|
|
43
|
-
push(text: string): string;
|
|
44
|
-
flush(): string;
|
|
45
|
-
private pushChar;
|
|
46
|
-
/** Try to decide the current line before it ends; returns bytes released. */
|
|
47
|
-
private decideMidLine;
|
|
48
|
-
/** Hand on `bytes` and let the rest of the line pass straight through. */
|
|
49
|
-
private release;
|
|
50
|
-
private endLine;
|
|
51
|
-
/**
|
|
52
|
-
* A complete line met with no region open. `held` is what was not handed
|
|
53
|
-
* on yet: the whole line when it was held to the end, otherwise "" (it was
|
|
54
|
-
* released mid-line as prose or as an unquoted line).
|
|
55
|
-
*/
|
|
56
|
-
private lineFromNone;
|
|
57
|
-
/** A held quoted JSON start, re-judged after each complete line. */
|
|
58
|
-
private settleCandidate;
|
|
59
|
-
}
|
|
60
|
-
/** The whole-text form, for the static splitter (identical bytes to any streaming of `source`). */
|
|
61
|
-
export declare function liftQuotedKindRegions(source: string): string;
|