@ai-matrx/agents 0.21.3 → 0.21.4
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 +33 -2
- 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/context/index.cjs +77 -7
- package/dist/context/index.cjs.map +1 -1
- package/dist/context/index.d.cts +29 -1
- package/dist/context/index.d.ts +29 -1
- package/dist/context/index.js +77 -7
- package/dist/context/index.js.map +1 -1
- package/dist/context/react/index.cjs +24 -11
- package/dist/context/react/index.cjs.map +1 -1
- package/dist/context/react/index.d.cts +6 -4
- package/dist/context/react/index.d.ts +6 -4
- package/dist/context/react/index.js +24 -11
- package/dist/context/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/mandates/snapshots/keys.0.21.4.json +652 -0
- package/package.json +2 -2
package/dist/context/index.cjs
CHANGED
|
@@ -24,19 +24,23 @@ __export(context_exports, {
|
|
|
24
24
|
DEFAULT_INLINE_CAP: () => DEFAULT_INLINE_CAP,
|
|
25
25
|
DEFAULT_INLINE_MAX_CHARS: () => DEFAULT_INLINE_MAX_CHARS,
|
|
26
26
|
DEFAULT_SURFACE_KEY: () => DEFAULT_SURFACE_KEY,
|
|
27
|
+
LAZY_SOURCE_DEFAULT_MAX: () => LAZY_SOURCE_DEFAULT_MAX,
|
|
27
28
|
buildContextWire: () => buildContextWire,
|
|
28
29
|
compareReceipt: () => compareReceipt,
|
|
29
30
|
contextValueChars: () => contextValueChars,
|
|
30
31
|
formatChars: () => formatChars,
|
|
31
32
|
lookupSavedRule: () => lookupSavedRule,
|
|
33
|
+
receiptRowToResolved: () => receiptRowToResolved,
|
|
32
34
|
resolveContextRow: () => resolveContextRow,
|
|
33
|
-
resolveContextRule: () => resolveContextRule
|
|
35
|
+
resolveContextRule: () => resolveContextRule,
|
|
36
|
+
serverDefaultMax: () => serverDefaultMax
|
|
34
37
|
});
|
|
35
38
|
module.exports = __toCommonJS(context_exports);
|
|
36
39
|
var CONTEXT_RULES_FEATURE = "context_rules";
|
|
37
40
|
var DEFAULT_SURFACE_KEY = "_default";
|
|
38
41
|
var DEFAULT_INLINE_MAX_CHARS = 200;
|
|
39
42
|
var DEFAULT_INLINE_CAP = 5e4;
|
|
43
|
+
var LAZY_SOURCE_DEFAULT_MAX = 6e3;
|
|
40
44
|
var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
41
45
|
var isValidLimit = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
|
|
42
46
|
function resolveContextRule(input) {
|
|
@@ -130,11 +134,53 @@ function lookupSavedRule(rows, surfaceKey, key) {
|
|
|
130
134
|
const merged = { ...base, ...own };
|
|
131
135
|
return Object.keys(merged).length === 0 ? null : merged;
|
|
132
136
|
}
|
|
137
|
+
var RICH_FORM_KEYS = /* @__PURE__ */ new Set([
|
|
138
|
+
"content",
|
|
139
|
+
"mutable",
|
|
140
|
+
"persist",
|
|
141
|
+
"source",
|
|
142
|
+
"type",
|
|
143
|
+
"label",
|
|
144
|
+
"description",
|
|
145
|
+
"max_inline_chars",
|
|
146
|
+
"default_max_inline_chars",
|
|
147
|
+
"summary_agent_id"
|
|
148
|
+
]);
|
|
149
|
+
var UUID_RE = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
|
|
150
|
+
function isPlainObject(v) {
|
|
151
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
152
|
+
}
|
|
153
|
+
function isResourceRef(v) {
|
|
154
|
+
if (v.__kind !== "resource_ref") return false;
|
|
155
|
+
const type = v.resource_type || v.type;
|
|
156
|
+
const id = v.resource_id || v.id;
|
|
157
|
+
return typeof type === "string" && type.trim() !== "" && typeof id === "string" && UUID_RE.test(id.trim());
|
|
158
|
+
}
|
|
159
|
+
function serverDefaultMax(value) {
|
|
160
|
+
if (!isPlainObject(value)) return null;
|
|
161
|
+
if (isResourceRef(value)) return 0;
|
|
162
|
+
if (!Object.keys(value).every((k) => RICH_FORM_KEYS.has(k))) return null;
|
|
163
|
+
const src = value.source;
|
|
164
|
+
const validSource = isPlainObject(src) && typeof src.kind === "string";
|
|
165
|
+
const hasContent = Object.prototype.hasOwnProperty.call(value, "content");
|
|
166
|
+
if (!hasContent && !validSource) return null;
|
|
167
|
+
if (isValidLimit(value.default_max_inline_chars)) return value.default_max_inline_chars;
|
|
168
|
+
if (validSource && (value.content === null || value.content === void 0)) {
|
|
169
|
+
const extra = isPlainObject(src.extra) ? src.extra : {};
|
|
170
|
+
return extra.resource_type ? 0 : LAZY_SOURCE_DEFAULT_MAX;
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
133
174
|
function resolveContextRow(source, savedRows, cap = DEFAULT_INLINE_CAP) {
|
|
134
175
|
const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;
|
|
135
|
-
const chars = source.chars !== void 0 ? source.chars : contextValueChars(envelopeContent(source.value));
|
|
176
|
+
const chars = source.chars !== void 0 ? source.chars : isLazySource(source.value) ? null : contextValueChars(envelopeContent(source.value));
|
|
136
177
|
const userRule = lookupSavedRule(savedRows, surfaceKey, source.key);
|
|
137
|
-
const
|
|
178
|
+
const layers = { ...source.layers ?? {} };
|
|
179
|
+
if (layers.default_max === void 0 || layers.default_max === null) {
|
|
180
|
+
const serverDefault = serverDefaultMax(source.value);
|
|
181
|
+
if (serverDefault !== null) layers.default_max = serverDefault;
|
|
182
|
+
}
|
|
183
|
+
const result = resolveContextRule({ ...layers, chars, user: userRule, cap });
|
|
138
184
|
const row = {
|
|
139
185
|
...result,
|
|
140
186
|
key: source.key,
|
|
@@ -147,12 +193,18 @@ function resolveContextRow(source, savedRows, cap = DEFAULT_INLINE_CAP) {
|
|
|
147
193
|
};
|
|
148
194
|
if (source.description !== void 0) row.description = source.description;
|
|
149
195
|
if (source.type !== void 0) row.type = source.type;
|
|
150
|
-
if (source.layers !== void 0) row.layers =
|
|
196
|
+
if (source.layers !== void 0 || layers.default_max !== void 0) row.layers = layers;
|
|
151
197
|
return row;
|
|
152
198
|
}
|
|
153
199
|
function isEnvelope(value) {
|
|
154
200
|
return !!value && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "content");
|
|
155
201
|
}
|
|
202
|
+
function isLazySource(value) {
|
|
203
|
+
if (!isPlainObject(value)) return false;
|
|
204
|
+
if (isResourceRef(value)) return true;
|
|
205
|
+
const src = value.source;
|
|
206
|
+
return isPlainObject(src) && typeof src.kind === "string" && (value.content === null || value.content === void 0);
|
|
207
|
+
}
|
|
156
208
|
function envelopeContent(value) {
|
|
157
209
|
return isEnvelope(value) ? value.content : value;
|
|
158
210
|
}
|
|
@@ -160,7 +212,7 @@ function buildContextWire(rows) {
|
|
|
160
212
|
const wire = {};
|
|
161
213
|
for (const row of rows) {
|
|
162
214
|
if (!row.include) continue;
|
|
163
|
-
const rich = isEnvelope(row.value);
|
|
215
|
+
const rich = isEnvelope(row.value) || isLazySource(row.value) && !isResourceRef(row.value);
|
|
164
216
|
const envelope = rich ? { ...row.value } : { content: row.value };
|
|
165
217
|
delete envelope.max_inline_chars;
|
|
166
218
|
if (envelope.type === void 0 && row.type !== void 0) envelope.type = row.type;
|
|
@@ -186,6 +238,7 @@ function sameRule(a, b) {
|
|
|
186
238
|
}
|
|
187
239
|
function compareReceipt(expected, receipt) {
|
|
188
240
|
const mismatches = [];
|
|
241
|
+
const modelReadsContext = receipt.model_reads_context !== false;
|
|
189
242
|
const used = /* @__PURE__ */ new Set();
|
|
190
243
|
const rows = receipt.rows ?? [];
|
|
191
244
|
for (const exp of expected) {
|
|
@@ -209,7 +262,7 @@ function compareReceipt(expected, receipt) {
|
|
|
209
262
|
actual: actual.max_inline_chars
|
|
210
263
|
});
|
|
211
264
|
}
|
|
212
|
-
if (exp.chars !== null && actual.chars !== null && actual.delivery !== exp.delivery) {
|
|
265
|
+
if (modelReadsContext && exp.chars !== null && actual.chars !== null && actual.delivery !== exp.delivery) {
|
|
213
266
|
mismatches.push({ key: exp.key, field: "delivery", expected: exp.delivery, actual: actual.delivery });
|
|
214
267
|
}
|
|
215
268
|
const expRule = normalizeUserRule(exp.userRule);
|
|
@@ -218,7 +271,24 @@ function compareReceipt(expected, receipt) {
|
|
|
218
271
|
mismatches.push({ key: exp.key, field: "user_rule", expected: expRule, actual: actRule });
|
|
219
272
|
}
|
|
220
273
|
}
|
|
221
|
-
return { mismatches, systemRows: rows.filter((r) => !used.has(r)) };
|
|
274
|
+
return { mismatches, systemRows: rows.filter((r) => !used.has(r)), modelReadsContext };
|
|
275
|
+
}
|
|
276
|
+
function receiptRowToResolved(row) {
|
|
277
|
+
const origin = row.surface_key !== DEFAULT_SURFACE_KEY ? "page" : row.origin === "client" ? "attached" : "system";
|
|
278
|
+
return {
|
|
279
|
+
key: row.key,
|
|
280
|
+
label: row.label,
|
|
281
|
+
surfaceKey: row.surface_key,
|
|
282
|
+
origin,
|
|
283
|
+
value: void 0,
|
|
284
|
+
chars: row.chars,
|
|
285
|
+
userRule: normalizeUserRule(row.user_rule),
|
|
286
|
+
include: row.include,
|
|
287
|
+
max_inline_chars: row.max_inline_chars,
|
|
288
|
+
delivery: row.delivery,
|
|
289
|
+
decided_by: { include: row.decided_by.include, max_inline_chars: row.decided_by.max_inline_chars },
|
|
290
|
+
clamped: row.clamped
|
|
291
|
+
};
|
|
222
292
|
}
|
|
223
293
|
function formatChars(n) {
|
|
224
294
|
if (n === null || n === void 0 || !Number.isFinite(n)) return "\u2014";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../context/index.ts"],"sourcesContent":["/**\n * @ai-matrx/agents/context — the context value rules, headless.\n *\n * Contract: common-docs/systems/scopes-context/context-delivery/RULES.md.\n * Parity corpus: ./rules-corpus.json (run here by rules.test.ts and by aidream's\n * pytest against the Python twin `resolve_context_rule`). Every function here\n * is pure, zero-React, and safe to import on a server.\n *\n * The one flow a client runs per turn:\n * rows = values.map(v => resolveContextRow(v, savedRows, cap)) // what the table shows\n * context = buildContextWire(rows) // what the request sends\n * …on `context_receipt`: compareReceipt(rows, receipt) // screen vs server\n */\n\n// ── Constants ───────────────────────────────────────────────────────────────\n\n/** `users.user_surface_state.feature` for the person's saved rules. */\nexport const CONTEXT_RULES_FEATURE = \"context_rules\";\n/** Surface key for values no surface emitted (attachments, server-added values). */\nexport const DEFAULT_SURFACE_KEY = \"_default\";\n/** The server's default inline threshold when no layer sets one. */\nexport const DEFAULT_INLINE_MAX_CHARS = 200;\n/** The default ceiling every resolved limit is clamped to (a server knob). */\nexport const DEFAULT_INLINE_CAP = 50000;\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\n/** Which layer decided a field — lowest to highest. */\nexport type ContextLayer = \"default\" | \"page\" | \"agent\" | \"you\";\nexport type ContextDelivery = \"inline\" | \"on_request\" | \"off\";\n\n/** One saved per-person rule (one key on one surface). Both fields optional. */\nexport interface SavedContextRule {\n include?: boolean;\n max_inline_chars?: number;\n}\n\n/** `{ [surface_key]: { [context key]: SavedContextRule } }` — the person's rows. */\nexport type SavedContextRuleRows = Record<string, Record<string, SavedContextRule>>;\n\n/** The agent layer for one key (RULES.md §2). */\nexport interface ContextAgentLayer {\n /** The agent's Context Policy declares this key (or the value is the agent's own seed). */\n declared?: boolean;\n /** The policy's ceiling; only read when `declared`. */\n max_inline_chars?: number | null;\n /** `auto_context_disabled` — closes every undeclared key. */\n kill_switch?: boolean;\n}\n\n/** The page layer for one key: the surface that emitted the value. */\nexport interface ContextSurfaceLayer {\n /** The surface declares this value (a `ui_surface_value` row / manifest entry). */\n declared?: boolean;\n /** `false` drops the value unless the agent declares it. */\n auto_context?: boolean | null;\n /** `inlineUpTo` / envelope `max_inline_chars` / DB column. */\n max_inline_chars?: number | null;\n}\n\n/** The non-person layers for one value — what the table's layer breakdown shows. */\nexport interface ContextRuleLayers {\n /** The server's per-key default limit; `null`/absent = 200. */\n default_max?: number | null;\n agent?: ContextAgentLayer | null;\n surface?: ContextSurfaceLayer | null;\n}\n\n/**\n * Input to `resolveContextRule`. Snake case on purpose: it is the corpus shape\n * and the Python twin's shape. `user` is typed loosely because invalid saved\n * values (non-boolean include, non-integer or negative limit) must be ignored,\n * not trusted.\n */\nexport interface ContextRuleInput extends ContextRuleLayers {\n /** Size in code points; `null` = an unresolved lazy source. */\n chars: number | null;\n user?: { include?: unknown; max_inline_chars?: unknown } | null;\n /** Ceiling; default `DEFAULT_INLINE_CAP`. */\n cap?: number;\n}\n\nexport interface ContextRuleResult {\n include: boolean;\n /** Effective limit, after the clamp. */\n max_inline_chars: number;\n delivery: ContextDelivery;\n decided_by: { include: ContextLayer; max_inline_chars: ContextLayer };\n clamped: boolean;\n}\n\n/** One receipt row — RULES.md §5, wire fields exactly. */\nexport interface ContextReceiptRow {\n key: string;\n label: string;\n surface_key: string;\n origin: \"client\" | \"server\" | \"surroundings\" | \"rule\";\n chars: number | null;\n include: boolean;\n max_inline_chars: number;\n delivery: ContextDelivery;\n decided_by: { include: ContextLayer; max_inline_chars: ContextLayer };\n user_rule: { include?: boolean; max_inline_chars?: number } | null;\n clamped: boolean;\n /** The CLIENT itself sent a value the person turned off. */\n client_sent_excluded: boolean;\n /** `\"model\"`: nothing reached a model that reads no context. */\n blocked_by: \"model\" | null;\n}\n\n/** The per-turn receipt the server streams as `context_receipt` — RULES.md §5. */\nexport interface ContextReceipt {\n version: 1;\n surface: string | null;\n cap: number;\n /** False: this turn's model cannot read context at all (every row `blocked_by: \"model\"`). */\n model_reads_context: boolean;\n /** The person's saved rules could not be read; the turn ran without them. */\n rules_error: string | null;\n rows: ContextReceiptRow[];\n}\n\nexport type ContextRowOrigin = \"page\" | \"attached\" | \"system\";\n\n/** A client's resolved row: what its table renders and what `buildContextWire` sends. */\nexport interface ResolvedContextRow extends ContextRuleResult {\n key: string;\n label: string;\n surfaceKey: string;\n origin: ContextRowOrigin;\n /** The raw value, or an already-rich envelope (`{ content, type, … }`). */\n value: unknown;\n chars: number | null;\n /** The saved rule that was applied (merged `_default` + surface row), or null. */\n userRule: SavedContextRule | null;\n description?: string;\n /** Context object type for the wire envelope (`text`, `json`, …). */\n type?: string;\n /** The non-person layers the rule was resolved from. Absent = default only. */\n layers?: ContextRuleLayers;\n}\n\n/** What a client knows about one value before resolution. */\nexport interface ContextRowSource {\n key: string;\n label: string;\n surfaceKey?: string;\n origin: ContextRowOrigin;\n value: unknown;\n /** Override the measured size (e.g. `null` for an unresolved lazy source). */\n chars?: number | null;\n description?: string;\n type?: string;\n layers?: ContextRuleLayers;\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst isNum = (v: unknown): v is number => typeof v === \"number\" && Number.isFinite(v);\nconst isValidLimit = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isInteger(v) && v >= 0;\n\n// ── resolveContextRule — RULES.md §2, line for line ─────────────────────────\n\nexport function resolveContextRule(input: ContextRuleInput): ContextRuleResult {\n const agent = input.agent ?? null;\n const surface = input.surface ?? null;\n const user = input.user ?? null;\n const cap = isNum(input.cap) ? input.cap : DEFAULT_INLINE_CAP;\n const agentDeclared = agent?.declared === true;\n\n let include = true;\n let includeBy: ContextLayer = \"default\";\n if (surface?.declared === true && surface.auto_context === false) {\n if (agentDeclared) {\n include = true;\n includeBy = \"agent\";\n } else {\n include = false;\n includeBy = \"page\";\n }\n }\n if (agent?.kill_switch === true && !agentDeclared) {\n include = false;\n includeBy = \"agent\";\n }\n if (user && typeof user.include === \"boolean\") {\n include = user.include;\n includeBy = \"you\";\n }\n\n let limit = isNum(input.default_max) ? input.default_max : DEFAULT_INLINE_MAX_CHARS;\n let limitBy: ContextLayer = \"default\";\n const a = agentDeclared && isNum(agent?.max_inline_chars) ? agent.max_inline_chars : null;\n const s = isNum(surface?.max_inline_chars) ? surface.max_inline_chars : null;\n if (a !== null && s !== null) {\n if (s < a) {\n limit = s;\n limitBy = \"page\";\n } else {\n limit = a;\n limitBy = \"agent\";\n }\n } else if (a !== null) {\n limit = a;\n limitBy = \"agent\";\n } else if (s !== null) {\n limit = s;\n limitBy = \"page\";\n }\n if (user && isValidLimit(user.max_inline_chars)) {\n limit = user.max_inline_chars;\n limitBy = \"you\";\n }\n const clamped = limit > cap;\n limit = Math.min(limit, cap);\n\n const chars = input.chars;\n const delivery: ContextDelivery = !include\n ? \"off\"\n : chars === null || chars === undefined || limit === 0 || chars > limit\n ? \"on_request\"\n : \"inline\";\n\n return {\n include,\n max_inline_chars: limit,\n delivery,\n decided_by: { include: includeBy, max_inline_chars: limitBy },\n clamped,\n };\n}\n\n// ── contextValueChars — RULES.md §1 ─────────────────────────────────────────\n\n/** Count Unicode code points (never UTF-16 units: `\"😀\".length` is 2). */\nfunction codePoints(s: string): number {\n let n = 0;\n for (const _ of s) n += 1;\n return n;\n}\n\n/**\n * Size of a value in code points, exactly as the server's `content_as_str()`:\n * a string is itself; anything else is `JSON.stringify(value, null, 2)`\n * (Python `json.dumps(value, ensure_ascii=False, indent=2)`). `undefined` → 0.\n */\nexport function contextValueChars(value: unknown): number {\n if (value === undefined) return 0;\n if (typeof value === \"string\") return codePoints(value);\n let text: string | undefined;\n try {\n text = JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n return text === undefined ? 0 : codePoints(text);\n}\n\n// ── lookupSavedRule — RULES.md §3 ───────────────────────────────────────────\n\nfunction sanitizeRule(raw: unknown): SavedContextRule {\n const out: SavedContextRule = {};\n if (!raw || typeof raw !== \"object\") return out;\n const r = raw as Record<string, unknown>;\n if (typeof r.include === \"boolean\") out.include = r.include;\n if (isValidLimit(r.max_inline_chars)) out.max_inline_chars = r.max_inline_chars;\n return out;\n}\n\n/**\n * The person's rule for one key on one surface: `_default[key]` merged per\n * field under `rows[surfaceKey][key]`, invalid fields stripped. `null` when\n * nothing valid is saved.\n */\nexport function lookupSavedRule(\n rows: SavedContextRuleRows | null | undefined,\n surfaceKey: string,\n key: string,\n): SavedContextRule | null {\n if (!rows || typeof rows !== \"object\") return null;\n const base = sanitizeRule(rows[DEFAULT_SURFACE_KEY]?.[key]);\n const own = surfaceKey === DEFAULT_SURFACE_KEY ? {} : sanitizeRule(rows[surfaceKey]?.[key]);\n const merged: SavedContextRule = { ...base, ...own };\n return Object.keys(merged).length === 0 ? null : merged;\n}\n\n// ── resolveContextRow — the glue a client runs per value ────────────────────\n\n/** Resolve one value into the row the table renders and the wire sends. */\nexport function resolveContextRow(\n source: ContextRowSource,\n savedRows: SavedContextRuleRows | null | undefined,\n cap: number = DEFAULT_INLINE_CAP,\n): ResolvedContextRow {\n const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;\n const chars =\n source.chars !== undefined ? source.chars : contextValueChars(envelopeContent(source.value));\n const userRule = lookupSavedRule(savedRows, surfaceKey, source.key);\n const result = resolveContextRule({ ...(source.layers ?? {}), chars, user: userRule, cap });\n const row: ResolvedContextRow = {\n ...result,\n key: source.key,\n label: source.label,\n surfaceKey,\n origin: source.origin,\n value: source.value,\n chars,\n userRule,\n };\n if (source.description !== undefined) row.description = source.description;\n if (source.type !== undefined) row.type = source.type;\n if (source.layers !== undefined) row.layers = source.layers;\n return row;\n}\n\n// ── buildContextWire — the ONLY producer of a request's `context` ──────────\n\nfunction isEnvelope(value: unknown): value is Record<string, unknown> {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n Object.prototype.hasOwnProperty.call(value, \"content\")\n );\n}\n\nfunction envelopeContent(value: unknown): unknown {\n return isEnvelope(value) ? value.content : value;\n}\n\n/**\n * Build the request's `context` object from the resolved rows. This is the\n * ONLY function that may produce it, so what the table shows and what the\n * request carries cannot drift apart.\n *\n * - A row whose `include` is false is omitted entirely — it never reaches the\n * wire (RULES.md §1: off means off everywhere).\n * - An included row becomes `{ content, type?, label, description?,\n * max_inline_chars? }`. An already-rich envelope (a value with its own\n * `content`, e.g. a working document carrying `source`/`mutable`/`persist`)\n * keeps every non-rule field it already has; `label`/`description`/`type`\n * are only filled when missing.\n * - `max_inline_chars` on the envelope is the PAGE layer's value only\n * (`layers.surface.max_inline_chars`, else the envelope's own page value).\n * The server reads the person's saved rules itself every turn (RULES.md §3)\n * and the agent layer from its own policy, so the wire must never carry the\n * person's number or the resolved effective limit dressed up as a page rule\n * — that would make a stale client value outrank the server's fresh read.\n */\nexport function buildContextWire(rows: readonly ResolvedContextRow[]): Record<string, unknown> {\n const wire: Record<string, unknown> = {};\n for (const row of rows) {\n if (!row.include) continue;\n const rich = isEnvelope(row.value);\n const envelope: Record<string, unknown> = rich\n ? { ...(row.value as Record<string, unknown>) }\n : { content: row.value };\n delete envelope.max_inline_chars;\n if (envelope.type === undefined && row.type !== undefined) envelope.type = row.type;\n if (envelope.label === undefined) envelope.label = row.label;\n if (envelope.description === undefined && row.description !== undefined) {\n envelope.description = row.description;\n }\n const pageLimit = row.layers?.surface?.max_inline_chars;\n const ownPageLimit = rich ? (row.value as Record<string, unknown>).max_inline_chars : undefined;\n if (isValidLimit(pageLimit)) envelope.max_inline_chars = pageLimit;\n else if (isValidLimit(ownPageLimit)) envelope.max_inline_chars = ownPageLimit;\n wire[row.key] = envelope;\n }\n return wire;\n}\n\n// ── compareReceipt — RULES.md §6 ────────────────────────────────────────────\n\nexport interface ContextReceiptMismatch {\n key: string;\n field: \"include\" | \"max_inline_chars\" | \"delivery\" | \"user_rule\" | \"missing\";\n expected: unknown;\n actual: unknown;\n}\n\nfunction normalizeUserRule(rule: SavedContextRule | null | undefined): SavedContextRule | null {\n const clean = sanitizeRule(rule);\n return Object.keys(clean).length === 0 ? null : clean;\n}\n\nfunction sameRule(a: SavedContextRule | null, b: SavedContextRule | null): boolean {\n if (a === null || b === null) return a === b;\n return a.include === b.include && a.max_inline_chars === b.max_inline_chars;\n}\n\n/**\n * Compare the rows the client resolved (and rendered, and sent) with the\n * receipt the server emitted for the same turn.\n *\n * For each expected row, the receipt row is matched by key + surface key, then\n * by key alone. Compared: `include`, `max_inline_chars`, `delivery` (only when\n * both sides know the size) and `user_rule`. An expected row absent from the\n * receipt is a `missing` mismatch — except a row the client correctly withheld\n * for a reason other than the person's own rule (page `auto_context` off, the\n * agent's kill switch): the client never sent it and the server lists only\n * the person's saved `include: false` rules as `origin: \"rule\"` rows, so its\n * absence is the truth, not a lie. Receipt rows nobody expected are returned\n * as `systemRows` — shown, never hidden.\n */\nexport function compareReceipt(\n expected: readonly ResolvedContextRow[],\n receipt: ContextReceipt,\n): { mismatches: ContextReceiptMismatch[]; systemRows: ContextReceiptRow[] } {\n const mismatches: ContextReceiptMismatch[] = [];\n const used = new Set<ContextReceiptRow>();\n const rows = receipt.rows ?? [];\n\n for (const exp of expected) {\n const actual =\n rows.find((r) => !used.has(r) && r.key === exp.key && r.surface_key === exp.surfaceKey) ??\n rows.find((r) => !used.has(r) && r.key === exp.key);\n if (!actual) {\n const withheldByOthers = !exp.include && exp.decided_by.include !== \"you\";\n if (!withheldByOthers) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: null });\n }\n continue;\n }\n used.add(actual);\n if (actual.include !== exp.include) {\n mismatches.push({ key: exp.key, field: \"include\", expected: exp.include, actual: actual.include });\n }\n if (actual.max_inline_chars !== exp.max_inline_chars) {\n mismatches.push({\n key: exp.key,\n field: \"max_inline_chars\",\n expected: exp.max_inline_chars,\n actual: actual.max_inline_chars,\n });\n }\n if (exp.chars !== null && actual.chars !== null && actual.delivery !== exp.delivery) {\n mismatches.push({ key: exp.key, field: \"delivery\", expected: exp.delivery, actual: actual.delivery });\n }\n const expRule = normalizeUserRule(exp.userRule);\n const actRule = normalizeUserRule(actual.user_rule);\n if (!sameRule(expRule, actRule)) {\n mismatches.push({ key: exp.key, field: \"user_rule\", expected: expRule, actual: actRule });\n }\n }\n\n return { mismatches, systemRows: rows.filter((r) => !used.has(r)) };\n}\n\n// ── Display ─────────────────────────────────────────────────────────────────\n\n/** Compact size for a tabular column: `950`, `18.4k`, `184k`, `1.2M`, `—` for unknown. */\nexport function formatChars(n: number | null | undefined): string {\n if (n === null || n === undefined || !Number.isFinite(n)) return \"—\";\n const abs = Math.abs(n);\n const trim = (s: string) => s.replace(/\\.0$/, \"\");\n if (abs < 1000) return String(Math.round(n));\n if (abs < 100_000) return `${trim((n / 1000).toFixed(1))}k`;\n if (abs < 1_000_000) return `${Math.round(n / 1000)}k`;\n return `${trim((n / 1_000_000).toFixed(1))}M`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../context/index.ts"],"sourcesContent":["/**\n * @ai-matrx/agents/context — the context value rules, headless.\n *\n * Contract: common-docs/systems/scopes-context/context-delivery/RULES.md.\n * Parity corpus: ./rules-corpus.json (run here by rules.test.ts and by aidream's\n * pytest against the Python twin `resolve_context_rule`). Every function here\n * is pure, zero-React, and safe to import on a server.\n *\n * The one flow a client runs per turn:\n * rows = values.map(v => resolveContextRow(v, savedRows, cap)) // what the table shows\n * context = buildContextWire(rows) // what the request sends\n * …on `context_receipt`: compareReceipt(rows, receipt) // screen vs server\n */\n\n// ── Constants ───────────────────────────────────────────────────────────────\n\n/** `users.user_surface_state.feature` for the person's saved rules. */\nexport const CONTEXT_RULES_FEATURE = \"context_rules\";\n/** Surface key for values no surface emitted (attachments, server-added values). */\nexport const DEFAULT_SURFACE_KEY = \"_default\";\n/** The server's default inline threshold when no layer sets one. */\nexport const DEFAULT_INLINE_MAX_CHARS = 200;\n/** The default ceiling every resolved limit is clamped to (a server knob). */\nexport const DEFAULT_INLINE_CAP = 50000;\n/** A lazy document's server default (RULES.md §2: \"a small lazy document 6000\"). */\nexport const LAZY_SOURCE_DEFAULT_MAX = 6000;\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\n/** Which layer decided a field — lowest to highest. */\nexport type ContextLayer = \"default\" | \"page\" | \"agent\" | \"you\";\nexport type ContextDelivery = \"inline\" | \"on_request\" | \"off\";\n\n/** One saved per-person rule (one key on one surface). Both fields optional. */\nexport interface SavedContextRule {\n include?: boolean;\n max_inline_chars?: number;\n}\n\n/** `{ [surface_key]: { [context key]: SavedContextRule } }` — the person's rows. */\nexport type SavedContextRuleRows = Record<string, Record<string, SavedContextRule>>;\n\n/** The agent layer for one key (RULES.md §2). */\nexport interface ContextAgentLayer {\n /** The agent's Context Policy declares this key (or the value is the agent's own seed). */\n declared?: boolean;\n /** The policy's ceiling; only read when `declared`. */\n max_inline_chars?: number | null;\n /** `auto_context_disabled` — closes every undeclared key. */\n kill_switch?: boolean;\n}\n\n/** The page layer for one key: the surface that emitted the value. */\nexport interface ContextSurfaceLayer {\n /** The surface declares this value (a `ui_surface_value` row / manifest entry). */\n declared?: boolean;\n /** `false` drops the value unless the agent declares it. */\n auto_context?: boolean | null;\n /** `inlineUpTo` / envelope `max_inline_chars` / DB column. */\n max_inline_chars?: number | null;\n}\n\n/** The non-person layers for one value — what the table's layer breakdown shows. */\nexport interface ContextRuleLayers {\n /** The server's per-key default limit; `null`/absent = 200. */\n default_max?: number | null;\n agent?: ContextAgentLayer | null;\n surface?: ContextSurfaceLayer | null;\n}\n\n/**\n * Input to `resolveContextRule`. Snake case on purpose: it is the corpus shape\n * and the Python twin's shape. `user` is typed loosely because invalid saved\n * values (non-boolean include, non-integer or negative limit) must be ignored,\n * not trusted.\n */\nexport interface ContextRuleInput extends ContextRuleLayers {\n /** Size in code points; `null` = an unresolved lazy source. */\n chars: number | null;\n user?: { include?: unknown; max_inline_chars?: unknown } | null;\n /** Ceiling; default `DEFAULT_INLINE_CAP`. */\n cap?: number;\n}\n\nexport interface ContextRuleResult {\n include: boolean;\n /** Effective limit, after the clamp. */\n max_inline_chars: number;\n delivery: ContextDelivery;\n decided_by: { include: ContextLayer; max_inline_chars: ContextLayer };\n clamped: boolean;\n}\n\n/** One receipt row — RULES.md §5, wire fields exactly. */\nexport interface ContextReceiptRow {\n key: string;\n label: string;\n surface_key: string;\n origin: \"client\" | \"server\" | \"surroundings\" | \"rule\";\n chars: number | null;\n include: boolean;\n max_inline_chars: number;\n delivery: ContextDelivery;\n decided_by: { include: ContextLayer; max_inline_chars: ContextLayer };\n user_rule: { include?: boolean; max_inline_chars?: number } | null;\n clamped: boolean;\n /** The CLIENT itself sent a value the person turned off. */\n client_sent_excluded: boolean;\n /** `\"model\"`: nothing reached a model that reads no context. */\n blocked_by: \"model\" | null;\n}\n\n/** The per-turn receipt the server streams as `context_receipt` — RULES.md §5. */\nexport interface ContextReceipt {\n version: 1;\n surface: string | null;\n cap: number;\n /** False: this turn's model cannot read context at all (every row `blocked_by: \"model\"`). */\n model_reads_context: boolean;\n /** The person's saved rules could not be read; the turn ran without them. */\n rules_error: string | null;\n rows: ContextReceiptRow[];\n}\n\nexport type ContextRowOrigin = \"page\" | \"attached\" | \"system\";\n\n/** A client's resolved row: what its table renders and what `buildContextWire` sends. */\nexport interface ResolvedContextRow extends ContextRuleResult {\n key: string;\n label: string;\n surfaceKey: string;\n origin: ContextRowOrigin;\n /** The raw value, or an already-rich envelope (`{ content, type, … }`). */\n value: unknown;\n chars: number | null;\n /** The saved rule that was applied (merged `_default` + surface row), or null. */\n userRule: SavedContextRule | null;\n description?: string;\n /** Context object type for the wire envelope (`text`, `json`, …). */\n type?: string;\n /** The non-person layers the rule was resolved from. Absent = default only. */\n layers?: ContextRuleLayers;\n}\n\n/** What a client knows about one value before resolution. */\nexport interface ContextRowSource {\n key: string;\n label: string;\n surfaceKey?: string;\n origin: ContextRowOrigin;\n value: unknown;\n /** Override the measured size (e.g. `null` for an unresolved lazy source). */\n chars?: number | null;\n description?: string;\n type?: string;\n layers?: ContextRuleLayers;\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst isNum = (v: unknown): v is number => typeof v === \"number\" && Number.isFinite(v);\nconst isValidLimit = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isInteger(v) && v >= 0;\n\n// ── resolveContextRule — RULES.md §2, line for line ─────────────────────────\n\nexport function resolveContextRule(input: ContextRuleInput): ContextRuleResult {\n const agent = input.agent ?? null;\n const surface = input.surface ?? null;\n const user = input.user ?? null;\n const cap = isNum(input.cap) ? input.cap : DEFAULT_INLINE_CAP;\n const agentDeclared = agent?.declared === true;\n\n let include = true;\n let includeBy: ContextLayer = \"default\";\n if (surface?.declared === true && surface.auto_context === false) {\n if (agentDeclared) {\n include = true;\n includeBy = \"agent\";\n } else {\n include = false;\n includeBy = \"page\";\n }\n }\n if (agent?.kill_switch === true && !agentDeclared) {\n include = false;\n includeBy = \"agent\";\n }\n if (user && typeof user.include === \"boolean\") {\n include = user.include;\n includeBy = \"you\";\n }\n\n let limit = isNum(input.default_max) ? input.default_max : DEFAULT_INLINE_MAX_CHARS;\n let limitBy: ContextLayer = \"default\";\n const a = agentDeclared && isNum(agent?.max_inline_chars) ? agent.max_inline_chars : null;\n const s = isNum(surface?.max_inline_chars) ? surface.max_inline_chars : null;\n if (a !== null && s !== null) {\n if (s < a) {\n limit = s;\n limitBy = \"page\";\n } else {\n limit = a;\n limitBy = \"agent\";\n }\n } else if (a !== null) {\n limit = a;\n limitBy = \"agent\";\n } else if (s !== null) {\n limit = s;\n limitBy = \"page\";\n }\n if (user && isValidLimit(user.max_inline_chars)) {\n limit = user.max_inline_chars;\n limitBy = \"you\";\n }\n const clamped = limit > cap;\n limit = Math.min(limit, cap);\n\n const chars = input.chars;\n const delivery: ContextDelivery = !include\n ? \"off\"\n : chars === null || chars === undefined || limit === 0 || chars > limit\n ? \"on_request\"\n : \"inline\";\n\n return {\n include,\n max_inline_chars: limit,\n delivery,\n decided_by: { include: includeBy, max_inline_chars: limitBy },\n clamped,\n };\n}\n\n// ── contextValueChars — RULES.md §1 ─────────────────────────────────────────\n\n/** Count Unicode code points (never UTF-16 units: `\"😀\".length` is 2). */\nfunction codePoints(s: string): number {\n let n = 0;\n for (const _ of s) n += 1;\n return n;\n}\n\n/**\n * Size of a value in code points, exactly as the server's `content_as_str()`:\n * a string is itself; anything else is `JSON.stringify(value, null, 2)`\n * (Python `json.dumps(value, ensure_ascii=False, indent=2)`). `undefined` → 0.\n */\nexport function contextValueChars(value: unknown): number {\n if (value === undefined) return 0;\n if (typeof value === \"string\") return codePoints(value);\n let text: string | undefined;\n try {\n text = JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n return text === undefined ? 0 : codePoints(text);\n}\n\n// ── lookupSavedRule — RULES.md §3 ───────────────────────────────────────────\n\nfunction sanitizeRule(raw: unknown): SavedContextRule {\n const out: SavedContextRule = {};\n if (!raw || typeof raw !== \"object\") return out;\n const r = raw as Record<string, unknown>;\n if (typeof r.include === \"boolean\") out.include = r.include;\n if (isValidLimit(r.max_inline_chars)) out.max_inline_chars = r.max_inline_chars;\n return out;\n}\n\n/**\n * The person's rule for one key on one surface: `_default[key]` merged per\n * field under `rows[surfaceKey][key]`, invalid fields stripped. `null` when\n * nothing valid is saved.\n */\nexport function lookupSavedRule(\n rows: SavedContextRuleRows | null | undefined,\n surfaceKey: string,\n key: string,\n): SavedContextRule | null {\n if (!rows || typeof rows !== \"object\") return null;\n const base = sanitizeRule(rows[DEFAULT_SURFACE_KEY]?.[key]);\n const own = surfaceKey === DEFAULT_SURFACE_KEY ? {} : sanitizeRule(rows[surfaceKey]?.[key]);\n const merged: SavedContextRule = { ...base, ...own };\n return Object.keys(merged).length === 0 ? null : merged;\n}\n\n// ── serverDefaultMax — the server's per-key default, read from the wire value ─\n\n/** The keys that make a dict the rich envelope form (aidream `_RICH_FORM_KEYS`). */\nconst RICH_FORM_KEYS = new Set([\n \"content\",\n \"mutable\",\n \"persist\",\n \"source\",\n \"type\",\n \"label\",\n \"description\",\n \"max_inline_chars\",\n \"default_max_inline_chars\",\n \"summary_agent_id\",\n]);\n\nconst UUID_RE = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === \"object\" && !Array.isArray(v);\n}\n\n/** A canonical resource reference the server promotes (aidream `parse_resource_reference`). */\nfunction isResourceRef(v: Record<string, unknown>): boolean {\n if (v.__kind !== \"resource_ref\") return false;\n const type = v.resource_type || v.type;\n const id = v.resource_id || v.id;\n return (\n typeof type === \"string\" &&\n type.trim() !== \"\" &&\n typeof id === \"string\" &&\n UUID_RE.test(id.trim())\n );\n}\n\n/**\n * The server's default inline limit for one wire value — the `default` layer\n * the server will use, before page/agent/you. Mirrors aidream\n * `context_rules._server_default_max` over the object `ContextManifest.build`\n * makes from the value (after `normalize_resource_context`):\n *\n * - a rich envelope (every key in the rich-form set, and `content` present or a\n * lazy `source` with a string `kind`) carrying a whole-number, non-negative\n * `default_max_inline_chars` → that;\n * - a top-level resource reference (`__kind: \"resource_ref\"` with a type and a\n * UUID id), or a lazy source whose `extra.resource_type` is set → 0\n * (descriptor-first);\n * - a lazy source (a `source` and null/absent `content`) → 6000;\n * - anything else → null (the server's 200).\n *\n * A resource reference nested as an envelope's `content` is NOT promoted by the\n * server (it reads top-level values only), so it is plain JSON content → null.\n */\nexport function serverDefaultMax(value: unknown): number | null {\n if (!isPlainObject(value)) return null;\n if (isResourceRef(value)) return 0;\n if (!Object.keys(value).every((k) => RICH_FORM_KEYS.has(k))) return null;\n const src = value.source;\n const validSource = isPlainObject(src) && typeof src.kind === \"string\";\n const hasContent = Object.prototype.hasOwnProperty.call(value, \"content\");\n if (!hasContent && !validSource) return null;\n if (isValidLimit(value.default_max_inline_chars)) return value.default_max_inline_chars;\n if (validSource && (value.content === null || value.content === undefined)) {\n const extra = isPlainObject(src.extra) ? src.extra : {};\n return extra.resource_type ? 0 : LAZY_SOURCE_DEFAULT_MAX;\n }\n return null;\n}\n\n// ── resolveContextRow — the glue a client runs per value ────────────────────\n\n/** Resolve one value into the row the table renders and the wire sends. */\nexport function resolveContextRow(\n source: ContextRowSource,\n savedRows: SavedContextRuleRows | null | undefined,\n cap: number = DEFAULT_INLINE_CAP,\n): ResolvedContextRow {\n const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;\n const chars =\n source.chars !== undefined\n ? source.chars\n : isLazySource(source.value)\n ? null\n : contextValueChars(envelopeContent(source.value));\n const userRule = lookupSavedRule(savedRows, surfaceKey, source.key);\n const layers: ContextRuleLayers = { ...(source.layers ?? {}) };\n if (layers.default_max === undefined || layers.default_max === null) {\n const serverDefault = serverDefaultMax(source.value);\n if (serverDefault !== null) layers.default_max = serverDefault;\n }\n const result = resolveContextRule({ ...layers, chars, user: userRule, cap });\n const row: ResolvedContextRow = {\n ...result,\n key: source.key,\n label: source.label,\n surfaceKey,\n origin: source.origin,\n value: source.value,\n chars,\n userRule,\n };\n if (source.description !== undefined) row.description = source.description;\n if (source.type !== undefined) row.type = source.type;\n if (source.layers !== undefined || layers.default_max !== undefined) row.layers = layers;\n return row;\n}\n\n// ── buildContextWire — the ONLY producer of a request's `context` ──────────\n\nfunction isEnvelope(value: unknown): value is Record<string, unknown> {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n Object.prototype.hasOwnProperty.call(value, \"content\")\n );\n}\n\n/** A source-only pointer (body deliberately absent) or a resource reference: size unknown. */\nfunction isLazySource(value: unknown): boolean {\n if (!isPlainObject(value)) return false;\n if (isResourceRef(value)) return true;\n const src = value.source;\n return (\n isPlainObject(src) &&\n typeof src.kind === \"string\" &&\n (value.content === null || value.content === undefined)\n );\n}\n\nfunction envelopeContent(value: unknown): unknown {\n return isEnvelope(value) ? value.content : value;\n}\n\n/**\n * Build the request's `context` object from the resolved rows. This is the\n * ONLY function that may produce it, so what the table shows and what the\n * request carries cannot drift apart.\n *\n * - A row whose `include` is false is omitted entirely — it never reaches the\n * wire (RULES.md §1: off means off everywhere).\n * - An included row becomes `{ content, type?, label, description?,\n * max_inline_chars? }`. An already-rich envelope (a value with its own\n * `content`, e.g. a working document carrying `source`/`mutable`/`persist`)\n * keeps every non-rule field it already has; `label`/`description`/`type`\n * are only filled when missing.\n * - `max_inline_chars` on the envelope is the PAGE layer's value only\n * (`layers.surface.max_inline_chars`, else the envelope's own page value).\n * The server reads the person's saved rules itself every turn (RULES.md §3)\n * and the agent layer from its own policy, so the wire must never carry the\n * person's number or the resolved effective limit dressed up as a page rule\n * — that would make a stale client value outrank the server's fresh read.\n */\nexport function buildContextWire(rows: readonly ResolvedContextRow[]): Record<string, unknown> {\n const wire: Record<string, unknown> = {};\n for (const row of rows) {\n if (!row.include) continue;\n // Rich: an envelope with `content`, or a source-only lazy pointer. Every\n // non-rule field survives — including `default_max_inline_chars`, the\n // server's per-key default (not a person's or a page's number).\n const rich = isEnvelope(row.value) || (isLazySource(row.value) && !isResourceRef(row.value as Record<string, unknown>));\n const envelope: Record<string, unknown> = rich\n ? { ...(row.value as Record<string, unknown>) }\n : { content: row.value };\n delete envelope.max_inline_chars;\n if (envelope.type === undefined && row.type !== undefined) envelope.type = row.type;\n if (envelope.label === undefined) envelope.label = row.label;\n if (envelope.description === undefined && row.description !== undefined) {\n envelope.description = row.description;\n }\n const pageLimit = row.layers?.surface?.max_inline_chars;\n const ownPageLimit = rich ? (row.value as Record<string, unknown>).max_inline_chars : undefined;\n if (isValidLimit(pageLimit)) envelope.max_inline_chars = pageLimit;\n else if (isValidLimit(ownPageLimit)) envelope.max_inline_chars = ownPageLimit;\n wire[row.key] = envelope;\n }\n return wire;\n}\n\n// ── compareReceipt — RULES.md §6 ────────────────────────────────────────────\n\nexport interface ContextReceiptMismatch {\n key: string;\n field: \"include\" | \"max_inline_chars\" | \"delivery\" | \"user_rule\" | \"missing\";\n expected: unknown;\n actual: unknown;\n}\n\nfunction normalizeUserRule(rule: SavedContextRule | null | undefined): SavedContextRule | null {\n const clean = sanitizeRule(rule);\n return Object.keys(clean).length === 0 ? null : clean;\n}\n\nfunction sameRule(a: SavedContextRule | null, b: SavedContextRule | null): boolean {\n if (a === null || b === null) return a === b;\n return a.include === b.include && a.max_inline_chars === b.max_inline_chars;\n}\n\n/**\n * Compare the rows the client resolved (and rendered, and sent) with the\n * receipt the server emitted for the same turn.\n *\n * For each expected row, the receipt row is matched by key + surface key, then\n * by key alone. Compared: `include`, `max_inline_chars`, `delivery` (only when\n * both sides know the size) and `user_rule`. An expected row absent from the\n * receipt is a `missing` mismatch — except a row the client correctly withheld\n * for a reason other than the person's own rule (page `auto_context` off, the\n * agent's kill switch): the client never sent it and the server lists only\n * the person's saved `include: false` rules as `origin: \"rule\"` rows, so its\n * absence is the truth, not a lie. Receipt rows nobody expected are returned\n * as `systemRows` — shown, never hidden.\n */\nexport function compareReceipt(\n expected: readonly ResolvedContextRow[],\n receipt: ContextReceipt,\n): { mismatches: ContextReceiptMismatch[]; systemRows: ContextReceiptRow[]; modelReadsContext: boolean } {\n const mismatches: ContextReceiptMismatch[] = [];\n // A turn whose model reads no context delivers nothing by design: every row\n // is `delivery: \"off\"`, `blocked_by: \"model\"`. That is not a lie about the\n // person's rules, so delivery is not compared; include / limit / rule still are.\n const modelReadsContext = receipt.model_reads_context !== false;\n const used = new Set<ContextReceiptRow>();\n const rows = receipt.rows ?? [];\n\n for (const exp of expected) {\n const actual =\n rows.find((r) => !used.has(r) && r.key === exp.key && r.surface_key === exp.surfaceKey) ??\n rows.find((r) => !used.has(r) && r.key === exp.key);\n if (!actual) {\n const withheldByOthers = !exp.include && exp.decided_by.include !== \"you\";\n if (!withheldByOthers) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: null });\n }\n continue;\n }\n used.add(actual);\n if (actual.include !== exp.include) {\n mismatches.push({ key: exp.key, field: \"include\", expected: exp.include, actual: actual.include });\n }\n if (actual.max_inline_chars !== exp.max_inline_chars) {\n mismatches.push({\n key: exp.key,\n field: \"max_inline_chars\",\n expected: exp.max_inline_chars,\n actual: actual.max_inline_chars,\n });\n }\n if (\n modelReadsContext &&\n exp.chars !== null &&\n actual.chars !== null &&\n actual.delivery !== exp.delivery\n ) {\n mismatches.push({ key: exp.key, field: \"delivery\", expected: exp.delivery, actual: actual.delivery });\n }\n const expRule = normalizeUserRule(exp.userRule);\n const actRule = normalizeUserRule(actual.user_rule);\n if (!sameRule(expRule, actRule)) {\n mismatches.push({ key: exp.key, field: \"user_rule\", expected: expRule, actual: actRule });\n }\n }\n\n return { mismatches, systemRows: rows.filter((r) => !used.has(r)), modelReadsContext };\n}\n\n// ── receiptRowToResolved — read-only rows for a sent message ────────────────\n\n/**\n * Turn one receipt row into the row a read-only table renders (a sent\n * message's context, read back from `chat.message.model_context`). The value\n * itself is not in the receipt, so `value` is undefined.\n */\nexport function receiptRowToResolved(row: ContextReceiptRow): ResolvedContextRow {\n const origin: ContextRowOrigin =\n row.surface_key !== DEFAULT_SURFACE_KEY ? \"page\" : row.origin === \"client\" ? \"attached\" : \"system\";\n return {\n key: row.key,\n label: row.label,\n surfaceKey: row.surface_key,\n origin,\n value: undefined,\n chars: row.chars,\n userRule: normalizeUserRule(row.user_rule),\n include: row.include,\n max_inline_chars: row.max_inline_chars,\n delivery: row.delivery,\n decided_by: { include: row.decided_by.include, max_inline_chars: row.decided_by.max_inline_chars },\n clamped: row.clamped,\n };\n}\n\n// ── Display ─────────────────────────────────────────────────────────────────\n\n/** Compact size for a tabular column: `950`, `18.4k`, `184k`, `1.2M`, `—` for unknown. */\nexport function formatChars(n: number | null | undefined): string {\n if (n === null || n === undefined || !Number.isFinite(n)) return \"—\";\n const abs = Math.abs(n);\n const trim = (s: string) => s.replace(/\\.0$/, \"\");\n if (abs < 1000) return String(Math.round(n));\n if (abs < 100_000) return `${trim((n / 1000).toFixed(1))}k`;\n if (abs < 1_000_000) return `${Math.round(n / 1000)}k`;\n return `${trim((n / 1_000_000).toFixed(1))}M`;\n}\n"],"mappings":"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","names":[]}
|
package/dist/context/index.d.cts
CHANGED
|
@@ -19,6 +19,8 @@ declare const DEFAULT_SURFACE_KEY = "_default";
|
|
|
19
19
|
declare const DEFAULT_INLINE_MAX_CHARS = 200;
|
|
20
20
|
/** The default ceiling every resolved limit is clamped to (a server knob). */
|
|
21
21
|
declare const DEFAULT_INLINE_CAP = 50000;
|
|
22
|
+
/** A lazy document's server default (RULES.md §2: "a small lazy document 6000"). */
|
|
23
|
+
declare const LAZY_SOURCE_DEFAULT_MAX = 6000;
|
|
22
24
|
/** Which layer decided a field — lowest to highest. */
|
|
23
25
|
type ContextLayer = "default" | "page" | "agent" | "you";
|
|
24
26
|
type ContextDelivery = "inline" | "on_request" | "off";
|
|
@@ -160,6 +162,25 @@ declare function contextValueChars(value: unknown): number;
|
|
|
160
162
|
* nothing valid is saved.
|
|
161
163
|
*/
|
|
162
164
|
declare function lookupSavedRule(rows: SavedContextRuleRows | null | undefined, surfaceKey: string, key: string): SavedContextRule | null;
|
|
165
|
+
/**
|
|
166
|
+
* The server's default inline limit for one wire value — the `default` layer
|
|
167
|
+
* the server will use, before page/agent/you. Mirrors aidream
|
|
168
|
+
* `context_rules._server_default_max` over the object `ContextManifest.build`
|
|
169
|
+
* makes from the value (after `normalize_resource_context`):
|
|
170
|
+
*
|
|
171
|
+
* - a rich envelope (every key in the rich-form set, and `content` present or a
|
|
172
|
+
* lazy `source` with a string `kind`) carrying a whole-number, non-negative
|
|
173
|
+
* `default_max_inline_chars` → that;
|
|
174
|
+
* - a top-level resource reference (`__kind: "resource_ref"` with a type and a
|
|
175
|
+
* UUID id), or a lazy source whose `extra.resource_type` is set → 0
|
|
176
|
+
* (descriptor-first);
|
|
177
|
+
* - a lazy source (a `source` and null/absent `content`) → 6000;
|
|
178
|
+
* - anything else → null (the server's 200).
|
|
179
|
+
*
|
|
180
|
+
* A resource reference nested as an envelope's `content` is NOT promoted by the
|
|
181
|
+
* server (it reads top-level values only), so it is plain JSON content → null.
|
|
182
|
+
*/
|
|
183
|
+
declare function serverDefaultMax(value: unknown): number | null;
|
|
163
184
|
/** Resolve one value into the row the table renders and the wire sends. */
|
|
164
185
|
declare function resolveContextRow(source: ContextRowSource, savedRows: SavedContextRuleRows | null | undefined, cap?: number): ResolvedContextRow;
|
|
165
186
|
/**
|
|
@@ -205,8 +226,15 @@ interface ContextReceiptMismatch {
|
|
|
205
226
|
declare function compareReceipt(expected: readonly ResolvedContextRow[], receipt: ContextReceipt): {
|
|
206
227
|
mismatches: ContextReceiptMismatch[];
|
|
207
228
|
systemRows: ContextReceiptRow[];
|
|
229
|
+
modelReadsContext: boolean;
|
|
208
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Turn one receipt row into the row a read-only table renders (a sent
|
|
233
|
+
* message's context, read back from `chat.message.model_context`). The value
|
|
234
|
+
* itself is not in the receipt, so `value` is undefined.
|
|
235
|
+
*/
|
|
236
|
+
declare function receiptRowToResolved(row: ContextReceiptRow): ResolvedContextRow;
|
|
209
237
|
/** Compact size for a tabular column: `950`, `18.4k`, `184k`, `1.2M`, `—` for unknown. */
|
|
210
238
|
declare function formatChars(n: number | null | undefined): string;
|
|
211
239
|
|
|
212
|
-
export { CONTEXT_RULES_FEATURE, type ContextAgentLayer, type ContextDelivery, type ContextLayer, type ContextReceipt, type ContextReceiptMismatch, type ContextReceiptRow, type ContextRowOrigin, type ContextRowSource, type ContextRuleInput, type ContextRuleLayers, type ContextRuleResult, type ContextSurfaceLayer, DEFAULT_INLINE_CAP, DEFAULT_INLINE_MAX_CHARS, DEFAULT_SURFACE_KEY, type ResolvedContextRow, type SavedContextRule, type SavedContextRuleRows, buildContextWire, compareReceipt, contextValueChars, formatChars, lookupSavedRule, resolveContextRow, resolveContextRule };
|
|
240
|
+
export { CONTEXT_RULES_FEATURE, type ContextAgentLayer, type ContextDelivery, type ContextLayer, type ContextReceipt, type ContextReceiptMismatch, type ContextReceiptRow, type ContextRowOrigin, type ContextRowSource, type ContextRuleInput, type ContextRuleLayers, type ContextRuleResult, type ContextSurfaceLayer, DEFAULT_INLINE_CAP, DEFAULT_INLINE_MAX_CHARS, DEFAULT_SURFACE_KEY, LAZY_SOURCE_DEFAULT_MAX, type ResolvedContextRow, type SavedContextRule, type SavedContextRuleRows, buildContextWire, compareReceipt, contextValueChars, formatChars, lookupSavedRule, receiptRowToResolved, resolveContextRow, resolveContextRule, serverDefaultMax };
|
package/dist/context/index.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ declare const DEFAULT_SURFACE_KEY = "_default";
|
|
|
19
19
|
declare const DEFAULT_INLINE_MAX_CHARS = 200;
|
|
20
20
|
/** The default ceiling every resolved limit is clamped to (a server knob). */
|
|
21
21
|
declare const DEFAULT_INLINE_CAP = 50000;
|
|
22
|
+
/** A lazy document's server default (RULES.md §2: "a small lazy document 6000"). */
|
|
23
|
+
declare const LAZY_SOURCE_DEFAULT_MAX = 6000;
|
|
22
24
|
/** Which layer decided a field — lowest to highest. */
|
|
23
25
|
type ContextLayer = "default" | "page" | "agent" | "you";
|
|
24
26
|
type ContextDelivery = "inline" | "on_request" | "off";
|
|
@@ -160,6 +162,25 @@ declare function contextValueChars(value: unknown): number;
|
|
|
160
162
|
* nothing valid is saved.
|
|
161
163
|
*/
|
|
162
164
|
declare function lookupSavedRule(rows: SavedContextRuleRows | null | undefined, surfaceKey: string, key: string): SavedContextRule | null;
|
|
165
|
+
/**
|
|
166
|
+
* The server's default inline limit for one wire value — the `default` layer
|
|
167
|
+
* the server will use, before page/agent/you. Mirrors aidream
|
|
168
|
+
* `context_rules._server_default_max` over the object `ContextManifest.build`
|
|
169
|
+
* makes from the value (after `normalize_resource_context`):
|
|
170
|
+
*
|
|
171
|
+
* - a rich envelope (every key in the rich-form set, and `content` present or a
|
|
172
|
+
* lazy `source` with a string `kind`) carrying a whole-number, non-negative
|
|
173
|
+
* `default_max_inline_chars` → that;
|
|
174
|
+
* - a top-level resource reference (`__kind: "resource_ref"` with a type and a
|
|
175
|
+
* UUID id), or a lazy source whose `extra.resource_type` is set → 0
|
|
176
|
+
* (descriptor-first);
|
|
177
|
+
* - a lazy source (a `source` and null/absent `content`) → 6000;
|
|
178
|
+
* - anything else → null (the server's 200).
|
|
179
|
+
*
|
|
180
|
+
* A resource reference nested as an envelope's `content` is NOT promoted by the
|
|
181
|
+
* server (it reads top-level values only), so it is plain JSON content → null.
|
|
182
|
+
*/
|
|
183
|
+
declare function serverDefaultMax(value: unknown): number | null;
|
|
163
184
|
/** Resolve one value into the row the table renders and the wire sends. */
|
|
164
185
|
declare function resolveContextRow(source: ContextRowSource, savedRows: SavedContextRuleRows | null | undefined, cap?: number): ResolvedContextRow;
|
|
165
186
|
/**
|
|
@@ -205,8 +226,15 @@ interface ContextReceiptMismatch {
|
|
|
205
226
|
declare function compareReceipt(expected: readonly ResolvedContextRow[], receipt: ContextReceipt): {
|
|
206
227
|
mismatches: ContextReceiptMismatch[];
|
|
207
228
|
systemRows: ContextReceiptRow[];
|
|
229
|
+
modelReadsContext: boolean;
|
|
208
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Turn one receipt row into the row a read-only table renders (a sent
|
|
233
|
+
* message's context, read back from `chat.message.model_context`). The value
|
|
234
|
+
* itself is not in the receipt, so `value` is undefined.
|
|
235
|
+
*/
|
|
236
|
+
declare function receiptRowToResolved(row: ContextReceiptRow): ResolvedContextRow;
|
|
209
237
|
/** Compact size for a tabular column: `950`, `18.4k`, `184k`, `1.2M`, `—` for unknown. */
|
|
210
238
|
declare function formatChars(n: number | null | undefined): string;
|
|
211
239
|
|
|
212
|
-
export { CONTEXT_RULES_FEATURE, type ContextAgentLayer, type ContextDelivery, type ContextLayer, type ContextReceipt, type ContextReceiptMismatch, type ContextReceiptRow, type ContextRowOrigin, type ContextRowSource, type ContextRuleInput, type ContextRuleLayers, type ContextRuleResult, type ContextSurfaceLayer, DEFAULT_INLINE_CAP, DEFAULT_INLINE_MAX_CHARS, DEFAULT_SURFACE_KEY, type ResolvedContextRow, type SavedContextRule, type SavedContextRuleRows, buildContextWire, compareReceipt, contextValueChars, formatChars, lookupSavedRule, resolveContextRow, resolveContextRule };
|
|
240
|
+
export { CONTEXT_RULES_FEATURE, type ContextAgentLayer, type ContextDelivery, type ContextLayer, type ContextReceipt, type ContextReceiptMismatch, type ContextReceiptRow, type ContextRowOrigin, type ContextRowSource, type ContextRuleInput, type ContextRuleLayers, type ContextRuleResult, type ContextSurfaceLayer, DEFAULT_INLINE_CAP, DEFAULT_INLINE_MAX_CHARS, DEFAULT_SURFACE_KEY, LAZY_SOURCE_DEFAULT_MAX, type ResolvedContextRow, type SavedContextRule, type SavedContextRuleRows, buildContextWire, compareReceipt, contextValueChars, formatChars, lookupSavedRule, receiptRowToResolved, resolveContextRow, resolveContextRule, serverDefaultMax };
|
package/dist/context/index.js
CHANGED
|
@@ -3,6 +3,7 @@ var CONTEXT_RULES_FEATURE = "context_rules";
|
|
|
3
3
|
var DEFAULT_SURFACE_KEY = "_default";
|
|
4
4
|
var DEFAULT_INLINE_MAX_CHARS = 200;
|
|
5
5
|
var DEFAULT_INLINE_CAP = 5e4;
|
|
6
|
+
var LAZY_SOURCE_DEFAULT_MAX = 6e3;
|
|
6
7
|
var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
7
8
|
var isValidLimit = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
|
|
8
9
|
function resolveContextRule(input) {
|
|
@@ -96,11 +97,53 @@ function lookupSavedRule(rows, surfaceKey, key) {
|
|
|
96
97
|
const merged = { ...base, ...own };
|
|
97
98
|
return Object.keys(merged).length === 0 ? null : merged;
|
|
98
99
|
}
|
|
100
|
+
var RICH_FORM_KEYS = /* @__PURE__ */ new Set([
|
|
101
|
+
"content",
|
|
102
|
+
"mutable",
|
|
103
|
+
"persist",
|
|
104
|
+
"source",
|
|
105
|
+
"type",
|
|
106
|
+
"label",
|
|
107
|
+
"description",
|
|
108
|
+
"max_inline_chars",
|
|
109
|
+
"default_max_inline_chars",
|
|
110
|
+
"summary_agent_id"
|
|
111
|
+
]);
|
|
112
|
+
var UUID_RE = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
|
|
113
|
+
function isPlainObject(v) {
|
|
114
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
115
|
+
}
|
|
116
|
+
function isResourceRef(v) {
|
|
117
|
+
if (v.__kind !== "resource_ref") return false;
|
|
118
|
+
const type = v.resource_type || v.type;
|
|
119
|
+
const id = v.resource_id || v.id;
|
|
120
|
+
return typeof type === "string" && type.trim() !== "" && typeof id === "string" && UUID_RE.test(id.trim());
|
|
121
|
+
}
|
|
122
|
+
function serverDefaultMax(value) {
|
|
123
|
+
if (!isPlainObject(value)) return null;
|
|
124
|
+
if (isResourceRef(value)) return 0;
|
|
125
|
+
if (!Object.keys(value).every((k) => RICH_FORM_KEYS.has(k))) return null;
|
|
126
|
+
const src = value.source;
|
|
127
|
+
const validSource = isPlainObject(src) && typeof src.kind === "string";
|
|
128
|
+
const hasContent = Object.prototype.hasOwnProperty.call(value, "content");
|
|
129
|
+
if (!hasContent && !validSource) return null;
|
|
130
|
+
if (isValidLimit(value.default_max_inline_chars)) return value.default_max_inline_chars;
|
|
131
|
+
if (validSource && (value.content === null || value.content === void 0)) {
|
|
132
|
+
const extra = isPlainObject(src.extra) ? src.extra : {};
|
|
133
|
+
return extra.resource_type ? 0 : LAZY_SOURCE_DEFAULT_MAX;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
99
137
|
function resolveContextRow(source, savedRows, cap = DEFAULT_INLINE_CAP) {
|
|
100
138
|
const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;
|
|
101
|
-
const chars = source.chars !== void 0 ? source.chars : contextValueChars(envelopeContent(source.value));
|
|
139
|
+
const chars = source.chars !== void 0 ? source.chars : isLazySource(source.value) ? null : contextValueChars(envelopeContent(source.value));
|
|
102
140
|
const userRule = lookupSavedRule(savedRows, surfaceKey, source.key);
|
|
103
|
-
const
|
|
141
|
+
const layers = { ...source.layers ?? {} };
|
|
142
|
+
if (layers.default_max === void 0 || layers.default_max === null) {
|
|
143
|
+
const serverDefault = serverDefaultMax(source.value);
|
|
144
|
+
if (serverDefault !== null) layers.default_max = serverDefault;
|
|
145
|
+
}
|
|
146
|
+
const result = resolveContextRule({ ...layers, chars, user: userRule, cap });
|
|
104
147
|
const row = {
|
|
105
148
|
...result,
|
|
106
149
|
key: source.key,
|
|
@@ -113,12 +156,18 @@ function resolveContextRow(source, savedRows, cap = DEFAULT_INLINE_CAP) {
|
|
|
113
156
|
};
|
|
114
157
|
if (source.description !== void 0) row.description = source.description;
|
|
115
158
|
if (source.type !== void 0) row.type = source.type;
|
|
116
|
-
if (source.layers !== void 0) row.layers =
|
|
159
|
+
if (source.layers !== void 0 || layers.default_max !== void 0) row.layers = layers;
|
|
117
160
|
return row;
|
|
118
161
|
}
|
|
119
162
|
function isEnvelope(value) {
|
|
120
163
|
return !!value && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "content");
|
|
121
164
|
}
|
|
165
|
+
function isLazySource(value) {
|
|
166
|
+
if (!isPlainObject(value)) return false;
|
|
167
|
+
if (isResourceRef(value)) return true;
|
|
168
|
+
const src = value.source;
|
|
169
|
+
return isPlainObject(src) && typeof src.kind === "string" && (value.content === null || value.content === void 0);
|
|
170
|
+
}
|
|
122
171
|
function envelopeContent(value) {
|
|
123
172
|
return isEnvelope(value) ? value.content : value;
|
|
124
173
|
}
|
|
@@ -126,7 +175,7 @@ function buildContextWire(rows) {
|
|
|
126
175
|
const wire = {};
|
|
127
176
|
for (const row of rows) {
|
|
128
177
|
if (!row.include) continue;
|
|
129
|
-
const rich = isEnvelope(row.value);
|
|
178
|
+
const rich = isEnvelope(row.value) || isLazySource(row.value) && !isResourceRef(row.value);
|
|
130
179
|
const envelope = rich ? { ...row.value } : { content: row.value };
|
|
131
180
|
delete envelope.max_inline_chars;
|
|
132
181
|
if (envelope.type === void 0 && row.type !== void 0) envelope.type = row.type;
|
|
@@ -152,6 +201,7 @@ function sameRule(a, b) {
|
|
|
152
201
|
}
|
|
153
202
|
function compareReceipt(expected, receipt) {
|
|
154
203
|
const mismatches = [];
|
|
204
|
+
const modelReadsContext = receipt.model_reads_context !== false;
|
|
155
205
|
const used = /* @__PURE__ */ new Set();
|
|
156
206
|
const rows = receipt.rows ?? [];
|
|
157
207
|
for (const exp of expected) {
|
|
@@ -175,7 +225,7 @@ function compareReceipt(expected, receipt) {
|
|
|
175
225
|
actual: actual.max_inline_chars
|
|
176
226
|
});
|
|
177
227
|
}
|
|
178
|
-
if (exp.chars !== null && actual.chars !== null && actual.delivery !== exp.delivery) {
|
|
228
|
+
if (modelReadsContext && exp.chars !== null && actual.chars !== null && actual.delivery !== exp.delivery) {
|
|
179
229
|
mismatches.push({ key: exp.key, field: "delivery", expected: exp.delivery, actual: actual.delivery });
|
|
180
230
|
}
|
|
181
231
|
const expRule = normalizeUserRule(exp.userRule);
|
|
@@ -184,7 +234,24 @@ function compareReceipt(expected, receipt) {
|
|
|
184
234
|
mismatches.push({ key: exp.key, field: "user_rule", expected: expRule, actual: actRule });
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
|
-
return { mismatches, systemRows: rows.filter((r) => !used.has(r)) };
|
|
237
|
+
return { mismatches, systemRows: rows.filter((r) => !used.has(r)), modelReadsContext };
|
|
238
|
+
}
|
|
239
|
+
function receiptRowToResolved(row) {
|
|
240
|
+
const origin = row.surface_key !== DEFAULT_SURFACE_KEY ? "page" : row.origin === "client" ? "attached" : "system";
|
|
241
|
+
return {
|
|
242
|
+
key: row.key,
|
|
243
|
+
label: row.label,
|
|
244
|
+
surfaceKey: row.surface_key,
|
|
245
|
+
origin,
|
|
246
|
+
value: void 0,
|
|
247
|
+
chars: row.chars,
|
|
248
|
+
userRule: normalizeUserRule(row.user_rule),
|
|
249
|
+
include: row.include,
|
|
250
|
+
max_inline_chars: row.max_inline_chars,
|
|
251
|
+
delivery: row.delivery,
|
|
252
|
+
decided_by: { include: row.decided_by.include, max_inline_chars: row.decided_by.max_inline_chars },
|
|
253
|
+
clamped: row.clamped
|
|
254
|
+
};
|
|
188
255
|
}
|
|
189
256
|
function formatChars(n) {
|
|
190
257
|
if (n === null || n === void 0 || !Number.isFinite(n)) return "\u2014";
|
|
@@ -200,12 +267,15 @@ export {
|
|
|
200
267
|
DEFAULT_INLINE_CAP,
|
|
201
268
|
DEFAULT_INLINE_MAX_CHARS,
|
|
202
269
|
DEFAULT_SURFACE_KEY,
|
|
270
|
+
LAZY_SOURCE_DEFAULT_MAX,
|
|
203
271
|
buildContextWire,
|
|
204
272
|
compareReceipt,
|
|
205
273
|
contextValueChars,
|
|
206
274
|
formatChars,
|
|
207
275
|
lookupSavedRule,
|
|
276
|
+
receiptRowToResolved,
|
|
208
277
|
resolveContextRow,
|
|
209
|
-
resolveContextRule
|
|
278
|
+
resolveContextRule,
|
|
279
|
+
serverDefaultMax
|
|
210
280
|
};
|
|
211
281
|
//# sourceMappingURL=index.js.map
|