apple-llm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1667 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AppleLLM: () => AppleLLM,
34
+ AppleLLMError: () => AppleLLMError,
35
+ CLOUD_CONTEXT_TOKENS: () => CLOUD_CONTEXT_TOKENS,
36
+ CLOUD_SHORTCUT_NAME: () => CLOUD_SHORTCUT_NAME,
37
+ CLOUD_SHORTCUT_NAME_WEB: () => CLOUD_SHORTCUT_NAME_WEB,
38
+ CloudClient: () => CloudClient,
39
+ ContextLengthError: () => ContextLengthError,
40
+ Conversation: () => Conversation,
41
+ DEFAULT_MAX_TOKENS: () => DEFAULT_MAX_TOKENS,
42
+ DEFAULT_TEMPERATURE: () => DEFAULT_TEMPERATURE,
43
+ DeviceClient: () => DeviceClient,
44
+ ModelUnavailableError: () => ModelUnavailableError,
45
+ QuotaError: () => QuotaError,
46
+ RefusalError: () => RefusalError,
47
+ SchemaRejectedError: () => SchemaRejectedError,
48
+ SetupRequiredError: () => SetupRequiredError,
49
+ TimeoutError: () => TimeoutError,
50
+ assertTools: () => assertTools,
51
+ cacheDir: () => cacheDir,
52
+ captureScreenshot: () => captureScreenshot,
53
+ cloudSetupHint: () => cloudSetupHint,
54
+ ensureBinary: () => ensureBinary,
55
+ extractJsonSpan: () => extractJsonSpan,
56
+ fingerprint: () => fingerprint,
57
+ helperSource: () => helperSource,
58
+ hostTarget: () => hostTarget,
59
+ installCloudShortcut: () => installCloudShortcut,
60
+ isAppleSiliconMac: () => isAppleSiliconMac,
61
+ parseImageFlag: () => parseImageFlag,
62
+ parseLlmJson: () => parseLlmJson,
63
+ probe: () => probe,
64
+ probeCloud: () => probeCloud,
65
+ probeDevice: () => probeDevice,
66
+ shortcutDefinition: () => shortcutDefinition,
67
+ stripCodeFences: () => stripCodeFences,
68
+ targetTripleFrom: () => targetTripleFrom,
69
+ toAppleSchema: () => toAppleSchema,
70
+ withDocuments: () => withDocuments
71
+ });
72
+ module.exports = __toCommonJS(index_exports);
73
+
74
+ // node_modules/tsup/assets/cjs_shims.js
75
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
76
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
77
+
78
+ // src/cloud.ts
79
+ var import_node_child_process2 = require("child_process");
80
+ var import_promises = require("fs/promises");
81
+ var import_node_os = __toESM(require("os"), 1);
82
+ var import_node_path = __toESM(require("path"), 1);
83
+ var import_node_util2 = require("util");
84
+
85
+ // src/errors.ts
86
+ var AppleLLMError = class extends Error {
87
+ constructor(message, tier) {
88
+ super(message);
89
+ this.tier = tier;
90
+ this.name = new.target.name;
91
+ }
92
+ tier;
93
+ };
94
+ var ModelUnavailableError = class extends AppleLLMError {
95
+ constructor(message, reason, tier) {
96
+ super(message, tier);
97
+ this.reason = reason;
98
+ }
99
+ reason;
100
+ };
101
+ var SchemaRejectedError = class extends AppleLLMError {
102
+ };
103
+ var ContextLengthError = class extends AppleLLMError {
104
+ constructor(message, tier, contextSize) {
105
+ super(message, tier);
106
+ this.contextSize = contextSize;
107
+ }
108
+ contextSize;
109
+ };
110
+ var QuotaError = class extends AppleLLMError {
111
+ constructor(message, tier, resetDate) {
112
+ super(message, tier);
113
+ this.resetDate = resetDate;
114
+ }
115
+ resetDate;
116
+ };
117
+ var TimeoutError = class extends AppleLLMError {
118
+ };
119
+ var SetupRequiredError = class extends AppleLLMError {
120
+ constructor(message, step, tier) {
121
+ super(message, tier);
122
+ this.step = step;
123
+ }
124
+ step;
125
+ };
126
+ var RefusalError = class extends AppleLLMError {
127
+ };
128
+ function unavailableMessage(reason) {
129
+ const base = "Apple\u2019s on-device model is not available";
130
+ if (reason?.includes("appleIntelligenceNotEnabled")) {
131
+ return `${base}: Apple Intelligence is turned off.
132
+ Enable it in System Settings \u203A Apple Intelligence & Siri, then try again.`;
133
+ }
134
+ if (reason?.includes("modelNotReady")) {
135
+ return `${base}: the model is still downloading.
136
+ Leave the Mac online and plugged in for a few minutes, then try again.`;
137
+ }
138
+ if (reason?.includes("deviceNotEligible")) {
139
+ return `${base}: this Mac is not eligible for Apple Intelligence.`;
140
+ }
141
+ if (reason?.includes("unsupportedPlatform")) {
142
+ return `${base}: this platform is not supported (macOS on Apple Silicon only).`;
143
+ }
144
+ if (reason?.includes("unsupportedOSVersion")) {
145
+ return `${base}: needs macOS 26 or later.`;
146
+ }
147
+ if (reason?.includes("noSwiftCompiler")) {
148
+ return `${base}: no Swift compiler found.
149
+ Install the Xcode command line tools with \`xcode-select --install\`, then try again.`;
150
+ }
151
+ return `${base}${reason ? `: ${reason}` : "."}`;
152
+ }
153
+ function toUnavailableReason(reason) {
154
+ if (reason?.includes("appleIntelligenceNotEnabled")) return "appleIntelligenceNotEnabled";
155
+ if (reason?.includes("modelNotReady")) return "modelNotReady";
156
+ if (reason?.includes("deviceNotEligible")) return "deviceNotEligible";
157
+ if (reason?.includes("unsupportedPlatform")) return "unsupportedPlatform";
158
+ if (reason?.includes("unsupportedOSVersion")) return "unsupportedOSVersion";
159
+ if (reason?.includes("noSwiftCompiler")) return "noSwiftCompiler";
160
+ return "unknown";
161
+ }
162
+
163
+ // src/json-recovery.ts
164
+ function stripCodeFences(text) {
165
+ const trimmed = text.trim();
166
+ const fenced = /```(?:json)?\s*\n?([\s\S]*?)```/i.exec(trimmed);
167
+ if (fenced?.[1] !== void 0) return fenced[1].trim();
168
+ if (trimmed.startsWith("```")) {
169
+ return trimmed.replace(/^```(?:json)?\s*/i, "").replace(/```\s*$/, "").trim();
170
+ }
171
+ return trimmed;
172
+ }
173
+ function extractJsonSpan(text) {
174
+ for (let start = 0; start < text.length; start += 1) {
175
+ const open = text[start];
176
+ if (open !== "{" && open !== "[") continue;
177
+ const close = open === "{" ? "}" : "]";
178
+ let depth = 0;
179
+ let inString = false;
180
+ let escaped = false;
181
+ for (let i = start; i < text.length; i += 1) {
182
+ const ch = text[i];
183
+ if (escaped) {
184
+ escaped = false;
185
+ continue;
186
+ }
187
+ if (ch === "\\") {
188
+ if (inString) escaped = true;
189
+ continue;
190
+ }
191
+ if (ch === '"') {
192
+ inString = !inString;
193
+ continue;
194
+ }
195
+ if (inString) continue;
196
+ if (ch === open) depth += 1;
197
+ else if (ch === close) {
198
+ depth -= 1;
199
+ if (depth === 0) return text.slice(start, i + 1);
200
+ }
201
+ }
202
+ }
203
+ return null;
204
+ }
205
+ function parseLlmJson(raw) {
206
+ const text = stripCodeFences(raw);
207
+ try {
208
+ return JSON.parse(text);
209
+ } catch {
210
+ }
211
+ let offset = 0;
212
+ while (offset < text.length) {
213
+ const span = extractJsonSpan(text.slice(offset));
214
+ if (span === null) break;
215
+ try {
216
+ return JSON.parse(span);
217
+ } catch {
218
+ }
219
+ const idx = text.indexOf(span, offset);
220
+ offset = (idx === -1 ? offset : idx) + 1;
221
+ }
222
+ throw new Error(`Model returned invalid JSON: ${raw.slice(0, 200)}`);
223
+ }
224
+
225
+ // src/target.ts
226
+ var import_node_child_process = require("child_process");
227
+ var import_node_util = require("util");
228
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
229
+ function isAppleSiliconMac() {
230
+ return process.platform === "darwin" && process.arch === "arm64";
231
+ }
232
+ function targetTripleFrom(versionOutput, arch = process.arch) {
233
+ const [major, minor = "0"] = versionOutput.trim().split(".");
234
+ if (!major || !/^\d+$/.test(major)) return null;
235
+ if (minor !== void 0 && !/^\d+$/.test(minor)) return null;
236
+ const cpu = arch === "x64" || arch === "x86_64" ? "x86_64" : "arm64";
237
+ return `${cpu}-apple-macos${major}.${minor}`;
238
+ }
239
+ async function macosMajor() {
240
+ try {
241
+ const { stdout } = await execFileAsync("sw_vers", ["-productVersion"]);
242
+ const major = Number.parseInt(stdout.trim().split(".")[0] ?? "", 10);
243
+ return Number.isFinite(major) ? major : null;
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+ async function hostTarget() {
249
+ for (const [cmd, args] of [
250
+ ["xcrun", ["--show-sdk-version"]],
251
+ ["sw_vers", ["-productVersion"]]
252
+ ]) {
253
+ try {
254
+ const { stdout } = await execFileAsync(cmd, args);
255
+ const triple = targetTripleFrom(stdout);
256
+ if (triple !== null) return triple;
257
+ } catch {
258
+ }
259
+ }
260
+ return null;
261
+ }
262
+ async function findSwiftc() {
263
+ try {
264
+ await execFileAsync("xcrun", ["-f", "swiftc"]);
265
+ return { command: "xcrun", prefixArgs: ["swiftc"] };
266
+ } catch {
267
+ }
268
+ const { stat: stat2 } = await import("fs/promises");
269
+ try {
270
+ await stat2("/usr/bin/swiftc");
271
+ return { command: "/usr/bin/swiftc", prefixArgs: [] };
272
+ } catch {
273
+ return null;
274
+ }
275
+ }
276
+
277
+ // src/cloud.ts
278
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
279
+ var CLOUD_SHORTCUT_NAME = "Apple LLM Cloud";
280
+ var CLOUD_SHORTCUT_NAME_WEB = "Apple LLM Cloud Web";
281
+ var CALL_TIMEOUT_MS = 12e4;
282
+ var CLOUD_CONTEXT_TOKENS = 32768;
283
+ var IMPORT_TIMEOUT_MS = 9e4;
284
+ var IMPORT_POLL_MS = 2e3;
285
+ function shortcutDefinition(webSearch = false) {
286
+ const parameters = {
287
+ // Stable UUID: re-running setup re-imports the same action rather than
288
+ // accumulating variants.
289
+ UUID: webSearch ? "B47F1A90-2D5E-4C83-A6F1-9E0C7B34D215" : "3827CFFE-3A65-4456-BFA0-49EF061ACEE3",
290
+ // Plain text out. "Automatic" reshapes the result to suit whatever action
291
+ // comes next, and nothing comes next here.
292
+ WFGenerativeResultType: "Text",
293
+ // The shortcut's own input, spliced in as a variable. U+FFFC is the
294
+ // object-replacement character marking the attachment's position.
295
+ WFLLMPrompt: {
296
+ Value: {
297
+ string: "\uFFFC",
298
+ attachmentsByRange: { "{0, 1}": { Type: "ExtensionInput" } }
299
+ },
300
+ WFSerializationType: "WFTextTokenString"
301
+ }
302
+ };
303
+ if (webSearch) parameters.WFAllowWebSearch = true;
304
+ return {
305
+ WFWorkflowMinimumClientVersionString: "900",
306
+ WFWorkflowMinimumClientVersion: 900,
307
+ WFWorkflowClientVersion: "5037.0.17",
308
+ WFWorkflowIcon: {
309
+ WFWorkflowIconStartColor: 431817727,
310
+ WFWorkflowIconGlyphNumber: 61440
311
+ },
312
+ WFWorkflowOutputContentItemClasses: [],
313
+ WFWorkflowHasOutputFallback: false,
314
+ WFWorkflowActions: [
315
+ {
316
+ WFWorkflowActionIdentifier: "is.workflow.actions.askllm",
317
+ WFWorkflowActionParameters: parameters
318
+ }
319
+ ],
320
+ WFWorkflowInputContentItemClasses: ["WFStringContentItem", "WFRichTextContentItem"],
321
+ WFWorkflowImportQuestions: [],
322
+ WFQuickActionSurfaces: [],
323
+ WFWorkflowTypes: ["WFWorkflowTypeShowInSearch"],
324
+ WFWorkflowHasShortcutInputVariables: true
325
+ };
326
+ }
327
+ async function listShortcuts() {
328
+ try {
329
+ const { stdout } = await execFileAsync2("shortcuts", ["list"], { timeout: 2e4 });
330
+ return stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "");
331
+ } catch {
332
+ return [];
333
+ }
334
+ }
335
+ async function installCount(name = CLOUD_SHORTCUT_NAME) {
336
+ return (await listShortcuts()).filter((l) => l === name).length;
337
+ }
338
+ function cloudSetupHint() {
339
+ return "The cloud tier needs a one-time shortcut installed.\nRun: apple-llm setup-cloud\nIt generates and signs the shortcut on this machine \u2014 no account, no API key, nothing uploaded.";
340
+ }
341
+ function duplicateMessage(count, name) {
342
+ return `${count} shortcuts are named "${name}"; \`shortcuts run\` cannot tell them apart and fails with "Couldn't find shortcut".
343
+ Delete the duplicates in the Shortcuts app, then try again.`;
344
+ }
345
+ async function installCloudShortcut(onProgress, options = {}) {
346
+ const name = options.webSearch === true ? CLOUD_SHORTCUT_NAME_WEB : CLOUD_SHORTCUT_NAME;
347
+ const existing = await installCount(name);
348
+ if (existing > 1) throw new AppleLLMError(duplicateMessage(existing, name), "cloud");
349
+ if (existing === 1 && options.force !== true) {
350
+ onProgress?.({ status: `"${name}" is already installed` });
351
+ return;
352
+ }
353
+ const dir = await (0, import_promises.mkdtemp)(import_node_path.default.join(import_node_os.default.tmpdir(), "apple-llm-shortcut-"));
354
+ try {
355
+ const jsonPath = import_node_path.default.join(dir, "definition.json");
356
+ const unsignedPath = import_node_path.default.join(dir, "unsigned.shortcut");
357
+ const signedPath = import_node_path.default.join(dir, `${name}.shortcut`);
358
+ await (0, import_promises.writeFile)(jsonPath, JSON.stringify(shortcutDefinition(options.webSearch)), "utf8");
359
+ await execFileAsync2("plutil", ["-convert", "binary1", "-o", unsignedPath, jsonPath]);
360
+ onProgress?.({ status: "signing the shortcut locally" });
361
+ await execFileAsync2("shortcuts", ["sign", "-m", "anyone", "-i", unsignedPath, "-o", signedPath]);
362
+ onProgress?.({ status: `installing "${name}" (Shortcuts imports asynchronously)` });
363
+ await execFileAsync2("open", [signedPath]);
364
+ const deadline = Date.now() + IMPORT_TIMEOUT_MS;
365
+ for (; ; ) {
366
+ if (await installCount(name) >= 1) break;
367
+ if (Date.now() > deadline) {
368
+ throw new SetupRequiredError(
369
+ `Shortcuts did not import "${name}" within ${IMPORT_TIMEOUT_MS / 1e3}s.
370
+ If a confirmation panel is open in the Shortcuts app, accept it and run setup again.`,
371
+ "setup-cloud",
372
+ "cloud"
373
+ );
374
+ }
375
+ await new Promise((resolve) => setTimeout(resolve, IMPORT_POLL_MS));
376
+ }
377
+ onProgress?.({ status: `"${name}" installed` });
378
+ } finally {
379
+ await (0, import_promises.rm)(dir, { recursive: true, force: true });
380
+ }
381
+ }
382
+ async function probeCloud() {
383
+ if (process.platform !== "darwin") {
384
+ return { available: false, installed: false, reason: `unsupportedPlatform: this machine reports "${process.platform}"` };
385
+ }
386
+ if (!isAppleSiliconMac()) {
387
+ return { available: false, installed: false, reason: "deviceNotEligible: Apple Intelligence needs Apple Silicon" };
388
+ }
389
+ try {
390
+ await execFileAsync2("shortcuts", ["list"], { timeout: 2e4 });
391
+ } catch {
392
+ return { available: false, installed: false, reason: "this system does not provide /usr/bin/shortcuts" };
393
+ }
394
+ const count = await installCount();
395
+ if (count === 0) {
396
+ return { available: false, installed: false, reason: cloudSetupHint() };
397
+ }
398
+ if (count > 1) {
399
+ return { available: false, installed: true, reason: duplicateMessage(count, CLOUD_SHORTCUT_NAME) };
400
+ }
401
+ return { available: true, installed: true, contextSize: CLOUD_CONTEXT_TOKENS };
402
+ }
403
+ var CloudClient = class {
404
+ ready = false;
405
+ /** Last known quota, set by `probe()` so a call can fail fast. */
406
+ quota;
407
+ /**
408
+ * Tell the client what the framework reported about the quota.
409
+ *
410
+ * Worth doing because a `shortcuts run` against an exhausted quota costs a
411
+ * full round trip to find out; this turns that into an immediate typed error.
412
+ */
413
+ setQuota(quota) {
414
+ this.quota = quota;
415
+ }
416
+ assertQuota() {
417
+ if (this.quota?.status !== "limitReached") return;
418
+ throw new QuotaError(
419
+ "Apple Private Cloud Compute quota is exhausted (reported by the framework before the call was made).\nFall back to the on-device tier, or try again later.",
420
+ "cloud",
421
+ this.quota.resetDate === void 0 ? void 0 : new Date(this.quota.resetDate)
422
+ );
423
+ }
424
+ get label() {
425
+ return "apple private cloud compute";
426
+ }
427
+ get contextSize() {
428
+ return CLOUD_CONTEXT_TOKENS;
429
+ }
430
+ async ensureReady(onProgress) {
431
+ if (this.ready) return;
432
+ const probe2 = await probeCloud();
433
+ if (!probe2.available) {
434
+ if (probe2.installed !== true) {
435
+ throw new SetupRequiredError(probe2.reason ?? cloudSetupHint(), "setup-cloud", "cloud");
436
+ }
437
+ throw new AppleLLMError(probe2.reason ?? "the cloud tier is unavailable", "cloud");
438
+ }
439
+ this.ready = true;
440
+ onProgress?.({ status: `apple private cloud compute ready (${CLOUD_CONTEXT_TOKENS} token context)` });
441
+ }
442
+ async text(request) {
443
+ await this.ensureReady();
444
+ this.assertQuota();
445
+ const name = request.webSearch === true ? CLOUD_SHORTCUT_NAME_WEB : CLOUD_SHORTCUT_NAME;
446
+ if (request.webSearch === true && await installCount(name) !== 1) {
447
+ throw new SetupRequiredError(
448
+ `webSearch needs the "${name}" shortcut.
449
+ Run: apple-llm setup-cloud --web-search`,
450
+ "setup-cloud --web-search",
451
+ "cloud"
452
+ );
453
+ }
454
+ const prompt = request.system ? `${request.system}
455
+
456
+ ${request.prompt}` : request.prompt;
457
+ const dir = await (0, import_promises.mkdtemp)(import_node_path.default.join(import_node_os.default.tmpdir(), "apple-llm-cloud-"));
458
+ try {
459
+ const inPath = import_node_path.default.join(dir, "prompt.txt");
460
+ const outPath = import_node_path.default.join(dir, "reply.txt");
461
+ await (0, import_promises.writeFile)(inPath, prompt, "utf8");
462
+ try {
463
+ await execFileAsync2("shortcuts", ["run", name, "-i", inPath, "-o", outPath], {
464
+ timeout: CALL_TIMEOUT_MS,
465
+ killSignal: "SIGKILL"
466
+ });
467
+ } catch (error) {
468
+ throw describeRunFailure(error, name);
469
+ }
470
+ let reply;
471
+ try {
472
+ reply = await (0, import_promises.readFile)(outPath, "utf8");
473
+ } catch {
474
+ throw new AppleLLMError("Apple cloud generation produced no output.", "cloud");
475
+ }
476
+ if (reply.trim() === "") {
477
+ throw new AppleLLMError("Apple cloud generation returned an empty reply.", "cloud");
478
+ }
479
+ return reply;
480
+ } finally {
481
+ await (0, import_promises.rm)(dir, { recursive: true, force: true });
482
+ }
483
+ }
484
+ /**
485
+ * There is no constrained decoding on this tier, so the schema is spelled out
486
+ * in the prompt and the reply is mined for JSON. The shape is a request here,
487
+ * not a guarantee — unlike on device.
488
+ */
489
+ async json(request) {
490
+ const instruction = `Reply with a single JSON value matching this JSON Schema. Output only the JSON, with no commentary and no code fence.
491
+
492
+ ${JSON.stringify(request.schema, null, 2)}`;
493
+ const system = request.system ? `${request.system}
494
+
495
+ ${instruction}` : instruction;
496
+ return parseLlmJson(await this.text({ ...request, system }));
497
+ }
498
+ // eslint-disable-next-line class-methods-use-this
499
+ close() {
500
+ }
501
+ };
502
+ function describeRunFailure(error, name = CLOUD_SHORTCUT_NAME) {
503
+ const err = error;
504
+ if (err.killed === true) {
505
+ return new TimeoutError(
506
+ `Apple cloud generation got no response within ${CALL_TIMEOUT_MS / 1e3}s. Open "${name}" in the Shortcuts app and check that Request is bound to the Shortcut Input variable \u2014 an unbound Request makes the action wait for a human.`,
507
+ "cloud"
508
+ );
509
+ }
510
+ const stderr = err.stderr?.trim() ?? "";
511
+ if (/maximum allowed length/i.test(stderr)) {
512
+ return new ContextLengthError(
513
+ `The prompt exceeded the cloud model's ~${CLOUD_CONTEXT_TOKENS}-token context window.`,
514
+ "cloud"
515
+ );
516
+ }
517
+ if (/QuotaLimitReached|quota/i.test(stderr)) {
518
+ const match = /(\d{4}-\d{2}-\d{2}[T ][\d:]+)/.exec(stderr);
519
+ const resetDate = match ? new Date(match[1].replace(" ", "T")) : void 0;
520
+ return new QuotaError(
521
+ `Apple Private Cloud Compute quota reached.${resetDate ? ` Resets ${resetDate.toISOString()}.` : ""}
522
+ Fall back to the on-device tier, or try again later.`,
523
+ "cloud",
524
+ resetDate !== void 0 && !Number.isNaN(resetDate.getTime()) ? resetDate : void 0
525
+ );
526
+ }
527
+ if (/Couldn.t find shortcut/i.test(stderr)) {
528
+ return new SetupRequiredError(
529
+ `Shortcuts could not find "${name}".
530
+ ${cloudSetupHint()}`,
531
+ "setup-cloud",
532
+ "cloud"
533
+ );
534
+ }
535
+ return new AppleLLMError(
536
+ `Apple cloud generation failed: ${stderr !== "" ? stderr : err.message ?? String(error)}`,
537
+ "cloud"
538
+ );
539
+ }
540
+
541
+ // src/device.ts
542
+ var import_node_child_process5 = require("child_process");
543
+ var import_node_util4 = require("util");
544
+
545
+ // src/compile.ts
546
+ var import_node_child_process3 = require("child_process");
547
+ var import_promises2 = require("fs/promises");
548
+ var import_node_crypto = require("crypto");
549
+ var import_node_os2 = __toESM(require("os"), 1);
550
+ var import_node_path2 = __toESM(require("path"), 1);
551
+ var import_node_url = require("url");
552
+ var import_node_util3 = require("util");
553
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
554
+ function cacheDir() {
555
+ return import_node_path2.default.join(import_node_os2.default.homedir(), "Library", "Caches", "apple-llm", "bin");
556
+ }
557
+ function fingerprint(source, triple) {
558
+ return (0, import_node_crypto.createHash)("sha256").update(`${source}
559
+ ${triple}`, "utf8").digest("hex").slice(0, 12);
560
+ }
561
+ var cachedSource;
562
+ async function helperSource() {
563
+ if (cachedSource !== void 0) return cachedSource;
564
+ const here = import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
565
+ const candidates = [
566
+ import_node_path2.default.join(here, "..", "swift", "helper.swift"),
567
+ import_node_path2.default.join(here, "swift", "helper.swift")
568
+ ];
569
+ for (const candidate of candidates) {
570
+ try {
571
+ cachedSource = await (0, import_promises2.readFile)(candidate, "utf8");
572
+ return cachedSource;
573
+ } catch {
574
+ }
575
+ }
576
+ throw new Error(
577
+ `apple-llm could not find its embedded helper.swift (looked in ${candidates.join(", ")}).`
578
+ );
579
+ }
580
+ async function fileExists(p) {
581
+ try {
582
+ await (0, import_promises2.stat)(p);
583
+ return true;
584
+ } catch {
585
+ return false;
586
+ }
587
+ }
588
+ var inFlight;
589
+ var cachedKey;
590
+ async function ensureBinary(onProgress, options = {}) {
591
+ if (options.force === true) {
592
+ inFlight = void 0;
593
+ cachedKey = void 0;
594
+ } else if (inFlight !== void 0 && cachedKey !== void 0) {
595
+ const pending = inFlight;
596
+ try {
597
+ const source = await helperSource();
598
+ const triple = await hostTarget() ?? process.arch;
599
+ if (fingerprint(source, triple) === cachedKey) return pending;
600
+ inFlight = void 0;
601
+ cachedKey = void 0;
602
+ } catch {
603
+ return pending;
604
+ }
605
+ }
606
+ if (inFlight === void 0) {
607
+ try {
608
+ const source = await helperSource();
609
+ const triple = await hostTarget() ?? process.arch;
610
+ cachedKey = fingerprint(source, triple);
611
+ } catch {
612
+ }
613
+ inFlight = build(onProgress).catch((err) => {
614
+ inFlight = void 0;
615
+ cachedKey = void 0;
616
+ throw err;
617
+ });
618
+ }
619
+ return inFlight;
620
+ }
621
+ async function build(onProgress) {
622
+ const source = await helperSource();
623
+ const triple = await hostTarget() ?? process.arch;
624
+ const dir = cacheDir();
625
+ const target = import_node_path2.default.join(dir, `fm-helper-${fingerprint(source, triple)}`);
626
+ if (await fileExists(target)) return target;
627
+ const swiftc = await findSwiftc();
628
+ if (swiftc === null) {
629
+ throw new ModelUnavailableError(
630
+ "apple-llm needs a Swift compiler to build its on-device helper, but none was found.\nInstall the Xcode command line tools with `xcode-select --install`, then try again.",
631
+ "noSwiftCompiler",
632
+ "device"
633
+ );
634
+ }
635
+ onProgress?.({ status: "building the Apple on-device helper (one time, a few seconds)" });
636
+ await (0, import_promises2.mkdir)(dir, { recursive: true });
637
+ const unique = `${process.pid}.${Math.random().toString(36).slice(2)}`;
638
+ const sourcePath = `${target}.${unique}.swift`;
639
+ const staging = `${target}.${unique}.tmp`;
640
+ const { writeFile: writeFile2 } = await import("fs/promises");
641
+ await writeFile2(sourcePath, source, "utf8");
642
+ try {
643
+ const targetArgs = triple.includes("-apple-") ? ["-target", triple] : [];
644
+ await execFileAsync3(swiftc.command, [
645
+ ...swiftc.prefixArgs,
646
+ ...targetArgs,
647
+ "-parse-as-library",
648
+ "-O",
649
+ sourcePath,
650
+ "-o",
651
+ staging
652
+ ]);
653
+ await (0, import_promises2.chmod)(staging, 493);
654
+ await (0, import_promises2.rename)(staging, target);
655
+ } catch (err) {
656
+ await (0, import_promises2.rm)(staging, { force: true });
657
+ if (await fileExists(target)) return target;
658
+ const reason = err instanceof Error ? err.message : String(err);
659
+ throw new ModelUnavailableError(
660
+ `Failed to build the Apple on-device helper with ${swiftc.command}.
661
+ ${reason.slice(0, 400)}
662
+ This usually means the macOS SDK predates the Foundation Models framework (macOS 26+).`,
663
+ "unsupportedOSVersion",
664
+ "device"
665
+ );
666
+ } finally {
667
+ await (0, import_promises2.rm)(sourcePath, { force: true });
668
+ }
669
+ onProgress?.({ status: "helper built" });
670
+ return target;
671
+ }
672
+
673
+ // src/protocol.ts
674
+ var import_node_child_process4 = require("child_process");
675
+ var defaultSpawner = (binary, args) => (0, import_node_child_process4.spawn)(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
676
+ var HelperServer = class {
677
+ constructor(binary, options = {}) {
678
+ this.binary = binary;
679
+ this.timeoutMs = options.timeoutMs ?? 12e4;
680
+ this.spawner = options.spawner ?? defaultSpawner;
681
+ }
682
+ binary;
683
+ child;
684
+ buffer = "";
685
+ queue = [];
686
+ /** Serialises callers so one request's reply cannot be handed to another. */
687
+ chain = Promise.resolve();
688
+ timeoutMs;
689
+ spawner;
690
+ start() {
691
+ if (this.child !== void 0 && this.child.exitCode === null && !this.child.killed) {
692
+ return this.child;
693
+ }
694
+ const child = this.spawner(this.binary, ["--serve"]);
695
+ this.child = child;
696
+ this.buffer = "";
697
+ child.stdout?.on("data", (chunk) => {
698
+ this.buffer += chunk.toString();
699
+ for (; ; ) {
700
+ const newline = this.buffer.indexOf("\n");
701
+ if (newline === -1) break;
702
+ const line = this.buffer.slice(0, newline).trim();
703
+ this.buffer = this.buffer.slice(newline + 1);
704
+ if (line === "") continue;
705
+ const head = this.queue[0];
706
+ if (head === void 0) continue;
707
+ if (head.streaming === true) {
708
+ let parsedOk = false;
709
+ let parsedDone = false;
710
+ let parsedDelta = "";
711
+ try {
712
+ const parsed = JSON.parse(line);
713
+ parsedOk = parsed.ok === true;
714
+ parsedDone = parsed.done === true;
715
+ parsedDelta = typeof parsed.delta === "string" ? parsed.delta : "";
716
+ } catch {
717
+ }
718
+ if (parsedOk && !parsedDone) {
719
+ clearTimeout(head.timer);
720
+ head.timer = setTimeout(() => this.timeOut(head), this.timeoutMs);
721
+ try {
722
+ head.onDelta?.(parsedDelta);
723
+ } catch {
724
+ }
725
+ continue;
726
+ }
727
+ this.queue.shift();
728
+ clearTimeout(head.timer);
729
+ head.resolve(line);
730
+ continue;
731
+ }
732
+ const pending = this.queue.shift();
733
+ if (pending === void 0) continue;
734
+ clearTimeout(pending.timer);
735
+ pending.resolve(line);
736
+ }
737
+ });
738
+ const fail = (err) => {
739
+ this.child = void 0;
740
+ const pending = this.queue;
741
+ this.queue = [];
742
+ for (const p of pending) {
743
+ clearTimeout(p.timer);
744
+ p.reject(err);
745
+ }
746
+ };
747
+ child.on("error", (err) => fail(err instanceof Error ? err : new Error(String(err))));
748
+ child.on("close", (code) => fail(new Error(`Apple helper exited with code ${code}`)));
749
+ child.stdin?.on("error", () => {
750
+ });
751
+ const killer = () => {
752
+ if (child.exitCode === null) child.kill();
753
+ };
754
+ process.once("exit", killer);
755
+ child.once("close", () => process.removeListener("exit", killer));
756
+ return child;
757
+ }
758
+ timeOut(entry) {
759
+ const index = this.queue.findIndex((p) => p.timer === entry.timer);
760
+ if (index < 0) return;
761
+ this.queue.splice(index, 1);
762
+ this.stop();
763
+ const err = new TimeoutError(
764
+ `Apple on-device generation timed out after ${this.timeoutMs / 1e3}s.`,
765
+ "device"
766
+ );
767
+ entry.reject(err);
768
+ }
769
+ send(payload) {
770
+ const run = async () => {
771
+ const child = this.start();
772
+ return await new Promise((resolve, reject) => {
773
+ const entry = { resolve, reject, timer: void 0 };
774
+ entry.timer = setTimeout(() => {
775
+ const index = this.queue.findIndex((p) => p.timer === entry.timer);
776
+ if (index >= 0) this.queue.splice(index, 1);
777
+ this.stop();
778
+ reject(new TimeoutError(`Apple on-device generation timed out after ${this.timeoutMs / 1e3}s.`, "device"));
779
+ }, this.timeoutMs);
780
+ this.queue.push(entry);
781
+ child.stdin?.write(`${payload.replace(/\n/g, " ")}
782
+ `);
783
+ });
784
+ };
785
+ const next = this.chain.then(run, run);
786
+ this.chain = next.catch(() => void 0);
787
+ return next;
788
+ }
789
+ /**
790
+ * Streaming request. The helper emits N delta lines plus a final done:true
791
+ * line; each delta is forwarded to onDelta as it arrives and the promise
792
+ * resolves with the final line. Serialised against send() through the same
793
+ * chain, so a stream never interleaves with another request.
794
+ */
795
+ stream(payload, onDelta) {
796
+ const run = async () => {
797
+ const child = this.start();
798
+ return await new Promise((resolve, reject) => {
799
+ const entry = {
800
+ resolve,
801
+ reject,
802
+ timer: void 0,
803
+ streaming: true,
804
+ onDelta,
805
+ parts: []
806
+ };
807
+ entry.timer = setTimeout(() => this.timeOut(entry), this.timeoutMs);
808
+ this.queue.push(entry);
809
+ child.stdin?.write(`${payload.replace(/\n/g, " ")}
810
+ `);
811
+ });
812
+ };
813
+ const next = this.chain.then(run, run);
814
+ this.chain = next.catch(() => void 0);
815
+ return next;
816
+ }
817
+ stop() {
818
+ const child = this.child;
819
+ this.child = void 0;
820
+ if (child !== void 0 && child.exitCode === null) {
821
+ child.stdin?.end();
822
+ child.kill();
823
+ }
824
+ }
825
+ };
826
+
827
+ // src/schema.ts
828
+ var SCHEMA_MAP_KEYS = ["properties", "$defs", "definitions"];
829
+ var SCHEMA_LIST_KEYS = ["anyOf", "oneOf", "allOf"];
830
+ var SCHEMA_KEYS = ["items", "not", "additionalItems"];
831
+ function isPlainObject(value) {
832
+ return value !== null && typeof value === "object" && !Array.isArray(value);
833
+ }
834
+ function sanitize(name, counter) {
835
+ const cleaned = name.replace(/[^A-Za-z0-9_]/g, "");
836
+ if (cleaned.length === 0 || /^[0-9]/.test(cleaned)) {
837
+ counter.n += 1;
838
+ return `Schema${counter.n}`;
839
+ }
840
+ return cleaned;
841
+ }
842
+ function titleFrom(name, ctx) {
843
+ const base = sanitize(name, ctx.counter);
844
+ if (!ctx.used.has(base)) {
845
+ ctx.used.add(base);
846
+ return base;
847
+ }
848
+ for (let i = 2; ; i += 1) {
849
+ const candidate = `${base}${i}`;
850
+ if (!ctx.used.has(candidate)) {
851
+ ctx.used.add(candidate);
852
+ return candidate;
853
+ }
854
+ }
855
+ }
856
+ function memberType(value) {
857
+ if (typeof value === "number") return Number.isInteger(value) ? "integer" : "number";
858
+ if (typeof value === "boolean") return "boolean";
859
+ return "string";
860
+ }
861
+ function rewriteRef(ref, ctx) {
862
+ const slash = ref.lastIndexOf("/");
863
+ if (slash === -1) return ref;
864
+ const name = ref.slice(slash + 1);
865
+ const title = ctx.defTitles.get(name);
866
+ return title === void 0 || title === name ? ref : `${ref.slice(0, slash + 1)}${title}`;
867
+ }
868
+ function convertNode(node, name, ctx) {
869
+ const out = {};
870
+ let nullable = false;
871
+ const converted = /* @__PURE__ */ new Map();
872
+ const properties = node.properties;
873
+ if (isPlainObject(properties)) {
874
+ for (const [key, value] of Object.entries(properties)) {
875
+ converted.set(
876
+ key,
877
+ isPlainObject(value) ? convertNode(value, key, ctx) : { schema: value, nullable: false }
878
+ );
879
+ }
880
+ }
881
+ for (const [key, value] of Object.entries(node)) {
882
+ if (key === "type" || key === "enum" || key === "required" || key === "properties") continue;
883
+ if (key === "$ref" && typeof value === "string") {
884
+ out.$ref = rewriteRef(value, ctx);
885
+ } else if (SCHEMA_MAP_KEYS.includes(key) && isPlainObject(value)) {
886
+ const isDefs = key === "$defs" || key === "definitions";
887
+ const mapped = {};
888
+ for (const [childName, childValue] of Object.entries(value)) {
889
+ if (!isPlainObject(childValue)) {
890
+ mapped[childName] = childValue;
891
+ continue;
892
+ }
893
+ const child = convertNode(childValue, childName, ctx).schema;
894
+ const reserved = isDefs ? ctx.defTitles.get(childName) : void 0;
895
+ if (reserved !== void 0) child.title = reserved;
896
+ mapped[childName] = child;
897
+ }
898
+ out[key] = mapped;
899
+ } else if (SCHEMA_LIST_KEYS.includes(key) && Array.isArray(value)) {
900
+ if (key === "allOf") {
901
+ const allOfConverted = value.map(
902
+ (entry) => isPlainObject(entry) ? convertNode(entry, name, ctx).schema : entry
903
+ );
904
+ if (allOfConverted.length === 1 && isPlainObject(allOfConverted[0])) {
905
+ for (const [k, v] of Object.entries(allOfConverted[0])) {
906
+ if (out[k] === void 0) out[k] = v;
907
+ }
908
+ } else {
909
+ out.allOf = allOfConverted;
910
+ }
911
+ } else {
912
+ const target = key === "oneOf" ? "anyOf" : key;
913
+ out[target] = value.map(
914
+ (entry) => isPlainObject(entry) ? convertNode(entry, name, ctx).schema : entry
915
+ );
916
+ }
917
+ } else if (SCHEMA_KEYS.includes(key) && isPlainObject(value)) {
918
+ out[key] = convertNode(value, name, ctx).schema;
919
+ } else {
920
+ out[key] = value;
921
+ }
922
+ }
923
+ const rawType = node.type;
924
+ if (Array.isArray(rawType)) {
925
+ const nonNull = rawType.filter((t) => t !== "null");
926
+ nullable = nonNull.length !== rawType.length;
927
+ if (nonNull.length > 1) {
928
+ out.anyOf = nonNull.map((t) => ({ type: t }));
929
+ } else {
930
+ out.type = nonNull[0] ?? "string";
931
+ }
932
+ } else if (rawType !== void 0) {
933
+ out.type = rawType;
934
+ }
935
+ const rawEnum = node.enum;
936
+ if (Array.isArray(rawEnum) && rawEnum.length > 0) {
937
+ const members = rawEnum.filter((v) => v !== null);
938
+ if (members.length !== rawEnum.length) nullable = true;
939
+ if (members.length > 0) {
940
+ delete out.type;
941
+ if (members.every((v) => typeof v === "string")) {
942
+ out.anyOf = members.map((value) => ({ type: "string", const: value }));
943
+ } else {
944
+ const types = [...new Set(members.map(memberType))];
945
+ if (types.length === 1) out.type = types[0];
946
+ else out.anyOf = types.map((t) => ({ type: t }));
947
+ }
948
+ }
949
+ }
950
+ const isObject = out.type === "object" || converted.size > 0;
951
+ if (isObject) {
952
+ const props = {};
953
+ for (const [key, child] of converted) props[key] = child.schema;
954
+ out.properties = props;
955
+ const originalRequired = Array.isArray(node.required) ? node.required : [];
956
+ out.required = originalRequired.filter((key) => !converted.get(key)?.nullable);
957
+ const keys = Object.keys(props);
958
+ const declaredOrder = Array.isArray(node["x-order"]) ? node["x-order"].filter((k) => typeof k === "string" && keys.includes(k)) : [];
959
+ out["x-order"] = [...declaredOrder, ...keys.filter((k) => !declaredOrder.includes(k))];
960
+ if (typeof out.title === "string") ctx.used.add(out.title);
961
+ else out.title = titleFrom(name, ctx);
962
+ if (out.additionalProperties === void 0) out.additionalProperties = false;
963
+ } else if (Array.isArray(node.required)) {
964
+ out.required = node.required;
965
+ }
966
+ if ((Array.isArray(out.anyOf) || Array.isArray(out.allOf)) && typeof out.title !== "string") {
967
+ out.title = titleFrom(name, ctx);
968
+ }
969
+ if (out.type === "string" && out.anyOf === void 0 && out.allOf === void 0)
970
+ delete out.title;
971
+ return { schema: out, nullable };
972
+ }
973
+ function reserveDefTitles(schema, ctx) {
974
+ for (const key of ["$defs", "definitions"]) {
975
+ const defs = schema[key];
976
+ if (!isPlainObject(defs)) continue;
977
+ for (const [name, value] of Object.entries(defs)) {
978
+ const declared = isPlainObject(value) && typeof value.title === "string" ? value.title : null;
979
+ const title = declared ?? sanitize(name, ctx.counter);
980
+ ctx.defTitles.set(name, title);
981
+ ctx.used.add(title);
982
+ }
983
+ }
984
+ }
985
+ function toAppleSchema(schema, rootName = "Response") {
986
+ const ctx = { counter: { n: 0 }, defTitles: /* @__PURE__ */ new Map(), used: /* @__PURE__ */ new Set() };
987
+ reserveDefTitles(schema, ctx);
988
+ return convertNode(schema, rootName, ctx).schema;
989
+ }
990
+
991
+ // src/device.ts
992
+ var execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process5.execFile);
993
+ var DEFAULT_TEMPERATURE = 0.4;
994
+ var DEFAULT_MAX_TOKENS = 2048;
995
+ var CALL_TIMEOUT_MS2 = 12e4;
996
+ var BUILT_IN_TOOLS = /* @__PURE__ */ new Set(["ocr", "barcode", "spotlight"]);
997
+ function assertTools(tools) {
998
+ if (tools === void 0) return;
999
+ for (const tool of tools) {
1000
+ if (!BUILT_IN_TOOLS.has(tool)) {
1001
+ throw new AppleLLMError(
1002
+ `Unknown tool "${tool}" (want ${[...BUILT_IN_TOOLS].join(", ")}).`,
1003
+ "device"
1004
+ );
1005
+ }
1006
+ }
1007
+ }
1008
+ async function probeDevice(onProgress) {
1009
+ if (process.platform !== "darwin") {
1010
+ return { available: false, reason: `unsupportedPlatform: this machine reports "${process.platform}"` };
1011
+ }
1012
+ if (!isAppleSiliconMac()) {
1013
+ return { available: false, reason: "deviceNotEligible: Apple Intelligence needs Apple Silicon" };
1014
+ }
1015
+ const major = await macosMajor();
1016
+ if (major !== null && major < 26) {
1017
+ return { available: false, reason: `unsupportedOSVersion: needs macOS 26 or later, this is ${major}` };
1018
+ }
1019
+ let binary;
1020
+ try {
1021
+ binary = await ensureBinary(onProgress);
1022
+ } catch (err) {
1023
+ return { available: false, reason: err instanceof Error ? err.message : String(err) };
1024
+ }
1025
+ try {
1026
+ const { stdout } = await execFileAsync4(binary, ["--probe"], { timeout: 2e4 });
1027
+ return JSON.parse(stdout.trim());
1028
+ } catch (err) {
1029
+ const reason = err instanceof Error ? err.message : String(err);
1030
+ return { available: false, reason: `the helper could not be run: ${reason.slice(0, 300)}` };
1031
+ }
1032
+ }
1033
+ function raiseFor(response, raw) {
1034
+ const detail = response.error ?? raw.slice(0, 200);
1035
+ switch (response.kind) {
1036
+ case "availability":
1037
+ throw new ModelUnavailableError(
1038
+ unavailableMessage(detail),
1039
+ toUnavailableReason(detail),
1040
+ "device"
1041
+ );
1042
+ case "schema":
1043
+ throw new SchemaRejectedError(
1044
+ `Apple rejected the response schema: ${detail}
1045
+ Every object needs a title, an x-order and additionalProperties; enums must be anyOf/const; unions cannot include null.`,
1046
+ "device"
1047
+ );
1048
+ case "context":
1049
+ throw new ContextLengthError(`The prompt exceeded the on-device context window: ${detail}`, "device");
1050
+ case "quota": {
1051
+ const parsed = response.resetDate === void 0 ? void 0 : new Date(response.resetDate);
1052
+ const resetDate = parsed !== void 0 && !Number.isNaN(parsed.getTime()) ? parsed : void 0;
1053
+ throw new QuotaError(
1054
+ `Apple rate limited the on-device model: ${detail}`,
1055
+ "device",
1056
+ resetDate
1057
+ );
1058
+ }
1059
+ case "guardrail":
1060
+ throw new RefusalError(`The on-device model declined to answer: ${detail}`, "device");
1061
+ default:
1062
+ throw new AppleLLMError(`Apple on-device generation failed: ${detail}`, "device");
1063
+ }
1064
+ }
1065
+ var DeviceClient = class {
1066
+ constructor(options = {}) {
1067
+ this.options = options;
1068
+ }
1069
+ options;
1070
+ binary;
1071
+ probeResult;
1072
+ server;
1073
+ get label() {
1074
+ const variant = this.probeResult?.variant;
1075
+ return variant ? `apple on-device (${variant})` : "apple on-device";
1076
+ }
1077
+ get contextSize() {
1078
+ return this.probeResult?.contextSize;
1079
+ }
1080
+ async ensureReady(onProgress) {
1081
+ if (this.probeResult?.available === true) return;
1082
+ const probe2 = await probeDevice(onProgress);
1083
+ this.probeResult = probe2;
1084
+ if (!probe2.available) {
1085
+ throw new ModelUnavailableError(
1086
+ unavailableMessage(probe2.reason),
1087
+ toUnavailableReason(probe2.reason),
1088
+ "device"
1089
+ );
1090
+ }
1091
+ this.binary = await ensureBinary(onProgress);
1092
+ onProgress?.({
1093
+ status: `apple on-device model ready${probe2.variant ? ` (${probe2.variant}, ${probe2.contextSize} token context)` : ""}`
1094
+ });
1095
+ }
1096
+ getProbe() {
1097
+ return this.probeResult;
1098
+ }
1099
+ /** Send one envelope and return the parsed reply, raising a typed error on failure. */
1100
+ async exchange(envelope) {
1101
+ await this.ensureReady();
1102
+ const binary = this.binary ?? await ensureBinary();
1103
+ this.server ??= new HelperServer(binary, { timeoutMs: CALL_TIMEOUT_MS2 });
1104
+ const raw = await this.server.send(JSON.stringify(envelope));
1105
+ let response;
1106
+ try {
1107
+ response = JSON.parse(raw);
1108
+ } catch {
1109
+ throw new AppleLLMError(`Apple helper returned an unreadable envelope: ${raw.slice(0, 200)}`, "device");
1110
+ }
1111
+ if (response.ok !== true) raiseFor(response, raw);
1112
+ return response;
1113
+ }
1114
+ /** One request. Returns the helper's `content` string, unparsed. */
1115
+ async complete(request) {
1116
+ assertTools(request.tools);
1117
+ const prompt = await withDocuments(request.prompt, request.documents);
1118
+ const response = await this.exchange({
1119
+ instructions: request.system ?? "",
1120
+ prompt,
1121
+ schema: request.schema ?? null,
1122
+ temperature: request.temperature ?? DEFAULT_TEMPERATURE,
1123
+ maxTokens: request.maxTokens ?? DEFAULT_MAX_TOKENS,
1124
+ includeSchemaInPrompt: request.includeSchemaInPrompt,
1125
+ reuseSession: request.reuseSession ?? false,
1126
+ sessionId: request.sessionId,
1127
+ tools: request.tools,
1128
+ images: request.images,
1129
+ useCase: request.useCase ?? this.options.useCase,
1130
+ guardrails: request.guardrails ?? this.options.guardrails,
1131
+ sampling: request.sampling
1132
+ });
1133
+ if (typeof response.content !== "string") {
1134
+ throw new AppleLLMError("Apple helper returned no content.", "device");
1135
+ }
1136
+ return response.content;
1137
+ }
1138
+ /**
1139
+ * Streaming text. Deltas arrive via onDelta as the model generates; the
1140
+ * promise resolves with the full text. Text only: the helper rejects
1141
+ * schema+stream, because partial JSON is not a usable delta.
1142
+ */
1143
+ async stream(prompt, options = {}) {
1144
+ assertTools(options.tools);
1145
+ const { onDelta, documents, ...rest } = options;
1146
+ const fullPrompt = await withDocuments(prompt, documents);
1147
+ await this.ensureReady();
1148
+ const binary = this.binary ?? await ensureBinary();
1149
+ this.server ??= new HelperServer(binary, { timeoutMs: CALL_TIMEOUT_MS2 });
1150
+ const envelope = {
1151
+ op: "stream",
1152
+ instructions: rest.system ?? "",
1153
+ prompt: fullPrompt,
1154
+ schema: null,
1155
+ temperature: rest.temperature ?? DEFAULT_TEMPERATURE,
1156
+ maxTokens: rest.maxTokens ?? DEFAULT_MAX_TOKENS,
1157
+ reuseSession: rest.reuseSession ?? false,
1158
+ sessionId: rest.sessionId,
1159
+ tools: rest.tools,
1160
+ images: rest.images,
1161
+ useCase: rest.useCase ?? this.options.useCase,
1162
+ guardrails: rest.guardrails ?? this.options.guardrails,
1163
+ sampling: rest.sampling
1164
+ };
1165
+ const raw = await this.server.stream(JSON.stringify(envelope), (delta) => {
1166
+ if (delta !== "") onDelta?.(delta);
1167
+ });
1168
+ let response;
1169
+ try {
1170
+ response = JSON.parse(raw);
1171
+ } catch {
1172
+ throw new AppleLLMError(`Apple helper returned an unreadable envelope: ${raw.slice(0, 200)}`, "device");
1173
+ }
1174
+ if (response.ok !== true) raiseFor(response, raw);
1175
+ if (typeof response.content !== "string") {
1176
+ throw new AppleLLMError("Apple helper returned no content.", "device");
1177
+ }
1178
+ return response.content;
1179
+ }
1180
+ /**
1181
+ * Conversation history for a named session: the mirrored turns the helper
1182
+ * persisted, oldest first. Survives helper restarts; the native transcript
1183
+ * does not, so treat a restart as a context break, not a loss.
1184
+ */
1185
+ async history(sessionId) {
1186
+ const response = await this.exchange({ op: "history", sessionId, instructions: "" });
1187
+ return {
1188
+ instructions: typeof response.instructions === "string" ? response.instructions : "",
1189
+ history: Array.isArray(response.history) ? response.history : []
1190
+ };
1191
+ }
1192
+ /** Drop a named session (and its persisted history), or all of them. */
1193
+ async resetSession(sessionId) {
1194
+ await this.exchange({ op: "reset", sessionId: sessionId ?? "", instructions: "" });
1195
+ }
1196
+ /**
1197
+ * How many tokens a prompt costs, before sending it.
1198
+ *
1199
+ * The point is to turn a ContextLengthError into an arithmetic check: compare
1200
+ * against `contextSize` and trim, rather than discovering the ceiling by
1201
+ * hitting it. Counts the instructions too, since they share the window.
1202
+ */
1203
+ async countTokens(prompt, options = {}) {
1204
+ const response = await this.exchange({
1205
+ op: "countTokens",
1206
+ instructions: options.system ?? "",
1207
+ prompt: await withDocuments(prompt, void 0),
1208
+ images: options.images,
1209
+ tools: options.tools
1210
+ });
1211
+ if (typeof response.tokens !== "number" || typeof response.contextSize !== "number") {
1212
+ throw new AppleLLMError("Apple helper returned an unreadable token count.", "device");
1213
+ }
1214
+ return { tokens: response.tokens, contextSize: response.contextSize };
1215
+ }
1216
+ /**
1217
+ * Load the model assets now so the first real call does not pay for it.
1218
+ *
1219
+ * Cheap and idempotent, but do not expect much on a warm machine: with the
1220
+ * assets already resident this measured 0.31s against 0.36s for an
1221
+ * unprewarmed first call — inside the noise. The win is on a genuinely cold
1222
+ * system, where the very first call to the framework here took 7.8s. Worth
1223
+ * calling at startup when you know a request is coming; not worth building
1224
+ * around.
1225
+ */
1226
+ async prewarm(system) {
1227
+ await this.exchange({ op: "prewarm", instructions: system ?? "" });
1228
+ }
1229
+ async text(prompt, options = {}) {
1230
+ return this.complete({ ...options, prompt, schema: null });
1231
+ }
1232
+ async json(prompt, options) {
1233
+ const content = await this.complete({ ...options, prompt, schema: toAppleSchema(options.schema) });
1234
+ try {
1235
+ return JSON.parse(content);
1236
+ } catch {
1237
+ throw new AppleLLMError(
1238
+ `Apple on-device model returned invalid JSON: ${content.slice(0, 200)}`,
1239
+ "device"
1240
+ );
1241
+ }
1242
+ }
1243
+ /** Shut the helper process down. Safe to call more than once. */
1244
+ close() {
1245
+ this.server?.stop();
1246
+ this.server = void 0;
1247
+ }
1248
+ };
1249
+ async function withDocuments(prompt, documents) {
1250
+ if (documents === void 0 || documents.length === 0) return prompt;
1251
+ const { readFile: readFile3, stat: stat2 } = await import("fs/promises");
1252
+ const parts = [prompt];
1253
+ for (const doc of documents) {
1254
+ let size = 0;
1255
+ try {
1256
+ size = (await stat2(doc)).size;
1257
+ } catch {
1258
+ throw new AppleLLMError(`Document not found: ${doc}`, "device");
1259
+ }
1260
+ if (size > 512e3) {
1261
+ throw new AppleLLMError(`Document too large to inline (>${512e3} bytes): ${doc}`, "device");
1262
+ }
1263
+ let text;
1264
+ try {
1265
+ text = await readFile3(doc, "utf8");
1266
+ } catch {
1267
+ throw new AppleLLMError(`Document is not readable text: ${doc}`, "device");
1268
+ }
1269
+ if (text.includes("\uFFFD")) {
1270
+ throw new AppleLLMError(`Document is not readable text: ${doc}`, "device");
1271
+ }
1272
+ parts.push(`
1273
+
1274
+ --- Document: ${doc} ---
1275
+ ${text}`);
1276
+ }
1277
+ return parts.join("");
1278
+ }
1279
+ function parseImageFlag(value) {
1280
+ const sep = value.lastIndexOf("::");
1281
+ if (sep > 0) {
1282
+ const path3 = value.slice(0, sep);
1283
+ const label = value.slice(sep + 2).trim();
1284
+ if (path3 !== "" && label !== "") return { path: path3, label };
1285
+ }
1286
+ return value;
1287
+ }
1288
+
1289
+ // src/index.ts
1290
+ async function probe(onProgress) {
1291
+ const [device, cloud] = await Promise.all([probeDevice(onProgress), probeCloud()]);
1292
+ if (device.cloud !== void 0) cloud.quota = device.cloud;
1293
+ return { device, cloud };
1294
+ }
1295
+ var AppleLLM = class {
1296
+ device;
1297
+ cloud;
1298
+ /** Which tier `auto` settled on, once resolved. */
1299
+ resolved;
1300
+ options;
1301
+ constructor(options = {}) {
1302
+ this.options = options;
1303
+ }
1304
+ /** Per-call device settings, falling back to the constructor defaults. */
1305
+ deviceDefaults(options) {
1306
+ return {
1307
+ temperature: options.temperature ?? this.options.temperature,
1308
+ maxTokens: options.maxTokens ?? this.options.maxTokens,
1309
+ images: options.images,
1310
+ documents: options.documents,
1311
+ sessionId: options.sessionId,
1312
+ tools: options.tools,
1313
+ useCase: options.useCase ?? this.options.useCase,
1314
+ guardrails: options.guardrails ?? this.options.guardrails,
1315
+ sampling: options.sampling ?? this.options.sampling
1316
+ };
1317
+ }
1318
+ get tier() {
1319
+ return this.options.tier ?? "auto";
1320
+ }
1321
+ /** Human-readable name of the tier in use, for logs. */
1322
+ get label() {
1323
+ if (this.resolved === "cloud") return this.cloud?.label ?? "apple private cloud compute";
1324
+ if (this.resolved === "device") return this.device?.label ?? "apple on-device";
1325
+ return `apple (${this.tier})`;
1326
+ }
1327
+ /**
1328
+ * Resolve the tier and do any one-time setup. Called automatically, but
1329
+ * exposed so a caller can pay the compile cost up front with a progress bar.
1330
+ */
1331
+ async ensureReady(onProgress) {
1332
+ const progress = onProgress ?? this.options.onProgress;
1333
+ if (this.resolved !== void 0) return;
1334
+ if (this.tier === "device") {
1335
+ this.device ??= new DeviceClient({
1336
+ useCase: this.options.useCase,
1337
+ guardrails: this.options.guardrails
1338
+ });
1339
+ await this.device.ensureReady(progress);
1340
+ this.resolved = "device";
1341
+ return;
1342
+ }
1343
+ if (this.tier === "cloud") {
1344
+ this.cloud ??= new CloudClient();
1345
+ await this.cloud.ensureReady(progress);
1346
+ this.cloud.setQuota((await probeDevice().catch(() => void 0))?.cloud);
1347
+ this.resolved = "cloud";
1348
+ return;
1349
+ }
1350
+ this.device ??= new DeviceClient({
1351
+ useCase: this.options.useCase,
1352
+ guardrails: this.options.guardrails
1353
+ });
1354
+ try {
1355
+ await this.device.ensureReady(progress);
1356
+ this.resolved = "device";
1357
+ return;
1358
+ } catch (deviceError) {
1359
+ this.cloud ??= new CloudClient();
1360
+ try {
1361
+ await this.cloud.ensureReady(progress);
1362
+ this.cloud.setQuota((await probeDevice().catch(() => void 0))?.cloud);
1363
+ this.resolved = "cloud";
1364
+ return;
1365
+ } catch (cloudError) {
1366
+ const deviceReason = deviceError instanceof Error ? deviceError.message : String(deviceError);
1367
+ const cloudReason = cloudError instanceof Error ? cloudError.message : String(cloudError);
1368
+ throw new ModelUnavailableError(
1369
+ `No Apple model is available.
1370
+
1371
+ On-device: ${deviceReason}
1372
+
1373
+ Cloud: ${cloudReason}`,
1374
+ deviceError instanceof ModelUnavailableError ? deviceError.reason : "unknown"
1375
+ );
1376
+ }
1377
+ }
1378
+ }
1379
+ async text(prompt, options = {}) {
1380
+ await this.ensureReady();
1381
+ if (this.resolved === "cloud") {
1382
+ if (options.images !== void 0 && options.images.length > 0) {
1383
+ throw new AppleLLMError("images needs the on-device tier", "cloud");
1384
+ }
1385
+ if (options.sessionId !== void 0) {
1386
+ throw new AppleLLMError("sessionId needs the on-device tier", "cloud");
1387
+ }
1388
+ if (options.tools !== void 0 && options.tools.length > 0) {
1389
+ throw new AppleLLMError("tools needs the on-device tier", "cloud");
1390
+ }
1391
+ if (options.documents !== void 0 && options.documents.length > 0) {
1392
+ throw new AppleLLMError("documents needs the on-device tier", "cloud");
1393
+ }
1394
+ return this.cloud.text({ system: options.system, prompt, webSearch: options.webSearch });
1395
+ }
1396
+ return this.device.text(prompt, { system: options.system, ...this.deviceDefaults(options) });
1397
+ }
1398
+ /**
1399
+ * Streaming text (device tier only). Deltas arrive via onDelta as the model
1400
+ * generates; the promise resolves with the full text. The Siri-app shape:
1401
+ * partials first, final answer at the end.
1402
+ */
1403
+ async stream(prompt, options = {}) {
1404
+ await this.ensureReady();
1405
+ if (this.resolved !== "device") {
1406
+ throw new AppleLLMError("stream needs the on-device tier.", "cloud");
1407
+ }
1408
+ const { onDelta, ...rest } = options;
1409
+ return this.device.stream(prompt, {
1410
+ system: rest.system,
1411
+ onDelta,
1412
+ ...this.deviceDefaults(rest)
1413
+ });
1414
+ }
1415
+ /** Conversation history for a named session (device tier only). */
1416
+ async history(sessionId) {
1417
+ await this.ensureReady();
1418
+ if (this.resolved !== "device") {
1419
+ throw new AppleLLMError("history needs the on-device tier.", "cloud");
1420
+ }
1421
+ return this.device.history(sessionId);
1422
+ }
1423
+ /** Drop a named session, or all sessions when omitted (device tier only). */
1424
+ async resetSession(sessionId) {
1425
+ await this.ensureReady();
1426
+ if (this.resolved === "device") await this.device.resetSession(sessionId);
1427
+ }
1428
+ /**
1429
+ * A named conversation: text/stream calls sharing one native transcript,
1430
+ * like one thread in the Siri app. History persists across helper restarts;
1431
+ * the native transcript does not (treated as a context break, not a loss).
1432
+ */
1433
+ conversation(sessionId, options = {}) {
1434
+ return new Conversation(this, sessionId, options);
1435
+ }
1436
+ /**
1437
+ * Write with Siri, anywhere you type: drafting, rewriting and feedback
1438
+ * built on the permissive-content-transformation guardrails. Device tier
1439
+ * only — these are transformation tasks the default guardrails refuse.
1440
+ */
1441
+ async rewrite(text, options = {}) {
1442
+ const { instruction, ...rest } = options;
1443
+ return this.text(`Rewrite the following text. ${instruction ?? "Keep the meaning, improve clarity."}
1444
+
1445
+ ---
1446
+ ${text}`, {
1447
+ ...rest,
1448
+ guardrails: rest.guardrails ?? "permissive",
1449
+ system: rest.system ?? "You rewrite text. Reply with only the rewritten text, no commentary."
1450
+ });
1451
+ }
1452
+ async proofread(text, options = {}) {
1453
+ return this.text(`Fix spelling, grammar and punctuation in the following text. Preserve the meaning and tone.
1454
+
1455
+ ---
1456
+ ${text}`, {
1457
+ ...options,
1458
+ guardrails: options.guardrails ?? "permissive",
1459
+ system: options.system ?? "You proofread text. Reply with only the corrected text, no commentary."
1460
+ });
1461
+ }
1462
+ async summarize(text, options = {}) {
1463
+ const { length, ...rest } = options;
1464
+ return this.text(`Summarize the following text in ${length ?? "one short paragraph"}.
1465
+
1466
+ ---
1467
+ ${text}`, {
1468
+ ...rest,
1469
+ guardrails: rest.guardrails ?? "permissive",
1470
+ system: rest.system ?? "You summarize text tersely."
1471
+ });
1472
+ }
1473
+ async draft(topic, options = {}) {
1474
+ const { kind, ...rest } = options;
1475
+ return this.text(`Write a ${kind ?? "short draft"} about the following topic.
1476
+
1477
+ ---
1478
+ ${topic}`, {
1479
+ ...rest,
1480
+ system: rest.system ?? "You are a helpful writing assistant."
1481
+ });
1482
+ }
1483
+ async tone(text, tone, options = {}) {
1484
+ return this.text(`Rewrite the following text to sound more ${tone}.
1485
+
1486
+ ---
1487
+ ${text}`, {
1488
+ ...options,
1489
+ guardrails: options.guardrails ?? "permissive",
1490
+ system: options.system ?? "You rewrite text. Reply with only the rewritten text, no commentary."
1491
+ });
1492
+ }
1493
+ /**
1494
+ * Ask about what's on screen: captures a screenshot (interactive selection
1495
+ * by default, like Cmd+Shift+Space Visual Intelligence) and asks the model
1496
+ * about it with vision. Device tier, macOS 27+.
1497
+ */
1498
+ async askScreen(question, options = {}) {
1499
+ await this.ensureReady();
1500
+ if (this.resolved !== "device") {
1501
+ throw new AppleLLMError("askScreen needs the on-device tier.", "cloud");
1502
+ }
1503
+ const { mode, ...rest } = options;
1504
+ const shot = await captureScreenshot(mode ?? "interactive");
1505
+ try {
1506
+ return await this.text(question, { ...rest, images: [...rest.images ?? [], shot] });
1507
+ } finally {
1508
+ const { rm: rm3 } = await import("fs/promises");
1509
+ await rm3(shot, { force: true }).catch(() => void 0);
1510
+ }
1511
+ }
1512
+ /**
1513
+ * Ask for JSON. On device the schema is *guaranteed* by constrained decoding.
1514
+ * On cloud it is requested in the prompt and recovered from the reply — the
1515
+ * cloud tier has no constrained decoding, so a bad shape is possible there.
1516
+ */
1517
+ async json(prompt, options) {
1518
+ await this.ensureReady();
1519
+ if (this.resolved === "cloud") {
1520
+ if (options.images !== void 0 && options.images.length > 0) {
1521
+ throw new AppleLLMError("images needs the on-device tier", "cloud");
1522
+ }
1523
+ return this.cloud.json({
1524
+ system: options.system,
1525
+ prompt,
1526
+ schema: options.schema,
1527
+ webSearch: options.webSearch
1528
+ });
1529
+ }
1530
+ return this.device.json(prompt, {
1531
+ system: options.system,
1532
+ schema: options.schema,
1533
+ ...this.deviceDefaults(options)
1534
+ });
1535
+ }
1536
+ /**
1537
+ * api-scribe's `LlmClient` shape, so it can drop its four files and depend on
1538
+ * this instead. Not used internally.
1539
+ */
1540
+ async completeJson(system, user, schema) {
1541
+ return this.json(user, { system, schema });
1542
+ }
1543
+ /**
1544
+ * How many tokens a prompt costs, before sending it. Device tier only.
1545
+ *
1546
+ * Turns a ContextLengthError into arithmetic: compare against `contextSize`
1547
+ * and trim, rather than finding the ceiling by hitting it.
1548
+ */
1549
+ async countTokens(prompt, options = {}) {
1550
+ await this.ensureReady();
1551
+ if (this.resolved !== "device") {
1552
+ throw new AppleLLMError("countTokens needs the on-device tier.", "cloud");
1553
+ }
1554
+ return this.device.countTokens(prompt, options);
1555
+ }
1556
+ /**
1557
+ * Load the model assets now so the first real call does not pay for it.
1558
+ * Device tier only; a no-op elsewhere. See `DeviceClient.prewarm` for what it
1559
+ * is actually worth (little, on a warm machine).
1560
+ */
1561
+ async prewarm(system) {
1562
+ await this.ensureReady();
1563
+ if (this.resolved === "device") await this.device.prewarm(system);
1564
+ }
1565
+ /** Release the long-lived helper process. Safe to call more than once. */
1566
+ close() {
1567
+ this.device?.close();
1568
+ this.cloud?.close();
1569
+ }
1570
+ };
1571
+ var Conversation = class {
1572
+ constructor(llm, sessionId, defaults = {}) {
1573
+ this.llm = llm;
1574
+ this.sessionId = sessionId;
1575
+ this.defaults = defaults;
1576
+ }
1577
+ llm;
1578
+ sessionId;
1579
+ defaults;
1580
+ async text(prompt, options = {}) {
1581
+ return this.llm.text(prompt, {
1582
+ system: options.system ?? this.defaults.system,
1583
+ ...options,
1584
+ sessionId: this.sessionId
1585
+ });
1586
+ }
1587
+ async stream(prompt, options = {}) {
1588
+ return this.llm.stream(prompt, {
1589
+ system: options.system ?? this.defaults.system,
1590
+ ...options,
1591
+ sessionId: this.sessionId
1592
+ });
1593
+ }
1594
+ async history() {
1595
+ return this.llm.history(this.sessionId);
1596
+ }
1597
+ async reset() {
1598
+ await this.llm.resetSession(this.sessionId);
1599
+ }
1600
+ };
1601
+ async function captureScreenshot(mode = "interactive") {
1602
+ const { execFile: execFile5 } = await import("child_process");
1603
+ const { promisify: promisify5 } = await import("util");
1604
+ const { mkdtemp: mkdtemp2, stat: stat2 } = await import("fs/promises");
1605
+ const os3 = await import("os");
1606
+ const path3 = await import("path");
1607
+ const execFileAsync5 = promisify5(execFile5);
1608
+ const dir = await mkdtemp2(path3.join(os3.tmpdir(), "apple-llm-screen-"));
1609
+ const out = path3.join(dir, "screen.png");
1610
+ const args = mode === "interactive" ? ["-i", "-x", out] : mode === "window" ? ["-w", "-x", out] : ["-x", out];
1611
+ try {
1612
+ await execFileAsync5("screencapture", args, { timeout: 12e4 });
1613
+ } catch (err) {
1614
+ throw new AppleLLMError(
1615
+ `Could not capture a screenshot (screencapture failed): ${err instanceof Error ? err.message : String(err)}`,
1616
+ "device"
1617
+ );
1618
+ }
1619
+ try {
1620
+ const st = await stat2(out);
1621
+ if (st.size === 0) throw new Error("empty capture");
1622
+ } catch {
1623
+ throw new AppleLLMError("Screenshot capture was cancelled or produced no image.", "device");
1624
+ }
1625
+ return out;
1626
+ }
1627
+ // Annotate the CommonJS export names for ESM import in node:
1628
+ 0 && (module.exports = {
1629
+ AppleLLM,
1630
+ AppleLLMError,
1631
+ CLOUD_CONTEXT_TOKENS,
1632
+ CLOUD_SHORTCUT_NAME,
1633
+ CLOUD_SHORTCUT_NAME_WEB,
1634
+ CloudClient,
1635
+ ContextLengthError,
1636
+ Conversation,
1637
+ DEFAULT_MAX_TOKENS,
1638
+ DEFAULT_TEMPERATURE,
1639
+ DeviceClient,
1640
+ ModelUnavailableError,
1641
+ QuotaError,
1642
+ RefusalError,
1643
+ SchemaRejectedError,
1644
+ SetupRequiredError,
1645
+ TimeoutError,
1646
+ assertTools,
1647
+ cacheDir,
1648
+ captureScreenshot,
1649
+ cloudSetupHint,
1650
+ ensureBinary,
1651
+ extractJsonSpan,
1652
+ fingerprint,
1653
+ helperSource,
1654
+ hostTarget,
1655
+ installCloudShortcut,
1656
+ isAppleSiliconMac,
1657
+ parseImageFlag,
1658
+ parseLlmJson,
1659
+ probe,
1660
+ probeCloud,
1661
+ probeDevice,
1662
+ shortcutDefinition,
1663
+ stripCodeFences,
1664
+ targetTripleFrom,
1665
+ toAppleSchema,
1666
+ withDocuments
1667
+ });