@ai-matrx/agents 0.18.1 → 0.18.5

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.
@@ -8,12 +8,12 @@ function deliveryPatch(delivery) {
8
8
  var DELIVERY_WORDS = {
9
9
  direct: {
10
10
  label: "Include the text",
11
- hint: "The text is handed to the AI with your request, so it reads all of it before it starts.",
11
+ hint: "Sent in full",
12
12
  summary: "Text included"
13
13
  },
14
14
  context: {
15
15
  label: "Let the AI look it up",
16
- hint: "Nothing is sent up front. The AI opens this Source and reads the parts it needs while it works \u2014 best for very large Sources.",
16
+ hint: "Read as needed",
17
17
  summary: "Looked up when needed"
18
18
  }
19
19
  };
@@ -90,7 +90,7 @@ function resumableInput(draft) {
90
90
  case "youtube":
91
91
  return input.url?.trim() ? input : null;
92
92
  case "audio":
93
- return input.fileId ? input : null;
93
+ return input.fileId || input.text?.trim() ? input : null;
94
94
  default:
95
95
  return null;
96
96
  }
@@ -106,12 +106,12 @@ function reloadedCard(draft, status) {
106
106
  return { action: "lost", sentence: RELOADED_WHILE_ADDING };
107
107
  }
108
108
  var MAX_KEPT_TEXT_KNOB = { feature: "sources", key: "max_kept_draft_chars" };
109
- var NOT_KEPT_FOR_RELOAD = "Too large to keep on this device, so it will not come back if the page reloads before it is added.";
109
+ var NOT_KEPT_FOR_RELOAD = "Too large to restore if the page reloads before it is added.";
110
110
  var WAITING_FOR_ORGANIZATION = "Waiting for an organization \u2014 choose one and this continues by itself.";
111
111
  var KEEP_WAITING_FOR_ORGANIZATION = "Read. Waiting for an organization to keep it \u2014 choose one and it is kept by itself.";
112
112
 
113
113
  // sources/runtime/fileSource.ts
114
- var UNREADABLE = "The file is stored, but it could not be read into a Source, so nothing in it can be used yet. Remove it, or add a different copy (a PDF, Word file or plain text reads best).";
114
+ var UNREADABLE = "Can't read this file \u2014 add a PDF, Word or text copy.";
115
115
  function nextFileStep(status, ctx) {
116
116
  const pdId = status?.processed_document_id ?? null;
117
117
  if (pdId) return { do: "keep", processedDocumentId: pdId };
@@ -119,7 +119,7 @@ function nextFileStep(status, ctx) {
119
119
  if (status?.state === "failed")
120
120
  return {
121
121
  do: "unreadable",
122
- reason: status.error?.message ? `The file could not be read: ${status.error.message}. Remove it, or add it again.` : UNREADABLE
122
+ reason: status.error?.message ? `Can't read this file: ${status.error.message}. Add it again.` : UNREADABLE
123
123
  };
124
124
  if (!ctx.started) return { do: "start" };
125
125
  return { do: "unreadable", reason: UNREADABLE };
@@ -235,191 +235,10 @@ function totalChars(value) {
235
235
  return value.sources.filter((entry) => entry.state === "ready" || entry.state === "processing").reduce((sum, entry) => sum + manifestEntryChars(entry), 0);
236
236
  }
237
237
 
238
- // sources/runtime/plan.ts
239
- var HEAVY_SHARE = 0.7;
240
- var PAGE_SUFFIX = (page) => page !== void 0 && page > 0 ? ` (page ${page})` : "";
241
- function renderedPartChars(segment, chars) {
242
- return `### Chunk ${segment.id}${PAGE_SUFFIX(segment.page)}`.length + 1 + chars;
243
- }
244
- function capParts(parts, maxChars) {
245
- if (!maxChars) return { kept: parts.map((seg) => ({ seg, chars: seg.chars })), capped: false };
246
- const kept = [];
247
- let used = 0;
248
- for (const seg of parts) {
249
- if (used + seg.chars <= maxChars) {
250
- kept.push({ seg, chars: seg.chars });
251
- used += seg.chars;
252
- continue;
253
- }
254
- const room = maxChars - used;
255
- if (room > 0 && kept.length === 0) kept.push({ seg, chars: room });
256
- return { kept, capped: true };
257
- }
258
- return { kept, capped: false };
259
- }
260
- function chosenForm(entry, ref) {
261
- const key = ref.representation ?? entry.default_form;
262
- return entry.forms.find((f) => f.form === key) ?? entry.forms.find((f) => f.form === entry.default_form) ?? null;
263
- }
264
- function isUsable(entry) {
265
- return entry.state === "ready" || entry.state === "processing";
266
- }
267
- function measure(entry, ref) {
268
- const form = chosenForm(entry, ref);
269
- const formChars = form?.chars ?? 0;
270
- const formLabel = form?.label ?? "Text";
271
- const segments = entry.segments ?? null;
272
- if (segments && segments.length > 0) {
273
- const wanted = ref.include_segments?.length ? new Set(ref.include_segments) : null;
274
- const chosen = wanted ? segments.filter((s) => wanted.has(s.id)) : segments;
275
- const { kept, capped: capped2 } = capParts(chosen, ref.max_chars);
276
- const body = kept.reduce((sum, k) => sum + renderedPartChars(k.seg, k.chars), 0);
277
- const sentChars = kept.length ? body + 2 * (kept.length - 1) : 0;
278
- return {
279
- formChars,
280
- formLabel,
281
- sentChars,
282
- exact: true,
283
- partsSent: kept.length,
284
- partsTotal: segments.length,
285
- capped: capped2
286
- };
287
- }
288
- const capped = ref.max_chars !== void 0 && formChars > ref.max_chars;
289
- return {
290
- formChars,
291
- formLabel,
292
- sentChars: capped ? ref.max_chars ?? formChars : formChars,
293
- exact: false,
294
- partsSent: null,
295
- partsTotal: null,
296
- capped
297
- };
298
- }
299
- function planSourceReview(input) {
300
- const estimateTokens = input.estimateTokens ?? estimateSourceTokens;
301
- const windowIsFallback = !input.modelWindowTokens || input.modelWindowTokens <= 0;
302
- const windowTokens = windowIsFallback ? input.fallbackWindowTokens : input.modelWindowTokens;
303
- let sentChars = 0;
304
- const entries = input.manifest.sources.map((entry, index) => {
305
- const ref = input.refs[index] ?? entry.ref;
306
- const m = measure(entry, ref);
307
- const base = { index, ref, entry, ...m };
308
- if (!isUsable(entry)) {
309
- return { ...base, status: "unusable", sentChars: 0, sentTokens: 0 };
310
- }
311
- if (sourceDelivery(ref) === "context") {
312
- return { ...base, status: "on_demand", sentChars: 0, sentTokens: 0 };
313
- }
314
- if (estimateTokens(sentChars + m.sentChars) > windowTokens) {
315
- return { ...base, status: "left_out", sentTokens: estimateTokens(m.sentChars) };
316
- }
317
- sentChars += m.sentChars;
318
- return { ...base, status: "included", sentTokens: estimateTokens(m.sentChars) };
319
- });
320
- const sentTokens = estimateTokens(sentChars);
321
- const leftOut = entries.filter((e) => e.status === "left_out");
322
- const share = windowTokens > 0 ? sentTokens / windowTokens : 0;
323
- const anythingGoesIn = entries.some((e) => e.status === "included" || e.status === "on_demand");
324
- const verdict = !anythingGoesIn && leftOut.length === 0 ? "empty" : leftOut.length > 0 ? "too_much" : share >= HEAVY_SHARE ? "heavy" : "fine";
325
- const kept = entries.filter((e) => e.status !== "left_out").map(
326
- (e) => createSourceRef(e.ref.resource_type, e.ref.resource_id, {
327
- representation: e.ref.representation,
328
- include_segments: e.ref.include_segments,
329
- delivery: e.ref.delivery,
330
- max_chars: e.ref.max_chars,
331
- promote: e.ref.promote,
332
- exclude: e.ref.exclude
333
- })
334
- );
335
- const grounding = input.base.grounding === "selected" && !kept.some((r) => r.include_segments?.length) ? void 0 : input.base.grounding;
336
- const sourceSet = createSourceSet(kept, {
337
- topic: input.base.topic,
338
- grounding,
339
- retrieve_query: input.base.retrieve_query,
340
- target_model_id: input.targetModelId ?? input.base.target_model_id
341
- });
342
- return {
343
- entries,
344
- sentChars,
345
- sentTokens,
346
- windowTokens,
347
- windowIsFallback,
348
- verdict,
349
- share,
350
- leftOut,
351
- sourceSet
352
- };
353
- }
354
-
355
- // sources/runtime/client.ts
356
- function marked(options) {
357
- return {
358
- bodyCarriedRead: true,
359
- ...options?.organizationId ? { organizationId: options.organizationId } : {},
360
- ...options?.signal ? { signal: options.signal } : {}
361
- };
362
- }
363
- function createSourcesClient(transport) {
364
- return {
365
- manifest: (sourceSet, options) => transport.post("/sources/manifest", { source_set: sourceSet }, marked(options)),
366
- resolve: (sourceSet, options) => transport.post("/sources/resolve", { source_set: sourceSet }, marked(options)),
367
- searchParts: (ref, query, options) => transport.post("/sources/parts/search", { source_ref: ref, query }, marked(options))
368
- };
369
- }
370
- var SourceDoorError = class extends Error {
371
- status;
372
- body;
373
- constructor(status, message, body) {
374
- super(message);
375
- this.name = "SourceDoorError";
376
- this.status = status;
377
- this.body = body;
378
- }
379
- };
380
- function refusalMessage(status, body) {
381
- if (body && typeof body === "object") {
382
- const record = body;
383
- const error = record.error;
384
- if (error && typeof error === "object") {
385
- const message = error.user_message ?? error.message;
386
- if (typeof message === "string" && message) return message;
387
- }
388
- if (typeof record.detail === "string" && record.detail) return record.detail;
389
- }
390
- return `The server answered ${status}.`;
391
- }
392
- function createFetchSourcesTransport(options) {
393
- const base = options.baseUrl.replace(/\/+$/, "");
394
- return {
395
- async post(path, body, call) {
396
- const doFetch = options.fetch ?? fetch;
397
- const headers = {
398
- "Content-Type": "application/json",
399
- ...options.headers ? await options.headers() : {}
400
- };
401
- if (call.organizationId) headers[options.organizationHeader ?? "X-Organization-Id"] = call.organizationId;
402
- const response = await doFetch(`${base}${path}`, {
403
- method: "POST",
404
- headers,
405
- body: JSON.stringify(body),
406
- ...call.signal ? { signal: call.signal } : {}
407
- });
408
- const text = await response.text();
409
- let parsed = null;
410
- try {
411
- parsed = text ? JSON.parse(text) : null;
412
- } catch {
413
- parsed = text;
414
- }
415
- if (!response.ok) throw new SourceDoorError(response.status, refusalMessage(response.status, parsed), parsed);
416
- return parsed;
417
- }
418
- };
419
- }
420
-
421
238
  // sources/runtime/state.ts
422
239
  var EMPTY_SOURCE_SET_STATE = Object.freeze({ cards: Object.freeze([]), topic: "" });
240
+ var PARTS_MATCHED_BY_PAGE = "The clean version is ready \u2014 your parts were matched to it by page.";
241
+ var PARTS_NOT_MATCHED = "The clean version is ready, but your parts could not be matched to it \u2014 choose parts again. Until then the whole Source goes in.";
423
242
  var SAME_SOURCE_AGAIN = "You added this again \u2014 it is the same Source, so it is listed once.";
424
243
  function isSourceDraft(value) {
425
244
  return !!value && typeof value === "object" && typeof value.kind === "string" && typeof value.label === "string";
@@ -486,7 +305,7 @@ function settleCard(state, id, patch, options = {}) {
486
305
  }));
487
306
  }
488
307
  function failCard(state, id, sentence) {
489
- return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence }));
308
+ return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence, manifest: null }));
490
309
  }
