@ai-matrx/agents 0.17.4 → 0.18.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.
- package/CHANGELOG.md +53 -0
- package/README.md +36 -1
- package/dist/content-transfer/index.cjs.map +1 -1
- package/dist/content-transfer/index.js.map +1 -1
- package/dist/content-transfer/react/index.cjs.map +1 -1
- package/dist/content-transfer/react/index.js.map +1 -1
- package/dist/mandates/index.cjs +2 -2
- package/dist/mandates/index.cjs.map +1 -1
- package/dist/mandates/index.d.cts +4 -4
- package/dist/mandates/index.d.ts +4 -4
- package/dist/mandates/index.js +2 -2
- package/dist/mandates/index.js.map +1 -1
- package/dist/sources/index.cjs.map +1 -1
- package/dist/sources/index.d.cts +7 -0
- package/dist/sources/index.d.ts +7 -0
- package/dist/sources/index.js.map +1 -1
- package/dist/sources/react/index.cjs +1163 -0
- package/dist/sources/react/index.cjs.map +1 -0
- package/dist/sources/react/index.d.cts +746 -0
- package/dist/sources/react/index.d.ts +746 -0
- package/dist/sources/react/index.js +1143 -0
- package/dist/sources/react/index.js.map +1 -0
- package/dist/sources/runtime/index.cjs +1354 -0
- package/dist/sources/runtime/index.cjs.map +1 -0
- package/dist/sources/runtime/index.d.cts +959 -0
- package/dist/sources/runtime/index.d.ts +959 -0
- package/dist/sources/runtime/index.js +1331 -0
- package/dist/sources/runtime/index.js.map +1 -0
- package/mandates/snapshots/keys.0.17.5.json +652 -0
- package/mandates/snapshots/keys.0.18.0.json +652 -0
- package/mandates/snapshots/keys.0.18.1.json +652 -0
- package/package.json +22 -2
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// sources/runtime/index.ts
|
|
21
|
+
var runtime_exports = {};
|
|
22
|
+
__export(runtime_exports, {
|
|
23
|
+
DELIVERY_CHOICES: () => DELIVERY_CHOICES,
|
|
24
|
+
DELIVERY_WORDS: () => DELIVERY_WORDS,
|
|
25
|
+
EMPTY_SOURCE_SET_STATE: () => EMPTY_SOURCE_SET_STATE,
|
|
26
|
+
HEAVY_SHARE: () => HEAVY_SHARE,
|
|
27
|
+
KEEP_WAITING_FOR_ORGANIZATION: () => KEEP_WAITING_FOR_ORGANIZATION,
|
|
28
|
+
MAX_KEPT_TEXT_KNOB: () => MAX_KEPT_TEXT_KNOB,
|
|
29
|
+
NOT_KEPT_FOR_RELOAD: () => NOT_KEPT_FOR_RELOAD,
|
|
30
|
+
RELOADED_RESUMING: () => RELOADED_RESUMING,
|
|
31
|
+
RELOADED_WHILE_ADDING: () => RELOADED_WHILE_ADDING,
|
|
32
|
+
SAME_SOURCE_AGAIN: () => SAME_SOURCE_AGAIN,
|
|
33
|
+
SOURCE_CHARS_PER_TOKEN: () => SOURCE_CHARS_PER_TOKEN,
|
|
34
|
+
SourceDoorError: () => SourceDoorError,
|
|
35
|
+
WAITING_FOR_ORGANIZATION: () => WAITING_FOR_ORGANIZATION,
|
|
36
|
+
addPendingCard: () => addPendingCard,
|
|
37
|
+
addReadyCard: () => addReadyCard,
|
|
38
|
+
allowedDeliveries: () => allowedDeliveries,
|
|
39
|
+
applyManifest: () => applyManifest,
|
|
40
|
+
applyReviewedSourceSet: () => applyReviewedSourceSet,
|
|
41
|
+
cardsToResume: () => cardsToResume,
|
|
42
|
+
cardsWaitingForOrganization: () => cardsWaitingForOrganization,
|
|
43
|
+
chosenForm: () => chosenForm,
|
|
44
|
+
continueAfterOrganization: () => continueAfterOrganization,
|
|
45
|
+
createFetchSourcesTransport: () => createFetchSourcesTransport,
|
|
46
|
+
createMemorySourceDraftStorage: () => createMemorySourceDraftStorage,
|
|
47
|
+
createMemorySourceSetStore: () => createMemorySourceSetStore,
|
|
48
|
+
createSourceIntake: () => createSourceIntake,
|
|
49
|
+
createSourceSetController: () => createSourceSetController,
|
|
50
|
+
createSourcesClient: () => createSourcesClient,
|
|
51
|
+
deliveryChoicesFor: () => deliveryChoicesFor,
|
|
52
|
+
deliveryPatch: () => deliveryPatch,
|
|
53
|
+
deliverySwitchedNote: () => deliverySwitchedNote,
|
|
54
|
+
draftKindForToken: () => draftKindForToken,
|
|
55
|
+
estimateSourceTokens: () => estimateSourceTokens,
|
|
56
|
+
failCard: () => failCard,
|
|
57
|
+
fileCardHeldForOrganization: () => fileCardHeldForOrganization,
|
|
58
|
+
fileSourcePollDelayMs: () => fileSourcePollDelayMs,
|
|
59
|
+
findParts: () => findParts,
|
|
60
|
+
fitCardDeliveries: () => fitCardDeliveries,
|
|
61
|
+
fitDelivery: () => fitDelivery,
|
|
62
|
+
hasSourceRef: () => hasSourceRef,
|
|
63
|
+
isAskableFileCard: () => isAskableFileCard,
|
|
64
|
+
isSourceDraft: () => isSourceDraft,
|
|
65
|
+
isWaitingFileCard: () => isWaitingFileCard,
|
|
66
|
+
isWordQuery: () => isWordQuery,
|
|
67
|
+
lookAtFileCards: () => lookAtFileCards,
|
|
68
|
+
matchesPart: () => matchesPart,
|
|
69
|
+
nextFileStep: () => nextFileStep,
|
|
70
|
+
partPages: () => partPages,
|
|
71
|
+
planSourceReview: () => planSourceReview,
|
|
72
|
+
readPersistedSourceInput: () => readPersistedSourceInput,
|
|
73
|
+
readySourceRefs: () => readySourceRefs,
|
|
74
|
+
registeredFileEdge: () => registeredFileEdge,
|
|
75
|
+
reloadedCard: () => reloadedCard,
|
|
76
|
+
removeCard: () => removeCard,
|
|
77
|
+
renderedPartChars: () => renderedPartChars,
|
|
78
|
+
restartCard: () => restartCard,
|
|
79
|
+
restoredCards: () => restoredCards,
|
|
80
|
+
resumableInput: () => resumableInput,
|
|
81
|
+
resumeInterrupted: () => resumeInterrupted,
|
|
82
|
+
sameSourceRef: () => sameSourceRef,
|
|
83
|
+
selectSettled: () => selectSettled,
|
|
84
|
+
selectSourceSet: () => selectSourceSet,
|
|
85
|
+
selectTotalChars: () => selectTotalChars,
|
|
86
|
+
setCardWaitForClean: () => setCardWaitForClean,
|
|
87
|
+
setSourceTopic: () => setSourceTopic,
|
|
88
|
+
settleCard: () => settleCard,
|
|
89
|
+
sourceAttachTargets: () => sourceAttachTargets,
|
|
90
|
+
sourceDelivery: () => sourceDelivery,
|
|
91
|
+
sourceKey: () => sourceKey,
|
|
92
|
+
sourceRecoveryGuards: () => sourceRecoveryGuards,
|
|
93
|
+
sourceRoomLeft: () => sourceRoomLeft,
|
|
94
|
+
toPersistedSourceInput: () => toPersistedSourceInput,
|
|
95
|
+
updateCardDraft: () => updateCardDraft,
|
|
96
|
+
updateCardRef: () => updateCardRef,
|
|
97
|
+
uploadCutOffSentence: () => uploadCutOffSentence,
|
|
98
|
+
withDefaultForm: () => withDefaultForm,
|
|
99
|
+
withDisplayNames: () => withDisplayNames
|
|
100
|
+
});
|
|
101
|
+
module.exports = __toCommonJS(runtime_exports);
|
|
102
|
+
|
|
103
|
+
// sources/runtime/delivery.ts
|
|
104
|
+
function sourceDelivery(ref) {
|
|
105
|
+
return ref?.delivery === "context" ? "context" : "direct";
|
|
106
|
+
}
|
|
107
|
+
function deliveryPatch(delivery) {
|
|
108
|
+
return delivery === "context" ? { delivery: "context", include_segments: void 0, max_chars: void 0 } : { delivery: void 0 };
|
|
109
|
+
}
|
|
110
|
+
var DELIVERY_WORDS = {
|
|
111
|
+
direct: {
|
|
112
|
+
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.",
|
|
114
|
+
summary: "Text included"
|
|
115
|
+
},
|
|
116
|
+
context: {
|
|
117
|
+
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.",
|
|
119
|
+
summary: "Looked up when needed"
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var DELIVERY_CHOICES = ["direct", "context"].map((value) => ({ value, ...DELIVERY_WORDS[value] }));
|
|
123
|
+
function allowedDeliveries(deliveries) {
|
|
124
|
+
const allowed = ["direct", "context"].filter((d) => !deliveries || deliveries.includes(d));
|
|
125
|
+
return allowed.length > 0 ? allowed : ["direct"];
|
|
126
|
+
}
|
|
127
|
+
function deliveryChoicesFor(deliveries) {
|
|
128
|
+
const allowed = allowedDeliveries(deliveries);
|
|
129
|
+
return DELIVERY_CHOICES.filter((c) => allowed.includes(c.value));
|
|
130
|
+
}
|
|
131
|
+
function deliverySwitchedNote(to) {
|
|
132
|
+
return to === "direct" ? `This Source was set to "${DELIVERY_WORDS.context.label}", which this page cannot use \u2014 it needs the text up front \u2014 so it was switched back to "${DELIVERY_WORDS.direct.label}".` : `This Source was set to "${DELIVERY_WORDS.direct.label}", which this page cannot use, so it was switched to "${DELIVERY_WORDS.context.label}".`;
|
|
133
|
+
}
|
|
134
|
+
function fitDelivery(ref, deliveries) {
|
|
135
|
+
if (!ref) return null;
|
|
136
|
+
const allowed = allowedDeliveries(deliveries);
|
|
137
|
+
const current = sourceDelivery(ref);
|
|
138
|
+
if (allowed.includes(current)) return null;
|
|
139
|
+
const to = allowed[0];
|
|
140
|
+
return { patch: deliveryPatch(to), to };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// sources/runtime/partsSearch.ts
|
|
144
|
+
var RANGE_RE = /^(\d+)\s*(?:-|–|—|to)\s*(\d+)$/;
|
|
145
|
+
var SPAN_RE = /(\d+)\s*[–—-]\s*(\d+)/;
|
|
146
|
+
function partPages(part) {
|
|
147
|
+
if (part.page === void 0 || part.page === null) return null;
|
|
148
|
+
const span = /^pages?\s/i.test(part.label) ? SPAN_RE.exec(part.label) : null;
|
|
149
|
+
if (span) return [Number(span[1]), Number(span[2])];
|
|
150
|
+
return [part.page, part.page];
|
|
151
|
+
}
|
|
152
|
+
function isWordQuery(query) {
|
|
153
|
+
const q = query.trim().toLowerCase();
|
|
154
|
+
return q.length > 0 && !/^\d+$/.test(q) && !RANGE_RE.test(q);
|
|
155
|
+
}
|
|
156
|
+
function normalise(text) {
|
|
157
|
+
return text.toLowerCase().replace(/\s+/g, " ");
|
|
158
|
+
}
|
|
159
|
+
function matchesPart(part, query, serverMatches) {
|
|
160
|
+
const q = query.trim().toLowerCase();
|
|
161
|
+
if (!q) return true;
|
|
162
|
+
const pages = partPages(part);
|
|
163
|
+
const range = RANGE_RE.exec(q);
|
|
164
|
+
if (range && pages) {
|
|
165
|
+
const from = Math.min(Number(range[1]), Number(range[2]));
|
|
166
|
+
const to = Math.max(Number(range[1]), Number(range[2]));
|
|
167
|
+
return pages[0] <= to && pages[1] >= from;
|
|
168
|
+
}
|
|
169
|
+
if (/^\d+$/.test(q) && pages) {
|
|
170
|
+
const n = Number(q);
|
|
171
|
+
return pages[0] <= n && n <= pages[1];
|
|
172
|
+
}
|
|
173
|
+
if (part.id.toLowerCase() === q) return true;
|
|
174
|
+
if (serverMatches?.has(part.id)) return true;
|
|
175
|
+
const haystack = normalise([part.label, part.preview ?? ""].join(" "));
|
|
176
|
+
return q.split(/\s+/).filter(Boolean).every((word) => haystack.includes(word));
|
|
177
|
+
}
|
|
178
|
+
function findParts(parts, query, serverMatches) {
|
|
179
|
+
return parts.filter((p) => matchesPart(p, query, serverMatches));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// sources/runtime/interrupted.ts
|
|
183
|
+
var RELOADED_RESUMING = "The page reloaded while this was being added \u2014 picking it up again.";
|
|
184
|
+
var RELOADED_WHILE_ADDING = "This was still being added when the page reloaded, so it did not finish. Add it again.";
|
|
185
|
+
function resumableInput(draft) {
|
|
186
|
+
const input = draft.input;
|
|
187
|
+
if (!input) return null;
|
|
188
|
+
switch (draft.kind) {
|
|
189
|
+
case "paste":
|
|
190
|
+
return input.text?.trim() ? input : null;
|
|
191
|
+
case "web":
|
|
192
|
+
case "youtube":
|
|
193
|
+
return input.url?.trim() ? input : null;
|
|
194
|
+
case "audio":
|
|
195
|
+
return input.fileId ? input : null;
|
|
196
|
+
default:
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function uploadCutOffSentence(draft) {
|
|
201
|
+
return `"${draft.label}" was still uploading when the page reloaded, so it did not arrive. Choose it again to add it.`;
|
|
202
|
+
}
|
|
203
|
+
function reloadedCard(draft, status) {
|
|
204
|
+
if (status !== "pending" && status !== "resolving") return null;
|
|
205
|
+
if (resumableInput(draft)) return { action: "resume", sentence: RELOADED_RESUMING };
|
|
206
|
+
if (draft.kind === "upload" || draft.kind === "image" || draft.kind === "audio")
|
|
207
|
+
return { action: "reoffer", sentence: uploadCutOffSentence(draft) };
|
|
208
|
+
return { action: "lost", sentence: RELOADED_WHILE_ADDING };
|
|
209
|
+
}
|
|
210
|
+
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.";
|
|
212
|
+
var WAITING_FOR_ORGANIZATION = "Waiting for an organization \u2014 choose one and this continues by itself.";
|
|
213
|
+
var KEEP_WAITING_FOR_ORGANIZATION = "Read. Waiting for an organization to keep it \u2014 choose one and it is kept by itself.";
|
|
214
|
+
|
|
215
|
+
// 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).";
|
|
217
|
+
function nextFileStep(status, ctx) {
|
|
218
|
+
const pdId = status?.processed_document_id ?? null;
|
|
219
|
+
if (pdId) return { do: "keep", processedDocumentId: pdId };
|
|
220
|
+
if (ctx.localRunning || status?.state === "running") return { do: "wait" };
|
|
221
|
+
if (status?.state === "failed")
|
|
222
|
+
return {
|
|
223
|
+
do: "unreadable",
|
|
224
|
+
reason: status.error?.message ? `The file could not be read: ${status.error.message}. Remove it, or add it again.` : UNREADABLE
|
|
225
|
+
};
|
|
226
|
+
if (!ctx.started) return { do: "start" };
|
|
227
|
+
return { do: "unreadable", reason: UNREADABLE };
|
|
228
|
+
}
|
|
229
|
+
function fileSourcePollDelayMs(round) {
|
|
230
|
+
if (round < 5) return 3e3;
|
|
231
|
+
if (round < 20) return 6e3;
|
|
232
|
+
return 15e3;
|
|
233
|
+
}
|
|
234
|
+
function isWaitingFileCard(card) {
|
|
235
|
+
return card.status === "ready" && card.draft.ref?.resource_type === "file" && !!card.draft.fileId && !card.draft.processedDocumentId;
|
|
236
|
+
}
|
|
237
|
+
function fileCardHeldForOrganization(card, activeOrganizationId, fileOrganizationId) {
|
|
238
|
+
if (!isWaitingFileCard(card)) return false;
|
|
239
|
+
if (activeOrganizationId) return false;
|
|
240
|
+
return !fileOrganizationId?.(card.draft.fileId);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// sources/runtime/tokens.ts
|
|
244
|
+
var SOURCE_CHARS_PER_TOKEN = {
|
|
245
|
+
prose: 2.9,
|
|
246
|
+
structured: 2.4
|
|
247
|
+
};
|
|
248
|
+
function estimateSourceTokens(chars) {
|
|
249
|
+
if (!Number.isFinite(chars) || chars <= 0) return 0;
|
|
250
|
+
return Math.ceil(chars / SOURCE_CHARS_PER_TOKEN.prose);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// sources/index.ts
|
|
254
|
+
var RESOURCE_REF_KIND = "resource_ref";
|
|
255
|
+
var SOURCE_SET_KIND = "source_set";
|
|
256
|
+
var RESOLVED_SOURCE_SET_KIND = "resolved_source_set";
|
|
257
|
+
var SOURCE_SET_VERSION = 1;
|
|
258
|
+
function isRecord(value) {
|
|
259
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
260
|
+
}
|
|
261
|
+
function createSourceRef(resourceType, resourceId, options = {}) {
|
|
262
|
+
const resource_type = resourceType.trim();
|
|
263
|
+
const resource_id = resourceId.trim();
|
|
264
|
+
if (!resource_type) throw new Error("createSourceRef: resource_type is required");
|
|
265
|
+
if (!resource_id) throw new Error("createSourceRef: resource_id is required");
|
|
266
|
+
if (options.max_chars !== void 0 && (!Number.isInteger(options.max_chars) || options.max_chars <= 0)) {
|
|
267
|
+
throw new Error("createSourceRef: max_chars must be a positive integer");
|
|
268
|
+
}
|
|
269
|
+
const exclude = Array.from(
|
|
270
|
+
new Set(
|
|
271
|
+
(options.exclude ?? []).map((value) => value.trim().toLowerCase()).filter(Boolean)
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
const include_segments = Array.from(
|
|
275
|
+
new Set((options.include_segments ?? []).map((id) => id.trim()).filter(Boolean))
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
__kind: RESOURCE_REF_KIND,
|
|
279
|
+
resource_type,
|
|
280
|
+
resource_id,
|
|
281
|
+
...options.representation ? { representation: options.representation } : {},
|
|
282
|
+
...include_segments.length ? { include_segments } : {},
|
|
283
|
+
...options.delivery ? { delivery: options.delivery } : {},
|
|
284
|
+
...options.max_chars !== void 0 ? { max_chars: options.max_chars } : {},
|
|
285
|
+
...options.promote ? { promote: options.promote } : {},
|
|
286
|
+
...exclude.length ? { exclude } : {}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function isSourceRef(value) {
|
|
290
|
+
return isRecord(value) && value.__kind === RESOURCE_REF_KIND && typeof value.resource_type === "string" && value.resource_type.length > 0 && typeof value.resource_id === "string" && value.resource_id.length > 0;
|
|
291
|
+
}
|
|
292
|
+
function createSourceSet(sources, options = {}) {
|
|
293
|
+
const seen = /* @__PURE__ */ new Set();
|
|
294
|
+
const unique = [];
|
|
295
|
+
for (const ref of sources) {
|
|
296
|
+
if (!isSourceRef(ref)) throw new Error("createSourceSet: every source must be a resource_ref");
|
|
297
|
+
const key = `${ref.resource_type}\0${ref.resource_id}\0${ref.representation ?? ""}`;
|
|
298
|
+
if (seen.has(key)) continue;
|
|
299
|
+
seen.add(key);
|
|
300
|
+
unique.push(ref);
|
|
301
|
+
}
|
|
302
|
+
const topic = options.topic?.trim();
|
|
303
|
+
const retrieve_query = options.retrieve_query?.trim();
|
|
304
|
+
if (options.grounding === "retrieve" && !retrieve_query) {
|
|
305
|
+
throw new Error('createSourceSet: grounding "retrieve" needs a retrieve_query');
|
|
306
|
+
}
|
|
307
|
+
if (options.grounding === "selected" && !unique.some((ref) => (ref.include_segments?.length ?? 0) > 0)) {
|
|
308
|
+
throw new Error('createSourceSet: grounding "selected" needs at least one Source with include_segments');
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
__kind: SOURCE_SET_KIND,
|
|
312
|
+
version: SOURCE_SET_VERSION,
|
|
313
|
+
sources: unique,
|
|
314
|
+
...topic ? { topic } : {},
|
|
315
|
+
...options.grounding ? { grounding: options.grounding } : {},
|
|
316
|
+
...retrieve_query ? { retrieve_query } : {},
|
|
317
|
+
...options.target_model_id ? { target_model_id: options.target_model_id } : {}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function manifestEntryChars(entry) {
|
|
321
|
+
const selected = entry.ref.include_segments;
|
|
322
|
+
let chars;
|
|
323
|
+
if (selected?.length && entry.segments?.length) {
|
|
324
|
+
const wanted = new Set(selected);
|
|
325
|
+
chars = entry.segments.filter((segment) => wanted.has(segment.id)).reduce((sum, segment) => sum + segment.chars, 0);
|
|
326
|
+
} else {
|
|
327
|
+
const formKey = entry.ref.representation ?? entry.default_form;
|
|
328
|
+
const form = entry.forms.find((candidate) => candidate.form === formKey) ?? entry.forms.find((candidate) => candidate.form === entry.default_form);
|
|
329
|
+
chars = form?.chars ?? 0;
|
|
330
|
+
}
|
|
331
|
+
return entry.ref.max_chars !== void 0 ? Math.min(chars, entry.ref.max_chars) : chars;
|
|
332
|
+
}
|
|
333
|
+
function totalChars(value) {
|
|
334
|
+
if (value.__kind === RESOLVED_SOURCE_SET_KIND) {
|
|
335
|
+
return value.sources.reduce((sum, source) => sum + source.text.length, 0);
|
|
336
|
+
}
|
|
337
|
+
return value.sources.filter((entry) => entry.state === "ready" || entry.state === "processing").reduce((sum, entry) => sum + manifestEntryChars(entry), 0);
|
|
338
|
+
}
|
|
339
|
+
|
|
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
|
+
// sources/runtime/state.ts
|
|
524
|
+
var EMPTY_SOURCE_SET_STATE = Object.freeze({ cards: Object.freeze([]), topic: "" });
|
|
525
|
+
var SAME_SOURCE_AGAIN = "You added this again \u2014 it is the same Source, so it is listed once.";
|
|
526
|
+
function isSourceDraft(value) {
|
|
527
|
+
return !!value && typeof value === "object" && typeof value.kind === "string" && typeof value.label === "string";
|
|
528
|
+
}
|
|
529
|
+
function sameSourceRef(a, b) {
|
|
530
|
+
return a.resource_type === b.resource_type && a.resource_id === b.resource_id;
|
|
531
|
+
}
|
|
532
|
+
function sourceKey(ref) {
|
|
533
|
+
return `${ref.resource_type}:${ref.resource_id}`;
|
|
534
|
+
}
|
|
535
|
+
function draftKindForToken(token) {
|
|
536
|
+
if (token === "file") return "files";
|
|
537
|
+
if (token === "note") return "notes";
|
|
538
|
+
if (token === "processed_document") return "your_sources";
|
|
539
|
+
return "records";
|
|
540
|
+
}
|
|
541
|
+
function withDefaultForm(ref, defaultForm) {
|
|
542
|
+
return defaultForm && !ref.representation ? createSourceRef(ref.resource_type, ref.resource_id, { ...ref, representation: defaultForm }) : ref;
|
|
543
|
+
}
|
|
544
|
+
function cardById(state, id) {
|
|
545
|
+
return state.cards.find((c) => c.id === id);
|
|
546
|
+
}
|
|
547
|
+
function replaceCard(state, id, patch) {
|
|
548
|
+
let changed = false;
|
|
549
|
+
const cards = state.cards.map((c) => {
|
|
550
|
+
if (c.id !== id) return c;
|
|
551
|
+
const next = patch(c);
|
|
552
|
+
if (next !== c) changed = true;
|
|
553
|
+
return next;
|
|
554
|
+
});
|
|
555
|
+
return changed ? { ...state, cards } : state;
|
|
556
|
+
}
|
|
557
|
+
function addReadyCard(state, id, draft, options = {}) {
|
|
558
|
+
const ref = draft.ref ? withDefaultForm(draft.ref, options.defaultForm) : null;
|
|
559
|
+
return {
|
|
560
|
+
...state,
|
|
561
|
+
cards: [...state.cards, { id, draft: { ...draft, ref }, status: "ready", error: null, manifest: null }]
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function addPendingCard(state, id, draft) {
|
|
565
|
+
return {
|
|
566
|
+
...state,
|
|
567
|
+
cards: [...state.cards, { id, draft: { ...draft, ref: null }, status: "resolving", error: null, manifest: null }]
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function settleCard(state, id, patch, options = {}) {
|
|
571
|
+
const card = cardById(state, id);
|
|
572
|
+
if (!card) return state;
|
|
573
|
+
const twin = state.cards.find((c) => c.id !== id && c.draft.ref && sameSourceRef(c.draft.ref, patch.ref));
|
|
574
|
+
if (twin) {
|
|
575
|
+
const without = { ...state, cards: state.cards.filter((c) => c.id !== id) };
|
|
576
|
+
if (twin.draft.notes?.includes(SAME_SOURCE_AGAIN)) return without;
|
|
577
|
+
return replaceCard(without, twin.id, (c) => ({
|
|
578
|
+
...c,
|
|
579
|
+
draft: { ...c.draft, notes: [...c.draft.notes ?? [], SAME_SOURCE_AGAIN] }
|
|
580
|
+
}));
|
|
581
|
+
}
|
|
582
|
+
return replaceCard(state, id, (c) => ({
|
|
583
|
+
...c,
|
|
584
|
+
// Landed: the kept input has done its job and leaves the draft.
|
|
585
|
+
draft: { ...c.draft, ...patch, input: void 0, ref: withDefaultForm(patch.ref, options.defaultForm) },
|
|
586
|
+
status: "ready",
|
|
587
|
+
error: null
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
function failCard(state, id, sentence) {
|
|
591
|
+
return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence }));
|
|
592
|
+
}
|
|
593
|
+
function restartCard(state, id) {
|
|
594
|
+
return replaceCard(state, id, (c) => ({ ...c, status: "resolving", error: null }));
|
|
595
|
+
}
|
|
596
|
+
function updateCardDraft(state, id, patch) {
|
|
597
|
+
return replaceCard(state, id, (c) => ({ ...c, draft: { ...c.draft, ...patch } }));
|
|
598
|
+
}
|
|
599
|
+
function removeCard(state, id) {
|
|
600
|
+
if (!cardById(state, id)) return state;
|
|
601
|
+
return { ...state, cards: state.cards.filter((c) => c.id !== id) };
|
|
602
|
+
}
|
|
603
|
+
function updateCardRef(state, id, options) {
|
|
604
|
+
return replaceCard(state, id, (c) => {
|
|
605
|
+
if (!c.draft.ref) return c;
|
|
606
|
+
const ref = createSourceRef(c.draft.ref.resource_type, c.draft.ref.resource_id, { ...c.draft.ref, ...options });
|
|
607
|
+
return { ...c, draft: { ...c.draft, ref } };
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
function setCardWaitForClean(state, id, wait) {
|
|
611
|
+
return replaceCard(state, id, (c) => ({ ...c, draft: { ...c.draft, waitForClean: wait } }));
|
|
612
|
+
}
|
|
613
|
+
function setSourceTopic(state, topic) {
|
|
614
|
+
return state.topic === topic ? state : { ...state, topic };
|
|
615
|
+
}
|
|
616
|
+
function applyReviewedSourceSet(state, set) {
|
|
617
|
+
let next = state;
|
|
618
|
+
for (const card of state.cards) {
|
|
619
|
+
const ref = card.draft.ref;
|
|
620
|
+
if (!ref) continue;
|
|
621
|
+
const match = set.sources.find((r) => sameSourceRef(r, ref));
|
|
622
|
+
if (!match) continue;
|
|
623
|
+
next = replaceCard(next, card.id, (c) => ({ ...c, draft: { ...c.draft, ref: match } }));
|
|
624
|
+
}
|
|
625
|
+
return set.topic !== void 0 ? setSourceTopic(next, set.topic) : next;
|
|
626
|
+
}
|
|
627
|
+
function applyManifest(state, manifest) {
|
|
628
|
+
let next = state;
|
|
629
|
+
for (const card of state.cards) {
|
|
630
|
+
const ref = card.draft.ref;
|
|
631
|
+
if (!ref || card.status !== "ready") continue;
|
|
632
|
+
const entry = manifest.sources.find((e) => sameSourceRef(e.ref, ref));
|
|
633
|
+
if (entry) next = replaceCard(next, card.id, (c) => ({ ...c, manifest: entry }));
|
|
634
|
+
}
|
|
635
|
+
return next;
|
|
636
|
+
}
|
|
637
|
+
function fitCardDeliveries(state, deliveries) {
|
|
638
|
+
let next = state;
|
|
639
|
+
for (const card of state.cards) {
|
|
640
|
+
const fit = fitDelivery(card.draft.ref, deliveries);
|
|
641
|
+
if (!fit) continue;
|
|
642
|
+
next = updateCardRef(next, card.id, fit.patch);
|
|
643
|
+
const note = deliverySwitchedNote(fit.to);
|
|
644
|
+
if (!card.draft.notes?.includes(note))
|
|
645
|
+
next = updateCardDraft(next, card.id, { notes: [...card.draft.notes ?? [], note] });
|
|
646
|
+
}
|
|
647
|
+
return next;
|
|
648
|
+
}
|
|
649
|
+
function readPersistedSourceInput(data) {
|
|
650
|
+
const record = data && typeof data === "object" ? data : void 0;
|
|
651
|
+
const sources = Array.isArray(record?.sources) ? record.sources.filter(
|
|
652
|
+
(c) => !!c && typeof c.id === "string" && isSourceDraft(c.draft)
|
|
653
|
+
) : [];
|
|
654
|
+
return { sources, topic: typeof record?.topic === "string" ? record.topic : "" };
|
|
655
|
+
}
|
|
656
|
+
function toPersistedSourceInput(state) {
|
|
657
|
+
return {
|
|
658
|
+
sources: state.cards.map((c) => ({ id: c.id, draft: c.draft, status: c.status, error: c.error })),
|
|
659
|
+
topic: state.topic
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function restoredCards(persisted) {
|
|
663
|
+
return persisted.sources.map((card) => {
|
|
664
|
+
const reloaded = reloadedCard(
|
|
665
|
+
card.draft,
|
|
666
|
+
card.status === "error" && card.error === WAITING_FOR_ORGANIZATION ? "resolving" : card.status
|
|
667
|
+
);
|
|
668
|
+
return {
|
|
669
|
+
id: card.id,
|
|
670
|
+
draft: card.draft,
|
|
671
|
+
status: reloaded ? "error" : card.status,
|
|
672
|
+
error: reloaded ? reloaded.sentence : card.error ?? null,
|
|
673
|
+
manifest: null
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
function readySourceRefs(state) {
|
|
678
|
+
return state.cards.flatMap((c) => c.status === "ready" && c.draft.ref ? [c.draft.ref] : []);
|
|
679
|
+
}
|
|
680
|
+
function hasSourceRef(state, resourceType, resourceId) {
|
|
681
|
+
return state.cards.some(
|
|
682
|
+
(c) => c.draft.ref?.resource_type === resourceType && c.draft.ref.resource_id === resourceId
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
function selectSourceSet(state, options = {}) {
|
|
686
|
+
const topic = state.topic.trim();
|
|
687
|
+
return createSourceSet(readySourceRefs(state), {
|
|
688
|
+
topic: topic || void 0,
|
|
689
|
+
grounding: "whole",
|
|
690
|
+
target_model_id: options.targetModelId
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
function selectTotalChars(state) {
|
|
694
|
+
const measured = state.cards.flatMap(
|
|
695
|
+
(c) => c.status === "ready" && c.manifest && c.draft.ref ? [{ ...c.manifest, ref: c.draft.ref }] : []
|
|
696
|
+
);
|
|
697
|
+
if (!measured.length) return 0;
|
|
698
|
+
return totalChars({
|
|
699
|
+
__kind: "source_manifest",
|
|
700
|
+
sources: measured,
|
|
701
|
+
total_chars: 0,
|
|
702
|
+
estimated_tokens: 0,
|
|
703
|
+
model_context_tokens: null
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
function selectSettled(state) {
|
|
707
|
+
return state.cards.every((c) => c.status === "ready");
|
|
708
|
+
}
|
|
709
|
+
function sourceRoomLeft(state, max) {
|
|
710
|
+
return max === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, max - state.cards.length);
|
|
711
|
+
}
|
|
712
|
+
function withDisplayNames(resolved, cards) {
|
|
713
|
+
const names = /* @__PURE__ */ new Map();
|
|
714
|
+
for (const c of cards) {
|
|
715
|
+
const label = c.draft.label?.trim();
|
|
716
|
+
if (c.draft.ref && label) names.set(sourceKey(c.draft.ref), label);
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
...resolved,
|
|
720
|
+
sources: resolved.sources.map((s) => {
|
|
721
|
+
const name = names.get(sourceKey(s.ref));
|
|
722
|
+
return name ? { ...s, label: name } : s;
|
|
723
|
+
})
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// sources/runtime/controller.ts
|
|
728
|
+
function randomId() {
|
|
729
|
+
const cryptoApi = globalThis.crypto;
|
|
730
|
+
if (cryptoApi?.randomUUID) return cryptoApi.randomUUID();
|
|
731
|
+
return `src-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
732
|
+
}
|
|
733
|
+
function errorText(error) {
|
|
734
|
+
return error instanceof Error ? error.message : String(error);
|
|
735
|
+
}
|
|
736
|
+
function createMemorySourceSetStore(initial = EMPTY_SOURCE_SET_STATE) {
|
|
737
|
+
let state = initial;
|
|
738
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
739
|
+
return {
|
|
740
|
+
getState: () => state,
|
|
741
|
+
setState(next) {
|
|
742
|
+
if (next === state) return;
|
|
743
|
+
state = next;
|
|
744
|
+
for (const listener of [...listeners]) listener();
|
|
745
|
+
},
|
|
746
|
+
subscribe(listener) {
|
|
747
|
+
listeners.add(listener);
|
|
748
|
+
return () => listeners.delete(listener);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
function createMemorySourceDraftStorage(initial = void 0) {
|
|
753
|
+
let saved = initial;
|
|
754
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
755
|
+
return {
|
|
756
|
+
load: () => saved,
|
|
757
|
+
save(value) {
|
|
758
|
+
saved = value;
|
|
759
|
+
for (const listener of [...listeners]) listener();
|
|
760
|
+
},
|
|
761
|
+
subscribe(listener) {
|
|
762
|
+
listeners.add(listener);
|
|
763
|
+
return () => listeners.delete(listener);
|
|
764
|
+
},
|
|
765
|
+
current: () => saved
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function createSourceSetController(adapter) {
|
|
769
|
+
const { store, persistence } = adapter;
|
|
770
|
+
let config = { ...adapter.config ?? {} };
|
|
771
|
+
const createId = adapter.createId ?? randomId;
|
|
772
|
+
const describe = adapter.describeError ?? errorText;
|
|
773
|
+
let meta = { measuring: false, manifestError: null };
|
|
774
|
+
const metaListeners = /* @__PURE__ */ new Set();
|
|
775
|
+
let hydrated = false;
|
|
776
|
+
let measurement = null;
|
|
777
|
+
const setMeta = (patch) => {
|
|
778
|
+
const next = { ...meta, ...patch };
|
|
779
|
+
if (next.measuring === meta.measuring && next.manifestError === meta.manifestError) return;
|
|
780
|
+
meta = next;
|
|
781
|
+
for (const listener of [...metaListeners]) listener();
|
|
782
|
+
};
|
|
783
|
+
const commit = (next) => {
|
|
784
|
+
hydrated = true;
|
|
785
|
+
if (next !== store.getState()) store.setState(next);
|
|
786
|
+
persistence?.save(toPersistedSourceInput(store.getState()));
|
|
787
|
+
};
|
|
788
|
+
const has = (id) => store.getState().cards.some((c) => c.id === id);
|
|
789
|
+
const controller = {
|
|
790
|
+
getState: () => store.getState(),
|
|
791
|
+
subscribe: (listener) => store.subscribe(listener),
|
|
792
|
+
getMeta: () => meta,
|
|
793
|
+
subscribeMeta(listener) {
|
|
794
|
+
metaListeners.add(listener);
|
|
795
|
+
return () => metaListeners.delete(listener);
|
|
796
|
+
},
|
|
797
|
+
configure(next) {
|
|
798
|
+
config = { ...next };
|
|
799
|
+
},
|
|
800
|
+
get config() {
|
|
801
|
+
return config;
|
|
802
|
+
},
|
|
803
|
+
restore(saved) {
|
|
804
|
+
if (hydrated) return false;
|
|
805
|
+
const live = store.getState();
|
|
806
|
+
if (live.cards.length > 0) {
|
|
807
|
+
hydrated = true;
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
const persisted = readPersistedSourceInput(saved);
|
|
811
|
+
if (persisted.sources.length === 0) return false;
|
|
812
|
+
hydrated = true;
|
|
813
|
+
store.setState({ cards: restoredCards(persisted), topic: live.topic || persisted.topic });
|
|
814
|
+
return true;
|
|
815
|
+
},
|
|
816
|
+
addReady(draft) {
|
|
817
|
+
const id = createId();
|
|
818
|
+
commit(addReadyCard(store.getState(), id, draft, { defaultForm: config.defaultForm }));
|
|
819
|
+
return id;
|
|
820
|
+
},
|
|
821
|
+
addPending(draft) {
|
|
822
|
+
const id = createId();
|
|
823
|
+
commit(addPendingCard(store.getState(), id, draft));
|
|
824
|
+
return id;
|
|
825
|
+
},
|
|
826
|
+
settle(id, patch) {
|
|
827
|
+
if (!has(id)) return;
|
|
828
|
+
commit(settleCard(store.getState(), id, patch, { defaultForm: config.defaultForm }));
|
|
829
|
+
},
|
|
830
|
+
fail(id, sentence) {
|
|
831
|
+
if (!has(id)) return;
|
|
832
|
+
commit(failCard(store.getState(), id, sentence));
|
|
833
|
+
},
|
|
834
|
+
restart(id) {
|
|
835
|
+
if (!has(id)) return false;
|
|
836
|
+
commit(restartCard(store.getState(), id));
|
|
837
|
+
return true;
|
|
838
|
+
},
|
|
839
|
+
updateDraft(id, patch) {
|
|
840
|
+
if (!has(id)) return;
|
|
841
|
+
commit(updateCardDraft(store.getState(), id, patch));
|
|
842
|
+
},
|
|
843
|
+
remove(id) {
|
|
844
|
+
commit(removeCard(store.getState(), id));
|
|
845
|
+
},
|
|
846
|
+
updateRef(id, options) {
|
|
847
|
+
if (!store.getState().cards.find((c) => c.id === id)?.draft.ref) return;
|
|
848
|
+
commit(updateCardRef(store.getState(), id, options));
|
|
849
|
+
},
|
|
850
|
+
setWaitForClean(id, wait) {
|
|
851
|
+
if (!has(id)) return;
|
|
852
|
+
commit(setCardWaitForClean(store.getState(), id, wait));
|
|
853
|
+
},
|
|
854
|
+
setTopic(topic) {
|
|
855
|
+
commit(setSourceTopic(store.getState(), topic));
|
|
856
|
+
},
|
|
857
|
+
hasRef: (resourceType, resourceId) => hasSourceRef(store.getState(), resourceType, resourceId),
|
|
858
|
+
liveCount: () => store.getState().cards.length,
|
|
859
|
+
toSourceSet: (options = {}) => selectSourceSet(store.getState(), options),
|
|
860
|
+
applySourceSet(set) {
|
|
861
|
+
commit(applyReviewedSourceSet(store.getState(), set));
|
|
862
|
+
},
|
|
863
|
+
async manifest() {
|
|
864
|
+
const refs = readySourceRefs(store.getState());
|
|
865
|
+
if (refs.length === 0) {
|
|
866
|
+
setMeta({ manifestError: null });
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
if (!adapter.client) throw new Error("createSourceSetController: manifest() needs a client in the adapter");
|
|
870
|
+
measurement?.abort();
|
|
871
|
+
const controllerForThis = new AbortController();
|
|
872
|
+
measurement = controllerForThis;
|
|
873
|
+
setMeta({ measuring: true });
|
|
874
|
+
try {
|
|
875
|
+
const result = await adapter.client.manifest(createSourceSet(refs), {
|
|
876
|
+
organizationId: config.organizationId ?? void 0,
|
|
877
|
+
signal: controllerForThis.signal
|
|
878
|
+
});
|
|
879
|
+
if (measurement !== controllerForThis) return null;
|
|
880
|
+
const next = applyManifest(store.getState(), result);
|
|
881
|
+
if (next !== store.getState()) store.setState(next);
|
|
882
|
+
setMeta({ manifestError: null });
|
|
883
|
+
return result;
|
|
884
|
+
} catch (err) {
|
|
885
|
+
if (measurement !== controllerForThis) return null;
|
|
886
|
+
setMeta({
|
|
887
|
+
manifestError: `Sizes and parts could not be read: ${describe(err)} Your picks are kept \u2014 try again.`
|
|
888
|
+
});
|
|
889
|
+
return null;
|
|
890
|
+
} finally {
|
|
891
|
+
if (measurement === controllerForThis) setMeta({ measuring: false });
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
async resolve(options = {}) {
|
|
895
|
+
if (!adapter.client) throw new Error("createSourceSetController: resolve() needs a client in the adapter");
|
|
896
|
+
const resolved = await adapter.client.resolve(controller.toSourceSet(options), {
|
|
897
|
+
organizationId: config.organizationId ?? void 0
|
|
898
|
+
});
|
|
899
|
+
return withDisplayNames(resolved, store.getState().cards);
|
|
900
|
+
},
|
|
901
|
+
fitDeliveries(deliveries = config.deliveries) {
|
|
902
|
+
const next = fitCardDeliveries(store.getState(), deliveries);
|
|
903
|
+
if (next !== store.getState()) commit(next);
|
|
904
|
+
},
|
|
905
|
+
roomLeft: () => sourceRoomLeft(store.getState(), config.max),
|
|
906
|
+
totalChars: () => selectTotalChars(store.getState()),
|
|
907
|
+
settled: () => selectSettled(store.getState())
|
|
908
|
+
};
|
|
909
|
+
return controller;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// sources/runtime/intake.ts
|
|
913
|
+
var import_associations = require("@ai-matrx/associations");
|
|
914
|
+
var TRANSCRIPT_NAME_MAX = 120;
|
|
915
|
+
function sourceAttachTargets(attachTo) {
|
|
916
|
+
return attachTo ? [
|
|
917
|
+
{
|
|
918
|
+
entity_type: attachTo.entityType,
|
|
919
|
+
entity_id: attachTo.entityId,
|
|
920
|
+
// The EDGE's label, which the registry names (a deck's Source is its
|
|
921
|
+
// "source", a scope's is "about") — never the target's display name,
|
|
922
|
+
// which the door refused as an unknown label (USI-3e).
|
|
923
|
+
label: null,
|
|
924
|
+
signal: true
|
|
925
|
+
}
|
|
926
|
+
] : [];
|
|
927
|
+
}
|
|
928
|
+
function hostOf(url) {
|
|
929
|
+
try {
|
|
930
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
931
|
+
} catch {
|
|
932
|
+
return url;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function isSaveItNotice(remedy) {
|
|
936
|
+
return typeof remedy === "string" && remedy.startsWith("save_it");
|
|
937
|
+
}
|
|
938
|
+
function landedNotes(notices) {
|
|
939
|
+
return (notices ?? []).map((n) => n.message).filter(Boolean);
|
|
940
|
+
}
|
|
941
|
+
function registeredFileEdge(targetType) {
|
|
942
|
+
if ((0, import_associations.isRegisteredPair)("file", targetType)) return "file_to_target";
|
|
943
|
+
if ((0, import_associations.isRegisteredPair)(targetType, "file")) return "target_to_file";
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
function createSourceIntake(set, deps) {
|
|
947
|
+
const { doors } = deps;
|
|
948
|
+
const attachTo = sourceAttachTargets(deps.attachTo);
|
|
949
|
+
const hold = (run) => deps.holdIntent ? deps.holdIntent(run) : run();
|
|
950
|
+
const notFiledSuffix = attachTo.length ? " or filed with what you are making" : "";
|
|
951
|
+
const failOrHold = (id, err) => {
|
|
952
|
+
set.fail(id, deps.waitsForOrganization(err) ? WAITING_FOR_ORGANIZATION : deps.failureSentence(err));
|
|
953
|
+
};
|
|
954
|
+
const needUser = () => {
|
|
955
|
+
const userId = deps.userId();
|
|
956
|
+
if (!userId) throw new Error("Your sign-in is still loading, so nothing was added. Try again in a moment.");
|
|
957
|
+
return userId;
|
|
958
|
+
};
|
|
959
|
+
const landText = async (body) => {
|
|
960
|
+
const landed = await doors.land({ ...body, keep: true, attach_to: attachTo });
|
|
961
|
+
return { id: landed.processed_document_id, notes: landedNotes(landed.notices) };
|
|
962
|
+
};
|
|
963
|
+
const landPaste = async (id, trimmed, name) => {
|
|
964
|
+
try {
|
|
965
|
+
const organizationId = await deps.ensureOrganization();
|
|
966
|
+
const body = await doors.buildTextLanding({ text: trimmed, name, organizationId, userId: needUser() });
|
|
967
|
+
const landed = await landText(body);
|
|
968
|
+
set.settle(id, {
|
|
969
|
+
label: body.name,
|
|
970
|
+
ref: createSourceRef("processed_document", landed.id),
|
|
971
|
+
processedDocumentId: landed.id,
|
|
972
|
+
notes: landed.notes
|
|
973
|
+
});
|
|
974
|
+
} catch (err) {
|
|
975
|
+
failOrHold(id, err);
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
const landWebPage = async (id, url) => {
|
|
979
|
+
try {
|
|
980
|
+
const organizationId = await deps.ensureOrganization();
|
|
981
|
+
const result = await doors.scrapeUrl(url);
|
|
982
|
+
if (!result) {
|
|
983
|
+
set.fail(
|
|
984
|
+
id,
|
|
985
|
+
"That page could not be read \u2014 it may need a sign-in or block robots. Try another link, or copy the text and use Paste text."
|
|
986
|
+
);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (!result.processedDocumentId) {
|
|
990
|
+
set.fail(
|
|
991
|
+
id,
|
|
992
|
+
result.sourceNotices[0]?.message ?? "The page was read but did not become a Source, and the server did not say why. Try again."
|
|
993
|
+
);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
let notes;
|
|
997
|
+
try {
|
|
998
|
+
const kept = await doors.keep(result.processedDocumentId, { attachTo, organizationId });
|
|
999
|
+
notes = [
|
|
1000
|
+
...landedNotes(result.sourceNotices.filter((n) => !isSaveItNotice(n.remedy))),
|
|
1001
|
+
...landedNotes(kept.notices)
|
|
1002
|
+
];
|
|
1003
|
+
} catch (err) {
|
|
1004
|
+
notes = [
|
|
1005
|
+
...landedNotes(result.sourceNotices),
|
|
1006
|
+
`It is in your Sources, but it could not be kept for reuse${notFiledSuffix}: ${deps.failureSentence(err)}`
|
|
1007
|
+
];
|
|
1008
|
+
}
|
|
1009
|
+
set.settle(id, {
|
|
1010
|
+
label: result.pageTitle || hostOf(url),
|
|
1011
|
+
ref: createSourceRef("processed_document", result.processedDocumentId),
|
|
1012
|
+
processedDocumentId: result.processedDocumentId,
|
|
1013
|
+
notes
|
|
1014
|
+
});
|
|
1015
|
+
} catch (err) {
|
|
1016
|
+
failOrHold(id, err);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const landYouTube = async (id, url, videoId) => {
|
|
1020
|
+
try {
|
|
1021
|
+
const organizationId = await deps.ensureOrganization();
|
|
1022
|
+
const { text, note, source } = await doors.fetchYouTubeTranscript(url);
|
|
1023
|
+
if (!text) {
|
|
1024
|
+
set.fail(
|
|
1025
|
+
id,
|
|
1026
|
+
note ?? "That video has no speech we could write out (no captions and no clear voice). Try another video, or paste a transcript."
|
|
1027
|
+
);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (source) {
|
|
1031
|
+
const label = `YouTube \u2014 ${source.title || videoId}`.slice(0, TRANSCRIPT_NAME_MAX);
|
|
1032
|
+
let notes = [];
|
|
1033
|
+
try {
|
|
1034
|
+
const kept = await doors.keep(source.processed_document_id, { attachTo, organizationId });
|
|
1035
|
+
notes = landedNotes(kept.notices);
|
|
1036
|
+
} catch (err) {
|
|
1037
|
+
notes = [
|
|
1038
|
+
`It was read, but its Source could not be kept for reuse${notFiledSuffix}: ${deps.failureSentence(err)}`
|
|
1039
|
+
];
|
|
1040
|
+
}
|
|
1041
|
+
set.settle(id, {
|
|
1042
|
+
label,
|
|
1043
|
+
ref: createSourceRef("processed_document", source.processed_document_id),
|
|
1044
|
+
processedDocumentId: source.processed_document_id,
|
|
1045
|
+
notes
|
|
1046
|
+
});
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const name = `YouTube \u2014 ${text.split(/\s+/).slice(0, 10).join(" ")}\u2026`.slice(0, TRANSCRIPT_NAME_MAX);
|
|
1050
|
+
const body = await doors.buildTextLanding({ text, name, organizationId, userId: needUser() });
|
|
1051
|
+
const landed = await landText({
|
|
1052
|
+
...body,
|
|
1053
|
+
// Identity is the video, so the same video twice is one Source.
|
|
1054
|
+
canonical_identity: `youtube:${videoId}`,
|
|
1055
|
+
provenance: { ...body.provenance ?? {}, origin_client: "youtube", capture_method: "captions", final_url: url }
|
|
1056
|
+
});
|
|
1057
|
+
set.settle(id, {
|
|
1058
|
+
label: name,
|
|
1059
|
+
ref: createSourceRef("processed_document", landed.id),
|
|
1060
|
+
processedDocumentId: landed.id,
|
|
1061
|
+
// Untimed: say why it has no parts to choose (the server's own sentence).
|
|
1062
|
+
notes: note ? [...landed.notes, note] : landed.notes
|
|
1063
|
+
});
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
failOrHold(id, err);
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
const fileAgainstTarget = async (fileId, organizationId) => {
|
|
1069
|
+
const target = deps.attachTo;
|
|
1070
|
+
if (!target) return null;
|
|
1071
|
+
const what = target.label ? `"${target.label}"` : "what you are making";
|
|
1072
|
+
const direction = registeredFileEdge(target.entityType);
|
|
1073
|
+
const cannot = `It was uploaded, but a file cannot be filed with ${what} (a ${target.entityType.replace(/_/g, " ")}), so it is not listed there.`;
|
|
1074
|
+
if (!direction) return cannot;
|
|
1075
|
+
if (direction === "file_to_target" && !(0, import_associations.isAssociationTargetType)(target.entityType)) return cannot;
|
|
1076
|
+
const linked = await doors.associate(
|
|
1077
|
+
direction === "file_to_target" ? {
|
|
1078
|
+
sourceType: "file",
|
|
1079
|
+
sourceId: fileId,
|
|
1080
|
+
targetType: target.entityType,
|
|
1081
|
+
targetId: target.entityId,
|
|
1082
|
+
orgId: organizationId
|
|
1083
|
+
} : {
|
|
1084
|
+
sourceType: target.entityType,
|
|
1085
|
+
sourceId: target.entityId,
|
|
1086
|
+
targetType: "file",
|
|
1087
|
+
targetId: fileId,
|
|
1088
|
+
orgId: organizationId
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1091
|
+
return linked.ok ? null : `It was uploaded, but it could not be filed with ${what}: ${linked.error.message}`;
|
|
1092
|
+
};
|
|
1093
|
+
const fileNotes = async (fileId) => {
|
|
1094
|
+
try {
|
|
1095
|
+
const organizationId = await deps.ensureOrganization();
|
|
1096
|
+
const filed = await fileAgainstTarget(fileId, organizationId);
|
|
1097
|
+
return filed ? [filed] : [];
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
return deps.waitsForOrganization(err) ? [] : [`It was uploaded, but it could not be filed: ${deps.failureSentence(err)}`];
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
const transcribeRecording = async (id, fileId, fileName) => {
|
|
1103
|
+
try {
|
|
1104
|
+
const organizationId = await deps.ensureOrganization();
|
|
1105
|
+
const transcription = await doors.transcribeFile({ fileId, organizationId });
|
|
1106
|
+
const text = (transcription.text ?? "").trim();
|
|
1107
|
+
if (text.length < 8) {
|
|
1108
|
+
set.fail(
|
|
1109
|
+
id,
|
|
1110
|
+
"We couldn't hear enough speech in that recording to write it out. Try a recording with a clear voice."
|
|
1111
|
+
);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const name = `Recording \u2014 ${fileName}`.slice(0, TRANSCRIPT_NAME_MAX);
|
|
1115
|
+
const body = await doors.buildTextLanding({ text, name, organizationId, userId: needUser() });
|
|
1116
|
+
const landed = await landText({
|
|
1117
|
+
...body,
|
|
1118
|
+
canonical_identity: `audio-transcript:${fileId}`,
|
|
1119
|
+
provenance: { ...body.provenance ?? {}, origin_client: "transcription", capture_method: "speech" }
|
|
1120
|
+
});
|
|
1121
|
+
set.settle(id, {
|
|
1122
|
+
label: name,
|
|
1123
|
+
ref: createSourceRef("processed_document", landed.id),
|
|
1124
|
+
processedDocumentId: landed.id,
|
|
1125
|
+
fileId,
|
|
1126
|
+
notes: landed.notes
|
|
1127
|
+
});
|
|
1128
|
+
} catch (err) {
|
|
1129
|
+
failOrHold(id, err);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
const intake = {
|
|
1133
|
+
async addPastedText(text, name) {
|
|
1134
|
+
const trimmed = text.trim();
|
|
1135
|
+
if (!trimmed) return;
|
|
1136
|
+
const max = deps.maxKeptChars();
|
|
1137
|
+
const kept = max !== null && trimmed.length <= max;
|
|
1138
|
+
const id = set.addPending({
|
|
1139
|
+
kind: "paste",
|
|
1140
|
+
label: name?.trim() || "Pasted text",
|
|
1141
|
+
input: kept ? { text: trimmed, name } : void 0,
|
|
1142
|
+
notes: kept ? void 0 : [NOT_KEPT_FOR_RELOAD]
|
|
1143
|
+
});
|
|
1144
|
+
await hold(() => landPaste(id, trimmed, name));
|
|
1145
|
+
},
|
|
1146
|
+
async addWebPage(raw) {
|
|
1147
|
+
const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
|
|
1148
|
+
const id = set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
|
|
1149
|
+
await hold(() => landWebPage(id, url));
|
|
1150
|
+
},
|
|
1151
|
+
async addScrapedPage(page) {
|
|
1152
|
+
const label = page.title || hostOf(page.url);
|
|
1153
|
+
if (!page.processedDocumentId) {
|
|
1154
|
+
const id2 = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
|
|
1155
|
+
await hold(() => landPaste(id2, page.text, label));
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
const pdId = page.processedDocumentId;
|
|
1159
|
+
const id = set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
|
|
1160
|
+
await hold(async () => {
|
|
1161
|
+
let notes = [];
|
|
1162
|
+
try {
|
|
1163
|
+
const organizationId = await deps.ensureOrganization();
|
|
1164
|
+
const kept = await doors.keep(pdId, { attachTo, organizationId });
|
|
1165
|
+
notes = landedNotes(kept.notices);
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
notes = deps.waitsForOrganization(err) ? [KEEP_WAITING_FOR_ORGANIZATION] : [`It is in your Sources, but it could not be kept for reuse${notFiledSuffix}: ${deps.failureSentence(err)}`];
|
|
1168
|
+
}
|
|
1169
|
+
set.settle(id, {
|
|
1170
|
+
label,
|
|
1171
|
+
ref: createSourceRef("processed_document", pdId),
|
|
1172
|
+
processedDocumentId: pdId,
|
|
1173
|
+
notes
|
|
1174
|
+
});
|
|
1175
|
+
});
|
|
1176
|
+
},
|
|
1177
|
+
async addUploaded(files, kind) {
|
|
1178
|
+
for (const file of files) {
|
|
1179
|
+
if (set.hasRef("file", file.fileId)) continue;
|
|
1180
|
+
const id = set.addPending({ kind, label: file.name, origin: "Uploaded file", fileId: file.fileId });
|
|
1181
|
+
const notes = await fileNotes(file.fileId);
|
|
1182
|
+
set.settle(id, { label: file.name, ref: createSourceRef("file", file.fileId), fileId: file.fileId, notes });
|
|
1183
|
+
}
|
|
1184
|
+
},
|
|
1185
|
+
async addUploadedRecording(file) {
|
|
1186
|
+
const id = set.addPending({
|
|
1187
|
+
kind: "audio",
|
|
1188
|
+
label: file.name,
|
|
1189
|
+
origin: "Recording",
|
|
1190
|
+
fileId: file.fileId,
|
|
1191
|
+
input: { fileId: file.fileId }
|
|
1192
|
+
});
|
|
1193
|
+
await hold(() => transcribeRecording(id, file.fileId, file.name));
|
|
1194
|
+
},
|
|
1195
|
+
addExisting(item) {
|
|
1196
|
+
if (set.hasRef(item.token, item.id)) return false;
|
|
1197
|
+
set.addReady({
|
|
1198
|
+
kind: draftKindForToken(item.token),
|
|
1199
|
+
label: item.title,
|
|
1200
|
+
ref: createSourceRef(item.token, item.id),
|
|
1201
|
+
fileId: item.token === "file" ? item.id : void 0,
|
|
1202
|
+
processedDocumentId: item.token === "processed_document" ? item.id : void 0
|
|
1203
|
+
});
|
|
1204
|
+
return true;
|
|
1205
|
+
},
|
|
1206
|
+
async addYouTube(raw) {
|
|
1207
|
+
const url = raw.trim();
|
|
1208
|
+
const videoId = deps.youtubeId(url);
|
|
1209
|
+
if (!videoId) return;
|
|
1210
|
+
const id = set.addPending({ kind: "youtube", label: "YouTube video", origin: url, input: { url } });
|
|
1211
|
+
await hold(() => landYouTube(id, url, videoId));
|
|
1212
|
+
},
|
|
1213
|
+
resume(card) {
|
|
1214
|
+
const input = resumableInput(card.draft);
|
|
1215
|
+
if (!input) return false;
|
|
1216
|
+
set.restart(card.id);
|
|
1217
|
+
switch (card.draft.kind) {
|
|
1218
|
+
case "paste":
|
|
1219
|
+
void landPaste(card.id, (input.text ?? "").trim(), input.name);
|
|
1220
|
+
return true;
|
|
1221
|
+
case "web":
|
|
1222
|
+
void landWebPage(card.id, input.url ?? "");
|
|
1223
|
+
return true;
|
|
1224
|
+
case "youtube": {
|
|
1225
|
+
const videoId = deps.youtubeId(input.url ?? "");
|
|
1226
|
+
if (!videoId) {
|
|
1227
|
+
set.fail(card.id, "That link is not a YouTube video any more. Remove it and paste the link again.");
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
1230
|
+
void landYouTube(card.id, input.url ?? "", videoId);
|
|
1231
|
+
return true;
|
|
1232
|
+
}
|
|
1233
|
+
case "audio":
|
|
1234
|
+
void transcribeRecording(card.id, input.fileId ?? "", card.draft.label);
|
|
1235
|
+
return true;
|
|
1236
|
+
default:
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
async retryFile(card, file) {
|
|
1241
|
+
set.restart(card.id);
|
|
1242
|
+
set.updateDraft(card.id, { label: file.name, fileId: file.fileId });
|
|
1243
|
+
if (card.draft.kind === "audio") {
|
|
1244
|
+
set.updateDraft(card.id, { input: { fileId: file.fileId } });
|
|
1245
|
+
await hold(() => transcribeRecording(card.id, file.fileId, file.name));
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
const notes = await fileNotes(file.fileId);
|
|
1249
|
+
set.settle(card.id, { label: file.name, ref: createSourceRef("file", file.fileId), fileId: file.fileId, notes });
|
|
1250
|
+
},
|
|
1251
|
+
async fileLanded(card, processedDocumentId) {
|
|
1252
|
+
const notes = (card.draft.notes ?? []).filter((n) => n !== KEEP_WAITING_FOR_ORGANIZATION);
|
|
1253
|
+
set.updateDraft(card.id, { processedDocumentId, notes });
|
|
1254
|
+
try {
|
|
1255
|
+
const organizationId = await deps.ensureOrganization();
|
|
1256
|
+
await doors.keep(processedDocumentId, { attachTo, organizationId });
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
if (deps.waitsForOrganization(err)) {
|
|
1259
|
+
set.updateDraft(card.id, { processedDocumentId, notes: [...notes, KEEP_WAITING_FOR_ORGANIZATION] });
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
set.updateDraft(card.id, {
|
|
1263
|
+
processedDocumentId,
|
|
1264
|
+
notes: [
|
|
1265
|
+
...notes,
|
|
1266
|
+
`It was read, but its Source could not be kept for reuse${notFiledSuffix}: ${deps.failureSentence(err)}`
|
|
1267
|
+
]
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
return intake;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// sources/runtime/recovery.ts
|
|
1276
|
+
var GUARDS = /* @__PURE__ */ Symbol.for("ai-matrx.agents.sources.recovery-guards");
|
|
1277
|
+
function sourceRecoveryGuards() {
|
|
1278
|
+
const slot = globalThis;
|
|
1279
|
+
let guards = slot[GUARDS];
|
|
1280
|
+
if (!guards) {
|
|
1281
|
+
guards = { resumedCards: /* @__PURE__ */ new Set(), keptFileSources: /* @__PURE__ */ new Set(), startedFileRuns: /* @__PURE__ */ new Set() };
|
|
1282
|
+
slot[GUARDS] = guards;
|
|
1283
|
+
}
|
|
1284
|
+
return guards;
|
|
1285
|
+
}
|
|
1286
|
+
function cardsToResume(cards) {
|
|
1287
|
+
return cards.filter((c) => c.status === "error" && c.error === RELOADED_RESUMING);
|
|
1288
|
+
}
|
|
1289
|
+
function resumeInterrupted(cards, intake) {
|
|
1290
|
+
const { resumedCards } = sourceRecoveryGuards();
|
|
1291
|
+
for (const card of cardsToResume(cards)) {
|
|
1292
|
+
if (resumedCards.has(card.id)) continue;
|
|
1293
|
+
resumedCards.add(card.id);
|
|
1294
|
+
intake.resume(card);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
function cardsWaitingForOrganization(cards) {
|
|
1298
|
+
return cards.filter(
|
|
1299
|
+
(s) => s.status === "error" && s.error === WAITING_FOR_ORGANIZATION || !!s.draft.processedDocumentId && !!s.draft.notes?.includes(KEEP_WAITING_FOR_ORGANIZATION)
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
function continueAfterOrganization(cards, intake) {
|
|
1303
|
+
for (const card of cards) {
|
|
1304
|
+
if (card.status === "error" && card.error === WAITING_FOR_ORGANIZATION) {
|
|
1305
|
+
intake.resume(card);
|
|
1306
|
+
} else if (card.draft.processedDocumentId && card.draft.notes?.includes(KEEP_WAITING_FOR_ORGANIZATION)) {
|
|
1307
|
+
void intake.fileLanded(card, card.draft.processedDocumentId);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function isAskableFileCard(card, deps) {
|
|
1312
|
+
return isWaitingFileCard(card) && !fileCardHeldForOrganization(card, deps.organizationId, deps.fileOrganizationId);
|
|
1313
|
+
}
|
|
1314
|
+
async function lookAtFileCards(cards, ctx) {
|
|
1315
|
+
const { keptFileSources, startedFileRuns } = sourceRecoveryGuards();
|
|
1316
|
+
let anyWaiting = false;
|
|
1317
|
+
for (const card of cards.filter((c) => isAskableFileCard(c, ctx))) {
|
|
1318
|
+
const fileId = card.draft.fileId;
|
|
1319
|
+
const guard = `${card.id}:${fileId}`;
|
|
1320
|
+
if (keptFileSources.has(guard)) continue;
|
|
1321
|
+
let status = null;
|
|
1322
|
+
try {
|
|
1323
|
+
status = await ctx.readFileState(fileId, ctx.signal);
|
|
1324
|
+
} catch {
|
|
1325
|
+
if (ctx.signal.aborted) return false;
|
|
1326
|
+
anyWaiting = true;
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (ctx.signal.aborted) return false;
|
|
1330
|
+
const localJob = ctx.runner.jobs.find((j) => j.cldFileId === fileId) ?? null;
|
|
1331
|
+
const step = nextFileStep(status, {
|
|
1332
|
+
started: startedFileRuns.has(guard),
|
|
1333
|
+
localRunning: localJob?.status === "running"
|
|
1334
|
+
});
|
|
1335
|
+
const localPdId = localJob?.processedDocumentId ?? null;
|
|
1336
|
+
if (step.do === "keep" || localPdId) {
|
|
1337
|
+
keptFileSources.add(guard);
|
|
1338
|
+
const pdId = step.do === "keep" ? step.processedDocumentId : localPdId;
|
|
1339
|
+
await ctx.intake.fileLanded(card, pdId);
|
|
1340
|
+
void ctx.set.manifest();
|
|
1341
|
+
} else if (step.do === "start") {
|
|
1342
|
+
startedFileRuns.add(guard);
|
|
1343
|
+
anyWaiting = true;
|
|
1344
|
+
void ctx.runner.runForCldFile(fileId, card.draft.label, "Reading it into a Source").catch(() => void 0);
|
|
1345
|
+
} else if (step.do === "unreadable") {
|
|
1346
|
+
keptFileSources.add(guard);
|
|
1347
|
+
ctx.set.fail(card.id, step.reason);
|
|
1348
|
+
} else {
|
|
1349
|
+
anyWaiting = true;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
return anyWaiting;
|
|
1353
|
+
}
|
|
1354
|
+
//# sourceMappingURL=index.cjs.map
|