@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.
@@ -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) {
@@ -511,14 +391,24 @@ function setCardWaitForClean(state, id, wait) {
511
391
  function setSourceTopic(state, topic) {
512
392
  return state.topic === topic ? state : { ...state, topic };
513
393
  }
514
- function applyReviewedSourceSet(state, set) {
394
+ function removedInReview(reviewed, answer) {
395
+ const kept = new Set(answer.sources.map(sourceKey));
396
+ return new Set(reviewed.sources.map(sourceKey).filter((k) => !kept.has(k)));
397
+ }
398
+ function applyReviewedSourceSet(state, set, reviewed) {
515
399
  let next = state;
516
- for (const card of state.cards) {
400
+ if (reviewed) {
401
+ const removed = removedInReview(reviewed, set);
402
+ for (const card of state.cards) {
403
+ if (card.draft.ref && removed.has(sourceKey(card.draft.ref))) next = removeCard(next, card.id);
404
+ }
405
+ }
406
+ for (const card of next.cards) {
517
407
  const ref = card.draft.ref;
518
408
  if (!ref) continue;
519
409
  const match = set.sources.find((r) => sameSourceRef(r, ref));
520
410
  if (!match) continue;
521
- next = replaceCard(next, card.id, (c) => ({ ...c, draft: { ...c.draft, ref: match } }));
411
+ next = replaceCard(next, card.id, (c) => rememberPickedParts({ ...c, draft: { ...c.draft, ref: match } }));
522
412
  }
523
413
  return set.topic !== void 0 ? setSourceTopic(next, set.topic) : next;
524
414
  }
@@ -528,7 +418,13 @@ function applyManifest(state, manifest) {
528
418
  const ref = card.draft.ref;
529
419
  if (!ref || card.status !== "ready") continue;
530
420
  const entry = manifest.sources.find((e) => sameSourceRef(e.ref, ref));
531
- if (entry) next = replaceCard(next, card.id, (c) => ({ ...c, manifest: entry }));
421
+ if (!entry) continue;
422
+ next = replaceCard(next, card.id, (c) => {
423
+ const carried = carryPickedParts(c, entry);
424
+ if (carried !== c) return carried;
425
+ const measured = { ...c, manifest: entry };
426
+ return c.draft.ref?.include_segments?.length && !c.draft.pickedParts ? rememberPickedParts(measured) : measured;
427
+ });
532
428
  }
533
429
  return next;
534
430
  }
@@ -601,6 +497,27 @@ function selectTotalChars(state) {
601
497
  model_context_tokens: null
602
498
  });
603
499
  }
500
+ function sourceEntryChars(entry, ref) {
501
+ if (entry.state !== "ready" && entry.state !== "processing") return null;
502
+ return totalChars({
503
+ __kind: "source_manifest",
504
+ sources: [{ ...entry, ref }],
505
+ total_chars: 0,
506
+ estimated_tokens: 0,
507
+ model_context_tokens: null
508
+ });
509
+ }
510
+ function sourceCardChars(card) {
511
+ return card.manifest && card.draft.ref ? sourceEntryChars(card.manifest, card.draft.ref) : null;
512
+ }
513
+ function sourceCardState(card) {
514
+ if (card.status === "pending" || card.status === "resolving") return "adding";
515
+ if (card.status === "error") return card.error === WAITING_FOR_ORGANIZATION ? "waiting_for_organization" : "failed";
516
+ return card.manifest?.state ?? "unmeasured";
517
+ }
518
+ function sourceCardMeasuredState(card) {
519
+ return card.status === "ready" ? card.manifest?.state ?? null : null;
520
+ }
604
521
  function selectSettled(state) {
605
522
  return state.cards.every((c) => c.status === "ready");
606
523
  }
@@ -622,6 +539,190 @@ function withDisplayNames(resolved, cards) {
622
539
  };
623
540
  }
624
541
 