491
310
  function restartCard(state, id) {
492
311
  return replaceCard(state, id, (c) => ({ ...c, status: "resolving", error: null }));
@@ -502,7 +321,68 @@ function updateCardRef(state, id, options) {
502
321
  return replaceCard(state, id, (c) => {
503
322
  if (!c.draft.ref) return c;
504
323
  const ref = createSourceRef(c.draft.ref.resource_type, c.draft.ref.resource_id, { ...c.draft.ref, ...options });
505
- return { ...c, draft: { ...c.draft, ref } };
324
+ return rememberPickedParts({ ...c, draft: { ...c.draft, ref } });
325
+ });
326
+ }
327
+ function rememberPickedParts(card) {
328
+ const ids = card.draft.ref?.include_segments ?? [];
329
+ if (ids.length === 0) {
330
+ return card.draft.pickedParts === void 0 ? card : { ...card, draft: { ...card.draft, pickedParts: void 0 } };
331
+ }
332
+ const segments = card.manifest?.segments ?? [];
333
+ const known = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
334
+ const picked = [];
335
+ for (const id of ids) {
336
+ const segment = segments.find((s) => s.id === id);
337
+ if (segment) {
338
+ const pages = partPages(segment);
339
+ picked.push({ id, label: segment.label, ...pages ? { pages } : {} });
340
+ } else {
341
+ const before = known.get(id);
342
+ if (before) picked.push(before);
343
+ }
344
+ }
345
+ return { ...card, draft: { ...card.draft, pickedParts: picked.length ? picked : void 0 } };
346
+ }
347
+ function withNote(notes, note, drop = []) {
348
+ const kept = (notes ?? []).filter((n) => n !== note && !drop.includes(n));
349
+ return [...kept, note];
350
+ }
351
+ function carryPickedParts(card, entry) {
352
+ const ref = card.draft.ref;
353
+ const picked = ref?.include_segments ?? [];
354
+ const segments = entry.segments ?? [];
355
+ if (!ref || picked.length === 0 || segments.length === 0) return card;
356
+ const ids = new Set(segments.map((s) => s.id));
357
+ const stale = picked.filter((id) => !ids.has(id));
358
+ if (stale.length === 0) return card;
359
+ const remembered = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
360
+ const oldSegments = card.manifest?.segments ?? [];
361
+ const spans = [];
362
+ let unknown = 0;
363
+ for (const id of stale) {
364
+ const old = oldSegments.find((s) => s.id === id);
365
+ const pages = remembered.get(id)?.pages ?? (old ? partPages(old) : null);
366
+ if (pages) spans.push(pages);
367
+ else unknown += 1;
368
+ }
369
+ const keep = new Set(picked.filter((id) => ids.has(id)));
370
+ for (const segment of segments) {
371
+ const pages = partPages(segment);
372
+ if (pages && spans.some(([from, to]) => pages[0] <= to && pages[1] >= from)) keep.add(segment.id);
373
+ }
374
+ const nextIds = segments.filter((s) => keep.has(s.id)).map((s) => s.id);
375
+ const matched = unknown === 0 && nextIds.length > 0;
376
+ const nextRef = createSourceRef(ref.resource_type, ref.resource_id, {
377
+ ...ref,
378
+ include_segments: matched ? nextIds : void 0
379
+ });
380
+ const note = matched ? PARTS_MATCHED_BY_PAGE : PARTS_NOT_MATCHED;
381
+ const other = matched ? PARTS_NOT_MATCHED : PARTS_MATCHED_BY_PAGE;
382
+ return rememberPickedParts({
383
+ ...card,
384
+ manifest: entry,
385
+ draft: { ...card.draft, ref: nextRef, notes: withNote(card.draft.notes, note, [other]) }
506
386
  });
507
387
  }
508
388
  function setCardWaitForClean(state, id, wait) {
@@ -518,7 +398,7 @@ function applyReviewedSourceSet(state, set) {
518
398
  if (!ref) continue;
519
399
  const match = set.sources.find((r) => sameSourceRef(r, ref));
520
400
  if (!match) continue;
521
- next = replaceCard(next, card.id, (c) => ({ ...c, draft: { ...c.draft, ref: match } }));
401
+ next = replaceCard(next, card.id, (c) => rememberPickedParts({ ...c, draft: { ...c.draft, ref: match } }));
522
402
  }
523
403
  return set.topic !== void 0 ? setSourceTopic(next, set.topic) : next;
524
404
  }
@@ -528,7 +408,13 @@ function applyManifest(state, manifest) {
528
408
  const ref = card.draft.ref;
529
409
  if (!ref || card.status !== "ready") continue;
530
410
  const entry = manifest.sources.find((e) => sameSourceRef(e.ref, ref));
531
- if (entry) next = replaceCard(next, card.id, (c) => ({ ...c, manifest: entry }));
411
+ if (!entry) continue;
412
+ next = replaceCard(next, card.id, (c) => {
413
+ const carried = carryPickedParts(c, entry);
414
+ if (carried !== c) return carried;
415
+ const measured = { ...c, manifest: entry };
416
+ return c.draft.ref?.include_segments?.length && !c.draft.pickedParts ? rememberPickedParts(measured) : measured;
417
+ });
532
418
  }
533
419
  return next;
534
420
  }
