@openclaw/imessage 2026.9.8 → 2026.10.1-beta.1

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 (33) hide show
  1. package/dist/.setup/{accounts-nzcU0cq2.mjs → accounts-evTpvJMv.mjs} +6 -12
  2. package/dist/.setup/{group-policy-90pw4tyG.mjs → actions-DJqbUmbN.mjs} +103 -269
  3. package/dist/.setup/{actions.runtime-DdBN29zj.mjs → actions.runtime-DL5i-Y4J.mjs} +49 -84
  4. package/dist/.setup/{api-B9Qx21G-.mjs → api-SAVMfMoY.mjs} +5 -5
  5. package/dist/.setup/{api-D2S6FhGw.mjs → api-oq_MryN7.mjs} +6 -6
  6. package/dist/.setup/{approval-handler.runtime-DKSq0DWq.mjs → approval-handler.runtime-Bj2NGNXF.mjs} +26 -62
  7. package/dist/.setup/{approval-reactions-Bp33xc83.mjs → approval-reactions-L9Ul7PAu.mjs} +3 -2
  8. package/dist/.setup/{channel-9fyvuN_p.mjs → channel-CL73qdMG.mjs} +134 -60
  9. package/dist/.setup/{monitor-fv9nRuTH.mjs → channel.runtime-DY7JJ0_9.mjs} +202 -206
  10. package/dist/.setup/{sanitize-outbound-3MLljgtY.mjs → client-12apu4Ip.mjs} +26 -635
  11. package/dist/.setup/{config-schema-4Q3_EqYt.mjs → config-schema-DXwa_3V5.mjs} +1 -1
  12. package/dist/.setup/{media-contract-CagwSMvo.mjs → media-contract-DjQGZlfl.mjs} +1 -1
  13. package/dist/.setup/{message-tool-api-D032l82H.mjs → message-tool-api-CqfRxLlT.mjs} +2 -2
  14. package/dist/.setup/{question-reactions-Cy-dmU_j.mjs → question-reactions-DJV-TnA8.mjs} +2 -1
  15. package/dist/.setup/{reaction-context-Br-vAJFp.mjs → reaction-context-By7kUMBC.mjs} +2 -7
  16. package/dist/.setup/{remote-file-DRPCaVmg.mjs → remote-file-BU7pA-Zi.mjs} +7 -11
  17. package/dist/.setup/sanitize-outbound-C0x4BPky.mjs +564 -0
  18. package/dist/.setup/{send-Cu9nl757.mjs → send-pkYwL6Rt.mjs} +56 -103
  19. package/dist/.setup/{setup-core-8MDdYHj5.mjs → setup-core-DqOBe-n0.mjs} +1 -1
  20. package/dist/.setup/{setup-surface-tGXFDdWh.mjs → setup-surface-zKhIinwY.mjs} +2 -2
  21. package/dist/api.js +6 -6
  22. package/dist/channel-config-api.js +1 -1
  23. package/dist/channel-plugin-api.js +1 -1
  24. package/dist/contract-api.js +1 -1
  25. package/dist/doctor-contract-api.js +29 -21
  26. package/dist/media-contract-api.js +1 -1
  27. package/dist/message-tool-api.js +1 -1
  28. package/dist/runtime-api.js +2 -24
  29. package/dist/setup-entry.js +1 -1
  30. package/openclaw.plugin.json +1 -1
  31. package/package.json +4 -4
  32. package/dist/.setup/channel.runtime-n-MPKL8k.mjs +0 -92
  33. package/dist/.setup/state-migrations-B-Q7bO0Y.mjs +0 -47
@@ -119,4 +119,4 @@ const IMessageConfigSchema = IMessageAccountSchemaBase.extend({
119
119
  });
120
120
  const IMessageChannelConfigSchema = buildChannelConfigSchema(IMessageConfigSchema, { uiHints: iMessageChannelConfigUiHints });
121
121
  //#endregion
122
- export { IMessageConfigSchema as n, IMessageChannelConfigSchema as t };
122
+ export { IMessageChannelConfigSchema as t };
@@ -1,4 +1,4 @@
1
- import { o as resolveIMessageAccount } from "./accounts-nzcU0cq2.mjs";
1
+ import { o as resolveIMessageAccount } from "./accounts-evTpvJMv.mjs";
2
2
  import { mergeInboundPathRoots } from "openclaw/plugin-sdk/channel-inbound";
3
3
  //#region extensions/imessage/src/media-contract.ts
4
4
  const DEFAULT_IMESSAGE_ATTACHMENT_ROOTS = ["/Users/*/Library/Messages/Attachments"];
@@ -1,5 +1,5 @@
1
1
  import { n as inferIMessageTargetChatType } from "./targets-BicBiYsM.mjs";
