@ai-matrx/agents 0.18.1 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,6 +27,8 @@ __export(runtime_exports, {
27
27
  KEEP_WAITING_FOR_ORGANIZATION: () => KEEP_WAITING_FOR_ORGANIZATION,
28
28
  MAX_KEPT_TEXT_KNOB: () => MAX_KEPT_TEXT_KNOB,
29
29
  NOT_KEPT_FOR_RELOAD: () => NOT_KEPT_FOR_RELOAD,
30
+ PARTS_MATCHED_BY_PAGE: () => PARTS_MATCHED_BY_PAGE,
31
+ PARTS_NOT_MATCHED: () => PARTS_NOT_MATCHED,
30
32
  RELOADED_RESUMING: () => RELOADED_RESUMING,
31
33
  RELOADED_WHILE_ADDING: () => RELOADED_WHILE_ADDING,
32
34
  SAME_SOURCE_AGAIN: () => SAME_SOURCE_AGAIN,
@@ -40,6 +42,7 @@ __export(runtime_exports, {
40
42
  applyReviewedSourceSet: () => applyReviewedSourceSet,
41
43
  cardsToResume: () => cardsToResume,
42
44
  cardsWaitingForOrganization: () => cardsWaitingForOrganization,
45
+ carryPickedParts: () => carryPickedParts,
43
46
  chosenForm: () => chosenForm,
44
47
  continueAfterOrganization: () => continueAfterOrganization,
45
48
  createFetchSourcesTransport: () => createFetchSourcesTransport,
@@ -73,7 +76,9 @@ __export(runtime_exports, {
73
76
  readySourceRefs: () => readySourceRefs,
74
77
  registeredFileEdge: () => registeredFileEdge,
75
78
  reloadedCard: () => reloadedCard,
79
+ rememberPickedParts: () => rememberPickedParts,
76
80
  removeCard: () => removeCard,
81
+ removedInReview: () => removedInReview,
77
82
  renderedPartChars: () => renderedPartChars,
78
83
  restartCard: () => restartCard,
79
84
  restoredCards: () => restoredCards,
@@ -87,7 +92,11 @@ __export(runtime_exports, {
87
92
  setSourceTopic: () => setSourceTopic,
88
93
  settleCard: () => settleCard,
89
94
  sourceAttachTargets: () => sourceAttachTargets,
95
+ sourceCardChars: () => sourceCardChars,
96
+ sourceCardMeasuredState: () => sourceCardMeasuredState,
97
+ sourceCardState: () => sourceCardState,
90
98
  sourceDelivery: () => sourceDelivery,
99
+ sourceEntryChars: () => sourceEntryChars,
91
100
  sourceKey: () => sourceKey,
92
101
  sourceRecoveryGuards: () => sourceRecoveryGuards,
93
102
  sourceRoomLeft: () => sourceRoomLeft,
@@ -110,12 +119,12 @@ function deliveryPatch(delivery) {
110
119
  var DELIVERY_WORDS = {
111
120
  direct: {
112
121
  label: "Include the text",
113
- hint: "The text is handed to the AI with your request, so it reads all of it before it starts.",
122
+ hint: "Sent in full",
114
123
  summary: "Text included"
115
124
  },
116
125
  context: {
117
126
  label: "Let the AI look it up",
118
- 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.",
127
+ hint: "Read as needed",
119
128
  summary: "Looked up when needed"
120
129
  }
121
130
  };
@@ -192,7 +201,7 @@ function resumableInput(draft) {
192
201
  case "youtube":
193
202
  return input.url?.trim() ? input : null;
194
203
  case "audio":
195
- return input.fileId ? input : null;
204
+ return input.fileId || input.text?.trim() ? input : null;
196
205
  default:
197
206
  return null;
198
207
  }
@@ -208,12 +217,12 @@ function reloadedCard(draft, status) {
208
217
  return { action: "lost", sentence: RELOADED_WHILE_ADDING };
209
218
  }
210
219
  var MAX_KEPT_TEXT_KNOB = { feature: "sources", key: "max_kept_draft_chars" };
211
- 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.";
220
+ var NOT_KEPT_FOR_RELOAD = "Too large to restore if the page reloads before it is added.";
212
221
  var WAITING_FOR_ORGANIZATION = "Waiting for an organization \u2014 choose one and this continues by itself.";
213
222
  var KEEP_WAITING_FOR_ORGANIZATION = "Read. Waiting for an organization to keep it \u2014 choose one and it is kept by itself.";
214
223
 
215
224
  // sources/runtime/fileSource.ts
216
- 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).";
225
+ var UNREADABLE = "Can't read this file \u2014 add a PDF, Word or text copy.";
217
226
  function nextFileStep(status, ctx) {
218
227
  const pdId = status?.processed_document_id ?? null;
219
228
  if (pdId) return { do: "keep", processedDocumentId: pdId };
@@ -221,7 +230,7 @@ function nextFileStep(status, ctx) {
221
230
  if (status?.state === "failed")
222
231
  return {
223
232
  do: "unreadable",
224
- reason: status.error?.message ? `The file could not be read: ${status.error.message}. Remove it, or add it again.` : UNREADABLE
233
+ reason: status.error?.message ? `Can't read this file: ${status.error.message}. Add it again.` : UNREADABLE
225
234
  };
226
235
  if (!ctx.started) return { do: "start" };
227
236
  return { do: "unreadable", reason: UNREADABLE };
@@ -337,191 +346,10 @@ function totalChars(value) {
337
346
  return value.sources.filter((entry) => entry.state === "ready" || entry.state === "processing").reduce((sum, entry) => sum + manifestEntryChars(entry), 0);
338
347
  }
339
348
 
340
- // sources/runtime/plan.ts
341
- var HEAVY_SHARE = 0.7;
342
- var PAGE_SUFFIX = (page) => page !== void 0 && page > 0 ? ` (page ${page})` : "";
343
- function renderedPartChars(segment, chars) {
344
- return `### Chunk ${segment.id}${PAGE_SUFFIX(segment.page)}`.length + 1 + chars;
345
- }
346
- function capParts(parts, maxChars) {
347
- if (!maxChars) return { kept: parts.map((seg) => ({ seg, chars: seg.chars })), capped: false };
348
- const kept = [];
349
- let used = 0;
350
- for (const seg of parts) {
351
- if (used + seg.chars <= maxChars) {
352
- kept.push({ seg, chars: seg.chars });
353
- used += seg.chars;
354
- continue;
355
- }
356
- const room = maxChars - used;
357
- if (room > 0 && kept.length === 0) kept.push({ seg, chars: room });
358
- return { kept, capped: true };
359
- }
360
- return { kept, capped: false };
361
- }
362
- function chosenForm(entry, ref) {
363
- const key = ref.representation ?? entry.default_form;
364
- return entry.forms.find((f) => f.form === key) ?? entry.forms.find((f) => f.form === entry.default_form) ?? null;
365
- }
366
- function isUsable(entry) {
367
- return entry.state === "ready" || entry.state === "processing";
368
- }
369
- function measure(entry, ref) {
370
- const form = chosenForm(entry, ref);
371
- const formChars = form?.chars ?? 0;
372
- const formLabel = form?.label ?? "Text";
373
- const segments = entry.segments ?? null;
374
- if (segments && segments.length > 0) {
375
- const wanted = ref.include_segments?.length ? new Set(ref.include_segments) : null;
376
- const chosen = wanted ? segments.filter((s) => wanted.has(s.id)) : segments;
377
- const { kept, capped: capped2 } = capParts(chosen, ref.max_chars);
378
- const body = kept.reduce((sum, k) => sum + renderedPartChars(k.seg, k.chars), 0);
379
- const sentChars = kept.length ? body + 2 * (kept.length - 1) : 0;
380
- return {
381
- formChars,
382
- formLabel,
383
- sentChars,
384
- exact: true,
385
- partsSent: kept.length,
386
- partsTotal: segments.length,
387
- capped: capped2
388
- };
389
- }
390
- const capped = ref.max_chars !== void 0 && formChars > ref.max_chars;
391
- return {
392
- formChars,
393
- formLabel,
394
- sentChars: capped ? ref.max_chars ?? formChars : formChars,
395
- exact: false,
396
- partsSent: null,
397
- partsTotal: null,
398
- capped
399
- };
400
- }
401
- function planSourceReview(input) {
402
- const estimateTokens = input.estimateTokens ?? estimateSourceTokens;
403
- const windowIsFallback = !input.modelWindowTokens || input.modelWindowTokens <= 0;
404
- const windowTokens = windowIsFallback ? input.fallbackWindowTokens : input.modelWindowTokens;
405
- let sentChars = 0;
406
- const entries = input.manifest.sources.map((entry, index) => {
407
- const ref = input.refs[index] ?? entry.ref;
408
- const m = measure(entry, ref);
409
- const base = { index, ref, entry, ...m };
410
- if (!isUsable(entry)) {
411
- return { ...base, status: "unusable", sentChars: 0, sentTokens: 0 };
412
- }
413
- if (sourceDelivery(ref) === "context") {
414
- return { ...base, status: "on_demand", sentChars: 0, sentTokens: 0 };
415
- }
416
- if (estimateTokens(sentChars + m.sentChars) > windowTokens) {
417
- return { ...base, status: "left_out", sentTokens: estimateTokens(m.sentChars) };
418
- }
419
- sentChars += m.sentChars;
420
- return { ...base, status: "included", sentTokens: estimateTokens(m.sentChars) };
421
- });
422
- const sentTokens = estimateTokens(sentChars);
423
- const leftOut = entries.filter((e) => e.status === "left_out");
424
- const share = windowTokens > 0 ? sentTokens / windowTokens : 0;
425
- const anythingGoesIn = entries.some((e) => e.status === "included" || e.status === "on_demand");
426
- const verdict = !anythingGoesIn && leftOut.length === 0 ? "empty" : leftOut.length > 0 ? "too_much" : share >= HEAVY_SHARE ? "heavy" : "fine";
427
- const kept = entries.filter((e) => e.status !== "left_out").map(
428
- (e) => createSourceRef(e.ref.resource_type, e.ref.resource_id, {
429
- representation: e.ref.representation,
430
- include_segments: e.ref.include_segments,
431
- delivery: e.ref.delivery,
432
- max_chars: e.ref.max_chars,
433
- promote: e.ref.promote,
434
- exclude: e.ref.exclude
435
- })
436
- );
437
- const grounding = input.base.grounding === "selected" && !kept.some((r) => r.include_segments?.length) ? void 0 : input.base.grounding;
438
- const sourceSet = createSourceSet(kept, {
439
- topic: input.base.topic,
440
- grounding,
441
- retrieve_query: input.base.retrieve_query,
442
- target_model_id: input.targetModelId ?? input.base.target_model_id
443
- });
444
- return {
445
- entries,
446
- sentChars,
447
- sentTokens,
448
- windowTokens,
449
- windowIsFallback,
450
- verdict,
451
- share,
452
- leftOut,
453
- sourceSet
454
- };
455
- }
456
-
457
- // sources/runtime/client.ts
458
- function marked(options) {
459
- return {
460
- bodyCarriedRead: true,
461
- ...options?.organizationId ? { organizationId: options.organizationId } : {},
462
- ...options?.signal ? { signal: options.signal } : {}
463
- };
464
- }
465
- function createSourcesClient(transport) {
466
- return {
467
- manifest: (sourceSet, options) => transport.post("/sources/manifest", { source_set: sourceSet }, marked(options)),
468
- resolve: (sourceSet, options) => transport.post("/sources/resolve", { source_set: sourceSet }, marked(options)),
469
- searchParts: (ref, query, options) => transport.post("/sources/parts/search", { source_ref: ref, query }, marked(options))
470
- };
471
- }
472
- var SourceDoorError = class extends Error {
473
- status;
474
- body;
475
- constructor(status, message, body) {
476
- super(message);
477
- this.name = "SourceDoorError";
478
- this.status = status;
479
- this.body = body;
480
- }
481
- };
482
- function refusalMessage(status, body) {
483
- if (body && typeof body === "object") {
484
- const record = body;
485
- const error = record.error;
486
- if (error && typeof error === "object") {
487
- const message = error.user_message ?? error.message;
488
- if (typeof message === "string" && message) return message;
489
- }
490
- if (typeof record.detail === "string" && record.detail) return record.detail;
491
- }
492
- return `The server answered ${status}.`;
493
- }
494
- function createFetchSourcesTransport(options) {
495
- const base = options.baseUrl.replace(/\/+$/, "");
496
- return {
497
- async post(path, body, call) {
498
- const doFetch = options.fetch ?? fetch;
499
- const headers = {
500
- "Content-Type": "application/json",
501
- ...options.headers ? await options.headers() : {}
502
- };
503
- if (call.organizationId) headers[options.organizationHeader ?? "X-Organization-Id"] = call.organizationId;
504
- const response = await doFetch(`${base}${path}`, {
505
- method: "POST",
506
- headers,
507
- body: JSON.stringify(body),
508
- ...call.signal ? { signal: call.signal } : {}
509
- });
510
- const text = await response.text();
511
- let parsed = null;
512
- try {
513
- parsed = text ? JSON.parse(text) : null;
514
- } catch {
515
- parsed = text;
516
- }
517
- if (!response.ok) throw new SourceDoorError(response.status, refusalMessage(response.status, parsed), parsed);
518
- return parsed;
519
- }
520
- };
521
- }
522
-
523
349
  // sources/runtime/state.ts
524
350
  var EMPTY_SOURCE_SET_STATE = Object.freeze({ cards: Object.freeze([]), topic: "" });
351
+ var PARTS_MATCHED_BY_PAGE = "The clean version is ready \u2014 your parts were matched to it by page.";
352
+ 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.";
525
353
  var SAME_SOURCE_AGAIN = "You added this again \u2014 it is the same Source, so it is listed once.";
526
354
  function isSourceDraft(value) {
527
355
  return !!value && typeof value === "object" && typeof value.kind === "string" && typeof value.label === "string";
@@ -588,7 +416,7 @@ function settleCard(state, id, patch, options = {}) {
588
416
  }));
589
417
  }
590
418
  function failCard(state, id, sentence) {
591
- return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence }));
419
+ return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence, manifest: null }));
592
420
  }
593
421
  function restartCard(state, id) {
594
422
  return replaceCard(state, id, (c) => ({ ...c, status: "resolving", error: null }));
@@ -604,7 +432,68 @@ function updateCardRef(state, id, options) {
604
432
  return replaceCard(state, id, (c) => {
605
433
  if (!c.draft.ref) return c;
606
434
  const ref = createSourceRef(c.draft.ref.resource_type, c.draft.ref.resource_id, { ...c.draft.ref, ...options });
607
- return { ...c, draft: { ...c.draft, ref } };
435
+ return rememberPickedParts({ ...c, draft: { ...c.draft, ref } });
436
+ });
437
+ }
438
+ function rememberPickedParts(card) {
439
+ const ids = card.draft.ref?.include_segments ?? [];
440
+ if (ids.length === 0) {
441
+ return card.draft.pickedParts === void 0 ? card : { ...card, draft: { ...card.draft, pickedParts: void 0 } };
442
+ }
443
+ const segments = card.manifest?.segments ?? [];
444
+ const known = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
445
+ const picked = [];
446
+ for (const id of ids) {
447
+ const segment = segments.find((s) => s.id === id);
448
+ if (segment) {
449
+ const pages = partPages(segment);
450
+ picked.push({ id, label: segment.label, ...pages ? { pages } : {} });
451
+ } else {
452
+ const before = known.get(id);
453
+ if (before) picked.push(before);
454
+ }
455
+ }
456
+ return { ...card, draft: { ...card.draft, pickedParts: picked.length ? picked : void 0 } };
457
+ }
458
+ function withNote(notes, note, drop = []) {
459
+ const kept = (notes ?? []).filter((n) => n !== note && !drop.includes(n));
460
+ return [...kept, note];
461
+ }
462
+ function carryPickedParts(card, entry) {
463
+ const ref = card.draft.ref;
464
+ const picked = ref?.include_segments ?? [];
465
+ const segments = entry.segments ?? [];
466
+ if (!ref || picked.length === 0 || segments.length === 0) return card;
467
+ const ids = new Set(segments.map((s) => s.id));
468
+ const stale = picked.filter((id) => !ids.has(id));
469
+ if (stale.length === 0) return card;
470
+ const remembered = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
471
+ const oldSegments = card.manifest?.segments ?? [];
472
+ const spans = [];
473
+ let unknown = 0;
474
+ for (const id of stale) {
475
+ const old = oldSegments.find((s) => s.id === id);
476
+ const pages = remembered.get(id)?.pages ?? (old ? partPages(old) : null);
477
+ if (pages) spans.push(pages);
478
+ else unknown += 1;
479
+ }
480
+ const keep = new Set(picked.filter((id) => ids.has(id)));
481
+ for (const segment of segments) {
482
+ const pages = partPages(segment);
483
+ if (pages && spans.some(([from, to]) => pages[0] <= to && pages[1] >= from)) keep.add(segment.id);
484
+ }
485
+ const nextIds = segments.filter((s) => keep.has(s.id)).map((s) => s.id);
486
+ const matched = unknown === 0 && nextIds.length > 0;
487
+ const nextRef = createSourceRef(ref.resource_type, ref.resource_id, {
488
+ ...ref,
489
+ include_segments: matched ? nextIds : void 0
490
+ });
491
+ const note = matched ? PARTS_MATCHED_BY_PAGE : PARTS_NOT_MATCHED;
492
+ const other = matched ? PARTS_NOT_MATCHED : PARTS_MATCHED_BY_PAGE;
493
+ return rememberPickedParts({
494
+ ...card,
495
+ manifest: entry,
496
+ draft: { ...card.draft, ref: nextRef, notes: withNote(card.draft.notes, note, [other]) }
608
497
  });
609
498
  }
610
499
  function setCardWaitForClean(state, id, wait) {
@@ -613,14 +502,24 @@ function setCardWaitForClean(state, id, wait) {
613
502
  function setSourceTopic(state, topic) {
614
503
  return state.topic === topic ? state : { ...state, topic };
615
504
  }
616
- function applyReviewedSourceSet(state, set) {
505
+ function removedInReview(reviewed, answer) {
506
+ const kept = new Set(answer.sources.map(sourceKey));
507
+ return new Set(reviewed.sources.map(sourceKey).filter((k) => !kept.has(k)));
508
+ }
509
+ function applyReviewedSourceSet(state, set, reviewed) {
617
510
  let next = state;
618
- for (const card of state.cards) {
511
+ if (reviewed) {
512
+ const removed = removedInReview(reviewed, set);
513
+ for (const card of state.cards) {
514
+ if (card.draft.ref && removed.has(sourceKey(card.draft.ref))) next = removeCard(next, card.id);
515
+ }
516
+ }
517
+ for (const card of next.cards) {
619
518
  const ref = card.draft.ref;
620
519
  if (!ref) continue;
621
520
  const match = set.sources.find((r) => sameSourceRef(r, ref));
622
521
  if (!match) continue;
623
- next = replaceCard(next, card.id, (c) => ({ ...c, draft: { ...c.draft, ref: match } }));
522
+ next = replaceCard(next, card.id, (c) => rememberPickedParts({ ...c, draft: { ...c.draft, ref: match } }));
624
523
  }
625
524
  return set.topic !== void 0 ? setSourceTopic(next, set.topic) : next;
626
525
  }
@@ -630,7 +529,13 @@ function applyManifest(state, manifest) {
630
529
  const ref = card.draft.ref;
631
530
  if (!ref || card.status !== "ready") continue;
632
531
  const entry = manifest.sources.find((e) => sameSourceRef(e.ref, ref));
633
- if (entry) next = replaceCard(next, card.id, (c) => ({ ...c, manifest: entry }));
532
+ if (!entry) continue;
533
+ next = replaceCard(next, card.id, (c) => {
534
+ const carried = carryPickedParts(c, entry);
535
+ if (carried !== c) return carried;
536
+ const measured = { ...c, manifest: entry };
537
+ return c.draft.ref?.include_segments?.length && !c.draft.pickedParts ? rememberPickedParts(measured) : measured;
538
+ });
634
539
  }
635
540
  return next;
636
541
  }
@@ -703,6 +608,27 @@ function selectTotalChars(state) {
703
608
  model_context_tokens: null
704
609
  });
705
610
  }
611
+ function sourceEntryChars(entry, ref) {
612
+ if (entry.state !== "ready" && entry.state !== "processing") return null;
613
+ return totalChars({
614
+ __kind: "source_manifest",
615
+ sources: [{ ...entry, ref }],
616
+ total_chars: 0,
617
+ estimated_tokens: 0,
618
+ model_context_tokens: null
619
+ });
620
+ }
621
+ function sourceCardChars(card) {
622
+ return card.manifest && card.draft.ref ? sourceEntryChars(card.manifest, card.draft.ref) : null;
623
+ }
624
+ function sourceCardState(card) {
625
+ if (card.status === "pending" || card.status === "resolving") return "adding";
626
+ if (card.status === "error") return card.error === WAITING_FOR_ORGANIZATION ? "waiting_for_organization" : "failed";
627
+ return card.manifest?.state ?? "unmeasured";
628
+ }
629
+ function sourceCardMeasuredState(card) {
630
+ return card.status === "ready" ? card.manifest?.state ?? null : null;
631
+ }
706
632
  function selectSettled(state) {
707
633
  return state.cards.every((c) => c.status === "ready");
708
634
  }
@@ -724,6 +650,190 @@ function withDisplayNames(resolved, cards) {
724
650
  };
725
651
  }
726
652
 
653
+ // sources/runtime/plan.ts
654
+ var HEAVY_SHARE = 0.7;
655
+ var PAGE_SUFFIX = (page) => page !== void 0 && page > 0 ? ` (page ${page})` : "";
656
+ function renderedPartChars(segment, chars) {
657
+ return `### Chunk ${segment.id}${PAGE_SUFFIX(segment.page)}`.length + 1 + chars;
658
+ }
659
+ function capParts(parts, maxChars) {
660
+ if (!maxChars) return { kept: parts.map((seg) => ({ seg, chars: seg.chars })), capped: false };
661
+ const kept = [];
662
+ let used = 0;
663
+ for (const seg of parts) {
664
+ if (used + seg.chars <= maxChars) {
665
+ kept.push({ seg, chars: seg.chars });
666
+ used += seg.chars;
667
+ continue;
668
+ }
669
+ const room = maxChars - used;
670
+ if (room > 0 && kept.length === 0) kept.push({ seg, chars: room });
671
+ return { kept, capped: true };
672
+ }
673
+ return { kept, capped: false };
674
+ }
675
+ function chosenForm(entry, ref) {
676
+ const key = ref.representation ?? entry.default_form;
677
+ return entry.forms.find((f) => f.form === key) ?? entry.forms.find((f) => f.form === entry.default_form) ?? null;
678
+ }
679
+ function isUsable(entry) {
680
+ return entry.state === "ready" || entry.state === "processing";
681
+ }
682
+ function measure(entry, ref) {
683
+ const form = chosenForm(entry, ref);
684
+ const formChars = form?.chars ?? 0;
685
+ const formLabel = form?.label ?? "Text";
686
+ const segments = entry.segments ?? null;
687
+ if (segments && segments.length > 0) {
688
+ const wanted = ref.include_segments?.length ? new Set(ref.include_segments) : null;
689
+ const chosen = wanted ? segments.filter((s) => wanted.has(s.id)) : segments;
690
+ const { kept, capped: capped2 } = capParts(chosen, ref.max_chars);
691
+ const body = kept.reduce((sum, k) => sum + renderedPartChars(k.seg, k.chars), 0);
692
+ const sentChars = kept.length ? body + 2 * (kept.length - 1) : 0;
693
+ return {
694
+ formChars,
695
+ formLabel,
696
+ sentChars,
697
+ exact: true,
698
+ partsSent: kept.length,
699
+ partsTotal: segments.length,
700
+ capped: capped2
701
+ };
702
+ }
703
+ const capped = ref.max_chars !== void 0 && formChars > ref.max_chars;
704
+ return {
705
+ formChars,
706
+ formLabel,
707
+ sentChars: capped ? ref.max_chars ?? formChars : formChars,
708
+ exact: false,
709
+ partsSent: null,
710
+ partsTotal: null,
711
+ capped
712
+ };
713
+ }
714
+ function planSourceReview(input) {
715
+ const estimateTokens = input.estimateTokens ?? estimateSourceTokens;
716
+ const windowIsFallback = !input.modelWindowTokens || input.modelWindowTokens <= 0;
717
+ const windowTokens = windowIsFallback ? input.fallbackWindowTokens : input.modelWindowTokens;
718
+ let sentChars = 0;
719
+ const entries = input.manifest.sources.map((entry, index) => {
720
+ const ref = input.refs[index] ?? entry.ref;
721
+ const m = measure(entry, ref);
722
+ const base = { index, ref, entry, ...m, chars: sourceEntryChars(entry, ref) ?? 0 };
723
+ if (!isUsable(entry)) {
724
+ return { ...base, status: "unusable", sentChars: 0, sentTokens: 0 };
725
+ }
726
+ if (sourceDelivery(ref) === "context") {
727
+ return { ...base, status: "on_demand", sentChars: 0, sentTokens: 0 };
728
+ }
729
+ if (estimateTokens(sentChars + m.sentChars) > windowTokens) {
730
+ return { ...base, status: "left_out", sentTokens: estimateTokens(m.sentChars) };
731
+ }
732
+ sentChars += m.sentChars;
733
+ return { ...base, status: "included", sentTokens: estimateTokens(m.sentChars) };
734
+ });
735
+ const sentTokens = estimateTokens(sentChars);
736
+ const leftOut = entries.filter((e) => e.status === "left_out");
737
+ const share = windowTokens > 0 ? sentTokens / windowTokens : 0;
738
+ const anythingGoesIn = entries.some((e) => e.status === "included" || e.status === "on_demand");
739
+ const verdict = !anythingGoesIn && leftOut.length === 0 ? "empty" : leftOut.length > 0 ? "too_much" : share >= HEAVY_SHARE ? "heavy" : "fine";
740
+ const kept = entries.filter((e) => e.status !== "left_out").map(
741
+ (e) => createSourceRef(e.ref.resource_type, e.ref.resource_id, {
742
+ representation: e.ref.representation,
743
+ include_segments: e.ref.include_segments,
744
+ delivery: e.ref.delivery,
745
+ max_chars: e.ref.max_chars,
746
+ promote: e.ref.promote,
747
+ exclude: e.ref.exclude
748
+ })
749
+ );
750
+ const grounding = input.base.grounding === "selected" && !kept.some((r) => r.include_segments?.length) ? void 0 : input.base.grounding;
751
+ const sourceSet = createSourceSet(kept, {
752
+ topic: input.base.topic,
753
+ grounding,
754
+ retrieve_query: input.base.retrieve_query,
755
+ target_model_id: input.targetModelId ?? input.base.target_model_id
756
+ });
757
+ return {
758
+ entries,
759
+ chars: entries.filter((e) => e.status === "included").reduce((sum, e) => sum + e.chars, 0),
760
+ sentChars,
761
+ sentTokens,
762
+ windowTokens,
763
+ windowIsFallback,
764
+ verdict,
765
+ share,
766
+ leftOut,
767
+ sourceSet
768
+ };
769
+ }
770
+
771
+ // sources/runtime/client.ts
772
+ function marked(options) {
773
+ return {
774
+ bodyCarriedRead: true,
775
+ ...options?.organizationId ? { organizationId: options.organizationId } : {},
776
+ ...options?.signal ? { signal: options.signal } : {}
777
+ };
778
+ }
779
+ function createSourcesClient(transport) {
780
+ return {
781
+ manifest: (sourceSet, options) => transport.post("/sources/manifest", { source_set: sourceSet }, marked(options)),
782
+ resolve: (sourceSet, options) => transport.post("/sources/resolve", { source_set: sourceSet }, marked(options)),
783
+ searchParts: (ref, query, options) => transport.post("/sources/parts/search", { source_ref: ref, query }, marked(options))
784
+ };
785
+ }
786
+ var SourceDoorError = class extends Error {
787
+ status;
788
+ body;
789
+ constructor(status, message, body) {
790
+ super(message);
791
+ this.name = "SourceDoorError";
792
+ this.status = status;
793
+ this.body = body;
794
+ }
795
+ };
796
+ function refusalMessage(status, body) {
797
+ if (body && typeof body === "object") {
798
+ const record = body;
799
+ const error = record.error;
800
+ if (error && typeof error === "object") {
801
+ const message = error.user_message ?? error.message;
802
+ if (typeof message === "string" && message) return message;
803
+ }
804
+ if (typeof record.detail === "string" && record.detail) return record.detail;
805
+ }
806
+ return `The server answered ${status}.`;
807
+ }
808
+ function createFetchSourcesTransport(options) {
809
+ const base = options.baseUrl.replace(/\/+$/, "");
810
+ return {
811
+ async post(path, body, call) {
812
+ const doFetch = options.fetch ?? fetch;
813
+ const headers = {
814
+ "Content-Type": "application/json",
815
+ ...options.headers ? await options.headers() : {}
816
+ };
817
+ if (call.organizationId) headers[options.organizationHeader ?? "X-Organization-Id"] = call.organizationId;
818
+ const response = await doFetch(`${base}${path}`, {
819
+ method: "POST",
820
+ headers,
821
+ body: JSON.stringify(body),
822
+ ...call.signal ? { signal: call.signal } : {}
823
+ });
824
+ const text = await response.text();
825
+ let parsed = null;
826
+ try {
827
+ parsed = text ? JSON.parse(text) : null;
828
+ } catch {
829
+ parsed = text;
830
+ }
831
+ if (!response.ok) throw new SourceDoorError(response.status, refusalMessage(response.status, parsed), parsed);
832
+ return parsed;
833
+ }
834
+ };
835
+ }
836
+
727
837
  // sources/runtime/controller.ts
728
838
  function randomId() {
729
839
  const cryptoApi = globalThis.crypto;
@@ -857,8 +967,8 @@ function createSourceSetController(adapter) {
857
967
  hasRef: (resourceType, resourceId) => hasSourceRef(store.getState(), resourceType, resourceId),
858
968
  liveCount: () => store.getState().cards.length,
859
969
  toSourceSet: (options = {}) => selectSourceSet(store.getState(), options),
860
- applySourceSet(set) {
861
- commit(applyReviewedSourceSet(store.getState(), set));
970
+ applySourceSet(set, options = {}) {
971
+ commit(applyReviewedSourceSet(store.getState(), set, options.reviewed));
862
972
  },
863
973
  async manifest() {
864
974
  const refs = readySourceRefs(store.getState());
@@ -1143,20 +1253,45 @@ function createSourceIntake(set, deps) {
1143
1253
  });
1144
1254
  await hold(() => landPaste(id, trimmed, name));
1145
1255
  },
1256
+ async addRecordedText(text, name) {
1257
+ const trimmed = text.trim();
1258
+ if (!trimmed) return;
1259
+ const max = deps.maxKeptChars();
1260
+ const kept = max !== null && trimmed.length <= max;
1261
+ const id = set.addPending({
1262
+ kind: "audio",
1263
+ label: name?.trim() || "Recording",
1264
+ origin: "Recording",
1265
+ input: kept ? { text: trimmed, name } : void 0,
1266
+ notes: kept ? void 0 : [NOT_KEPT_FOR_RELOAD]
1267
+ });
1268
+ await hold(() => landPaste(id, trimmed, name));
1269
+ },
1146
1270
  async addWebPage(raw) {
1147
1271
  const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1148
1272
  const id = set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1149
1273
  await hold(() => landWebPage(id, url));
1150
1274
  },
1151
- async addScrapedPage(page) {
1275
+ beginWebPage(raw) {
1276
+ const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1277
+ return set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1278
+ },
1279
+ async addScrapedPage(page, pendingId) {
1152
1280
  const label = page.title || hostOf(page.url);
1281
+ const cardFor = () => {
1282
+ if (pendingId && set.restart(pendingId)) {
1283
+ set.updateDraft(pendingId, { label, origin: page.url, input: { url: page.url } });
1284
+ return pendingId;
1285
+ }
1286
+ return set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1287
+ };
1153
1288
  if (!page.processedDocumentId) {
1154
- const id2 = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1289
+ const id2 = cardFor();
1155
1290
  await hold(() => landPaste(id2, page.text, label));
1156
1291
  return;
1157
1292
  }
1158
1293
  const pdId = page.processedDocumentId;
1159
- const id = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1294
+ const id = cardFor();
1160
1295
  await hold(async () => {
1161
1296
  let notes = [];
1162
1297
  try {
@@ -1231,7 +1366,8 @@ function createSourceIntake(set, deps) {
1231
1366
  return true;
1232
1367
  }
1233
1368
  case "audio":
1234
- void transcribeRecording(card.id, input.fileId ?? "", card.draft.label);
1369
+ if (input.fileId) void transcribeRecording(card.id, input.fileId, card.draft.label);
1370
+ else void landPaste(card.id, (input.text ?? "").trim(), input.name);
1235
1371
  return true;
1236
1372
  default:
1237
1373
  return false;