@@ -601,6 +487,27 @@ function selectTotalChars(state) {
601
487
  model_context_tokens: null
602
488
  });
603
489
  }
490
+ function sourceEntryChars(entry, ref) {
491
+ if (entry.state !== "ready" && entry.state !== "processing") return null;
492
+ return totalChars({
493
+ __kind: "source_manifest",
494
+ sources: [{ ...entry, ref }],
495
+ total_chars: 0,
496
+ estimated_tokens: 0,
497
+ model_context_tokens: null
498
+ });
499
+ }
500
+ function sourceCardChars(card) {
501
+ return card.manifest && card.draft.ref ? sourceEntryChars(card.manifest, card.draft.ref) : null;
502
+ }
503
+ function sourceCardState(card) {
504
+ if (card.status === "pending" || card.status === "resolving") return "adding";
505
+ if (card.status === "error") return card.error === WAITING_FOR_ORGANIZATION ? "waiting_for_organization" : "failed";
506
+ return card.manifest?.state ?? "unmeasured";
507
+ }
508
+ function sourceCardMeasuredState(card) {
509
+ return card.status === "ready" ? card.manifest?.state ?? null : null;
510
+ }
604
511
  function selectSettled(state) {
605
512
  return state.cards.every((c) => c.status === "ready");
606
513
  }