2
- import { c as getCachedIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-nzcU0cq2.mjs";
2
+ import { c as getCachedIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-evTpvJMv.mjs";
3
3
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
4
4
  import { Type } from "typebox";
5
5
  import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
@@ -98,7 +98,7 @@ function describeIMessageMessageTool({ cfg, accountId, chatType, currentChannelI
98
98
  const actions = /* @__PURE__ */ new Set();
99
99
  for (const action of IMESSAGE_ACTION_NAMES) {
100
100
  const spec = IMESSAGE_ACTIONS[action];
101
- if (!spec?.gate || !gate(spec.gate)) continue;
101
+ if (!gate(spec.gate)) continue;
102
102
  if (privateApiStatus?.available === false) continue;
103
103
  if (action === "edit" && privateApiStatus?.selectors && !privateApiStatus.selectors.editMessage && !privateApiStatus.selectors.editMessageItem) continue;
104
104
  if (action === "unsend" && privateApiStatus?.selectors?.retractMessagePart !== true) continue;
@@ -1,5 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BBjsoOtd.mjs";
2
- import { n as normalizeIMessageGuid, t as resolveIMessageReactionContext } from "./reaction-context-Br-vAJFp.mjs";
2
+ import { i as normalizeIMessageGuid } from "./actions-DJqbUmbN.mjs";
3
+ import { t as resolveIMessageReactionContext } from "./reaction-context-By7kUMBC.mjs";
3
4
  import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
4
5
  //#region extensions/imessage/src/question-reactions.ts
5
6
  var question_reactions_exports = /* @__PURE__ */ __exportAll({
@@ -1,9 +1,4 @@
1
- //#region extensions/imessage/src/message-guid.ts
2
- /** Strip the `p:<n>/` part prefix Messages puts on some GUIDs so keys match. */
3
- function normalizeIMessageGuid(value) {
4
- return value.trim().replace(/^p:\d+\//iu, "");
5
- }
6
- //#endregion
1
+ import { i as normalizeIMessageGuid } from "./actions-DJqbUmbN.mjs";
7
2
  //#region extensions/imessage/src/monitor/reaction-context.ts
8
3
  const TAPBACK_TEXT_PATTERNS = [
9
4
  {
@@ -108,4 +103,4 @@ function resolveIMessageReactionContext(message, bodyText) {
108
103
  return resolveTapbackTextContext(bodyText);
109
104
  }
110
105
  //#endregion
111
- export { normalizeIMessageGuid as n, resolveIMessageReactionContext as t };
106
+ export { resolveIMessageReactionContext as t };
@@ -1,13 +1,13 @@
1
- import { p as resolveLocalIMessageChatDbPath } from "./accounts-nzcU0cq2.mjs";
2
- import { T as isIMessageEmailChatIdentifier, m as resolveIMessageMessageId, p as resolveIMessageCachedResourceBinding, w as chatContextFromIMessageTarget } from "./sanitize-outbound-3MLljgtY.mjs";
1
+ import { p as resolveLocalIMessageChatDbPath } from "./accounts-evTpvJMv.mjs";
2
+ import { b as chatContextFromIMessageTarget, l as resolveIMessageCachedResourceBinding, u as resolveIMessageMessageId, x as isIMessageEmailChatIdentifier } from "./client-12apu4Ip.mjs";
3
3
  import path from "node:path";
4
4
  import { normalizeScpRemoteHost } from "openclaw/plugin-sdk/host-runtime";
5
5
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
6
6
  import { createSubsystemLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
7
  import { randomUUID } from "node:crypto";
8
8
  import { resolveRuntimeWorkerUrl, runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
- import { openSqliteWorkerStore } from "openclaw/plugin-sdk/sqlite-runtime";
10
9
  import { sanitizeTempFileName } from "openclaw/plugin-sdk/temp-path";
10
+ import { openSqliteWorkerStore } from "openclaw/plugin-sdk/sqlite-runtime";
11
11
  //#region extensions/imessage/src/chat-db.ts
12
12
  function openIMessageChatDbReader(databasePath) {
13
13
  return openSqliteWorkerStore({
@@ -205,11 +205,6 @@ const SSH_OPTIONS = [
205
205
  ];
206
206
  const CLEANUP_TIMEOUT_MS = 1e4;
207
207
  const log = createSubsystemLogger("channels/imessage");
208
- function commandError(label, result) {
209
- if (result.code === 0 && result.termination === "exit") return;
210
- const detail = result.stderr.trim() || result.stdout.trim();
211
- return /* @__PURE__ */ new Error(`${label} failed (${result.termination}${result.code === null ? "" : `, code ${result.code}`})${detail ? `: ${detail}` : ""}`);
212
- }
213
208
  async function runChecked(run, label, argv, options) {
214
209
  const result = await run(argv, {
215
210
  killProcessTree: true,
@@ -223,9 +218,10 @@ async function runChecked(run, label, argv, options) {
223
218
  },
224
219
  ...options
225
220
  });
226
- const error = commandError(label, result);
227
- if (error) throw error;
228
- return result;
221
+ if (result.code !== 0 || result.termination !== "exit") {
222
+ const detail = result.stderr.trim() || result.stdout.trim();
223
+ throw new Error(`${label} failed (${result.termination}${result.code === null ? "" : `, code ${result.code}`})${detail ? `: ${detail}` : ""}`);
224
+ }
229
225
  }
230
226
  function requireToken(createToken) {
231
227
  const token = createToken().replaceAll("-", "").toLowerCase();
@@ -0,0 +1,564 @@
1
+ import { FormatCapabilityProfile, findCodeRegions, isInsideCode, markdownToIR, renderMarkdownWithAttributedRanges, sanitizeAssistantVisibleText, stripMarkdown, tokenizeHtmlTags } from "openclaw/plugin-sdk/text-chunking";
2
+ //#region extensions/imessage/src/markdown-format.ts
3
+ const IMESSAGE_FORMAT_PROFILE = FormatCapabilityProfile.define({
4
+ mechanism: "ranges",
5
+ constructs: {
6
+ spoiler: "strip",
7
+ codeInline: "fallback",
8
+ codeBlock: "fallback",
9
+ codeLanguage: "strip",
10
+ linkLabel: "fallback",
11
+ heading: "fallback",
12
+ bulletList: "fallback",
13
+ orderedList: "fallback",
14
+ taskList: "fallback",
15
+ table: "fallback",
16
+ blockquote: "fallback",
17
+ image: "fallback",
18
+ mention: "strip"
19
+ },
20
+ chunk: {
21
+ limit: 4e3,
22
+ unit: "utf16"
23
+ }
24
+ });
25
+ const IMESSAGE_CODE_PROFILE = FormatCapabilityProfile.define({
26
+ ...IMESSAGE_FORMAT_PROFILE,
27
+ constructs: {
28
+ ...IMESSAGE_FORMAT_PROFILE.constructs,
29
+ codeInline: "native"
30
+ }
31
+ });
32
+ const IMESSAGE_STYLE_MAP = {
33
+ bold: "bold",
34
+ italic: "italic",
35
+ underline: "underline",
36
+ strikethrough: "strikethrough"
37
+ };
38
+ function codeDelimiter(content) {
39
+ const longestRun = Math.max(0, ...[...content.matchAll(/`+/gu)].map((match) => match[0].length));
40
+ return "`".repeat(longestRun + 1);
41
+ }
42
+ function restoreCodeMarkers(text, ranges, codeRanges) {
43
+ if (codeRanges.length === 0) return {
44
+ text,
45
+ ranges
46
+ };
47
+ let rendered = "";
48
+ let cursor = 0;
49
+ const edits = codeRanges.map((range) => {
50
+ const end = range.start + range.length;
51
+ const content = text.slice(range.start, end);
52
+ const marker = codeDelimiter(content);
53
+ const padding = content.startsWith("`") || content.endsWith("`") ? " " : "";
54
+ rendered += text.slice(cursor, range.start) + `${marker}${padding}${content}${padding}${marker}`;
55
+ cursor = end;
56
+ return {
57
+ end,
58
+ shift: rendered.length - end
59
+ };
60
+ });
61
+ rendered += text.slice(cursor);
62
+ const mapOffset = (offset) => {
63
+ let low = 0;
64
+ let high = edits.length;
65
+ while (low < high) {
66
+ const middle = low + Math.floor((high - low) / 2);
67
+ const edit = edits[middle];
68
+ if (edit && edit.end <= offset) low = middle + 1;
69
+ else high = middle;
70
+ }
71
+ return offset + (edits[low - 1]?.shift ?? 0);
72
+ };
73
+ return {
74
+ text: rendered,
75
+ ranges: ranges.map((range) => {
76
+ const start = mapOffset(range.start);
77
+ return {
78
+ ...range,
79
+ start,
80
+ length: mapOffset(range.start + range.length) - start
81
+ };
82
+ })
83
+ };
84
+ }
85
+ function extractMarkdownFormatRuns(input) {
86
+ const ir = markdownToIR(input, {
87
+ autolink: false,
88
+ enableHtmlUnderline: true,
89
+ headingStyle: "rich",
90
+ linkify: false,
91
+ preserveDunderIdentifiers: true,
92
+ preserveSourceBlockSpacing: true
93
+ });
94
+ const rendered = renderMarkdownWithAttributedRanges(ir, { styleMap: IMESSAGE_STYLE_MAP }, IMESSAGE_FORMAT_PROFILE);
95
+ const codeRanges = ir.styles.some((span) => span.style === "code") ? renderMarkdownWithAttributedRanges(ir, { styleMap: { code: "code" } }, IMESSAGE_CODE_PROFILE).ranges : [];
96
+ return restoreCodeMarkers(rendered.text, rendered.ranges.map(({ start, length, style }) => ({
97
+ start,
98
+ length,
99
+ styles: [style]
100
+ })), codeRanges);
101
+ }
102
+ //#endregion
103
+ //#region extensions/imessage/src/monitor/sanitize-outbound.ts
104
+ /**
105
+ * Patterns that indicate assistant-internal metadata leaked into text.
106
+ * These must never reach a user-facing channel.
107
+ */
108
+ const INTERNAL_SEPARATOR_RE = /(?:#\+){2,}#?/g;
109
+ const ASSISTANT_ROLE_MARKER_RE = /\bassistant\s+to\s*=\s*\w+/gi;
110
+ const ROLE_TURN_MARKER_RE = /^[ \t]*(?:>[ \t]*)*(?:user|system|assistant)[ \t]*:[ \t]*(?=\r?$)/gim;
111
+ const FENCED_ROLE_MARKER_RE = /^[ \t]*(user|system|assistant)[ \t]*:[ \t]*(?=\r?$)/gim;
112
+ const FINAL_PRIVATE_MARKER_RE = /(?:#\+){2,}#?|\bassistant\s+to\s*=\s*\w+|^[ \t]*(?:user|system|assistant)[ \t]*:[ \t]*(?=\r?$)/gim;
113
+ const PRIVATE_USE_START = 57344;
114
+ const PRIVATE_USE_END = 63743;
115
+ const MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES = 32;
116
+ const PLAIN_TEXT_HTML_TAG_RE = /<\/?[a-z][a-z0-9_.:-]*(?=[\s/>])[^>]*>/gi;
117
+ const PRIVATE_PROVIDER_TAG_NAMES = [
118
+ "think",
119
+ "thinking",
120
+ "thought",
121
+ "reasoning",
122
+ "antthinking",
123
+ "antml:think",
124
+ "antml:thinking",
125
+ "antml:thought",
126
+ "antml:reasoning",
127
+ "mm:think",
128
+ "mm:thinking",
129
+ "mm:thought",
130
+ "mm:reasoning",
131
+ "relevant_memories",
132
+ "relevant-memories",
133
+ "tool_call",
134
+ "tool_result",
135
+ "function_call",
136
+ "function_calls",
137
+ "function_response",
138
+ "function",
139
+ "tool_calls",
140
+ "antml:invoke",
141
+ "antml:parameter",
142
+ "invoke",
143
+ "parameter"
144
+ ];
145
+ const PRIVATE_PROVIDER_TAG_NAME_SET = new Set(PRIVATE_PROVIDER_TAG_NAMES);
146
+ const OPAQUE_PRIVATE_RUNTIME_CONTEXT_BLOCK_RE = /^<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>(?:(?!<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>)[\s\S])*<<<END_OPENCLAW_INTERNAL_CONTEXT>>>/;
147
+ const PRIVATE_RUNTIME_CONTEXT_BLOCK_RE = /<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>(?:(?!<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>)[\s\S])*<<<END_OPENCLAW_INTERNAL_CONTEXT>>>/g;
148
+ const NESTED_PRIVATE_MARKUP_START_RE = /<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>|<\s*\/?\s*[A-Za-z][A-Za-z0-9_.:-]*(?=\s|\/?>)/g;
149
+ const OUTER_PRIVATE_TAG_FRAGMENT_RE = /^\s*\/?\s*([a-z][a-z0-9_.:-]*)?$/i;
150
+ const OPAQUE_PRIVATE_MARKUP_BLOCK_RE = /^<\s*(system-reminder|previous_response|details|summary)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/i;
151
+ const REMOVABLE_PRIVATE_MARKUP_TAG_RE = /^<\s*\/?\s*[a-z][a-z0-9_.:-]*(?=\s|\/?>)[^>]*>/i;
152
+ const PRIVATE_PROVIDER_TAG_FRAGMENT_RE = /^[a-z0-9_.:-]*/i;
153
+ const PRIVATE_RUNTIME_SCAFFOLDING_TAG_TOKEN_RE = /<\s*(\/?)\s*(system-reminder|previous_response)\b/gi;
154
+ function assertSafeIMessageOutboundMarkup(text) {
155
+ for (const nested of text.matchAll(NESTED_PRIVATE_MARKUP_START_RE)) {
156
+ const nestedStart = nested.index ?? 0;
157
+ const outerStart = text.lastIndexOf("<", nestedStart - 1);
158
+ if (outerStart < 0) continue;
159
+ const outer = OUTER_PRIVATE_TAG_FRAGMENT_RE.exec(text.slice(outerStart + 1, nestedStart));
160
+ if (!outer) continue;
161
+ let candidate = (outer[1] ?? "").toLowerCase();
162
+ if (!PRIVATE_PROVIDER_TAG_NAMES.some((name) => name.startsWith(candidate))) continue;
163
+ let cursor = nestedStart;
164
+ for (let depth = 0; depth < MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES; depth += 1) {
165
+ const remaining = text.slice(cursor);
166
+ const removed = OPAQUE_PRIVATE_RUNTIME_CONTEXT_BLOCK_RE.exec(remaining)?.[0] ?? OPAQUE_PRIVATE_MARKUP_BLOCK_RE.exec(remaining)?.[0] ?? REMOVABLE_PRIVATE_MARKUP_TAG_RE.exec(remaining)?.[0];
167
+ if (!removed) break;
168
+ cursor += removed.length;
169
+ const suffix = PRIVATE_PROVIDER_TAG_FRAGMENT_RE.exec(text.slice(cursor))?.[0] ?? "";
170
+ candidate += suffix.toLowerCase();
171
+ cursor += suffix.length;
172
+ if (PRIVATE_PROVIDER_TAG_NAME_SET.has(candidate) && /[\s/>]/.test(text.charAt(cursor))) throw new Error("iMessage outbound ambiguous nested HTML is not allowed");
173
+ if (!PRIVATE_PROVIDER_TAG_NAMES.some((name) => name.startsWith(candidate))) break;
174
+ if (depth + 1 >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES && (OPAQUE_PRIVATE_RUNTIME_CONTEXT_BLOCK_RE.test(text.slice(cursor)) || REMOVABLE_PRIVATE_MARKUP_TAG_RE.test(text.slice(cursor)))) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
175
+ }
176
+ }
177
+ }
178
+ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
179
+ const openBlocks = [];
180
+ const completedBlocks = [];
181
+ const codeRegions = findCodeRegions(text);
182
+ const parsedHtmlTags = [...tokenizeHtmlTags(text)];
183
+ const htmlTags = parsedHtmlTags.map(({ start, end }) => ({
184
+ start,
185
+ end,
186
+ terminated: true
187
+ }));
188
+ const rawTextHtml = [];
189
+ const rawTextTagNames = /* @__PURE__ */ new Set([
190
+ "script",
191
+ "style",
192
+ "textarea",
193
+ "title",
194
+ "xmp",
195
+ "iframe",
196
+ "noembed",
197
+ "noframes",
198
+ "noscript",
199
+ "plaintext"
200
+ ]);
201
+ for (const [index, opener] of parsedHtmlTags.entries()) {
202
+ if (opener.closing || !rawTextTagNames.has(opener.name)) continue;
203
+ const closing = opener.name === "plaintext" ? void 0 : parsedHtmlTags.slice(index + 1).find((candidate) => {
204
+ return candidate.closing && candidate.name === opener.name;
205
+ });
206
+ rawTextHtml.push({
207
+ start: opener.start,
208
+ end: closing?.end ?? text.length,
209
+ terminated: Boolean(closing)
210
+ });
211
+ if (rawTextHtml.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
212
+ }
213
+ const opaqueHtml = [];
214
+ for (let cursor = 0; cursor < text.length;) {
215
+ const start = text.indexOf("<", cursor);
216
+ if (start < 0) break;
217
+ const enclosingTag = htmlTags.find((tag) => tag.start < start && start < tag.end);
218
+ if (enclosingTag) {
219
+ cursor = enclosingTag.end;
220
+ continue;
221
+ }
222
+ let end = -1;
223
+ let delimiterLength = 0;
224
+ if (text.startsWith("<!--", start)) {
225
+ end = text.indexOf("-->", start + 4);
226
+ delimiterLength = 3;
227
+ } else if (/^<!\[CDATA\[/i.test(text.slice(start, start + 9))) {
228
+ end = text.indexOf("]]>", start + 9);
229
+ delimiterLength = 3;
230
+ } else if (text.startsWith("<?", start)) {
231
+ let quote;
232
+ for (let index = start + 2; index < text.length - 1; index += 1) {
233
+ const character = text[index];
234
+ if (quote) {
235
+ if (character === quote) quote = void 0;
236
+ } else if (character === "'" || character === "\"") quote = character;
237
+ else if (character === "?" && text[index + 1] === ">") {
238
+ end = index;
239
+ delimiterLength = 2;
240
+ break;
241
+ }
242
+ }
243
+ } else if (/^<![a-z][a-z0-9_.:-]*\b/i.test(text.slice(start))) {
244
+ let quote;
245
+ let bracketDepth = 0;
246
+ for (let index = start + 2; index < text.length; index += 1) {
247
+ const character = text[index];
248
+ if (quote) {
249
+ if (character === quote) quote = void 0;
250
+ } else if (character === "'" || character === "\"") quote = character;
251
+ else if (character === "[") bracketDepth += 1;
252
+ else if (character === "]" && bracketDepth > 0) bracketDepth -= 1;
253
+ else if (character === ">" && bracketDepth === 0) {
254
+ end = index;
255
+ delimiterLength = 1;
256
+ break;
257
+ }
258
+ }
259
+ } else if (text.startsWith("<!", start) || text.startsWith("</", start) && start + 2 < text.length && !/[A-Za-z>]/.test(text[start + 2] ?? "")) {
260
+ end = text.indexOf(">", start + 2);
261
+ delimiterLength = 1;
262
+ } else {
263
+ cursor = start + 1;
264
+ continue;
265
+ }
266
+ if (opaqueHtml.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
267
+ const terminated = end >= 0;
268
+ const regionEnd = terminated ? end + delimiterLength : text.length;
269
+ opaqueHtml.push({
270
+ start,
271
+ end: regionEnd,
272
+ terminated
273
+ });
274
+ cursor = regionEnd;
275
+ }
276
+ htmlTags.push(...opaqueHtml);
277
+ htmlTags.sort((left, right) => left.start - right.start || right.end - left.end);
278
+ let htmlTagIndex = 0;
279
+ let scannedTags = 0;
280
+ let scannedThrough = 0;
281
+ for (const token of text.matchAll(PRIVATE_RUNTIME_SCAFFOLDING_TAG_TOKEN_RE)) {
282
+ const start = token.index ?? 0;
283
+ if (start < scannedThrough) continue;
284
+ while ((htmlTags[htmlTagIndex]?.end ?? 0) <= start && htmlTagIndex < htmlTags.length) htmlTagIndex += 1;
285
+ const enclosingHtmlTag = htmlTags[htmlTagIndex];
286
+ if (enclosingHtmlTag && enclosingHtmlTag.start < start && start < enclosingHtmlTag.end) continue;
287
+ const codeRegion = codeRegions.find((region) => isInsideCode(start, [region]));
288
+ const enclosingBlock = openBlocks.at(-1);
289
+ const enclosingRawText = rawTextHtml.find((region) => {
290
+ return region.start < start && start < region.end;
291
+ });
292
+ if (enclosingBlock && enclosingRawText && enclosingBlock.start < enclosingRawText.start) {
293
+ enclosingBlock.sawNested = true;
294
+ continue;
295
+ }
296
+ if (enclosingBlock && codeRegion && enclosingBlock.codeRegion !== codeRegion) {
297
+ enclosingBlock.sawNested = true;
298
+ continue;
299
+ }
300
+ if (enclosingBlock?.codeRegion && enclosingBlock.codeRegion !== codeRegion) throw new Error("iMessage outbound runtime scaffolding is malformed");
301
+ if (++scannedTags > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
302
+ let quote;
303
+ let end = start + token[0].length;
304
+ for (; end < text.length; end += 1) {
305
+ if (end - start > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
306
+ const character = text[end];
307
+ if (quote) {
308
+ if (character === quote) quote = void 0;
309
+ } else if (character === "'" || character === "\"") quote = character;
310
+ else if (character === "<") throw new Error("iMessage outbound runtime scaffolding is malformed");
311
+ else if (character === ">") break;
312
+ }
313
+ if (end >= text.length || quote) throw new Error("iMessage outbound runtime scaffolding is malformed");
314
+ end += 1;
315
+ if (enclosingBlock?.codeRegion && enclosingBlock.codeRegion === codeRegion) {
316
+ if (!enclosingBlock.nestedCodeRegions) {
317
+ if (codeRegion.end - enclosingBlock.end > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 3) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
318
+ enclosingBlock.nestedCodeRegions = findCodeRegions(text.slice(enclosingBlock.end, codeRegion.end));
319
+ if (enclosingBlock.nestedCodeRegions.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
320
+ }
321
+ if (isInsideCode(start - enclosingBlock.end, enclosingBlock.nestedCodeRegions)) {
322
+ enclosingBlock.sawNested = true;
323
+ continue;
324
+ }
325
+ }
326
+ scannedThrough = end;
327
+ const markup = text.slice(start, end);
328
+ const closing = token[1] === "/";
329
+ if (!closing && /\/\s*>$/.test(markup)) {
330
+ const parent = openBlocks.at(-1);
331
+ if (parent) parent.sawNested = true;
332
+ else completedBlocks.push({
333
+ start,
334
+ end
335
+ });
336
+ continue;
337
+ }
338
+ const name = (token[2] ?? "").toLowerCase();
339
+ if (!closing) {
340
+ if (openBlocks.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
341
+ const parent = openBlocks.at(-1);
342
+ if (parent) parent.sawNested = true;
343
+ openBlocks.push({
344
+ name,
345
+ start,
346
+ end,
347
+ sawNested: false,
348
+ codeRegion
349
+ });
350
+ continue;
351
+ }
352
+ const opening = openBlocks.at(-1);
353
+ if (!opening) {
354
+ completedBlocks.push({
355
+ start,
356
+ end
357
+ });
358
+ continue;
359
+ }
360
+ if (opening.name !== name) throw new Error("iMessage outbound runtime scaffolding is malformed");
361
+ openBlocks.pop();
362
+ completedBlocks.push({
363
+ start: opening.start,
364
+ end
365
+ });
366
+ }
367
+ if (openBlocks.length > 1 || openBlocks[0]?.sawNested || openBlocks[0] && [...opaqueHtml, ...rawTextHtml].some((region) => !region.terminated && openBlocks[0].start < region.start)) throw new Error("iMessage outbound runtime scaffolding is malformed");
368
+ const orphan = openBlocks[0];
369
+ if (orphan) completedBlocks.push({
370
+ start: orphan.start,
371
+ end: orphan.end
372
+ });
373
+ completedBlocks.sort((left, right) => left.start - right.start || right.end - left.end);
374
+ const outermost = [];
375
+ for (const block of completedBlocks) {
376
+ const previous = outermost.at(-1);
377
+ if (!previous || block.start >= previous.end) outermost.push(block);
378
+ }
379
+ let current = text;
380
+ for (const block of outermost.toReversed()) {
381
+ verifyProtectedRoles?.(current);
382
+ const stripped = current.slice(0, block.start) + current.slice(block.end);
383
+ assertSafeIMessageOutboundMarkup(stripped);
384
+ verifyProtectedRoles?.(stripped);
385
+ current = stripped;
386
+ }
387
+ return current;
388
+ }
389
+ function stripIMessagePrivateRuntimeScaffolding(text, verifyProtectedRoles) {
390
+ assertSafeIMessageOutboundMarkup(text);
391
+ let current = text;
392
+ for (let depth = 0; depth < MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES; depth += 1) {
393
+ let changed = false;
394
+ for (const strip of [(value) => value.replace(PRIVATE_RUNTIME_CONTEXT_BLOCK_RE, ""), (value) => stripIMessageBalancedPrivateRuntimeBlocks(value, verifyProtectedRoles)]) {
395
+ verifyProtectedRoles?.(current);
396
+ const stripped = strip(current);
397
+ assertSafeIMessageOutboundMarkup(stripped);
398
+ verifyProtectedRoles?.(stripped);
399
+ if (stripped !== current) {
400
+ current = stripped;
401
+ changed = true;
402
+ }
403
+ }
404
+ if (!changed) return current;
405
+ }
406
+ throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
407
+ }
408
+ function sanitizeIMessageAssistantVisibleText(text, verifyProtectedRoles) {
409
+ const cleaned = stripIMessagePrivateRuntimeScaffolding(text, verifyProtectedRoles);
410
+ if (!text.trim()) return text;
411
+ const leadingWhitespace = /^\s*/u.exec(text)?.[0] ?? "";
412
+ const trailingWhitespace = /\s*$/u.exec(text)?.[0] ?? "";
413
+ verifyProtectedRoles?.(cleaned);
414
+ const canonical = cleaned.trim();
415
+ verifyProtectedRoles?.(canonical);
416
+ const visible = sanitizeAssistantVisibleText(canonical);
417
+ verifyProtectedRoles?.(visible);
418
+ return visible ? `${leadingWhitespace}${visible}${trailingWhitespace}` : "";
419
+ }
420
+ function isClosedFencedCodeRegion(text, region) {
421
+ const lineStart = text.lastIndexOf("\n", region.start - 1) + 1;
422
+ if (!/^ {0,3}$/.test(text.slice(lineStart, region.start))) return false;
423
+ const source = text.slice(region.start, region.end);
424
+ const openingFence = /^ {0,3}(`{3,}|~{3,})[^\r\n]*\r?\n/.exec(source)?.[1];
425
+ if (!openingFence) return false;
426
+ const closingFence = new RegExp(`\r?\n {0,3}${openingFence[0]}{${openingFence.length},}[\t ]*(?:\r?\n)?$`);
427
+ const lineEnd = text.indexOf("\n", region.end);
428
+ return closingFence.test(source) && /^[\t ]*\r?$/.test(text.slice(region.end, lineEnd < 0 ? text.length : lineEnd));
429
+ }
430
+ /** Preserve authenticated fenced role mappings through the final native-render scrub. */
431
+ function protectIMessageFencedRoleMarkers(text) {
432
+ const source = stripIMessagePrivateRuntimeScaffolding(text);
433
+ const closedFencedRegions = findCodeRegions(source).filter((region) => isClosedFencedCodeRegion(source, region));
434
+ const protectedRoles = /* @__PURE__ */ new Map();
435
+ let privateUseCodePoint = PRIVATE_USE_START;
436
+ const protectedText = source.replace(FENCED_ROLE_MARKER_RE, (marker, role, offset) => {
437
+ if (!isInsideCode(offset, closedFencedRegions)) return marker;
438
+ while (privateUseCodePoint <= PRIVATE_USE_END) {
439
+ const protectedGlyph = String.fromCharCode(privateUseCodePoint++);
440
+ const token = protectedGlyph.repeat(role.length);
441
+ if (text.includes(protectedGlyph) || protectedRoles.has(token)) continue;
442
+ protectedRoles.set(token, role);
443
+ return marker.replace(role, token);
444
+ }
445
+ throw new Error("iMessage outbound role protection is unavailable");
446
+ });
447
+ const verifyProtectedRoles = (visible) => {
448
+ assertSafeIMessageOutboundMarkup(visible);
449
+ let previous = -1;
450
+ for (const token of protectedRoles.keys()) {
451
+ const first = visible.indexOf(token);
452
+ if (first < 0 || first < previous || visible.includes(token, first + token.length)) throw new Error("iMessage outbound role protection failed");
453
+ for (let offset = visible.indexOf(token[0] ?? ""); offset >= 0;) {
454
+ if (offset < first || offset >= first + token.length) throw new Error("iMessage outbound role protection failed");
455
+ offset = visible.indexOf(token[0] ?? "", offset + 1);
456
+ }
457
+ previous = first + token.length;
458
+ }
459
+ };
460
+ return {
461
+ text: protectedText,
462
+ verifyProtectedRoles,
463
+ sanitizeFormatted: (formatted) => {
464
+ verifyProtectedRoles(formatted.text);
465
+ let visible = formatted.text;
466
+ let ranges = formatted.ranges;
467
+ for (let remainingPasses = visible.length;; remainingPasses--) {
468
+ verifyProtectedRoles(visible);
469
+ const assistantVisible = sanitizeIMessageAssistantVisibleText(visible, verifyProtectedRoles);
470
+ const assistantTextChanged = assistantVisible !== visible;
471
+ if (assistantTextChanged) {
472
+ visible = assistantVisible;
473
+ ranges = [];
474
+ verifyProtectedRoles(visible);
475
+ }
476
+ const removed = [];
477
+ const cleaned = visible.replace(FINAL_PRIVATE_MARKER_RE, (marker, offset) => {
478
+ removed.push({
479
+ start: offset,
480
+ end: offset + marker.length
481
+ });
482
+ return "";
483
+ });
484
+ if (removed.length > 0) {
485
+ const mapOffset = (offset) => offset - removed.reduce((total, edit) => total + Math.max(0, Math.min(offset, edit.end) - edit.start), 0);
486
+ ranges = ranges.flatMap((range) => {
487
+ const start = mapOffset(range.start);
488
+ const length = mapOffset(range.start + range.length) - start;
489
+ return length > 0 ? [{
490
+ ...range,
491
+ start,
492
+ length
493
+ }] : [];
494
+ });
495
+ visible = cleaned;
496
+ verifyProtectedRoles(visible);
497
+ }
498
+ if (removed.length === 0) {
499
+ let projection = visible;
500
+ for (let depth = 0;; depth += 1) {
501
+ verifyProtectedRoles(projection);
502
+ const renderedProjection = extractMarkdownFormatRuns(projection).text;
503
+ verifyProtectedRoles(renderedProjection);
504
+ const unwrappedProjection = stripMarkdown(renderedProjection);
505
+ verifyProtectedRoles(unwrappedProjection);
506
+ let htmlProjection = unwrappedProjection;
507
+ for (let htmlDepth = 0;; htmlDepth += 1) {
508
+ verifyProtectedRoles(htmlProjection);
509
+ const assistantProjection = sanitizeIMessageAssistantVisibleText(htmlProjection, verifyProtectedRoles);
510
+ verifyProtectedRoles(assistantProjection);
511
+ if (assistantProjection !== htmlProjection) throw new Error("iMessage outbound hidden assistant content is not allowed");
512
+ const htmlRemoved = htmlProjection.replace(PLAIN_TEXT_HTML_TAG_RE, "");
513
+ verifyProtectedRoles(htmlRemoved);
514
+ if (htmlRemoved === htmlProjection) break;
515
+ if (htmlDepth + 1 >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES) throw new Error("iMessage outbound HTML security projection exceeded its limit");
516
+ htmlProjection = htmlRemoved;
517
+ }
518
+ if (unwrappedProjection === projection) break;
519
+ if (depth + 1 >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES) throw new Error("iMessage outbound Markdown security projection exceeded its limit");
520
+ projection = unwrappedProjection;
521
+ }
522
+ if (!assistantTextChanged) break;
523
+ }
524
+ if (remainingPasses <= 0) throw new Error("iMessage outbound role sanitization failed");
525
+ }
526
+ verifyProtectedRoles(visible);
527
+ for (const [token, role] of protectedRoles) visible = visible.replace(token, role);
528
+ return {
529
+ text: visible,
530
+ ranges
531
+ };
532
+ }
533
+ };
534
+ }
535
+ /** Keep each transport's existing rendered or raw wire contract behind one final security gate. */
536
+ function sanitizeIMessageFinalOutboundText(text, options = {}) {
537
+ const protection = options.protection ?? protectIMessageFencedRoleMarkers(text);
538
+ const source = options.protection ? text : protection.text;
539
+ protection.verifyProtectedRoles(source);
540
+ const protectedText = sanitizeIMessageAssistantVisibleText(source, protection.verifyProtectedRoles);
541
+ protection.verifyProtectedRoles(protectedText);
542
+ const formatted = options.formatMarkdown && protectedText.trim() ? extractMarkdownFormatRuns(protectedText) : {
543
+ text: protectedText,
544
+ ranges: []
545
+ };
546
+ return protection.sanitizeFormatted(formatted);
547
+ }
548
+ /**
549
+ * Strip all assistant-internal scaffolding from outbound text before delivery.
550
+ * Applies reasoning/thinking tag removal, memory tag removal, and
551
+ * model-specific internal separator stripping.
552
+ */
553
+ function sanitizeOutboundText(text) {
554
+ if (!text) return text;
555
+ let cleaned = sanitizeIMessageAssistantVisibleText(text);
556
+ cleaned = cleaned.replace(INTERNAL_SEPARATOR_RE, "");
557
+ cleaned = cleaned.replace(ASSISTANT_ROLE_MARKER_RE, "");
558
+ const closedFencedRegions = findCodeRegions(cleaned).filter((region) => isClosedFencedCodeRegion(cleaned, region));
559
+ cleaned = cleaned.replace(ROLE_TURN_MARKER_RE, (marker, offset) => isInsideCode(offset, closedFencedRegions) ? marker : "");
560
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
561
+ return cleaned;
562
+ }
563
+ //#endregion
564
+ export { extractMarkdownFormatRuns as i, sanitizeIMessageFinalOutboundText as n, sanitizeOutboundText as r, protectIMessageFencedRoleMarkers as t };