542
+ // sources/runtime/plan.ts
543
+ var HEAVY_SHARE = 0.7;
544
+ var PAGE_SUFFIX = (page) => page !== void 0 && page > 0 ? ` (page ${page})` : "";
545
+ function renderedPartChars(segment, chars) {
546
+ return `### Chunk ${segment.id}${PAGE_SUFFIX(segment.page)}`.length + 1 + chars;
547
+ }
548
+ function capParts(parts, maxChars) {
549
+ if (!maxChars) return { kept: parts.map((seg) => ({ seg, chars: seg.chars })), capped: false };
550
+ const kept = [];
551
+ let used = 0;
552
+ for (const seg of parts) {
553
+ if (used + seg.chars <= maxChars) {
554
+ kept.push({ seg, chars: seg.chars });
555
+ used += seg.chars;
556
+ continue;
557
+ }
558
+ const room = maxChars - used;
559
+ if (room > 0 && kept.length === 0) kept.push({ seg, chars: room });
560
+ return { kept, capped: true };
561
+ }
562
+ return { kept, capped: false };
563
+ }
564
+ function chosenForm(entry, ref) {
565
+ const key = ref.representation ?? entry.default_form;
566
+ return entry.forms.find((f) => f.form === key) ?? entry.forms.find((f) => f.form === entry.default_form) ?? null;
567
+ }
568
+ function isUsable(entry) {
569
+ return entry.state === "ready" || entry.state === "processing";
570
+ }
571
+ function measure(entry, ref) {
572
+ const form = chosenForm(entry, ref);
573
+ const formChars = form?.chars ?? 0;
574
+ const formLabel = form?.label ?? "Text";
575
+ const segments = entry.segments ?? null;
576
+ if (segments && segments.length > 0) {
577
+ const wanted = ref.include_segments?.length ? new Set(ref.include_segments) : null;
578
+ const chosen = wanted ? segments.filter((s) => wanted.has(s.id)) : segments;
579
+ const { kept, capped: capped2 } = capParts(chosen, ref.max_chars);
580
+ const body = kept.reduce((sum, k) => sum + renderedPartChars(k.seg, k.chars), 0);
581
+ const sentChars = kept.length ? body + 2 * (kept.length - 1) : 0;
582
+ return {
583
+ formChars,
584
+ formLabel,
585
+ sentChars,
586
+ exact: true,
587
+ partsSent: kept.length,
588
+ partsTotal: segments.length,
589
+ capped: capped2
590
+ };
591
+ }
592
+ const capped = ref.max_chars !== void 0 && formChars > ref.max_chars;
593
+ return {
594
+ formChars,
595
+ formLabel,
596
+ sentChars: capped ? ref.max_chars ?? formChars : formChars,
597
+ exact: false,
598
+ partsSent: null,
599
+ partsTotal: null,
600
+ capped
601
+ };
602
+ }
603
+ function planSourceReview(input) {
604
+ const estimateTokens = input.estimateTokens ?? estimateSourceTokens;
605
+ const windowIsFallback = !input.modelWindowTokens || input.modelWindowTokens <= 0;
606
+ const windowTokens = windowIsFallback ? input.fallbackWindowTokens : input.modelWindowTokens;
607
+ let sentChars = 0;
608
+ const entries = input.manifest.sources.map((entry, index) => {
609
+ const ref = input.refs[index] ?? entry.ref;
610
+ const m = measure(entry, ref);
611
+ const base = { index, ref, entry, ...m, chars: sourceEntryChars(entry, ref) ?? 0 };
612
+ if (!isUsable(entry)) {
613
+ return { ...base, status: "unusable", sentChars: 0, sentTokens: 0 };
614
+ }
615
+ if (sourceDelivery(ref) === "context") {
616
+ return { ...base, status: "on_demand", sentChars: 0, sentTokens: 0 };
617
+ }
618
+ if (estimateTokens(sentChars + m.sentChars) > windowTokens) {
619
+ return { ...base, status: "left_out", sentTokens: estimateTokens(m.sentChars) };
620
+ }
621
+ sentChars += m.sentChars;
622
+ return { ...base, status: "included", sentTokens: estimateTokens(m.sentChars) };
623
+ });
624
+ const sentTokens = estimateTokens(sentChars);
625
+ const leftOut = entries.filter((e) => e.status === "left_out");
626
+ const share = windowTokens > 0 ? sentTokens / windowTokens : 0;
627
+ const anythingGoesIn = entries.some((e) => e.status === "included" || e.status === "on_demand");
628
+ const verdict = !anythingGoesIn && leftOut.length === 0 ? "empty" : leftOut.length > 0 ? "too_much" : share >= HEAVY_SHARE ? "heavy" : "fine";
629
+ const kept = entries.filter((e) => e.status !== "left_out").map(
630
+ (e) => createSourceRef(e.ref.resource_type, e.ref.resource_id, {
631
+ representation: e.ref.representation,
632
+ include_segments: e.ref.include_segments,
633
+ delivery: e.ref.delivery,
634
+ max_chars: e.ref.max_chars,
635
+ promote: e.ref.promote,
636
+ exclude: e.ref.exclude
637
+ })
638
+ );
639
+ const grounding = input.base.grounding === "selected" && !kept.some((r) => r.include_segments?.length) ? void 0 : input.base.grounding;
640
+ const sourceSet = createSourceSet(kept, {
641
+ topic: input.base.topic,
642
+ grounding,
643
+ retrieve_query: input.base.retrieve_query,
644
+ target_model_id: input.targetModelId ?? input.base.target_model_id
645
+ });
646
+ return {
647
+ entries,
648
+ chars: entries.filter((e) => e.status === "included").reduce((sum, e) => sum + e.chars, 0),
649
+ sentChars,
650
+ sentTokens,
651
+ windowTokens,
652
+ windowIsFallback,
653
+ verdict,
654
+ share,
655
+ leftOut,
656
+ sourceSet
657
+ };
658
+ }
659
+
660
+ // sources/runtime/client.ts
661
+ function marked(options) {
662
+ return {
663
+ bodyCarriedRead: true,
664
+ ...options?.organizationId ? { organizationId: options.organizationId } : {},
665
+ ...options?.signal ? { signal: options.signal } : {}
666
+ };
667
+ }
668
+ function createSourcesClient(transport) {
669
+ return {
670
+ manifest: (sourceSet, options) => transport.post("/sources/manifest", { source_set: sourceSet }, marked(options)),
671
+ resolve: (sourceSet, options) => transport.post("/sources/resolve", { source_set: sourceSet }, marked(options)),
672
+ searchParts: (ref, query, options) => transport.post("/sources/parts/search", { source_ref: ref, query }, marked(options))
673
+ };
674
+ }
675
+ var SourceDoorError = class extends Error {
676
+ status;
677
+ body;
678
+ constructor(status, message, body) {
679
+ super(message);
680
+ this.name = "SourceDoorError";
681
+ this.status = status;
682
+ this.body = body;
683
+ }
684
+ };
685
+ function refusalMessage(status, body) {
686
+ if (body && typeof body === "object") {
687
+ const record = body;
688
+ const error = record.error;
689
+ if (error && typeof error === "object") {
690
+ const message = error.user_message ?? error.message;
691
+ if (typeof message === "string" && message) return message;
692
+ }
693
+ if (typeof record.detail === "string" && record.detail) return record.detail;
694
+ }
695
+ return `The server answered ${status}.`;
696
+ }
697
+ function createFetchSourcesTransport(options) {
698
+ const base = options.baseUrl.replace(/\/+$/, "");
699
+ return {
700
+ async post(path, body, call) {
701
+ const doFetch = options.fetch ?? fetch;
702
+ const headers = {
703
+ "Content-Type": "application/json",
704
+ ...options.headers ? await options.headers() : {}
705
+ };
706
+ if (call.organizationId) headers[options.organizationHeader ?? "X-Organization-Id"] = call.organizationId;
707
+ const response = await doFetch(`${base}${path}`, {
708
+ method: "POST",
709
+ headers,
710
+ body: JSON.stringify(body),
711
+ ...call.signal ? { signal: call.signal } : {}
712
+ });
713
+ const text = await response.text();
714
+ let parsed = null;
715
+ try {
716
+ parsed = text ? JSON.parse(text) : null;
717
+ } catch {
718
+ parsed = text;
719
+ }
720
+ if (!response.ok) throw new SourceDoorError(response.status, refusalMessage(response.status, parsed), parsed);
721
+ return parsed;
722
+ }
723
+ };
724
+ }
725
+
625
726
  // sources/runtime/controller.ts