@@ -622,6 +529,190 @@ function withDisplayNames(resolved, cards) {
622
529
  };
623
530
  }
624
531
 
532
+ // sources/runtime/plan.ts
533
+ var HEAVY_SHARE = 0.7;
534
+ var PAGE_SUFFIX = (page) => page !== void 0 && page > 0 ? ` (page ${page})` : "";
535
+ function renderedPartChars(segment, chars) {
536
+ return `### Chunk ${segment.id}${PAGE_SUFFIX(segment.page)}`.length + 1 + chars;
537
+ }
538
+ function capParts(parts, maxChars) {
539
+ if (!maxChars) return { kept: parts.map((seg) => ({ seg, chars: seg.chars })), capped: false };
540
+ const kept = [];
541
+ let used = 0;
542
+ for (const seg of parts) {
543
+ if (used + seg.chars <= maxChars) {
544
+ kept.push({ seg, chars: seg.chars });
545
+ used += seg.chars;
546
+ continue;
547
+ }
548
+ const room = maxChars - used;
549
+ if (room > 0 && kept.length === 0) kept.push({ seg, chars: room });
550
+ return { kept, capped: true };
551
+ }
552
+ return { kept, capped: false };
553
+ }
554
+ function chosenForm(entry, ref) {
555
+ const key = ref.representation ?? entry.default_form;
556
+ return entry.forms.find((f) => f.form === key) ?? entry.forms.find((f) => f.form === entry.default_form) ?? null;
557
+ }
558
+ function isUsable(entry) {
559
+ return entry.state === "ready" || entry.state === "processing";
560
+ }
561
+ function measure(entry, ref) {
562
+ const form = chosenForm(entry, ref);
563
+ const formChars = form?.chars ?? 0;
564
+ const formLabel = form?.label ?? "Text";
565
+ const segments = entry.segments ?? null;
566
+ if (segments && segments.length > 0) {
567
+ const wanted = ref.include_segments?.length ? new Set(ref.include_segments) : null;
568
+ const chosen = wanted ? segments.filter((s) => wanted.has(s.id)) : segments;
569
+ const { kept, capped: capped2 } = capParts(chosen, ref.max_chars);
570
+ const body = kept.reduce((sum, k) => sum + renderedPartChars(k.seg, k.chars), 0);
571
+ const sentChars = kept.length ? body + 2 * (kept.length - 1) : 0;
572
+ return {
573
+ formChars,
574
+ formLabel,
575
+ sentChars,
576
+ exact: true,
577
+ partsSent: kept.length,
578
+ partsTotal: segments.length,
579
+ capped: capped2
580
+ };
581
+ }
582
+ const capped = ref.max_chars !== void 0 && formChars > ref.max_chars;
583
+ return {
584
+ formChars,
585
+ formLabel,
586
+ sentChars: capped ? ref.max_chars ?? formChars : formChars,
587
+ exact: false,
588
+ partsSent: null,
589
+ partsTotal: null,
590
+ capped
591
+ };
592
+ }
593
+ function planSourceReview(input) {
594
+ const estimateTokens = input.estimateTokens ?? estimateSourceTokens;
595
+ const windowIsFallback = !input.modelWindowTokens || input.modelWindowTokens <= 0;
596
+ const windowTokens = windowIsFallback ? input.fallbackWindowTokens : input.modelWindowTokens;
597
+ let sentChars = 0;
598
+ const entries = input.manifest.sources.map((entry, index) => {
599
+ const ref = input.refs[index] ?? entry.ref;
600
+ const m = measure(entry, ref);
601
+ const base = { index, ref, entry, ...m, chars: sourceEntryChars(entry, ref) ?? 0 };
602
+ if (!isUsable(entry)) {
603
+ return { ...base, status: "unusable", sentChars: 0, sentTokens: 0 };
604
+ }
605
+ if (sourceDelivery(ref) === "context") {
606
+ return { ...base, status: "on_demand", sentChars: 0, sentTokens: 0 };
607
+ }
608
+ if (estimateTokens(sentChars + m.sentChars) > windowTokens) {
609
+ return { ...base, status: "left_out", sentTokens: estimateTokens(m.sentChars) };
610
+ }
611
+ sentChars += m.sentChars;
612
+ return { ...base, status: "included", sentTokens: estimateTokens(m.sentChars) };
613
+ });
614
+ const sentTokens = estimateTokens(sentChars);
615
+ const leftOut = entries.filter((e) => e.status === "left_out");
616
+ const share = windowTokens > 0 ? sentTokens / windowTokens : 0;
617
+ const anythingGoesIn = entries.some((e) => e.status === "included" || e.status === "on_demand");
618
+ const verdict = !anythingGoesIn && leftOut.length === 0 ? "empty" : leftOut.length > 0 ? "too_much" : share >= HEAVY_SHARE ? "heavy" : "fine";
619
+ const kept = entries.filter((e) => e.status !== "left_out").map(
620
+ (e) => createSourceRef(e.ref.resource_type, e.ref.resource_id, {
621
+ representation: e.ref.representation,
622
+ include_segments: e.ref.include_segments,
623
+ delivery: e.ref.delivery,
624
+ max_chars: e.ref.max_chars,
625
+ promote: e.ref.promote,
626
+ exclude: e.ref.exclude
627
+ })
628
+ );
629
+ const grounding = input.base.grounding === "selected" && !kept.some((r) => r.include_segments?.length) ? void 0 : input.base.grounding;
630
+ const sourceSet = createSourceSet(kept, {
631
+ topic: input.base.topic,
632
+ grounding,
633
+ retrieve_query: input.base.retrieve_query,
634
+ target_model_id: input.targetModelId ?? input.base.target_model_id
635
+ });
636
+ return {
637
+ entries,
638
+ chars: entries.filter((e) => e.status === "included").reduce((sum, e) => sum + e.chars, 0),
639
+ sentChars,
640
+ sentTokens,
641
+ windowTokens,
642
+ windowIsFallback,
643
+ verdict,
644
+ share,
645
+ leftOut,
646
+ sourceSet
647
+ };
648
+ }
649
+
650
+ // sources/runtime/client.ts
651
+ function marked(options) {
652
+ return {
653
+ bodyCarriedRead: true,
654
+ ...options?.organizationId ? { organizationId: options.organizationId } : {},
655
+ ...options?.signal ? { signal: options.signal } : {}
656
+ };
657
+ }
658
+ function createSourcesClient(transport) {
659
+ return {
660
+ manifest: (sourceSet, options) => transport.post("/sources/manifest", { source_set: sourceSet }, marked(options)),
661
+ resolve: (sourceSet, options) => transport.post("/sources/resolve", { source_set: sourceSet }, marked(options)),
662
+ searchParts: (ref, query, options) => transport.post("/sources/parts/search", { source_ref: ref, query }, marked(options))
663
+ };
664
+ }
665
+ var SourceDoorError = class extends Error {
666
+ status;
667
+ body;
668
+ constructor(status, message, body) {
669
+ super(message);
670
+ this.name = "SourceDoorError";
671
+ this.status = status;
672
+ this.body = body;
673
+ }
674
+ };
675
+ function refusalMessage(status, body) {
676
+ if (body && typeof body === "object") {
677
+ const record = body;
678
+ const error = record.error;
679
+ if (error && typeof error === "object") {
680
+ const message = error.user_message ?? error.message;
681
+ if (typeof message === "string" && message) return message;
682
+ }
683
+ if (typeof record.detail === "string" && record.detail) return record.detail;
684
+ }
685
+ return `The server answered ${status}.`;
686
+ }
687
+ function createFetchSourcesTransport(options) {
688
+ const base = options.baseUrl.replace(/\/+$/, "");
689
+ return {
690
+ async post(path, body, call) {
691
+ const doFetch = options.fetch ?? fetch;
692
+ const headers = {
693
+ "Content-Type": "application/json",
694
+ ...options.headers ? await options.headers() : {}
695
+ };
696
+ if (call.organizationId) headers[options.organizationHeader ?? "X-Organization-Id"] = call.organizationId;
697
+ const response = await doFetch(`${base}${path}`, {
698
+ method: "POST",
699
+ headers,
700
+ body: JSON.stringify(body),
701
+ ...call.signal ? { signal: call.signal } : {}
702
+ });
703
+ const text = await response.text();
704
+ let parsed = null;
705
+ try {
706
+ parsed = text ? JSON.parse(text) : null;
707
+ } catch {
708
+ parsed = text;
709
+ }
710
+ if (!response.ok) throw new SourceDoorError(response.status, refusalMessage(response.status, parsed), parsed);
711
+ return parsed;
712
+ }
713
+ };
714
+ }
715
+
625
716
  // sources/runtime/controller.ts
