@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.
Files changed (123) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
  3. package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
  4. package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
  5. package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
  6. package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
  7. package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
  8. package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
  9. package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
  10. package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
  11. package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
  12. package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
  13. package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
  14. package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
  15. package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
  16. package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
  17. package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
  18. package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
  19. package/dist/agents/conversation-export/conversation-transfer.js +1 -1
  20. package/dist/agents/conversation-export/document-markdown.js +1 -1
  21. package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
  22. package/dist/agents/hooks/useModelControls.js +1 -1
  23. package/dist/agents/redux/agent-definition/converters.js +1 -1
  24. package/dist/agents/redux/agent-definition/slice.js +1 -1
  25. package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
  26. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +1 -1
  27. package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
  28. package/dist/agents/redux/execution-system/thunks/process-stream.js +1 -1
  29. package/dist/agents/redux/execution-system/thunks/resolve-base-url.js +17 -0
  30. package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
  31. package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
  32. package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +14 -2
  33. package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
  34. package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
  35. package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
  36. package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
  37. package/dist/agents/routing/confidential-routing.d.ts +52 -0
  38. package/dist/agents/routing/confidential-routing.js +38 -0
  39. package/dist/agents/utils/canvasItemContext.js +1 -1
  40. package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
  41. package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
  42. package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
  43. package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
  44. package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
  45. package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
  46. package/dist/cx-conversation/AssistantMessage.js +1 -1
  47. package/dist/host/configure.js +4 -1
  48. package/dist/host/contract.d.ts +14 -1
  49. package/dist/host/index.d.ts +1 -0
  50. package/dist/host/index.js +1 -0
  51. package/dist/mandates/llm-params.d.ts +1 -1
  52. package/dist/mandates/output-contract.js +1 -1
  53. package/dist/mandates/service.d.ts +1 -1
  54. package/dist/mandates/service.js +1 -1
  55. package/dist/mandates/test-run-core.js +1 -1
  56. package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
  57. package/dist/surfaces/services/surface-config.service.js +1 -1
  58. package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
  59. package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
  60. package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
  61. package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +1 -1
  62. package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
  63. package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
  64. package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
  65. package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
  66. package/dist/tool-call-visualization/result-fields/shape.js +1 -1
  67. package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
  68. package/dist/tool-call-visualization/utils/human-copy.js +3 -3
  69. package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
  70. package/dist/ui/mandates-storage/provision-shapes.js +1 -1
  71. package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
  72. package/dist/ui/treatment-shape.d.ts +1 -1
  73. package/dist/ui/treatment-shape.js +1 -1
  74. package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
  75. package/package.json +26 -9
  76. package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
  77. package/dist/agents/hooks/useWarmAgent.js +0 -25
  78. package/dist/agents/utils/artifactId.d.ts +0 -34
  79. package/dist/agents/utils/artifactId.js +0 -15
  80. package/dist/ui/ShimmerText.d.ts +0 -5
  81. package/dist/ui/ShimmerText.js +0 -21
  82. package/dist/ui/addressWithoutNavigating.d.ts +0 -68
  83. package/dist/ui/addressWithoutNavigating.js +0 -60
  84. package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
  85. package/dist/utils/content-ir/decision-answers-ids.js +0 -6
  86. package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
  87. package/dist/utils/content-ir/envelope-parity.js +0 -32
  88. package/dist/utils/content-ir/instance-title.d.ts +0 -26
  89. package/dist/utils/content-ir/instance-title.js +0 -39
  90. package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
  91. package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
  92. package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
  93. package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
  94. package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
  95. package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
  96. package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
  97. package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
  98. package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
  99. package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
  100. package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
  101. package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
  102. package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
  103. package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
  104. package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
  105. package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
  106. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
  107. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
  108. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
  109. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
  110. package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
  111. package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
  112. package/dist/utils/inline-decision/decision-options.d.ts +0 -21
  113. package/dist/utils/inline-decision/decision-options.js +0 -32
  114. package/dist/utils/json.d.ts +0 -63
  115. package/dist/utils/json.js +0 -22
  116. package/dist/utils/markdown/plain-title.d.ts +0 -25
  117. package/dist/utils/markdown/plain-title.js +0 -51
  118. package/dist/utils/rendered-find.d.ts +0 -9
  119. package/dist/utils/rendered-find.js +0 -68
  120. package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
  121. package/dist/utils/rich-editor/core/caret-context.js +0 -73
  122. package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
  123. 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;