626
727
  function randomId() {
627
728
  const cryptoApi = globalThis.crypto;
@@ -755,8 +856,8 @@ function createSourceSetController(adapter) {
755
856
  hasRef: (resourceType, resourceId) => hasSourceRef(store.getState(), resourceType, resourceId),
756
857
  liveCount: () => store.getState().cards.length,
757
858
  toSourceSet: (options = {}) => selectSourceSet(store.getState(), options),
758
- applySourceSet(set) {
759
- commit(applyReviewedSourceSet(store.getState(), set));
859
+ applySourceSet(set, options = {}) {
860
+ commit(applyReviewedSourceSet(store.getState(), set, options.reviewed));
760
861
  },
761
862
  async manifest() {
762
863
  const refs = readySourceRefs(store.getState());
@@ -1041,20 +1142,45 @@ function createSourceIntake(set, deps) {
1041
1142
  });
1042
1143
  await hold(() => landPaste(id, trimmed, name));
1043
1144
  },
1145
+ async addRecordedText(text, name) {
1146
+ const trimmed = text.trim();
1147
+ if (!trimmed) return;
1148
+ const max = deps.maxKeptChars();
1149
+ const kept = max !== null && trimmed.length <= max;
1150
+ const id = set.addPending({
1151
+ kind: "audio",
1152
+ label: name?.trim() || "Recording",
1153
+ origin: "Recording",
1154
+ input: kept ? { text: trimmed, name } : void 0,
1155
+ notes: kept ? void 0 : [NOT_KEPT_FOR_RELOAD]
1156
+ });
1157
+ await hold(() => landPaste(id, trimmed, name));
1158
+ },
1044
1159
  async addWebPage(raw) {
1045
1160
  const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1046
1161
  const id = set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1047
1162
  await hold(() => landWebPage(id, url));
1048
1163
  },
1049
- async addScrapedPage(page) {
1164
+ beginWebPage(raw) {
1165
+ const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
1166
+ return set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
1167
+ },
1168
+ async addScrapedPage(page, pendingId) {
1050
1169
  const label = page.title || hostOf(page.url);
1170
+ const cardFor = () => {
1171
+ if (pendingId && set.restart(pendingId)) {
1172
+ set.updateDraft(pendingId, { label, origin: page.url, input: { url: page.url } });
1173
+ return pendingId;
1174
+ }
1175
+ return set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1176
+ };
1051
1177
  if (!page.processedDocumentId) {
1052
- const id2 = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1178
+ const id2 = cardFor();
1053
1179
  await hold(() => landPaste(id2, page.text, label));
1054
1180
  return;
1055
1181
  }
1056
1182
  const pdId = page.processedDocumentId;
1057
- const id = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
1183
+ const id = cardFor();
1058
1184
  await hold(async () => {
1059
1185
  let notes = [];
1060
1186
  try {
@@ -1129,7 +1255,8 @@ function createSourceIntake(set, deps) {
1129
1255
  return true;
1130
1256
  }
1131
1257
  case "audio":
1132
- void transcribeRecording(card.id, input.fileId ?? "", card.draft.label);
1258
+ if (input.fileId) void transcribeRecording(card.id, input.fileId, card.draft.label);
1259
+ else void landPaste(card.id, (input.text ?? "").trim(), input.name);
1133
1260
  return true;
1134
1261
  default:
1135
1262
  return false;
@@ -1257,6 +1384,8 @@ export {
1257
1384
  KEEP_WAITING_FOR_ORGANIZATION,
1258
1385
  MAX_KEPT_TEXT_KNOB,
1259
1386
  NOT_KEPT_FOR_RELOAD,
1387
+ PARTS_MATCHED_BY_PAGE,
1388
+ PARTS_NOT_MATCHED,
1260
1389
  RELOADED_RESUMING,
1261
1390
  RELOADED_WHILE_ADDING,
1262
1391
  SAME_SOURCE_AGAIN,
@@ -1270,6 +1399,7 @@ export {
1270
1399
  applyReviewedSourceSet,
1271
1400
  cardsToResume,
1272
1401
  cardsWaitingForOrganization,
1402
+ carryPickedParts,
1273
1403
  chosenForm,
1274
1404
  continueAfterOrganization,
1275
1405
  createFetchSourcesTransport,
@@ -1303,7 +1433,9 @@ export {
1303
1433
  readySourceRefs,
1304
1434
  registeredFileEdge,
1305
1435
  reloadedCard,
1436
+ rememberPickedParts,
1306
1437
  removeCard,
1438
+ removedInReview,
1307
1439
  renderedPartChars,
1308
1440
  restartCard,
1309
1441
  restoredCards,
@@ -1317,7 +1449,11 @@ export {
1317
1449
  setSourceTopic,
1318
1450
  settleCard,
1319
1451
  sourceAttachTargets,
1452
+ sourceCardChars,
1453
+ sourceCardMeasuredState,
1454
+ sourceCardState,
1320
1455
  sourceDelivery,
1456
+ sourceEntryChars,
1321
1457
  sourceKey,
1322
1458
  sourceRecoveryGuards,
1323
1459
  sourceRoomLeft,