@librechat/agents 3.6.8 → 3.6.10
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/dist/cjs/agents/AgentContext.cjs +46 -39
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/agents/projection.cjs +2 -2
- package/dist/cjs/agents/projection.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +28 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +87 -16
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +10 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +14 -0
- package/dist/cjs/messages/alternation.cjs +125 -20
- package/dist/cjs/messages/alternation.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +195 -18
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +488 -118
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/handoffCue.cjs +3 -2
- package/dist/cjs/messages/handoffCue.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -0
- package/dist/cjs/messages/injected.cjs +5 -2
- package/dist/cjs/messages/injected.cjs.map +1 -1
- package/dist/cjs/messages/provenance.cjs +411 -0
- package/dist/cjs/messages/provenance.cjs.map +1 -0
- package/dist/cjs/messages/prune.cjs +18 -13
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/toolResultTypes.cjs +663 -0
- package/dist/cjs/messages/toolResultTypes.cjs.map +1 -0
- package/dist/cjs/run.cjs +7 -4
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +21 -4
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CallerCapabilities.cjs +48 -0
- package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs +34 -0
- package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +21 -4
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +25 -24
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +12 -4
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +13 -4
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +47 -11
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
- package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +19 -9
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +2 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/toolContent.cjs +2 -2
- package/dist/cjs/utils/toolContent.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +46 -39
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/agents/projection.mjs +2 -2
- package/dist/esm/agents/projection.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +30 -20
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +87 -16
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +10 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -3
- package/dist/esm/messages/alternation.mjs +125 -20
- package/dist/esm/messages/alternation.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +196 -19
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +488 -119
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/handoffCue.mjs +3 -2
- package/dist/esm/messages/handoffCue.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -0
- package/dist/esm/messages/injected.mjs +5 -2
- package/dist/esm/messages/injected.mjs.map +1 -1
- package/dist/esm/messages/provenance.mjs +401 -0
- package/dist/esm/messages/provenance.mjs.map +1 -0
- package/dist/esm/messages/prune.mjs +18 -13
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/toolResultTypes.mjs +657 -0
- package/dist/esm/messages/toolResultTypes.mjs.map +1 -0
- package/dist/esm/run.mjs +7 -4
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +21 -4
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CallerCapabilities.mjs +45 -0
- package/dist/esm/tools/CallerCapabilities.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticCallerPolicy.mjs +32 -0
- package/dist/esm/tools/ProgrammaticCallerPolicy.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +21 -4
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +25 -24
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +14 -6
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +15 -6
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +47 -12
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
- package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +19 -9
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +2 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/toolContent.mjs +2 -2
- package/dist/esm/utils/toolContent.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +8 -13
- package/dist/types/agents/projection.d.ts +3 -1
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/llm/openai/index.d.ts +20 -0
- package/dist/types/messages/format.d.ts +5 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/provenance.d.ts +85 -0
- package/dist/types/messages/toolResultTypes.d.ts +33 -0
- package/dist/types/tools/CallerCapabilities.d.ts +12 -0
- package/dist/types/tools/ProgrammaticCallerPolicy.d.ts +20 -0
- package/dist/types/tools/ToolNode.d.ts +4 -7
- package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +4 -0
- package/dist/types/tools/ptcTimeout.d.ts +8 -0
- package/dist/types/tools/search/types.d.ts +15 -0
- package/dist/types/types/graph.d.ts +3 -1
- package/dist/types/types/tools.d.ts +13 -2
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +109 -67
- package/src/agents/projection.ts +9 -1
- package/src/graphs/Graph.ts +56 -23
- package/src/graphs/MultiAgentGraph.ts +175 -33
- package/src/llm/openai/index.ts +43 -0
- package/src/messages/alternation.ts +217 -34
- package/src/messages/core.ts +358 -38
- package/src/messages/format.ts +958 -137
- package/src/messages/handoffCue.ts +7 -4
- package/src/messages/index.ts +1 -0
- package/src/messages/injected.ts +9 -6
- package/src/messages/provenance.ts +897 -0
- package/src/messages/prune.ts +28 -10
- package/src/messages/toolResultTypes.ts +1174 -0
- package/src/run.ts +9 -4
- package/src/tools/BashProgrammaticToolCalling.ts +46 -12
- package/src/tools/CallerCapabilities.ts +78 -0
- package/src/tools/ProgrammaticCallerPolicy.ts +97 -0
- package/src/tools/ProgrammaticToolCalling.ts +41 -7
- package/src/tools/ToolNode.ts +70 -52
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +24 -26
- package/src/tools/local/LocalProgrammaticToolCalling.ts +28 -23
- package/src/tools/local/resolveLocalExecutionTools.ts +132 -13
- package/src/tools/ptcTimeout.ts +6 -0
- package/src/tools/search/search.ts +25 -8
- package/src/tools/search/tool.ts +2 -0
- package/src/tools/search/types.ts +16 -0
- package/src/tools/subagent/SubagentExecutor.ts +8 -4
- package/src/types/graph.ts +3 -0
- package/src/types/tools.ts +14 -3
- package/src/utils/toolContent.ts +2 -2
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
import { isProxy } from 'node:util/types';
|
|
2
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
3
|
+
|
|
4
|
+
export const PROVIDER_MESSAGE_PROVENANCE_VERSION = 1 as const;
|
|
5
|
+
|
|
6
|
+
/** Recommended consumer trust bounds. Producers preserve complete lineage
|
|
7
|
+
* above these limits; security-sensitive consumers must reject oversized
|
|
8
|
+
* envelopes and take their fail-closed path rather than truncate attribution. */
|
|
9
|
+
export const PROVIDER_MESSAGE_PROVENANCE_LIMITS = Object.freeze({
|
|
10
|
+
maxParts: 256,
|
|
11
|
+
maxIndicesPerPart: 256,
|
|
12
|
+
maxTotalIndexRefs: 4_096,
|
|
13
|
+
maxSourceMessageIds: 256,
|
|
14
|
+
maxSourceMessageIdLength: 512,
|
|
15
|
+
maxSourceContentPartIndex: 4_095,
|
|
16
|
+
} as const);
|
|
17
|
+
|
|
18
|
+
/** Authorship of one logical contribution to a provider-bound message. */
|
|
19
|
+
export type ProviderMessageAttribution =
|
|
20
|
+
| 'user'
|
|
21
|
+
| 'model'
|
|
22
|
+
| 'tool'
|
|
23
|
+
| 'synthetic';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Lineage for one logical contribution to a provider-bound message.
|
|
27
|
+
* `sourceContentPartIndices` index the persisted source message's `content`
|
|
28
|
+
* array before formatting, filtering, or summary-boundary slicing.
|
|
29
|
+
*/
|
|
30
|
+
export interface ProviderMessageProvenancePart {
|
|
31
|
+
readonly attribution: ProviderMessageAttribution;
|
|
32
|
+
readonly sourceMessageId?: string;
|
|
33
|
+
readonly sourceContentPartIndices?: readonly number[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Stable, versioned provenance carried in `BaseMessage.additional_kwargs`. */
|
|
37
|
+
export interface ProviderMessageProvenance {
|
|
38
|
+
readonly version: typeof PROVIDER_MESSAGE_PROVENANCE_VERSION;
|
|
39
|
+
readonly parts: readonly ProviderMessageProvenancePart[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Inert marker used only when a derived message must retain invalidity. */
|
|
43
|
+
export interface InvalidProviderMessageProvenance {
|
|
44
|
+
readonly version: typeof PROVIDER_MESSAGE_PROVENANCE_VERSION;
|
|
45
|
+
readonly parts: null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Distinguishes absent metadata from an explicitly malformed envelope. */
|
|
49
|
+
export type ProviderMessageProvenanceState =
|
|
50
|
+
| { readonly status: 'absent' }
|
|
51
|
+
| { readonly status: 'invalid' }
|
|
52
|
+
| {
|
|
53
|
+
readonly status: 'valid';
|
|
54
|
+
readonly provenance: ProviderMessageProvenance;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Distinguishes absent lineage from validated ids and malformed metadata. */
|
|
58
|
+
export type ProviderSourceMessageIdsState =
|
|
59
|
+
| { readonly status: 'absent' }
|
|
60
|
+
| { readonly status: 'invalid' }
|
|
61
|
+
| {
|
|
62
|
+
readonly status: 'valid';
|
|
63
|
+
readonly sourceMessageIds: readonly string[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Typed subset of `additional_kwargs` exposed at provider callbacks. */
|
|
67
|
+
export interface ProviderMessageProvenanceAdditionalKwargs {
|
|
68
|
+
readonly provenance?:
|
|
69
|
+
| ProviderMessageProvenance
|
|
70
|
+
| InvalidProviderMessageProvenance;
|
|
71
|
+
readonly sourceMessageId?: string;
|
|
72
|
+
readonly sourceMessageIds?: readonly string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface UntrustedProviderMessageAdditionalKwargs
|
|
76
|
+
extends Record<string, unknown> {
|
|
77
|
+
provenance?: unknown;
|
|
78
|
+
sourceMessageId?: unknown;
|
|
79
|
+
sourceMessageIds?: unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const PROVIDER_MESSAGE_ATTRIBUTIONS: ReadonlySet<ProviderMessageAttribution> =
|
|
83
|
+
new Set(['user', 'model', 'tool', 'synthetic']);
|
|
84
|
+
/** Only envelopes built from copied/frozen inputs by the setter enter this
|
|
85
|
+
* identity set. It avoids repeated O(n) canonicalization without trusting a
|
|
86
|
+
* message or caller-owned envelope identity. */
|
|
87
|
+
const immutableProviderMessageProvenance = new WeakSet<object>();
|
|
88
|
+
/** Plural source-id arrays minted by the setter are copied and frozen before
|
|
89
|
+
* entering this set. Their complete lineage may intentionally exceed the
|
|
90
|
+
* public trust bounds without forcing repeated validation on reads. */
|
|
91
|
+
const immutableProviderSourceMessageIds = new WeakSet<object>();
|
|
92
|
+
/** Binds the two immutable objects produced by one setter call. Independently
|
|
93
|
+
* valid envelopes from different messages must not be combined into a new
|
|
94
|
+
* oversized lineage that no setter ever published. */
|
|
95
|
+
const immutableProviderMessageSourceIds = new WeakMap<
|
|
96
|
+
object,
|
|
97
|
+
readonly string[]
|
|
98
|
+
>();
|
|
99
|
+
const absentProviderMessageProvenanceState = Object.freeze({
|
|
100
|
+
status: 'absent' as const,
|
|
101
|
+
});
|
|
102
|
+
const invalidProviderMessageProvenanceState = Object.freeze({
|
|
103
|
+
status: 'invalid' as const,
|
|
104
|
+
});
|
|
105
|
+
const absentProviderSourceMessageIdsState = Object.freeze({
|
|
106
|
+
status: 'absent' as const,
|
|
107
|
+
});
|
|
108
|
+
const invalidProviderSourceMessageIdsState = Object.freeze({
|
|
109
|
+
status: 'invalid' as const,
|
|
110
|
+
});
|
|
111
|
+
/** Fresh projections use this inert envelope to preserve explicit invalidity
|
|
112
|
+
* without retaining any hostile caller-owned object or array. */
|
|
113
|
+
const invalidProviderMessageProvenanceSentinel: InvalidProviderMessageProvenance =
|
|
114
|
+
Object.freeze({
|
|
115
|
+
version: PROVIDER_MESSAGE_PROVENANCE_VERSION,
|
|
116
|
+
parts: null,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function normalizeSourceMessageId(
|
|
120
|
+
candidate: unknown,
|
|
121
|
+
enforceTrustBounds = false
|
|
122
|
+
): string | undefined {
|
|
123
|
+
if (typeof candidate !== 'string') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
enforceTrustBounds &&
|
|
128
|
+
candidate.length >
|
|
129
|
+
PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxSourceMessageIdLength
|
|
130
|
+
) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const normalized = candidate.trim();
|
|
134
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function appendSourceMessageId(
|
|
138
|
+
result: string[],
|
|
139
|
+
seen: Set<string>,
|
|
140
|
+
candidate: unknown,
|
|
141
|
+
enforceTrustBounds = false
|
|
142
|
+
): boolean {
|
|
143
|
+
const sourceMessageId = normalizeSourceMessageId(
|
|
144
|
+
candidate,
|
|
145
|
+
enforceTrustBounds
|
|
146
|
+
);
|
|
147
|
+
if (sourceMessageId == null) {
|
|
148
|
+
return candidate === undefined;
|
|
149
|
+
}
|
|
150
|
+
if (seen.has(sourceMessageId)) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
seen.add(sourceMessageId);
|
|
154
|
+
result.push(sourceMessageId);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeSourceContentPartIndices(
|
|
159
|
+
indices: readonly number[] | undefined,
|
|
160
|
+
trustState?: { totalIndexRefs: number }
|
|
161
|
+
): number[] | undefined {
|
|
162
|
+
if (indices == null) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
if (isProxy(indices) || !Array.isArray(indices)) {
|
|
166
|
+
throw new TypeError(
|
|
167
|
+
'Provider source content part indices must be a non-empty array'
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const length = indices.length;
|
|
171
|
+
if (!Number.isSafeInteger(length) || length === 0) {
|
|
172
|
+
throw new TypeError(
|
|
173
|
+
'Provider source content part indices must be a non-empty array'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
if (
|
|
177
|
+
trustState != null &&
|
|
178
|
+
(length > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxIndicesPerPart ||
|
|
179
|
+
trustState.totalIndexRefs + length >
|
|
180
|
+
PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxTotalIndexRefs)
|
|
181
|
+
) {
|
|
182
|
+
throw new TypeError('Provider source content part indices exceed limits');
|
|
183
|
+
}
|
|
184
|
+
if (trustState != null) {
|
|
185
|
+
trustState.totalIndexRefs += length;
|
|
186
|
+
}
|
|
187
|
+
const normalized: number[] = [];
|
|
188
|
+
const seen = new Set<number>();
|
|
189
|
+
for (let position = 0; position < length; position++) {
|
|
190
|
+
const index = indices[position];
|
|
191
|
+
if (
|
|
192
|
+
!Number.isSafeInteger(index) ||
|
|
193
|
+
index < 0 ||
|
|
194
|
+
(trustState != null &&
|
|
195
|
+
index > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxSourceContentPartIndex)
|
|
196
|
+
) {
|
|
197
|
+
throw new TypeError('Invalid provider source content part index');
|
|
198
|
+
}
|
|
199
|
+
if (seen.has(index)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
seen.add(index);
|
|
203
|
+
normalized.push(index);
|
|
204
|
+
}
|
|
205
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function normalizeProvenancePart(
|
|
209
|
+
part: ProviderMessageProvenancePart | null | undefined,
|
|
210
|
+
requireCanonicalSourceMessageId = false,
|
|
211
|
+
trustState?: { totalIndexRefs: number; sourceMessageIdRefs: number }
|
|
212
|
+
): ProviderMessageProvenancePart {
|
|
213
|
+
if (part == null || typeof part !== 'object') {
|
|
214
|
+
throw new TypeError('Invalid provider message provenance attribution');
|
|
215
|
+
}
|
|
216
|
+
/** Capture each potentially accessor-backed public input exactly once before
|
|
217
|
+
* validation so a changing getter cannot pass one value and publish another. */
|
|
218
|
+
const attribution = part.attribution;
|
|
219
|
+
const sourceMessageIdInput = part.sourceMessageId;
|
|
220
|
+
const sourceContentPartIndicesInput = part.sourceContentPartIndices;
|
|
221
|
+
if (!PROVIDER_MESSAGE_ATTRIBUTIONS.has(attribution)) {
|
|
222
|
+
throw new TypeError('Invalid provider message provenance attribution');
|
|
223
|
+
}
|
|
224
|
+
const sourceMessageId = normalizeSourceMessageId(
|
|
225
|
+
sourceMessageIdInput,
|
|
226
|
+
trustState != null
|
|
227
|
+
);
|
|
228
|
+
if (
|
|
229
|
+
sourceMessageIdInput !== undefined &&
|
|
230
|
+
(sourceMessageId == null ||
|
|
231
|
+
(requireCanonicalSourceMessageId &&
|
|
232
|
+
sourceMessageId !== sourceMessageIdInput))
|
|
233
|
+
) {
|
|
234
|
+
throw new TypeError('Invalid provider source message id');
|
|
235
|
+
}
|
|
236
|
+
if (sourceMessageId != null && trustState != null) {
|
|
237
|
+
trustState.sourceMessageIdRefs++;
|
|
238
|
+
if (
|
|
239
|
+
trustState.sourceMessageIdRefs >
|
|
240
|
+
PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxSourceMessageIds
|
|
241
|
+
) {
|
|
242
|
+
throw new TypeError('Provider source message ids exceed limits');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const sourceContentPartIndices = normalizeSourceContentPartIndices(
|
|
246
|
+
sourceContentPartIndicesInput,
|
|
247
|
+
trustState
|
|
248
|
+
);
|
|
249
|
+
return Object.freeze({
|
|
250
|
+
attribution,
|
|
251
|
+
...(sourceMessageId != null && { sourceMessageId }),
|
|
252
|
+
...(sourceContentPartIndices != null && {
|
|
253
|
+
sourceContentPartIndices: Object.freeze(sourceContentPartIndices),
|
|
254
|
+
}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function normalizeProvenanceParts(
|
|
259
|
+
parts: unknown,
|
|
260
|
+
requireCanonicalSourceMessageId = false,
|
|
261
|
+
enforceTrustBounds = false
|
|
262
|
+
): readonly ProviderMessageProvenancePart[] {
|
|
263
|
+
if (isProxy(parts) || !Array.isArray(parts)) {
|
|
264
|
+
throw new TypeError('Provider message provenance parts must be an array');
|
|
265
|
+
}
|
|
266
|
+
const length = parts.length;
|
|
267
|
+
if (!Number.isSafeInteger(length) || length === 0) {
|
|
268
|
+
throw new TypeError('Provider message provenance parts cannot be empty');
|
|
269
|
+
}
|
|
270
|
+
if (
|
|
271
|
+
enforceTrustBounds &&
|
|
272
|
+
length > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxParts
|
|
273
|
+
) {
|
|
274
|
+
throw new TypeError('Provider message provenance parts exceed limits');
|
|
275
|
+
}
|
|
276
|
+
const trustState = enforceTrustBounds
|
|
277
|
+
? { totalIndexRefs: 0, sourceMessageIdRefs: 0 }
|
|
278
|
+
: undefined;
|
|
279
|
+
const normalizedParts: ProviderMessageProvenancePart[] = [];
|
|
280
|
+
for (let index = 0; index < length; index++) {
|
|
281
|
+
const part = parts[index] as
|
|
282
|
+
| ProviderMessageProvenancePart
|
|
283
|
+
| null
|
|
284
|
+
| undefined;
|
|
285
|
+
normalizedParts.push(
|
|
286
|
+
normalizeProvenancePart(part, requireCanonicalSourceMessageId, trustState)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return Object.freeze(normalizedParts);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeProviderMessageProvenance(
|
|
293
|
+
provenanceInput: unknown
|
|
294
|
+
): ProviderMessageProvenance | undefined {
|
|
295
|
+
try {
|
|
296
|
+
if (provenanceInput == null || typeof provenanceInput !== 'object') {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
if (immutableProviderMessageProvenance.has(provenanceInput)) {
|
|
300
|
+
return provenanceInput as ProviderMessageProvenance;
|
|
301
|
+
}
|
|
302
|
+
const provenance = provenanceInput as {
|
|
303
|
+
version?: unknown;
|
|
304
|
+
parts?: unknown;
|
|
305
|
+
};
|
|
306
|
+
/** Capture accessor-backed envelope fields exactly once, then publish only
|
|
307
|
+
* a canonical immutable copy rather than returning the untrusted object. */
|
|
308
|
+
const version = provenance.version;
|
|
309
|
+
const partsInput = provenance.parts;
|
|
310
|
+
if (version !== PROVIDER_MESSAGE_PROVENANCE_VERSION) {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
const parts = normalizeProvenanceParts(partsInput, true, true);
|
|
314
|
+
return Object.freeze({
|
|
315
|
+
version: PROVIDER_MESSAGE_PROVENANCE_VERSION,
|
|
316
|
+
parts,
|
|
317
|
+
});
|
|
318
|
+
} catch {
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getUntrustedAdditionalKwargs(
|
|
324
|
+
message: BaseMessage
|
|
325
|
+
): UntrustedProviderMessageAdditionalKwargs | undefined {
|
|
326
|
+
try {
|
|
327
|
+
const additionalKwargs: unknown = message.additional_kwargs;
|
|
328
|
+
return additionalKwargs != null && typeof additionalKwargs === 'object'
|
|
329
|
+
? (additionalKwargs as UntrustedProviderMessageAdditionalKwargs)
|
|
330
|
+
: undefined;
|
|
331
|
+
} catch {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function readAdditionalKwarg(
|
|
337
|
+
additionalKwargs: UntrustedProviderMessageAdditionalKwargs | undefined,
|
|
338
|
+
key: keyof UntrustedProviderMessageAdditionalKwargs
|
|
339
|
+
): unknown {
|
|
340
|
+
try {
|
|
341
|
+
return additionalKwargs?.[key];
|
|
342
|
+
} catch {
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function collectProviderSourceMessageIds(
|
|
348
|
+
provenance: ProviderMessageProvenance | undefined,
|
|
349
|
+
pluralInput: unknown,
|
|
350
|
+
singularInput: unknown
|
|
351
|
+
): string[] | undefined {
|
|
352
|
+
const result: string[] = [];
|
|
353
|
+
const seen = new Set<string>();
|
|
354
|
+
const trustedProvenance =
|
|
355
|
+
provenance != null && immutableProviderMessageProvenance.has(provenance);
|
|
356
|
+
const boundPlural = trustedProvenance
|
|
357
|
+
? immutableProviderMessageSourceIds.get(provenance!)
|
|
358
|
+
: undefined;
|
|
359
|
+
let hasUntrustedSourceMetadata = provenance != null && !trustedProvenance;
|
|
360
|
+
let trustedPlural = false;
|
|
361
|
+
const provenanceParts = provenance?.parts;
|
|
362
|
+
|
|
363
|
+
/** Resolve trust across the setter-minted envelope/id pair before walking
|
|
364
|
+
* either collection. A trusted object spliced from another setter call is
|
|
365
|
+
* no longer a trusted combined lineage, so the public bounds apply to both
|
|
366
|
+
* sides and oversized inputs fail before any per-item work. */
|
|
367
|
+
let plural: readonly unknown[] | undefined;
|
|
368
|
+
let pluralLength = 0;
|
|
369
|
+
try {
|
|
370
|
+
if (pluralInput !== undefined) {
|
|
371
|
+
if (isProxy(pluralInput) || !Array.isArray(pluralInput)) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
plural = pluralInput;
|
|
375
|
+
pluralLength = pluralInput.length;
|
|
376
|
+
if (!Number.isSafeInteger(pluralLength) || pluralLength === 0) {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
trustedPlural = immutableProviderSourceMessageIds.has(pluralInput);
|
|
380
|
+
const bindingMismatch = trustedProvenance && boundPlural !== pluralInput;
|
|
381
|
+
if (bindingMismatch) {
|
|
382
|
+
trustedPlural = false;
|
|
383
|
+
}
|
|
384
|
+
if (!trustedPlural) {
|
|
385
|
+
hasUntrustedSourceMetadata = true;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} catch {
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** A primitive singular id has no identity of its own. Treat it as the
|
|
393
|
+
* setter's compatibility duplicate only when it exactly matches the last id
|
|
394
|
+
* of an immutable plural array published by that setter. This classification
|
|
395
|
+
* happens before either collection is walked so malformed singular metadata
|
|
396
|
+
* cannot force work over an otherwise trusted oversized lineage. */
|
|
397
|
+
const trustedSingularSourceIds =
|
|
398
|
+
boundPlural ?? (trustedPlural ? plural : undefined);
|
|
399
|
+
let trustedSingularSourceIdsLength = 0;
|
|
400
|
+
if (trustedSingularSourceIds != null) {
|
|
401
|
+
trustedSingularSourceIdsLength =
|
|
402
|
+
trustedSingularSourceIds === plural
|
|
403
|
+
? pluralLength
|
|
404
|
+
: trustedSingularSourceIds.length;
|
|
405
|
+
}
|
|
406
|
+
const unboundedSingular =
|
|
407
|
+
singularInput === undefined
|
|
408
|
+
? undefined
|
|
409
|
+
: normalizeSourceMessageId(singularInput);
|
|
410
|
+
const trustedSingularDuplicate =
|
|
411
|
+
singularInput !== undefined &&
|
|
412
|
+
typeof singularInput === 'string' &&
|
|
413
|
+
singularInput === unboundedSingular &&
|
|
414
|
+
trustedSingularSourceIds != null &&
|
|
415
|
+
trustedSingularSourceIdsLength > 0 &&
|
|
416
|
+
trustedSingularSourceIds[trustedSingularSourceIdsLength - 1] ===
|
|
417
|
+
singularInput;
|
|
418
|
+
if (singularInput !== undefined && !trustedSingularDuplicate) {
|
|
419
|
+
hasUntrustedSourceMetadata = true;
|
|
420
|
+
if (normalizeSourceMessageId(singularInput, true) == null) {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** Any untrusted lineage field revokes the setter-only size exemption for
|
|
426
|
+
* every collection participating in the union. Preflight all public bounds
|
|
427
|
+
* before reading a part or plural element. */
|
|
428
|
+
if (hasUntrustedSourceMetadata) {
|
|
429
|
+
try {
|
|
430
|
+
if (
|
|
431
|
+
provenanceParts != null &&
|
|
432
|
+
provenanceParts.length > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxParts
|
|
433
|
+
) {
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
if (
|
|
437
|
+
plural != null &&
|
|
438
|
+
pluralLength > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxSourceMessageIds
|
|
439
|
+
) {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const provenanceRequiresValidation =
|
|
447
|
+
!trustedProvenance || hasUntrustedSourceMetadata;
|
|
448
|
+
const pluralRequiresValidation = !trustedPlural || hasUntrustedSourceMetadata;
|
|
449
|
+
|
|
450
|
+
if (provenanceParts != null) {
|
|
451
|
+
const length = provenanceParts.length;
|
|
452
|
+
for (let index = 0; index < length; index++) {
|
|
453
|
+
if (
|
|
454
|
+
!appendSourceMessageId(
|
|
455
|
+
result,
|
|
456
|
+
seen,
|
|
457
|
+
provenanceParts[index].sourceMessageId,
|
|
458
|
+
provenanceRequiresValidation
|
|
459
|
+
)
|
|
460
|
+
) {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
if (plural != null) {
|
|
468
|
+
for (let index = 0; index < pluralLength; index++) {
|
|
469
|
+
if (
|
|
470
|
+
!appendSourceMessageId(
|
|
471
|
+
result,
|
|
472
|
+
seen,
|
|
473
|
+
plural[index],
|
|
474
|
+
pluralRequiresValidation
|
|
475
|
+
)
|
|
476
|
+
) {
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
} catch {
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
if (singularInput !== undefined) {
|
|
485
|
+
if (trustedSingularDuplicate) {
|
|
486
|
+
if (!seen.has(singularInput)) {
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
if (!appendSourceMessageId(result, seen, singularInput, true)) {
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (
|
|
496
|
+
hasUntrustedSourceMetadata &&
|
|
497
|
+
result.length > PROVIDER_MESSAGE_PROVENANCE_LIMITS.maxSourceMessageIds
|
|
498
|
+
) {
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function copyOwnEnumerableDataProperties(
|
|
505
|
+
input: object
|
|
506
|
+
): Record<PropertyKey, unknown> {
|
|
507
|
+
if (isProxy(input)) {
|
|
508
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
509
|
+
}
|
|
510
|
+
const result: Record<PropertyKey, unknown> = {};
|
|
511
|
+
const descriptors = Object.getOwnPropertyDescriptors(input);
|
|
512
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
513
|
+
const descriptor = Reflect.get(descriptors, key) as PropertyDescriptor;
|
|
514
|
+
if (descriptor.enumerable !== true) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (!('value' in descriptor)) {
|
|
518
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
519
|
+
}
|
|
520
|
+
Object.defineProperty(result, key, {
|
|
521
|
+
configurable: true,
|
|
522
|
+
enumerable: true,
|
|
523
|
+
value: descriptor.value,
|
|
524
|
+
writable: true,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return result;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** Legacy Human rows are treated as user-authored when no typed metadata is
|
|
531
|
+
* available. Unknown custom message types take the same conservative path. */
|
|
532
|
+
function inferLegacyMessageAttribution(
|
|
533
|
+
message: BaseMessage
|
|
534
|
+
): ProviderMessageAttribution {
|
|
535
|
+
const messageType = message.type;
|
|
536
|
+
if (messageType === 'ai') {
|
|
537
|
+
return 'model';
|
|
538
|
+
}
|
|
539
|
+
if (messageType === 'tool') {
|
|
540
|
+
return 'tool';
|
|
541
|
+
}
|
|
542
|
+
if (messageType === 'system') {
|
|
543
|
+
return 'synthetic';
|
|
544
|
+
}
|
|
545
|
+
return 'user';
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Returns explicitly stamped provenance without inferring from message role. */
|
|
549
|
+
export function getProviderMessageProvenance(
|
|
550
|
+
message: BaseMessage
|
|
551
|
+
): ProviderMessageProvenance | undefined {
|
|
552
|
+
const additionalKwargs = getUntrustedAdditionalKwargs(message);
|
|
553
|
+
const provenanceInput = readAdditionalKwarg(additionalKwargs, 'provenance');
|
|
554
|
+
return normalizeProviderMessageProvenance(provenanceInput);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** Safely preserves the semantic difference between absent and invalid input. */
|
|
558
|
+
export function inspectProviderMessageProvenance(
|
|
559
|
+
message: BaseMessage
|
|
560
|
+
): ProviderMessageProvenanceState {
|
|
561
|
+
let additionalKwargs: unknown;
|
|
562
|
+
try {
|
|
563
|
+
additionalKwargs = message.additional_kwargs;
|
|
564
|
+
} catch {
|
|
565
|
+
return invalidProviderMessageProvenanceState;
|
|
566
|
+
}
|
|
567
|
+
if (additionalKwargs == null || typeof additionalKwargs !== 'object') {
|
|
568
|
+
return absentProviderMessageProvenanceState;
|
|
569
|
+
}
|
|
570
|
+
let provenanceInput: unknown;
|
|
571
|
+
try {
|
|
572
|
+
provenanceInput = (additionalKwargs as { provenance?: unknown })
|
|
573
|
+
.provenance;
|
|
574
|
+
} catch {
|
|
575
|
+
return invalidProviderMessageProvenanceState;
|
|
576
|
+
}
|
|
577
|
+
if (provenanceInput == null) {
|
|
578
|
+
return absentProviderMessageProvenanceState;
|
|
579
|
+
}
|
|
580
|
+
const provenance = normalizeProviderMessageProvenance(provenanceInput);
|
|
581
|
+
return provenance == null
|
|
582
|
+
? invalidProviderMessageProvenanceState
|
|
583
|
+
: { status: 'valid', provenance };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function readProviderSourceMessageIds(
|
|
587
|
+
message: BaseMessage
|
|
588
|
+
): string[] | null | undefined {
|
|
589
|
+
let additionalKwargs: unknown;
|
|
590
|
+
try {
|
|
591
|
+
additionalKwargs = message.additional_kwargs;
|
|
592
|
+
} catch {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
if (additionalKwargs == null || typeof additionalKwargs !== 'object') {
|
|
596
|
+
return undefined;
|
|
597
|
+
}
|
|
598
|
+
if (isProxy(additionalKwargs)) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
let provenanceInput: unknown;
|
|
602
|
+
let pluralInput: unknown;
|
|
603
|
+
let singularInput: unknown;
|
|
604
|
+
try {
|
|
605
|
+
const sourceMetadata =
|
|
606
|
+
additionalKwargs as UntrustedProviderMessageAdditionalKwargs;
|
|
607
|
+
provenanceInput = sourceMetadata.provenance;
|
|
608
|
+
pluralInput = sourceMetadata.sourceMessageIds;
|
|
609
|
+
singularInput = sourceMetadata.sourceMessageId;
|
|
610
|
+
} catch {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
const hasProvenanceInput = provenanceInput != null;
|
|
614
|
+
const hasLegacyInput =
|
|
615
|
+
pluralInput !== undefined || singularInput !== undefined;
|
|
616
|
+
if (!hasProvenanceInput && !hasLegacyInput) {
|
|
617
|
+
return undefined;
|
|
618
|
+
}
|
|
619
|
+
const provenance = normalizeProviderMessageProvenance(provenanceInput);
|
|
620
|
+
if (hasProvenanceInput && provenance == null) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
return (
|
|
624
|
+
collectProviderSourceMessageIds(
|
|
625
|
+
provenance,
|
|
626
|
+
pluralInput,
|
|
627
|
+
singularInput
|
|
628
|
+
) ?? null
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** Safely distinguishes missing source lineage from malformed metadata. */
|
|
633
|
+
export function inspectProviderSourceMessageIds(
|
|
634
|
+
message: BaseMessage
|
|
635
|
+
): ProviderSourceMessageIdsState {
|
|
636
|
+
const sourceMessageIds = readProviderSourceMessageIds(message);
|
|
637
|
+
if (sourceMessageIds === undefined) {
|
|
638
|
+
return absentProviderSourceMessageIdsState;
|
|
639
|
+
}
|
|
640
|
+
if (sourceMessageIds === null) {
|
|
641
|
+
return invalidProviderSourceMessageIdsState;
|
|
642
|
+
}
|
|
643
|
+
return { status: 'valid', sourceMessageIds };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/** True when indexed contributions uniquely cover every current content part. */
|
|
647
|
+
export function hasBijectiveProviderContentPartMapping(
|
|
648
|
+
parts: readonly ProviderMessageProvenancePart[],
|
|
649
|
+
contentPartCount: number
|
|
650
|
+
): boolean {
|
|
651
|
+
if (!Number.isSafeInteger(contentPartCount) || contentPartCount <= 0) {
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
const seen = new Set<number>();
|
|
655
|
+
for (const part of parts) {
|
|
656
|
+
const indices = part.sourceContentPartIndices;
|
|
657
|
+
if (indices == null) {
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
for (const index of indices) {
|
|
661
|
+
if (
|
|
662
|
+
!Number.isSafeInteger(index) ||
|
|
663
|
+
index < 0 ||
|
|
664
|
+
index >= contentPartCount ||
|
|
665
|
+
seen.has(index)
|
|
666
|
+
) {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
seen.add(index);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return seen.size === contentPartCount;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Returns every explicit persisted source id in stable content order.
|
|
677
|
+
* Typed parts, plural lineage, and the legacy singular id are unioned in that
|
|
678
|
+
* precedence order; duplicates are removed without reordering.
|
|
679
|
+
*/
|
|
680
|
+
export function getProviderSourceMessageIds(message: BaseMessage): string[] {
|
|
681
|
+
const additionalKwargs = getUntrustedAdditionalKwargs(message);
|
|
682
|
+
const provenanceInput = readAdditionalKwarg(additionalKwargs, 'provenance');
|
|
683
|
+
const pluralInput = readAdditionalKwarg(additionalKwargs, 'sourceMessageIds');
|
|
684
|
+
const singularInput = readAdditionalKwarg(
|
|
685
|
+
additionalKwargs,
|
|
686
|
+
'sourceMessageId'
|
|
687
|
+
);
|
|
688
|
+
return (
|
|
689
|
+
collectProviderSourceMessageIds(
|
|
690
|
+
normalizeProviderMessageProvenance(provenanceInput),
|
|
691
|
+
pluralInput,
|
|
692
|
+
singularInput
|
|
693
|
+
) ?? []
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function publishProviderMessageProvenance(
|
|
698
|
+
message: BaseMessage,
|
|
699
|
+
provenance: unknown,
|
|
700
|
+
sourceMessageIds?: readonly string[]
|
|
701
|
+
): void {
|
|
702
|
+
if (isProxy(message)) {
|
|
703
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
704
|
+
}
|
|
705
|
+
const liveDescriptor = Object.getOwnPropertyDescriptor(
|
|
706
|
+
message,
|
|
707
|
+
'additional_kwargs'
|
|
708
|
+
);
|
|
709
|
+
const lcKwargsDescriptor = Object.getOwnPropertyDescriptor(
|
|
710
|
+
message,
|
|
711
|
+
'lc_kwargs'
|
|
712
|
+
);
|
|
713
|
+
if (
|
|
714
|
+
liveDescriptor == null ||
|
|
715
|
+
!('value' in liveDescriptor) ||
|
|
716
|
+
liveDescriptor.writable !== true ||
|
|
717
|
+
lcKwargsDescriptor == null ||
|
|
718
|
+
!('value' in lcKwargsDescriptor) ||
|
|
719
|
+
lcKwargsDescriptor.writable !== true
|
|
720
|
+
) {
|
|
721
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
722
|
+
}
|
|
723
|
+
const currentAdditionalKwargsInput: unknown = liveDescriptor.value;
|
|
724
|
+
if (
|
|
725
|
+
currentAdditionalKwargsInput == null ||
|
|
726
|
+
typeof currentAdditionalKwargsInput !== 'object'
|
|
727
|
+
) {
|
|
728
|
+
throw new TypeError('Invalid provider message additional kwargs');
|
|
729
|
+
}
|
|
730
|
+
let replacement: UntrustedProviderMessageAdditionalKwargs;
|
|
731
|
+
try {
|
|
732
|
+
replacement = copyOwnEnumerableDataProperties(
|
|
733
|
+
currentAdditionalKwargsInput
|
|
734
|
+
) as UntrustedProviderMessageAdditionalKwargs;
|
|
735
|
+
} catch {
|
|
736
|
+
throw new TypeError('Invalid provider message additional kwargs');
|
|
737
|
+
}
|
|
738
|
+
const lcKwargsInput: unknown = lcKwargsDescriptor.value;
|
|
739
|
+
if (lcKwargsInput == null || typeof lcKwargsInput !== 'object') {
|
|
740
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
741
|
+
}
|
|
742
|
+
let serializedReplacement: Record<PropertyKey, unknown>;
|
|
743
|
+
try {
|
|
744
|
+
serializedReplacement = copyOwnEnumerableDataProperties(lcKwargsInput);
|
|
745
|
+
} catch {
|
|
746
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
747
|
+
}
|
|
748
|
+
replacement.provenance = provenance;
|
|
749
|
+
if (sourceMessageIds != null && sourceMessageIds.length > 0) {
|
|
750
|
+
replacement.sourceMessageIds = sourceMessageIds;
|
|
751
|
+
replacement.sourceMessageId = sourceMessageIds[sourceMessageIds.length - 1];
|
|
752
|
+
} else {
|
|
753
|
+
delete replacement.sourceMessageIds;
|
|
754
|
+
delete replacement.sourceMessageId;
|
|
755
|
+
}
|
|
756
|
+
serializedReplacement.additional_kwargs = replacement;
|
|
757
|
+
try {
|
|
758
|
+
/** Both own data properties are prevalidated before one descriptor batch,
|
|
759
|
+
* so custom accessors cannot observe or create a split publication. */
|
|
760
|
+
Object.defineProperties(message, {
|
|
761
|
+
additional_kwargs: { ...liveDescriptor, value: replacement },
|
|
762
|
+
lc_kwargs: { ...lcKwargsDescriptor, value: serializedReplacement },
|
|
763
|
+
});
|
|
764
|
+
} catch {
|
|
765
|
+
throw new TypeError('Invalid provider message serialization kwargs');
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/** Replaces typed provenance and synchronizes its stable plural source ids. */
|
|
770
|
+
export function setProviderMessageProvenance(
|
|
771
|
+
message: BaseMessage,
|
|
772
|
+
parts: readonly ProviderMessageProvenancePart[]
|
|
773
|
+
): void {
|
|
774
|
+
const normalizedParts = normalizeProvenanceParts(parts);
|
|
775
|
+
const provenance: ProviderMessageProvenance = Object.freeze({
|
|
776
|
+
version: PROVIDER_MESSAGE_PROVENANCE_VERSION,
|
|
777
|
+
parts: normalizedParts,
|
|
778
|
+
});
|
|
779
|
+
immutableProviderMessageProvenance.add(provenance);
|
|
780
|
+
const sourceMessageIds: string[] = [];
|
|
781
|
+
const seen = new Set<string>();
|
|
782
|
+
for (const part of normalizedParts) {
|
|
783
|
+
appendSourceMessageId(sourceMessageIds, seen, part.sourceMessageId);
|
|
784
|
+
}
|
|
785
|
+
if (sourceMessageIds.length > 0) {
|
|
786
|
+
const immutableSourceMessageIds = Object.freeze(sourceMessageIds);
|
|
787
|
+
immutableProviderSourceMessageIds.add(immutableSourceMessageIds);
|
|
788
|
+
immutableProviderMessageSourceIds.set(
|
|
789
|
+
provenance,
|
|
790
|
+
immutableSourceMessageIds
|
|
791
|
+
);
|
|
792
|
+
publishProviderMessageProvenance(
|
|
793
|
+
message,
|
|
794
|
+
provenance,
|
|
795
|
+
immutableSourceMessageIds
|
|
796
|
+
);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
publishProviderMessageProvenance(message, provenance);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/** Publishes the canonical fail-closed marker for malformed provenance. */
|
|
803
|
+
export function setInvalidProviderMessageProvenance(
|
|
804
|
+
message: BaseMessage
|
|
805
|
+
): void {
|
|
806
|
+
publishProviderMessageProvenance(
|
|
807
|
+
message,
|
|
808
|
+
invalidProviderMessageProvenanceSentinel
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/** Marks a provider-visible runtime message as host-generated context. */
|
|
813
|
+
export function stampSyntheticProviderMessage<T extends BaseMessage>(
|
|
814
|
+
message: T
|
|
815
|
+
): T {
|
|
816
|
+
setProviderMessageProvenance(message, [{ attribution: 'synthetic' }]);
|
|
817
|
+
return message;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Adds one logical lineage contribution, migrating legacy ids on first use.
|
|
822
|
+
* This compatibility helper rebuilds validated public metadata; hot formatters
|
|
823
|
+
* should accumulate locally and call `setProviderMessageProvenance` once.
|
|
824
|
+
*/
|
|
825
|
+
export function appendProviderMessageProvenance(
|
|
826
|
+
message: BaseMessage,
|
|
827
|
+
part: ProviderMessageProvenancePart
|
|
828
|
+
): void {
|
|
829
|
+
let normalizedPart = normalizeProvenancePart(part);
|
|
830
|
+
const additionalKwargs = getUntrustedAdditionalKwargs(message);
|
|
831
|
+
const provenanceInput = readAdditionalKwarg(additionalKwargs, 'provenance');
|
|
832
|
+
const pluralInput = readAdditionalKwarg(additionalKwargs, 'sourceMessageIds');
|
|
833
|
+
const singularInput = readAdditionalKwarg(
|
|
834
|
+
additionalKwargs,
|
|
835
|
+
'sourceMessageId'
|
|
836
|
+
);
|
|
837
|
+
const provenance = normalizeProviderMessageProvenance(provenanceInput);
|
|
838
|
+
const existing = provenance?.parts ?? [];
|
|
839
|
+
const representedSourceIds = new Set<string>();
|
|
840
|
+
for (const existingPart of existing) {
|
|
841
|
+
if (existingPart.sourceMessageId != null) {
|
|
842
|
+
representedSourceIds.add(existingPart.sourceMessageId);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const sourceMessageIds =
|
|
846
|
+
collectProviderSourceMessageIds(provenance, pluralInput, singularInput) ??
|
|
847
|
+
[];
|
|
848
|
+
const missingLegacySourceIds = sourceMessageIds.filter(
|
|
849
|
+
(sourceMessageId) => !representedSourceIds.has(sourceMessageId)
|
|
850
|
+
);
|
|
851
|
+
const migratedParts: ProviderMessageProvenancePart[] = [...existing];
|
|
852
|
+
const legacyAttribution = inferLegacyMessageAttribution(message);
|
|
853
|
+
for (const sourceMessageId of missingLegacySourceIds) {
|
|
854
|
+
migratedParts.push({
|
|
855
|
+
attribution: legacyAttribution,
|
|
856
|
+
sourceMessageId,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
if (normalizedPart.sourceMessageId == null && sourceMessageIds.length === 1) {
|
|
860
|
+
normalizedPart = {
|
|
861
|
+
...normalizedPart,
|
|
862
|
+
sourceMessageId: sourceMessageIds[0],
|
|
863
|
+
};
|
|
864
|
+
const migratedIndex = migratedParts.findIndex(
|
|
865
|
+
(migratedPart, index) =>
|
|
866
|
+
index >= existing.length &&
|
|
867
|
+
migratedPart.attribution === normalizedPart.attribution &&
|
|
868
|
+
migratedPart.sourceMessageId === sourceMessageIds[0]
|
|
869
|
+
);
|
|
870
|
+
if (migratedIndex >= 0) {
|
|
871
|
+
migratedParts.splice(migratedIndex, 1);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const lastPart = migratedParts[migratedParts.length - 1];
|
|
875
|
+
if (
|
|
876
|
+
migratedParts.length > 0 &&
|
|
877
|
+
lastPart.attribution === normalizedPart.attribution &&
|
|
878
|
+
lastPart.sourceMessageId === normalizedPart.sourceMessageId &&
|
|
879
|
+
(lastPart.sourceContentPartIndices == null) ===
|
|
880
|
+
(normalizedPart.sourceContentPartIndices == null)
|
|
881
|
+
) {
|
|
882
|
+
const sourceContentPartIndices = normalizeSourceContentPartIndices([
|
|
883
|
+
...(lastPart.sourceContentPartIndices ?? []),
|
|
884
|
+
...(normalizedPart.sourceContentPartIndices ?? []),
|
|
885
|
+
]);
|
|
886
|
+
migratedParts[migratedParts.length - 1] = {
|
|
887
|
+
attribution: lastPart.attribution,
|
|
888
|
+
...(lastPart.sourceMessageId != null && {
|
|
889
|
+
sourceMessageId: lastPart.sourceMessageId,
|
|
890
|
+
}),
|
|
891
|
+
...(sourceContentPartIndices != null && { sourceContentPartIndices }),
|
|
892
|
+
};
|
|
893
|
+
} else {
|
|
894
|
+
migratedParts.push(normalizedPart);
|
|
895
|
+
}
|
|
896
|
+
setProviderMessageProvenance(message, migratedParts);
|
|
897
|
+
}
|