626
717
  function randomId() {
627
718
  const cryptoApi = globalThis.crypto;
@@ -1041,20 +1132,45 @@ function createSourceIntake(set, deps) {
1041
1132
  });
1042
1133
  await hold(() => landPaste(id, trimmed, name));
1043
1134
  },
1135
+ async addRecordedText(text, name) {
1136
+ const trimmed = text.trim();
1137
+ if (!trimmed) return;
1138
+ const max = deps.maxKeptChars();
1139
+ const kept = max !== null && trimmed.length <= max;
1140
+ const id = set.addPending({
1141
+ kind: "audio",
1142
+ label: name?.trim() || "Recording",
1143
+ origin: "Recording",
1144
+ input: kept ? { text: trimmed, name } : void 0,
1145
+ notes: kept ? void 0 : [NOT_KEPT_FOR_RELOAD]
1146
+ });
1147
+ await hold(() => landPaste(id, trimmed, name));
1148
+ },
1044
1149
  async addWebPage(raw) {
1045
1150
  const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1046
1151
  const id = set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1047
1152
  await hold(() => landWebPage(id, url));
1048
1153
  },
1049
- async addScrapedPage(page) {
1154
+ beginWebPage(raw) {
1155
+ const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1156
+ return set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1157
+ },
1158
+ async addScrapedPage(page, pendingId) {
1050
1159
  const label = page.title || hostOf(page.url);
1160
+ const cardFor = () => {
1161
+ if (pendingId && set.restart(pendingId)) {
1162
+ set.updateDraft(pendingId, { label, origin: page.url, input: { url: page.url } });
1163
+ return pendingId;
1164
+ }
1165
+ return set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1166
+ };
1051
1167
  if (!page.processedDocumentId) {
1052
- const id2 = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1168
+ const id2 = cardFor();
1053
1169
  await hold(() => landPaste(id2, page.text, label));
1054
1170
  return;
1055
1171
  }
1056
1172
  const pdId = page.processedDocumentId;
1057
- const id = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1173
+ const id = cardFor();
1058
1174
  await hold(async () => {
1059
1175
  let notes = [];
1060
1176
  try {
@@ -1129,7 +1245,8 @@ function createSourceIntake(set, deps) {
1129
1245
  return true;
1130
1246
  }
1131
1247
  case "audio":
1132
- void transcribeRecording(card.id, input.fileId ?? "", card.draft.label);
1248
+ if (input.fileId) void transcribeRecording(card.id, input.fileId, card.draft.label);
1249
+ else void landPaste(card.id, (input.text ?? "").trim(), input.name);
1133
1250
  return true;
1134
1251
  default:
1135
1252
  return false;
@@ -1257,6 +1374,8 @@ export {
1257
1374
  KEEP_WAITING_FOR_ORGANIZATION,
1258
1375
  MAX_KEPT_TEXT_KNOB,
1259
1376
  NOT_KEPT_FOR_RELOAD,
1377
+ PARTS_MATCHED_BY_PAGE,
1378
+ PARTS_NOT_MATCHED,
1260
1379
  RELOADED_RESUMING,
1261
1380
  RELOADED_WHILE_ADDING,
1262
1381
  SAME_SOURCE_AGAIN,
@@ -1270,6 +1389,7 @@ export {
1270
1389
  applyReviewedSourceSet,
1271
1390
  cardsToResume,
1272
1391
  cardsWaitingForOrganization,
1392
+ carryPickedParts,
1273
1393
  chosenForm,
1274
1394
  continueAfterOrganization,
1275
1395
  createFetchSourcesTransport,
@@ -1303,6 +1423,7 @@ export {
1303
1423
  readySourceRefs,
1304
1424
  registeredFileEdge,
1305
1425
  reloadedCard,
1426
+ rememberPickedParts,
1306
1427
  removeCard,
1307
1428
  renderedPartChars,
1308
1429
  restartCard,
@@ -1317,7 +1438,11 @@ export {
1317
1438
  setSourceTopic,
1318
1439
  settleCard,
1319
1440
  sourceAttachTargets,
1441
+ sourceCardChars,
1442
+ sourceCardMeasuredState,
1443
+ sourceCardState,
1320
1444
  sourceDelivery,
1445
+ sourceEntryChars,
1321
1446
  sourceKey,
1322
1447
  sourceRecoveryGuards,
1323
1448
  sourceRoomLeft,