@ai-matrx/agents 0.16.20 → 0.17.2
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 +44 -0
- package/dist/content-transfer/index.cjs +17 -0
- package/dist/content-transfer/index.cjs.map +1 -1
- package/dist/content-transfer/index.js +17 -0
- package/dist/content-transfer/index.js.map +1 -1
- package/dist/content-transfer/react/index.cjs +17 -0
- package/dist/content-transfer/react/index.cjs.map +1 -1
- package/dist/content-transfer/react/index.js +17 -0
- package/dist/content-transfer/react/index.js.map +1 -1
- package/dist/mandates/index.cjs +19 -2
- package/dist/mandates/index.cjs.map +1 -1
- package/dist/mandates/index.d.cts +22 -5
- package/dist/mandates/index.d.ts +22 -5
- package/dist/mandates/index.js +19 -2
- package/dist/mandates/index.js.map +1 -1
- package/dist/sources/index.cjs +124 -0
- package/dist/sources/index.cjs.map +1 -0
- package/dist/sources/index.d.cts +174 -0
- package/dist/sources/index.d.ts +174 -0
- package/dist/sources/index.js +103 -0
- package/dist/sources/index.js.map +1 -0
- package/mandates/snapshots/keys.0.17.0.json +652 -0
- package/mandates/snapshots/keys.0.17.2.json +652 -0
- package/package.json +11 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ai-matrx/agents/sources` — THE ONE SOURCE PAYLOAD (contract v1, frozen 2026-09-27).
|
|
3
|
+
*
|
|
4
|
+
* Every request that is built from a person's Sources carries them as a
|
|
5
|
+
* `SourceSet`: a list of `SourceRef` pointers (never blobs) plus how the model
|
|
6
|
+
* should be grounded in them. The server answers with a `SourceManifest`
|
|
7
|
+
* (sizes and states, never bodies — POST /sources/manifest) or a
|
|
8
|
+
* `ResolvedSourceSet` (grounded text for generators — POST /sources/resolve).
|
|
9
|
+
*
|
|
10
|
+
* Contract of record: common-docs `projects/unified-source-input/DESIGN.md`
|
|
11
|
+
* § "Contract v1 — FROZEN". A shape change here is an AMENDMENT there first,
|
|
12
|
+
* with its Pydantic twin beside aidream's `parse_resource_reference`.
|
|
13
|
+
*
|
|
14
|
+
* `SourceRef` IS the existing `resource_ref` pointer already on the wire
|
|
15
|
+
* (aidream `services/conversation_context/resource_context.py`), extended with
|
|
16
|
+
* optional fields only — never coin a second pointer type.
|
|
17
|
+
*
|
|
18
|
+
* Pure module: no React, no I/O, no "use client".
|
|
19
|
+
*/
|
|
20
|
+
declare const RESOURCE_REF_KIND: "resource_ref";
|
|
21
|
+
declare const SOURCE_SET_KIND: "source_set";
|
|
22
|
+
declare const SOURCE_MANIFEST_KIND: "source_manifest";
|
|
23
|
+
declare const RESOLVED_SOURCE_SET_KIND: "resolved_source_set";
|
|
24
|
+
/** The wire version every `SourceSet` carries. */
|
|
25
|
+
declare const SOURCE_SET_VERSION: 1;
|
|
26
|
+
/** A bounded preview of one representation promoted into the prompt. */
|
|
27
|
+
interface ResourcePromotion {
|
|
28
|
+
representation: string;
|
|
29
|
+
max_chars?: number;
|
|
30
|
+
}
|
|
31
|
+
type SourceDelivery = "direct" | "context";
|
|
32
|
+
type SourceGrounding = "whole" | "selected" | "retrieve";
|
|
33
|
+
/** The pointer — the existing `resource_ref` envelope, extended (all new fields optional). */
|
|
34
|
+
interface SourceRef {
|
|
35
|
+
__kind: typeof RESOURCE_REF_KIND;
|
|
36
|
+
/** Server resource token: "file" (A2; "cld_file" is a server alias) | "processed_document" | "note" | "fc_set" | … */
|
|
37
|
+
resource_type: string;
|
|
38
|
+
resource_id: string;
|
|
39
|
+
/** "clean" | "raw" | "pdf" | a family representation key. Omitted = server's Clean → Raw → original fallback. */
|
|
40
|
+
representation?: string;
|
|
41
|
+
/** Hand-picked Segment ids (rag.kg_chunks ids or "<resource_id>:<n>"). */
|
|
42
|
+
include_segments?: string[];
|
|
43
|
+
/** Include the text (default "direct") | the AI fetches it on demand ("context"). */
|
|
44
|
+
delivery?: SourceDelivery;
|
|
45
|
+
/** Per-Source cap chosen on the review page. */
|
|
46
|
+
max_chars?: number;
|
|
47
|
+
promote?: ResourcePromotion | ResourcePromotion[];
|
|
48
|
+
exclude?: string[];
|
|
49
|
+
}
|
|
50
|
+
interface SourceSet {
|
|
51
|
+
__kind: typeof SOURCE_SET_KIND;
|
|
52
|
+
version: typeof SOURCE_SET_VERSION;
|
|
53
|
+
sources: SourceRef[];
|
|
54
|
+
/** Only for "Just a topic". */
|
|
55
|
+
topic?: string;
|
|
56
|
+
/** Default "whole". */
|
|
57
|
+
grounding?: SourceGrounding;
|
|
58
|
+
/** Required in spirit when grounding = "retrieve". */
|
|
59
|
+
retrieve_query?: string;
|
|
60
|
+
/** The model whose context window sets the budget. */
|
|
61
|
+
target_model_id?: string;
|
|
62
|
+
}
|
|
63
|
+
type SourceState = "ready" | "processing" | "failed" | "unavailable";
|
|
64
|
+
interface SourceManifestForm {
|
|
65
|
+
form: string;
|
|
66
|
+
label: string;
|
|
67
|
+
chars: number;
|
|
68
|
+
available: boolean;
|
|
69
|
+
}
|
|
70
|
+
interface SourceManifestSegment {
|
|
71
|
+
id: string;
|
|
72
|
+
label: string;
|
|
73
|
+
page?: number;
|
|
74
|
+
chars: number;
|
|
75
|
+
}
|
|
76
|
+
interface SourceManifestEntry {
|
|
77
|
+
ref: SourceRef;
|
|
78
|
+
label: string;
|
|
79
|
+
resource_type: string;
|
|
80
|
+
state: SourceState;
|
|
81
|
+
state_detail?: string;
|
|
82
|
+
forms: SourceManifestForm[];
|
|
83
|
+
default_form: string;
|
|
84
|
+
segments?: SourceManifestSegment[];
|
|
85
|
+
}
|
|
86
|
+
/** POST /sources/manifest { source_set } → sizes and states, never bodies. */
|
|
87
|
+
interface SourceManifest {
|
|
88
|
+
__kind: typeof SOURCE_MANIFEST_KIND;
|
|
89
|
+
sources: SourceManifestEntry[];
|
|
90
|
+
total_chars: number;
|
|
91
|
+
estimated_tokens: number;
|
|
92
|
+
/** null = unknown model; the UI says so. */
|
|
93
|
+
model_context_tokens: number | null;
|
|
94
|
+
}
|
|
95
|
+
interface ResolvedSourceSegment {
|
|
96
|
+
id: string;
|
|
97
|
+
page?: number;
|
|
98
|
+
chars: number;
|
|
99
|
+
}
|
|
100
|
+
interface ResolvedSource {
|
|
101
|
+
ref: SourceRef;
|
|
102
|
+
label: string;
|
|
103
|
+
form_used: string;
|
|
104
|
+
/** Grounded: "### Chunk <id> (page N)" blocks; never re-chunked. */
|
|
105
|
+
text: string;
|
|
106
|
+
segments: ResolvedSourceSegment[];
|
|
107
|
+
file_id?: string;
|
|
108
|
+
processed_document_id?: string;
|
|
109
|
+
/** "processing" = the raw fallback was used. */
|
|
110
|
+
state: "ready" | "processing";
|
|
111
|
+
truncated: boolean;
|
|
112
|
+
/** Every stand-in announces itself. */
|
|
113
|
+
notes: string[];
|
|
114
|
+
}
|
|
115
|
+
/** A1 (2026-09-27): "not_ready" = a file not yet read. */
|
|
116
|
+
type DroppedSourceReason = "no_access" | "missing" | "failed" | "over_budget" | "not_ready";
|
|
117
|
+
interface DroppedSource {
|
|
118
|
+
ref: SourceRef;
|
|
119
|
+
reason: DroppedSourceReason;
|
|
120
|
+
/** A1: what happened and what to do. */
|
|
121
|
+
detail?: string;
|
|
122
|
+
}
|
|
123
|
+
/** POST /sources/resolve { source_set } → grounded text for generators. */
|
|
124
|
+
interface ResolvedSourceSet {
|
|
125
|
+
__kind: typeof RESOLVED_SOURCE_SET_KIND;
|
|
126
|
+
sources: ResolvedSource[];
|
|
127
|
+
dropped: DroppedSource[];
|
|
128
|
+
total_chars: number;
|
|
129
|
+
}
|
|
130
|
+
/** Builder options. `undefined` is accepted everywhere so callers can forward an existing ref's optional fields. */
|
|
131
|
+
interface SourceRefOptions {
|
|
132
|
+
representation?: string | undefined;
|
|
133
|
+
include_segments?: readonly string[] | undefined;
|
|
134
|
+
delivery?: SourceDelivery | undefined;
|
|
135
|
+
max_chars?: number | undefined;
|
|
136
|
+
promote?: ResourcePromotion | ResourcePromotion[] | undefined;
|
|
137
|
+
exclude?: readonly string[] | undefined;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build one pointer. Empty optional fields are omitted (so the wire stays
|
|
141
|
+
* minimal and equal pointers serialise equally); `exclude` is trimmed,
|
|
142
|
+
* lower-cased and de-duplicated exactly as the server normalises it;
|
|
143
|
+
* `include_segments` is de-duplicated in order. Throws on an empty type or id —
|
|
144
|
+
* a pointer to nothing is never built silently.
|
|
145
|
+
*/
|
|
146
|
+
declare function createSourceRef(resourceType: string, resourceId: string, options?: SourceRefOptions): SourceRef;
|
|
147
|
+
declare function isSourceRef(value: unknown): value is SourceRef;
|
|
148
|
+
interface SourceSetOptions {
|
|
149
|
+
topic?: string | undefined;
|
|
150
|
+
grounding?: SourceGrounding | undefined;
|
|
151
|
+
retrieve_query?: string | undefined;
|
|
152
|
+
target_model_id?: string | undefined;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build one envelope. Duplicate pointers (same type + id + representation) are
|
|
156
|
+
* collapsed, keeping the first. `grounding: "retrieve"` without a query and
|
|
157
|
+
* `grounding: "selected"` with no Source carrying `include_segments` throw —
|
|
158
|
+
* both would otherwise resolve to something the person never asked for.
|
|
159
|
+
*/
|
|
160
|
+
declare function createSourceSet(sources: readonly SourceRef[], options?: SourceSetOptions): SourceSet;
|
|
161
|
+
/** Structural guard for a wire value claiming to be a v1 `SourceSet`. */
|
|
162
|
+
declare function isSourceSet(value: unknown): value is SourceSet;
|
|
163
|
+
/**
|
|
164
|
+
* Characters that will actually go in, computed from the Sources themselves
|
|
165
|
+
* (never trusting a stale `total_chars`):
|
|
166
|
+
* - `ResolvedSourceSet`: the sum of every resolved text's length.
|
|
167
|
+
* - `SourceManifest`: per Source, the chosen form's size (the ref's
|
|
168
|
+
* `representation`, else `default_form`), or the picked Segments' sizes when
|
|
169
|
+
* `include_segments` is set, capped by the ref's `max_chars`. Sources whose
|
|
170
|
+
* state is "failed" or "unavailable" contribute nothing — they cannot go in.
|
|
171
|
+
*/
|
|
172
|
+
declare function totalChars(value: SourceManifest | ResolvedSourceSet): number;
|
|
173
|
+
|
|
174
|
+
export { type DroppedSource, type DroppedSourceReason, RESOLVED_SOURCE_SET_KIND, RESOURCE_REF_KIND, type ResolvedSource, type ResolvedSourceSegment, type ResolvedSourceSet, type ResourcePromotion, SOURCE_MANIFEST_KIND, SOURCE_SET_KIND, SOURCE_SET_VERSION, type SourceDelivery, type SourceGrounding, type SourceManifest, type SourceManifestEntry, type SourceManifestForm, type SourceManifestSegment, type SourceRef, type SourceRefOptions, type SourceSet, type SourceSetOptions, type SourceState, createSourceRef, createSourceSet, isSourceRef, isSourceSet, totalChars };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// sources/index.ts
|
|
2
|
+
var RESOURCE_REF_KIND = "resource_ref";
|
|
3
|
+
var SOURCE_SET_KIND = "source_set";
|
|
4
|
+
var SOURCE_MANIFEST_KIND = "source_manifest";
|
|
5
|
+
var RESOLVED_SOURCE_SET_KIND = "resolved_source_set";
|
|
6
|
+
var SOURCE_SET_VERSION = 1;
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function createSourceRef(resourceType, resourceId, options = {}) {
|
|
11
|
+
const resource_type = resourceType.trim();
|
|
12
|
+
const resource_id = resourceId.trim();
|
|
13
|
+
if (!resource_type) throw new Error("createSourceRef: resource_type is required");
|
|
14
|
+
if (!resource_id) throw new Error("createSourceRef: resource_id is required");
|
|
15
|
+
if (options.max_chars !== void 0 && (!Number.isInteger(options.max_chars) || options.max_chars <= 0)) {
|
|
16
|
+
throw new Error("createSourceRef: max_chars must be a positive integer");
|
|
17
|
+
}
|
|
18
|
+
const exclude = Array.from(
|
|
19
|
+
new Set(
|
|
20
|
+
(options.exclude ?? []).map((value) => value.trim().toLowerCase()).filter(Boolean)
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
const include_segments = Array.from(
|
|
24
|
+
new Set((options.include_segments ?? []).map((id) => id.trim()).filter(Boolean))
|
|
25
|
+
);
|
|
26
|
+
return {
|
|
27
|
+
__kind: RESOURCE_REF_KIND,
|
|
28
|
+
resource_type,
|
|
29
|
+
resource_id,
|
|
30
|
+
...options.representation ? { representation: options.representation } : {},
|
|
31
|
+
...include_segments.length ? { include_segments } : {},
|
|
32
|
+
...options.delivery ? { delivery: options.delivery } : {},
|
|
33
|
+
...options.max_chars !== void 0 ? { max_chars: options.max_chars } : {},
|
|
34
|
+
...options.promote ? { promote: options.promote } : {},
|
|
35
|
+
...exclude.length ? { exclude } : {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function isSourceRef(value) {
|
|
39
|
+
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;
|
|
40
|
+
}
|
|
41
|
+
function createSourceSet(sources, options = {}) {
|
|
42
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43
|
+
const unique = [];
|
|
44
|
+
for (const ref of sources) {
|
|
45
|
+
if (!isSourceRef(ref)) throw new Error("createSourceSet: every source must be a resource_ref");
|
|
46
|
+
const key = `${ref.resource_type}\0${ref.resource_id}\0${ref.representation ?? ""}`;
|
|
47
|
+
if (seen.has(key)) continue;
|
|
48
|
+
seen.add(key);
|
|
49
|
+
unique.push(ref);
|
|
50
|
+
}
|
|
51
|
+
const topic = options.topic?.trim();
|
|
52
|
+
const retrieve_query = options.retrieve_query?.trim();
|
|
53
|
+
if (options.grounding === "retrieve" && !retrieve_query) {
|
|
54
|
+
throw new Error('createSourceSet: grounding "retrieve" needs a retrieve_query');
|
|
55
|
+
}
|
|
56
|
+
if (options.grounding === "selected" && !unique.some((ref) => (ref.include_segments?.length ?? 0) > 0)) {
|
|
57
|
+
throw new Error('createSourceSet: grounding "selected" needs at least one Source with include_segments');
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
__kind: SOURCE_SET_KIND,
|
|
61
|
+
version: SOURCE_SET_VERSION,
|
|
62
|
+
sources: unique,
|
|
63
|
+
...topic ? { topic } : {},
|
|
64
|
+
...options.grounding ? { grounding: options.grounding } : {},
|
|
65
|
+
...retrieve_query ? { retrieve_query } : {},
|
|
66
|
+
...options.target_model_id ? { target_model_id: options.target_model_id } : {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function isSourceSet(value) {
|
|
70
|
+
return isRecord(value) && value.__kind === SOURCE_SET_KIND && value.version === SOURCE_SET_VERSION && Array.isArray(value.sources) && value.sources.every(isSourceRef);
|
|
71
|
+
}
|
|
72
|
+
function manifestEntryChars(entry) {
|
|
73
|
+
const selected = entry.ref.include_segments;
|
|
74
|
+
let chars;
|
|
75
|
+
if (selected?.length && entry.segments?.length) {
|
|
76
|
+
const wanted = new Set(selected);
|
|
77
|
+
chars = entry.segments.filter((segment) => wanted.has(segment.id)).reduce((sum, segment) => sum + segment.chars, 0);
|
|
78
|
+
} else {
|
|
79
|
+
const formKey = entry.ref.representation ?? entry.default_form;
|
|
80
|
+
const form = entry.forms.find((candidate) => candidate.form === formKey) ?? entry.forms.find((candidate) => candidate.form === entry.default_form);
|
|
81
|
+
chars = form?.chars ?? 0;
|
|
82
|
+
}
|
|
83
|
+
return entry.ref.max_chars !== void 0 ? Math.min(chars, entry.ref.max_chars) : chars;
|
|
84
|
+
}
|
|
85
|
+
function totalChars(value) {
|
|
86
|
+
if (value.__kind === RESOLVED_SOURCE_SET_KIND) {
|
|
87
|
+
return value.sources.reduce((sum, source) => sum + source.text.length, 0);
|
|
88
|
+
}
|
|
89
|
+
return value.sources.filter((entry) => entry.state === "ready" || entry.state === "processing").reduce((sum, entry) => sum + manifestEntryChars(entry), 0);
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
RESOLVED_SOURCE_SET_KIND,
|
|
93
|
+
RESOURCE_REF_KIND,
|
|
94
|
+
SOURCE_MANIFEST_KIND,
|
|
95
|
+
SOURCE_SET_KIND,
|
|
96
|
+
SOURCE_SET_VERSION,
|
|
97
|
+
createSourceRef,
|
|
98
|
+
createSourceSet,
|
|
99
|
+
isSourceRef,
|
|
100
|
+
isSourceSet,
|
|
101
|
+
totalChars
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../sources/index.ts"],"sourcesContent":["/**\n * `@ai-matrx/agents/sources` — THE ONE SOURCE PAYLOAD (contract v1, frozen 2026-09-27).\n *\n * Every request that is built from a person's Sources carries them as a\n * `SourceSet`: a list of `SourceRef` pointers (never blobs) plus how the model\n * should be grounded in them. The server answers with a `SourceManifest`\n * (sizes and states, never bodies — POST /sources/manifest) or a\n * `ResolvedSourceSet` (grounded text for generators — POST /sources/resolve).\n *\n * Contract of record: common-docs `projects/unified-source-input/DESIGN.md`\n * § \"Contract v1 — FROZEN\". A shape change here is an AMENDMENT there first,\n * with its Pydantic twin beside aidream's `parse_resource_reference`.\n *\n * `SourceRef` IS the existing `resource_ref` pointer already on the wire\n * (aidream `services/conversation_context/resource_context.py`), extended with\n * optional fields only — never coin a second pointer type.\n *\n * Pure module: no React, no I/O, no \"use client\".\n */\n\nexport const RESOURCE_REF_KIND = \"resource_ref\" as const;\nexport const SOURCE_SET_KIND = \"source_set\" as const;\nexport const SOURCE_MANIFEST_KIND = \"source_manifest\" as const;\nexport const RESOLVED_SOURCE_SET_KIND = \"resolved_source_set\" as const;\n\n/** The wire version every `SourceSet` carries. */\nexport const SOURCE_SET_VERSION = 1 as const;\n\n/** A bounded preview of one representation promoted into the prompt. */\nexport interface ResourcePromotion {\n representation: string;\n max_chars?: number;\n}\n\nexport type SourceDelivery = \"direct\" | \"context\";\nexport type SourceGrounding = \"whole\" | \"selected\" | \"retrieve\";\n\n/** The pointer — the existing `resource_ref` envelope, extended (all new fields optional). */\nexport interface SourceRef {\n __kind: typeof RESOURCE_REF_KIND;\n /** Server resource token: \"file\" (A2; \"cld_file\" is a server alias) | \"processed_document\" | \"note\" | \"fc_set\" | … */\n resource_type: string;\n resource_id: string;\n /** \"clean\" | \"raw\" | \"pdf\" | a family representation key. Omitted = server's Clean → Raw → original fallback. */\n representation?: string;\n /** Hand-picked Segment ids (rag.kg_chunks ids or \"<resource_id>:<n>\"). */\n include_segments?: string[];\n /** Include the text (default \"direct\") | the AI fetches it on demand (\"context\"). */\n delivery?: SourceDelivery;\n /** Per-Source cap chosen on the review page. */\n max_chars?: number;\n promote?: ResourcePromotion | ResourcePromotion[];\n exclude?: string[];\n}\n\nexport interface SourceSet {\n __kind: typeof SOURCE_SET_KIND;\n version: typeof SOURCE_SET_VERSION;\n sources: SourceRef[];\n /** Only for \"Just a topic\". */\n topic?: string;\n /** Default \"whole\". */\n grounding?: SourceGrounding;\n /** Required in spirit when grounding = \"retrieve\". */\n retrieve_query?: string;\n /** The model whose context window sets the budget. */\n target_model_id?: string;\n}\n\nexport type SourceState = \"ready\" | \"processing\" | \"failed\" | \"unavailable\";\n\nexport interface SourceManifestForm {\n form: string;\n label: string;\n chars: number;\n available: boolean;\n}\n\nexport interface SourceManifestSegment {\n id: string;\n label: string;\n page?: number;\n chars: number;\n}\n\nexport interface SourceManifestEntry {\n ref: SourceRef;\n label: string;\n resource_type: string;\n state: SourceState;\n state_detail?: string;\n forms: SourceManifestForm[];\n default_form: string;\n segments?: SourceManifestSegment[];\n}\n\n/** POST /sources/manifest { source_set } → sizes and states, never bodies. */\nexport interface SourceManifest {\n __kind: typeof SOURCE_MANIFEST_KIND;\n sources: SourceManifestEntry[];\n total_chars: number;\n estimated_tokens: number;\n /** null = unknown model; the UI says so. */\n model_context_tokens: number | null;\n}\n\nexport interface ResolvedSourceSegment {\n id: string;\n page?: number;\n chars: number;\n}\n\nexport interface ResolvedSource {\n ref: SourceRef;\n label: string;\n form_used: string;\n /** Grounded: \"### Chunk <id> (page N)\" blocks; never re-chunked. */\n text: string;\n segments: ResolvedSourceSegment[];\n file_id?: string;\n processed_document_id?: string;\n /** \"processing\" = the raw fallback was used. */\n state: \"ready\" | \"processing\";\n truncated: boolean;\n /** Every stand-in announces itself. */\n notes: string[];\n}\n\n/** A1 (2026-09-27): \"not_ready\" = a file not yet read. */\nexport type DroppedSourceReason =\n | \"no_access\"\n | \"missing\"\n | \"failed\"\n | \"over_budget\"\n | \"not_ready\";\n\nexport interface DroppedSource {\n ref: SourceRef;\n reason: DroppedSourceReason;\n /** A1: what happened and what to do. */\n detail?: string;\n}\n\n/** POST /sources/resolve { source_set } → grounded text for generators. */\nexport interface ResolvedSourceSet {\n __kind: typeof RESOLVED_SOURCE_SET_KIND;\n sources: ResolvedSource[];\n dropped: DroppedSource[];\n total_chars: number;\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers\n// ---------------------------------------------------------------------------\n\n/** Builder options. `undefined` is accepted everywhere so callers can forward an existing ref's optional fields. */\nexport interface SourceRefOptions {\n representation?: string | undefined;\n include_segments?: readonly string[] | undefined;\n delivery?: SourceDelivery | undefined;\n max_chars?: number | undefined;\n promote?: ResourcePromotion | ResourcePromotion[] | undefined;\n exclude?: readonly string[] | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Build one pointer. Empty optional fields are omitted (so the wire stays\n * minimal and equal pointers serialise equally); `exclude` is trimmed,\n * lower-cased and de-duplicated exactly as the server normalises it;\n * `include_segments` is de-duplicated in order. Throws on an empty type or id —\n * a pointer to nothing is never built silently.\n */\nexport function createSourceRef(\n resourceType: string,\n resourceId: string,\n options: SourceRefOptions = {},\n): SourceRef {\n const resource_type = resourceType.trim();\n const resource_id = resourceId.trim();\n if (!resource_type) throw new Error(\"createSourceRef: resource_type is required\");\n if (!resource_id) throw new Error(\"createSourceRef: resource_id is required\");\n if (\n options.max_chars !== undefined &&\n (!Number.isInteger(options.max_chars) || options.max_chars <= 0)\n ) {\n throw new Error(\"createSourceRef: max_chars must be a positive integer\");\n }\n const exclude = Array.from(\n new Set(\n (options.exclude ?? [])\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean),\n ),\n );\n const include_segments = Array.from(\n new Set((options.include_segments ?? []).map((id) => id.trim()).filter(Boolean)),\n );\n return {\n __kind: RESOURCE_REF_KIND,\n resource_type,\n resource_id,\n ...(options.representation ? { representation: options.representation } : {}),\n ...(include_segments.length ? { include_segments } : {}),\n ...(options.delivery ? { delivery: options.delivery } : {}),\n ...(options.max_chars !== undefined ? { max_chars: options.max_chars } : {}),\n ...(options.promote ? { promote: options.promote } : {}),\n ...(exclude.length ? { exclude } : {}),\n };\n}\n\nexport function isSourceRef(value: unknown): value is SourceRef {\n return (\n isRecord(value) &&\n value.__kind === RESOURCE_REF_KIND &&\n typeof value.resource_type === \"string\" &&\n value.resource_type.length > 0 &&\n typeof value.resource_id === \"string\" &&\n value.resource_id.length > 0\n );\n}\n\nexport interface SourceSetOptions {\n topic?: string | undefined;\n grounding?: SourceGrounding | undefined;\n retrieve_query?: string | undefined;\n target_model_id?: string | undefined;\n}\n\n/**\n * Build one envelope. Duplicate pointers (same type + id + representation) are\n * collapsed, keeping the first. `grounding: \"retrieve\"` without a query and\n * `grounding: \"selected\"` with no Source carrying `include_segments` throw —\n * both would otherwise resolve to something the person never asked for.\n */\nexport function createSourceSet(\n sources: readonly SourceRef[],\n options: SourceSetOptions = {},\n): SourceSet {\n const seen = new Set<string>();\n const unique: SourceRef[] = [];\n for (const ref of sources) {\n if (!isSourceRef(ref)) throw new Error(\"createSourceSet: every source must be a resource_ref\");\n const key = `${ref.resource_type}\\u0000${ref.resource_id}\\u0000${ref.representation ?? \"\"}`;\n if (seen.has(key)) continue;\n seen.add(key);\n unique.push(ref);\n }\n const topic = options.topic?.trim();\n const retrieve_query = options.retrieve_query?.trim();\n if (options.grounding === \"retrieve\" && !retrieve_query) {\n throw new Error('createSourceSet: grounding \"retrieve\" needs a retrieve_query');\n }\n if (\n options.grounding === \"selected\" &&\n !unique.some((ref) => (ref.include_segments?.length ?? 0) > 0)\n ) {\n throw new Error('createSourceSet: grounding \"selected\" needs at least one Source with include_segments');\n }\n return {\n __kind: SOURCE_SET_KIND,\n version: SOURCE_SET_VERSION,\n sources: unique,\n ...(topic ? { topic } : {}),\n ...(options.grounding ? { grounding: options.grounding } : {}),\n ...(retrieve_query ? { retrieve_query } : {}),\n ...(options.target_model_id ? { target_model_id: options.target_model_id } : {}),\n };\n}\n\n/** Structural guard for a wire value claiming to be a v1 `SourceSet`. */\nexport function isSourceSet(value: unknown): value is SourceSet {\n return (\n isRecord(value) &&\n value.__kind === SOURCE_SET_KIND &&\n value.version === SOURCE_SET_VERSION &&\n Array.isArray(value.sources) &&\n value.sources.every(isSourceRef)\n );\n}\n\nfunction manifestEntryChars(entry: SourceManifestEntry): number {\n const selected = entry.ref.include_segments;\n let chars: number;\n if (selected?.length && entry.segments?.length) {\n const wanted = new Set(selected);\n chars = entry.segments\n .filter((segment) => wanted.has(segment.id))\n .reduce((sum, segment) => sum + segment.chars, 0);\n } else {\n const formKey = entry.ref.representation ?? entry.default_form;\n const form =\n entry.forms.find((candidate) => candidate.form === formKey) ??\n entry.forms.find((candidate) => candidate.form === entry.default_form);\n chars = form?.chars ?? 0;\n }\n return entry.ref.max_chars !== undefined ? Math.min(chars, entry.ref.max_chars) : chars;\n}\n\n/**\n * Characters that will actually go in, computed from the Sources themselves\n * (never trusting a stale `total_chars`):\n * - `ResolvedSourceSet`: the sum of every resolved text's length.\n * - `SourceManifest`: per Source, the chosen form's size (the ref's\n * `representation`, else `default_form`), or the picked Segments' sizes when\n * `include_segments` is set, capped by the ref's `max_chars`. Sources whose\n * state is \"failed\" or \"unavailable\" contribute nothing — they cannot go in.\n */\nexport function totalChars(value: SourceManifest | ResolvedSourceSet): number {\n if (value.__kind === RESOLVED_SOURCE_SET_KIND) {\n return value.sources.reduce((sum, source) => sum + source.text.length, 0);\n }\n return value.sources\n .filter((entry) => entry.state === \"ready\" || entry.state === \"processing\")\n .reduce((sum, entry) => sum + manifestEntryChars(entry), 0);\n}\n"],"mappings":"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","names":[]}
|