@ai-matrx/agents 0.25.2 → 0.25.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/content-transfer/index.cjs.map +1 -1
  3. package/dist/content-transfer/index.js.map +1 -1
  4. package/dist/content-transfer/react/index.cjs.map +1 -1
  5. package/dist/content-transfer/react/index.js.map +1 -1
  6. package/dist/context/index.cjs +12 -3
  7. package/dist/context/index.cjs.map +1 -1
  8. package/dist/context/index.d.cts +27 -1
  9. package/dist/context/index.d.ts +27 -1
  10. package/dist/context/index.js +12 -3
  11. package/dist/context/index.js.map +1 -1
  12. package/dist/context/react/index.cjs +76 -38
  13. package/dist/context/react/index.cjs.map +1 -1
  14. package/dist/context/react/index.d.cts +29 -1
  15. package/dist/context/react/index.d.ts +29 -1
  16. package/dist/context/react/index.js +76 -38
  17. package/dist/context/react/index.js.map +1 -1
  18. package/dist/index.cjs +26 -0
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +26 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/mandates/index.cjs +2 -2
  25. package/dist/mandates/index.cjs.map +1 -1
  26. package/dist/mandates/index.d.cts +4 -4
  27. package/dist/mandates/index.d.ts +4 -4
  28. package/dist/mandates/index.js +2 -2
  29. package/dist/mandates/index.js.map +1 -1
  30. package/dist/matrx/index.cjs +26 -0
  31. package/dist/matrx/index.cjs.map +1 -1
  32. package/dist/matrx/index.d.cts +17 -1
  33. package/dist/matrx/index.d.ts +17 -1
  34. package/dist/matrx/index.js +26 -0
  35. package/dist/matrx/index.js.map +1 -1
  36. package/mandates/snapshots/keys.0.25.3.json +652 -0
  37. package/mandates/snapshots/keys.0.25.4.json +652 -0
  38. package/package.json +3 -3
@@ -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/** 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 * A client row's delivery: the server's three, plus `\"server\"` — an included\n * value the server resolves itself (`isServerResolved`), whose delivery the\n * client does not claim. Never on the wire or in a receipt.\n */\nexport type ContextRowDelivery = ContextDelivery | \"server\";\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 /**\n * `\"model\"`: nothing reached a model that reads no context. `\"self_check\"`:\n * the server's post-render safety check stripped it (RULES.md §4/§5) —\n * the row stays, `delivery: \"off\"`.\n */\n blocked_by: ContextBlockedBy | null;\n /**\n * Set when the server did not keep the value under its own key — still a\n * row, never missing (RULES.md §5): `\"expanded\"` (a surroundings envelope\n * became one row per part), `\"directive\"` (a reserved directive the server\n * acted on), `\"renamed\"` (an earlier key spelling, held under the stable\n * key), `\"unaccounted\"` (a server path removed it with no reason — filed as\n * a defect). Absent from an older server.\n */\n consumed_as?: ContextConsumedAs | null;\n /** The keys (each with its own row) that now carry what this value held. */\n consumed_into?: string[];\n}\n\n/** How the server consumed a value it did not keep under its own key (receipt `consumed_as`). */\nexport type ContextConsumedAs = \"expanded\" | \"directive\" | \"renamed\" | \"unaccounted\";\n\n/**\n * The reserved keys the server never keeps under their own name, and what it\n * does with each — the receipt rows say so (`consumed_as`). The same table is\n * the parity corpus's `consumed_key_cases` (aidream `RESERVED_CONSUMED_KEYS`).\n */\nexport const RESERVED_CONSUMED_KEYS: Readonly<Record<string, ContextConsumedAs>> = {\n surface_chain: \"expanded\",\n window_forms: \"expanded\",\n __table_access: \"directive\",\n __google_files: \"directive\",\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/** Why the server kept a value from the model on this turn (receipt `blocked_by`). */\nexport type ContextBlockedBy = \"model\" | \"self_check\";\n\n/** A client's resolved row: what its table renders and what `buildContextWire` sends. */\nexport interface ResolvedContextRow extends Omit<ContextRuleResult, \"delivery\"> {\n delivery: ContextRowDelivery;\n /**\n * The server resolves this value (a resource reference, a `*_id` UUID): size\n * and delivery are unknown (`chars: null`, `delivery: \"server\"` when\n * included), and its default limit is not predicted. Include and the\n * person's own rule still resolve normally.\n */\n serverResolved?: boolean;\n /** Display-only: chars / limit / delivery were filled from a receipt (`applyReceiptToRows`). */\n fromReceipt?: boolean;\n /** From a receipt: why the server kept this value from the model this turn. */\n blockedBy?: ContextBlockedBy;\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/**\n * The person's rule for one key, drift-proof (identical to aidream's\n * `lookup_user_rule_for`). While a primary surface P is in play, every key\n * filed under `_default` OR under P resolves to the same per-field merge\n * `{ ..._default[key], ...rows[P][key] }` (P wins) — whichever surface the\n * client's or the server's manifest says \"declares\" the key. A code manifest\n * and the DB manifest can disagree (sync drift); this makes the person's rule\n * land the same on both sides anyway. A surroundings key (any other surface)\n * keeps its own surface: `{ ..._default[key], ...rows[surfaceKey][key] }`.\n * With no primary surface this is exactly `lookupSavedRule`.\n *\n * Saving is unchanged: page values save to P, everything else to `_default`.\n */\nexport function lookupSavedRuleFor(\n rows: SavedContextRuleRows | null | undefined,\n primarySurface: string | null | undefined,\n surfaceKey: string,\n key: string,\n): SavedContextRule | null {\n const primary = primarySurface && primarySurface !== DEFAULT_SURFACE_KEY ? primarySurface : null;\n if (primary && (surfaceKey === DEFAULT_SURFACE_KEY || surfaceKey === primary)) {\n return lookupSavedRule(rows, primary, key);\n }\n return lookupSavedRule(rows, surfaceKey, key);\n}\n\n// ── serverDefaultMax — the server's per-key default, read from the wire value ─\n\n/**\n * The keys that make a dict the rich envelope form — exactly aidream\n * `context_objects._RICH_FORM_KEYS`. A dict with ANY other key is raw content\n * on the server, `content` key or not.\n */\nexport const RICH_ENVELOPE_KEYS: ReadonlySet<string> = 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\n/**\n * A UUID as the server's `_uuid` (Python `UUID(value.strip())`) accepts it:\n * hyphens optional, `{…}` braces and `urn:uuid:` prefixes allowed.\n */\nfunction isUuidLike(value: string): boolean {\n const hex = value\n .trim()\n .replace(/urn:/gi, \"\")\n .replace(/uuid:/gi, \"\")\n .replace(/^\\{+|\\}+$/g, \"\")\n .replace(/-/g, \"\");\n return /^[0-9a-f]{32}$/i.test(hex);\n}\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 isUuidLike(id)\n );\n}\n\nfunction hasOwn(o: Record<string, unknown>, k: string): boolean {\n return Object.prototype.hasOwnProperty.call(o, k);\n}\n\nfunction onlyRichKeys(o: Record<string, unknown>): boolean {\n return Object.keys(o).every((k) => RICH_ENVELOPE_KEYS.has(k));\n}\n\nfunction hasLazySource(o: Record<string, unknown>): boolean {\n return isPlainObject(o.source) && typeof o.source.kind === \"string\";\n}\n\n/**\n * A rich envelope exactly as the server reads one (aidream\n * `_split_rich_context_value`): a plain object with its own `content` whose\n * every key is in `RICH_ENVELOPE_KEYS`. A record that merely HAS `content`\n * (a note `{ id, title, content }`) is raw content, not an envelope.\n */\nexport function isRichEnvelope(value: unknown): value is Record<string, unknown> {\n return isPlainObject(value) && hasOwn(value, \"content\") && onlyRichKeys(value);\n}\n\n/** The source-only lazy pointer form: rich keys only, no `content`, a `source` with a `kind`. */\nfunction isLazyPointer(value: unknown): value is Record<string, unknown> {\n return isPlainObject(value) && !hasOwn(value, \"content\") && onlyRichKeys(value) && hasLazySource(value);\n}\n\n/**\n * Split one wire value into `{ content, overrides }` exactly as aidream\n * `_split_rich_context_value`: a rich envelope or lazy pointer → its `content`\n * (null when absent) and every other key; anything else → the whole value as\n * content, no overrides.\n */\nexport function splitContextValue(value: unknown): {\n content: unknown;\n overrides: Record<string, unknown>;\n} {\n if (isRichEnvelope(value) || isLazyPointer(value)) {\n const { content = null, ...overrides } = value;\n return { content, overrides };\n }\n return { content: value, overrides: {} };\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 (!isRichEnvelope(value) && !isLazyPointer(value)) return null;\n if (isValidLimit(value.default_max_inline_chars)) return value.default_max_inline_chars;\n if (hasLazySource(value) && (value.content === null || value.content === undefined)) {\n const src = value.source as Record<string, unknown>;\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 /** The page in play; makes the rule lookup drift-proof (`lookupSavedRuleFor`). */\n primarySurface?: string | null,\n): ResolvedContextRow {\n const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;\n const serverResolved = isServerResolved(source.key, source.value);\n const chars = serverResolved\n ? null\n : source.chars !== undefined\n ? source.chars\n : unresolvedSize(source.value)\n ? null\n : contextValueChars(splitContextValue(source.value).content);\n const userRule = lookupSavedRuleFor(savedRows, primarySurface, surfaceKey, source.key);\n const layers: ContextRuleLayers = { ...(source.layers ?? {}) };\n if (!serverResolved && (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 delivery: serverResolved && result.include ? \"server\" : result.delivery,\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 if (serverResolved) row.serverResolved = true;\n return row;\n}\n\n// ── buildContextWire — the ONLY producer of a request's `context` ──────────\n\n/**\n * Size the server cannot know yet: a lazy pointer (or an envelope with a lazy\n * source and null content), or a resource reference it will promote to one.\n */\nfunction unresolvedSize(value: unknown): boolean {\n if (!isPlainObject(value)) return false;\n if (isResourceRef(value)) return true;\n return (\n (isRichEnvelope(value) || isLazyPointer(value)) &&\n hasLazySource(value) &&\n (value.content === null || value.content === undefined)\n );\n}\n\nconst KIND_RESOURCE_REF = \"resource_ref\";\n\n/**\n * Values the server acts on by their top-level shape in `request.context`\n * (aidream `normalize_resource_context` → `parse_resource_reference`, which\n * reads ONLY top-level values):\n *\n * - a resource reference — any object whose `__kind` is `\"resource_ref\"`\n * (its own promote/exclude/overlay policy rides inside it);\n * - a bare UUID string under a conventional `*_id` key (`file_id`,\n * `attached_file_id`, `current_note_id`, …), which the server promotes when\n * the noun is a registered resource type. The registry lives on the server,\n * so every `*_id` key with a UUID passes through; for an unregistered noun\n * the server reads the bare string exactly as it would read it wrapped;\n * - a reserved key the server consumes by shape (`RESERVED_CONSUMED_KEYS`:\n * `surface_chain`, `window_forms`, `__table_access`, `__google_files`) —\n * wrapped, the server reads `{content, label}` instead of its list or grant\n * and drops it.\n *\n * These ship exactly as-is: no label, type, description or limit added.\n */\nexport function isWireDirective(key: string, value: unknown): boolean {\n if (Object.hasOwn(RESERVED_CONSUMED_KEYS, key)) return true;\n if (isPlainObject(value)) return value.__kind === KIND_RESOURCE_REF;\n return (\n typeof value === \"string\" &&\n isIdKey(key) &&\n isUuidLike(value)\n );\n}\n\n/** The server's key rule for promoting a bare id (`_resource_type_for_key`): a `*_id` key. */\nfunction isIdKey(key: string): boolean {\n return key.trim().toLowerCase().endsWith(\"_id\");\n}\n\n/**\n * A value whose size, default limit and delivery the SERVER decides by\n * resolving it — so the client must not predict them:\n *\n * - a top-level resource reference (`__kind: \"resource_ref\"`), bare or as a\n * rich envelope's `content`;\n * - a UUID string under a `*_id` key, bare or as a rich envelope's `content`.\n *\n * The server only promotes an id when its noun is in the resource registry\n * (aidream `_resource_type_for_key`), which the client cannot see, so every\n * `*_id` UUID counts. This is a deliberate superset: on a value the server\n * does not promote, the client only stops predicting — it never makes a wrong\n * promise.\n */\nexport function isServerResolved(key: string, value: unknown): boolean {\n const inner = isRichEnvelope(value) ? value.content : value;\n if (isPlainObject(inner)) return inner.__kind === KIND_RESOURCE_REF;\n return typeof inner === \"string\" && isIdKey(key) && isUuidLike(inner);\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 (`isRichEnvelope`, e.g. a\n * working document carrying `source`/`mutable`/`persist`, or a lazy pointer)\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 // A row with no value (a receipt-built display row) has nothing to send.\n if (row.value === undefined) 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 // A record that merely has `content` is NOT rich: it is wrapped whole, or\n // the server would read it as raw content and drop the envelope fields.\n // A top-level directive the server acts on by SHAPE ships exactly as-is:\n // wrapped, it becomes plain JSON and silently stops resolving.\n if (isWireDirective(row.key, row.value)) {\n wire[row.key] = row.value;\n continue;\n }\n const rich = isRichEnvelope(row.value) || isLazyPointer(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 * `\"self_check\"`: a real difference from what was shown — the server's\n * safety check stripped the value before the model call. Say \"removed by\n * the server's safety check\", not \"the screen was wrong\".\n */\n reason?: \"self_check\";\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. A row the server consumed\n * (`consumed_as`: expanded / directive / renamed) is compared on `include`\n * and `user_rule` only — its parts carry their own rows; an `\"unaccounted\"`\n * row, or an included envelope `\"expanded\"` into no parts, is a `missing`\n * mismatch.\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 // A consumed value (expanded / directive / renamed) was not delivered as\n // itself: its parts carry their own rows, limits and deliveries. Only\n // include and the person's rule are this key's own promise.\n const consumed = Boolean(actual.consumed_as);\n // A server path removed the value with no reason: kept as a row so it is\n // never silently missing, and still a broken promise on this screen.\n if (actual.consumed_as === \"unaccounted\" && exp.include) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: \"unaccounted\" });\n }\n // An envelope the screen showed as sent that the server expanded into no\n // parts delivered nothing (a window whose fields were all unreadable):\n // \"expanded\" alone would pass it silently.\n if (\n actual.consumed_as === \"expanded\" &&\n actual.include &&\n exp.include &&\n (actual.consumed_into ?? []).length === 0\n ) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: \"expanded_into_nothing\" });\n }\n // A server-resolved value's limit is only the client's to promise when the\n // person set one; its default-derived limit and delivery are the server's.\n const promisesLimit =\n !consumed && (!exp.serverResolved || exp.userRule?.max_inline_chars !== undefined);\n if (promisesLimit && 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 // The safety check stripped a value the person included: always a\n // mismatch (server-resolved or not), tagged with its reason.\n const selfChecked = actual.blocked_by === \"self_check\" && exp.include;\n if (selfChecked) {\n mismatches.push({\n key: exp.key,\n field: \"delivery\",\n expected: exp.delivery,\n actual: actual.delivery,\n reason: \"self_check\",\n });\n }\n if (\n !selfChecked &&\n !consumed &&\n modelReadsContext &&\n !exp.serverResolved &&\n exp.delivery !== \"server\" &&\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/**\n * For DISPLAY only: fill each server-resolved row's `chars`,\n * `max_inline_chars` and `delivery` from the latest receipt row for the same\n * key (key + surface key, then key), marking it `fromReceipt`. Other rows are\n * returned unchanged. Never compare these rows — `compareReceipt` takes the\n * rows the client resolved before sending.\n */\nexport function applyReceiptToRows(\n rows: readonly ResolvedContextRow[],\n receipt: ContextReceipt | null | undefined,\n): ResolvedContextRow[] {\n const receiptRows = receipt?.rows ?? [];\n return rows.map((row) => {\n if (!row.serverResolved) return row;\n const hit =\n receiptRows.find((r) => r.key === row.key && r.surface_key === row.surfaceKey) ??\n receiptRows.find((r) => r.key === row.key);\n if (!hit) return row;\n return {\n ...row,\n chars: hit.chars,\n max_inline_chars: hit.max_inline_chars,\n delivery: hit.delivery,\n fromReceipt: true,\n ...(hit.blocked_by ? { blockedBy: hit.blocked_by } : {}),\n };\n });\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 layers: receiptLayers(row),\n ...(row.blocked_by ? { blockedBy: row.blocked_by } : {}),\n };\n}\n\n/**\n * Rebuild the non-person layers from what the server applied, so a read-only\n * table shows the applied limit as the inherited value and marks the layer\n * that decided it. The limit goes on the layer named by\n * `decided_by.max_inline_chars` (`you` is carried by `userRule`); include is\n * rebuilt the same way for a page `auto_context` off or the agent's kill switch.\n */\n/**\n * Keys the client has a value for but is NOT sending this turn (excluded by\n * the page, the agent or the person's rule). Clients send this list as the\n * request's `context_withheld`, so the receipt's `origin: \"rule\"` rows are\n * limited to keys actually withheld on this page. Deduplicated, in row order.\n */\nexport function withheldKeys(rows: readonly ResolvedContextRow[]): string[] {\n const out: string[] = [];\n for (const row of rows) {\n if (row.include || row.value === undefined || out.includes(row.key)) continue;\n out.push(row.key);\n }\n return out;\n}\n\nconst DOCUMENT_KEY_PREFIXES = [\"attached_document_\", \"attached_file_\", \"resource_file_\"];\n\n/**\n * Receipt rows the client never sent (`compareReceipt(...).systemRows`:\n * server-seeded attachments, scope seeds, saved off-rules…) as table rows, so\n * a person can see and govern them — the normal Include switch and Inline max\n * apply. Documents (`attached_document_*`, `attached_file_*`,\n * `resource_file_*`) group as \"attached\", everything else as \"system\". The\n * server resolved them (`serverResolved`), values come from the receipt\n * (`fromReceipt`), and `surfaceKey` is the row's own `surface_key` — so a rule\n * saved for one writes to `_default` unless the server filed it under a page.\n * `value` is undefined: `buildContextWire` never sends these rows.\n */\nexport function systemRowsToResolved(systemRows: readonly ContextReceiptRow[]): ResolvedContextRow[] {\n return systemRows.map((receiptRow) => {\n const row = receiptRowToResolved(receiptRow);\n const isDocument = DOCUMENT_KEY_PREFIXES.some((p) => receiptRow.key.startsWith(p));\n return {\n ...row,\n origin: isDocument ? \"attached\" : \"system\",\n serverResolved: true,\n fromReceipt: true,\n };\n });\n}\n\nfunction receiptLayers(row: ContextReceiptRow): ContextRuleLayers {\n const layers: ContextRuleLayers = {};\n const limit = row.max_inline_chars;\n const limitBy = row.decided_by.max_inline_chars;\n const includeBy = row.decided_by.include;\n if (limitBy === \"default\") layers.default_max = limit;\n // Page auto_context off: the page dropped it, or the agent re-admitted it.\n const pageOff = (includeBy === \"page\" && !row.include) || (includeBy === \"agent\" && row.include);\n if (limitBy === \"page\" || pageOff) {\n layers.surface = {\n declared: true,\n auto_context: !pageOff,\n max_inline_chars: limitBy === \"page\" ? limit : null,\n };\n }\n if (limitBy === \"agent\") {\n // An agent limit means the key is declared; a declared key the agent decided to include\n // was re-admitted past a page auto_context off. (The kill switch never closes a declared key.)\n layers.agent = { declared: true, max_inline_chars: limit };\n } else if (includeBy === \"agent\") {\n // Off = the kill switch closing an undeclared key; on = a declaration re-admitting a\n // page auto_context-off value.\n layers.agent = row.include ? { declared: true } : { declared: false, kill_switch: true };\n }\n return layers;\n}\n\n// ── attachmentContextKey — the context key of a conversation attachment ─────\n\n/**\n * The context key the server files a conversation attachment under, derivable\n * by every client from `GET /conversations/{id}/attachments` (aidream\n * `conversation_attachments/briefing.py`; corpus `attachment_key_cases`):\n *\n * `attached_resource_` + encode(`${provider}:${resourceRef}`)\n *\n * where encode keeps ASCII letters, digits and `-`, and writes every other\n * UTF-8 byte as `_` + two lowercase hex digits (`_` itself is `_5f`). Unique\n * across providers, case-preserving (Drive ids), stable.\n */\nexport function attachmentContextKey(provider: string, resourceRef: string): string {\n const bytes = new TextEncoder().encode(`${provider}:${resourceRef}`);\n let out = \"attached_resource_\";\n for (const b of bytes) {\n const keep =\n (b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x5a) || (b >= 0x61 && b <= 0x7a) || b === 0x2d;\n out += keep ? String.fromCharCode(b) : `_${b.toString(16).padStart(2, \"0\")}`;\n }\n return out;\n}\n\n// ── humanizeContextKey — the one key-to-words rule (client and server) ──────\n\n/** Words always written in capitals when they are a whole word of a key. */\nexport const CONTEXT_KEY_ACRONYMS: readonly string[] = [\n \"ID\", \"URL\", \"PDF\", \"HTML\", \"JSON\", \"API\", \"AI\", \"UI\", \"CSV\", \"SMS\", \"SEO\", \"LLM\", \"MCP\", \"UUID\", \"ISO\",\n];\nconst ACRONYM_SET = new Set(CONTEXT_KEY_ACRONYMS.map((a) => a.toLowerCase()));\n\n/**\n * A context key in words — the label of a value no page or agent named. The\n * rule (aidream `humanize_context_key` runs the same corpus `humanize_cases`):\n *\n * 1. split on runs of `_`, `-` and whitespace; drop empty parts;\n * 2. a part that is, case-insensitively, one of `CONTEXT_KEY_ACRONYMS` is\n * upper-cased; any other part gets its first character upper-cased and the\n * rest lower-cased (acronyms are whole parts only: `url2pdf` → `Url2pdf`);\n * 3. join with single spaces.\n *\n * `note_id` → `Note ID`, `current_note_published_to_web` → `Current Note Published To Web`.\n */\nexport function humanizeContextKey(key: string): string {\n return key\n .split(/[_\\-\\s]+/)\n .filter((part) => part.length > 0)\n .map((part) =>\n ACRONYM_SET.has(part.toLowerCase())\n ? part.toUpperCase()\n : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),\n )\n .join(\" \");\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":";AAiBO,IAAM,wBAAwB;AAE9B,IAAM,sBAAsB;AAE5B,IAAM,2BAA2B;AAEjC,IAAM,qBAAqB;AAE3B,IAAM,0BAA0B;AAoHhC,IAAM,yBAAsE;AAAA,EACjF,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,gBAAgB;AAClB;AAiEA,IAAM,QAAQ,CAAC,MAA4B,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC;AACrF,IAAM,eAAe,CAAC,MACpB,OAAO,MAAM,YAAY,OAAO,UAAU,CAAC,KAAK,KAAK;AAIhD,SAAS,mBAAmB,OAA4C;AAC7E,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,MAAM,MAAM;AAC3C,QAAM,gBAAgB,OAAO,aAAa;AAE1C,MAAI,UAAU;AACd,MAAI,YAA0B;AAC9B,MAAI,SAAS,aAAa,QAAQ,QAAQ,iBAAiB,OAAO;AAChE,QAAI,eAAe;AACjB,gBAAU;AACV,kBAAY;AAAA,IACd,OAAO;AACL,gBAAU;AACV,kBAAY;AAAA,IACd;AAAA,EACF;AACA,MAAI,OAAO,gBAAgB,QAAQ,CAAC,eAAe;AACjD,cAAU;AACV,gBAAY;AAAA,EACd;AACA,MAAI,QAAQ,OAAO,KAAK,YAAY,WAAW;AAC7C,cAAU,KAAK;AACf,gBAAY;AAAA,EACd;AAEA,MAAI,QAAQ,MAAM,MAAM,WAAW,IAAI,MAAM,cAAc;AAC3D,MAAI,UAAwB;AAC5B,QAAM,IAAI,iBAAiB,MAAM,OAAO,gBAAgB,IAAI,MAAM,mBAAmB;AACrF,QAAM,IAAI,MAAM,SAAS,gBAAgB,IAAI,QAAQ,mBAAmB;AACxE,MAAI,MAAM,QAAQ,MAAM,MAAM;AAC5B,QAAI,IAAI,GAAG;AACT,cAAQ;AACR,gBAAU;AAAA,IACZ,OAAO;AACL,cAAQ;AACR,gBAAU;AAAA,IACZ;AAAA,EACF,WAAW,MAAM,MAAM;AACrB,YAAQ;AACR,cAAU;AAAA,EACZ,WAAW,MAAM,MAAM;AACrB,YAAQ;AACR,cAAU;AAAA,EACZ;AACA,MAAI,QAAQ,aAAa,KAAK,gBAAgB,GAAG;AAC/C,YAAQ,KAAK;AACb,cAAU;AAAA,EACZ;AACA,QAAM,UAAU,QAAQ;AACxB,UAAQ,KAAK,IAAI,OAAO,GAAG;AAE3B,QAAM,QAAQ,MAAM;AACpB,QAAM,WAA4B,CAAC,UAC/B,QACA,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,QAAQ,QAC9D,eACA;AAEN,SAAO;AAAA,IACL;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA,YAAY,EAAE,SAAS,WAAW,kBAAkB,QAAQ;AAAA,IAC5D;AAAA,EACF;AACF;AAKA,SAAS,WAAW,GAAmB;AACrC,MAAI,IAAI;AACR,aAAW,KAAK,EAAG,MAAK;AACxB,SAAO;AACT;AAOO,SAAS,kBAAkB,OAAwB;AACxD,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO,WAAW,KAAK;AACtD,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,EACtC,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,SAAO,SAAS,SAAY,IAAI,WAAW,IAAI;AACjD;AAIA,SAAS,aAAa,KAAgC;AACpD,QAAM,MAAwB,CAAC;AAC/B,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,YAAY,UAAW,KAAI,UAAU,EAAE;AACpD,MAAI,aAAa,EAAE,gBAAgB,EAAG,KAAI,mBAAmB,EAAE;AAC/D,SAAO;AACT;AAOO,SAAS,gBACd,MACA,YACA,KACyB;AACzB,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,OAAO,aAAa,KAAK,mBAAmB,IAAI,GAAG,CAAC;AAC1D,QAAM,MAAM,eAAe,sBAAsB,CAAC,IAAI,aAAa,KAAK,UAAU,IAAI,GAAG,CAAC;AAC1F,QAAM,SAA2B,EAAE,GAAG,MAAM,GAAG,IAAI;AACnD,SAAO,OAAO,KAAK,MAAM,EAAE,WAAW,IAAI,OAAO;AACnD;AAeO,SAAS,mBACd,MACA,gBACA,YACA,KACyB;AACzB,QAAM,UAAU,kBAAkB,mBAAmB,sBAAsB,iBAAiB;AAC5F,MAAI,YAAY,eAAe,uBAAuB,eAAe,UAAU;AAC7E,WAAO,gBAAgB,MAAM,SAAS,GAAG;AAAA,EAC3C;AACA,SAAO,gBAAgB,MAAM,YAAY,GAAG;AAC9C;AASO,IAAM,qBAA0C,oBAAI,IAAI;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,SAAS,WAAW,OAAwB;AAC1C,QAAM,MAAM,MACT,KAAK,EACL,QAAQ,UAAU,EAAE,EACpB,QAAQ,WAAW,EAAE,EACrB,QAAQ,cAAc,EAAE,EACxB,QAAQ,MAAM,EAAE;AACnB,SAAO,kBAAkB,KAAK,GAAG;AACnC;AAEA,SAAS,cAAc,GAA0C;AAC/D,SAAO,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AACzD;AAGA,SAAS,cAAc,GAAqC;AAC1D,MAAI,EAAE,WAAW,eAAgB,QAAO;AACxC,QAAM,OAAO,EAAE,iBAAiB,EAAE;AAClC,QAAM,KAAK,EAAE,eAAe,EAAE;AAC9B,SACE,OAAO,SAAS,YAChB,KAAK,KAAK,MAAM,MAChB,OAAO,OAAO,YACd,WAAW,EAAE;AAEjB;AAEA,SAAS,OAAO,GAA4B,GAAoB;AAC9D,SAAO,OAAO,UAAU,eAAe,KAAK,GAAG,CAAC;AAClD;AAEA,SAAS,aAAa,GAAqC;AACzD,SAAO,OAAO,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,mBAAmB,IAAI,CAAC,CAAC;AAC9D;AAEA,SAAS,cAAc,GAAqC;AAC1D,SAAO,cAAc,EAAE,MAAM,KAAK,OAAO,EAAE,OAAO,SAAS;AAC7D;AAQO,SAAS,eAAe,OAAkD;AAC/E,SAAO,cAAc,KAAK,KAAK,OAAO,OAAO,SAAS,KAAK,aAAa,KAAK;AAC/E;AAGA,SAAS,cAAc,OAAkD;AACvE,SAAO,cAAc,KAAK,KAAK,CAAC,OAAO,OAAO,SAAS,KAAK,aAAa,KAAK,KAAK,cAAc,KAAK;AACxG;AAQO,SAAS,kBAAkB,OAGhC;AACA,MAAI,eAAe,KAAK,KAAK,cAAc,KAAK,GAAG;AACjD,UAAM,EAAE,UAAU,MAAM,GAAG,UAAU,IAAI;AACzC,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AACA,SAAO,EAAE,SAAS,OAAO,WAAW,CAAC,EAAE;AACzC;AAoBO,SAAS,iBAAiB,OAA+B;AAC9D,MAAI,CAAC,cAAc,KAAK,EAAG,QAAO;AAClC,MAAI,cAAc,KAAK,EAAG,QAAO;AACjC,MAAI,CAAC,eAAe,KAAK,KAAK,CAAC,cAAc,KAAK,EAAG,QAAO;AAC5D,MAAI,aAAa,MAAM,wBAAwB,EAAG,QAAO,MAAM;AAC/D,MAAI,cAAc,KAAK,MAAM,MAAM,YAAY,QAAQ,MAAM,YAAY,SAAY;AACnF,UAAM,MAAM,MAAM;AAClB,UAAM,QAAQ,cAAc,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC;AACtD,WAAO,MAAM,gBAAgB,IAAI;AAAA,EACnC;AACA,SAAO;AACT;AAKO,SAAS,kBACd,QACA,WACA,MAAc,oBAEd,gBACoB;AACpB,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,iBAAiB,iBAAiB,OAAO,KAAK,OAAO,KAAK;AAChE,QAAM,QAAQ,iBACV,OACA,OAAO,UAAU,SACf,OAAO,QACP,eAAe,OAAO,KAAK,IACzB,OACA,kBAAkB,kBAAkB,OAAO,KAAK,EAAE,OAAO;AACjE,QAAM,WAAW,mBAAmB,WAAW,gBAAgB,YAAY,OAAO,GAAG;AACrF,QAAM,SAA4B,EAAE,GAAI,OAAO,UAAU,CAAC,EAAG;AAC7D,MAAI,CAAC,mBAAmB,OAAO,gBAAgB,UAAa,OAAO,gBAAgB,OAAO;AACxF,UAAM,gBAAgB,iBAAiB,OAAO,KAAK;AACnD,QAAI,kBAAkB,KAAM,QAAO,cAAc;AAAA,EACnD;AACA,QAAM,SAAS,mBAAmB,EAAE,GAAG,QAAQ,OAAO,MAAM,UAAU,IAAI,CAAC;AAC3E,QAAM,MAA0B;AAAA,IAC9B,GAAG;AAAA,IACH,UAAU,kBAAkB,OAAO,UAAU,WAAW,OAAO;AAAA,IAC/D,KAAK,OAAO;AAAA,IACZ,OAAO,OAAO;AAAA,IACd;AAAA,IACA,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACA,MAAI,OAAO,gBAAgB,OAAW,KAAI,cAAc,OAAO;AAC/D,MAAI,OAAO,SAAS,OAAW,KAAI,OAAO,OAAO;AACjD,MAAI,OAAO,WAAW,UAAa,OAAO,gBAAgB,OAAW,KAAI,SAAS;AAClF,MAAI,eAAgB,KAAI,iBAAiB;AACzC,SAAO;AACT;AAQA,SAAS,eAAe,OAAyB;AAC/C,MAAI,CAAC,cAAc,KAAK,EAAG,QAAO;AAClC,MAAI,cAAc,KAAK,EAAG,QAAO;AACjC,UACG,eAAe,KAAK,KAAK,cAAc,KAAK,MAC7C,cAAc,KAAK,MAClB,MAAM,YAAY,QAAQ,MAAM,YAAY;AAEjD;AAEA,IAAM,oBAAoB;AAqBnB,SAAS,gBAAgB,KAAa,OAAyB;AACpE,MAAI,OAAO,OAAO,wBAAwB,GAAG,EAAG,QAAO;AACvD,MAAI,cAAc,KAAK,EAAG,QAAO,MAAM,WAAW;AAClD,SACE,OAAO,UAAU,YACjB,QAAQ,GAAG,KACX,WAAW,KAAK;AAEpB;AAGA,SAAS,QAAQ,KAAsB;AACrC,SAAO,IAAI,KAAK,EAAE,YAAY,EAAE,SAAS,KAAK;AAChD;AAgBO,SAAS,iBAAiB,KAAa,OAAyB;AACrE,QAAM,QAAQ,eAAe,KAAK,IAAI,MAAM,UAAU;AACtD,MAAI,cAAc,KAAK,EAAG,QAAO,MAAM,WAAW;AAClD,SAAO,OAAO,UAAU,YAAY,QAAQ,GAAG,KAAK,WAAW,KAAK;AACtE;AAqBO,SAAS,iBAAiB,MAA8D;AAC7F,QAAM,OAAgC,CAAC;AACvC,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,IAAI,QAAS;AAElB,QAAI,IAAI,UAAU,OAAW;AAQ7B,QAAI,gBAAgB,IAAI,KAAK,IAAI,KAAK,GAAG;AACvC,WAAK,IAAI,GAAG,IAAI,IAAI;AACpB;AAAA,IACF;AACA,UAAM,OAAO,eAAe,IAAI,KAAK,KAAK,cAAc,IAAI,KAAK;AACjE,UAAM,WAAoC,OACtC,EAAE,GAAI,IAAI,MAAkC,IAC5C,EAAE,SAAS,IAAI,MAAM;AACzB,WAAO,SAAS;AAChB,QAAI,SAAS,SAAS,UAAa,IAAI,SAAS,OAAW,UAAS,OAAO,IAAI;AAC/E,QAAI,SAAS,UAAU,OAAW,UAAS,QAAQ,IAAI;AACvD,QAAI,SAAS,gBAAgB,UAAa,IAAI,gBAAgB,QAAW;AACvE,eAAS,cAAc,IAAI;AAAA,IAC7B;AACA,UAAM,YAAY,IAAI,QAAQ,SAAS;AACvC,UAAM,eAAe,OAAQ,IAAI,MAAkC,mBAAmB;AACtF,QAAI,aAAa,SAAS,EAAG,UAAS,mBAAmB;AAAA,aAChD,aAAa,YAAY,EAAG,UAAS,mBAAmB;AACjE,SAAK,IAAI,GAAG,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAiBA,SAAS,kBAAkB,MAAoE;AAC7F,QAAM,QAAQ,aAAa,IAAI;AAC/B,SAAO,OAAO,KAAK,KAAK,EAAE,WAAW,IAAI,OAAO;AAClD;AAEA,SAAS,SAAS,GAA4B,GAAqC;AACjF,MAAI,MAAM,QAAQ,MAAM,KAAM,QAAO,MAAM;AAC3C,SAAO,EAAE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE;AAC7D;AAoBO,SAAS,eACd,UACA,SACuG;AACvG,QAAM,aAAuC,CAAC;AAI9C,QAAM,oBAAoB,QAAQ,wBAAwB;AAC1D,QAAM,OAAO,oBAAI,IAAuB;AACxC,QAAM,OAAO,QAAQ,QAAQ,CAAC;AAE9B,aAAW,OAAO,UAAU;AAC1B,UAAM,SACJ,KAAK,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,QAAQ,IAAI,OAAO,EAAE,gBAAgB,IAAI,UAAU,KACtF,KAAK,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,QAAQ,IAAI,GAAG;AACpD,QAAI,CAAC,QAAQ;AACX,YAAM,mBAAmB,CAAC,IAAI,WAAW,IAAI,WAAW,YAAY;AACpE,UAAI,CAAC,kBAAkB;AACrB,mBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,IAAI,UAAU,QAAQ,KAAK,CAAC;AAAA,MAC1F;AACA;AAAA,IACF;AACA,SAAK,IAAI,MAAM;AACf,QAAI,OAAO,YAAY,IAAI,SAAS;AAClC,iBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,IAAI,SAAS,QAAQ,OAAO,QAAQ,CAAC;AAAA,IACnG;AAIA,UAAM,WAAW,QAAQ,OAAO,WAAW;AAG3C,QAAI,OAAO,gBAAgB,iBAAiB,IAAI,SAAS;AACvD,iBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,IAAI,UAAU,QAAQ,cAAc,CAAC;AAAA,IACnG;AAIA,QACE,OAAO,gBAAgB,cACvB,OAAO,WACP,IAAI,YACH,OAAO,iBAAiB,CAAC,GAAG,WAAW,GACxC;AACA,iBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,IAAI,UAAU,QAAQ,wBAAwB,CAAC;AAAA,IAC7G;AAGA,UAAM,gBACJ,CAAC,aAAa,CAAC,IAAI,kBAAkB,IAAI,UAAU,qBAAqB;AAC1E,QAAI,iBAAiB,OAAO,qBAAqB,IAAI,kBAAkB;AACrE,iBAAW,KAAK;AAAA,QACd,KAAK,IAAI;AAAA,QACT,OAAO;AAAA,QACP,UAAU,IAAI;AAAA,QACd,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAGA,UAAM,cAAc,OAAO,eAAe,gBAAgB,IAAI;AAC9D,QAAI,aAAa;AACf,iBAAW,KAAK;AAAA,QACd,KAAK,IAAI;AAAA,QACT,OAAO;AAAA,QACP,UAAU,IAAI;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,QACE,CAAC,eACD,CAAC,YACD,qBACA,CAAC,IAAI,kBACL,IAAI,aAAa,YACjB,IAAI,UAAU,QACd,OAAO,UAAU,QACjB,OAAO,aAAa,IAAI,UACxB;AACA,iBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,YAAY,UAAU,IAAI,UAAU,QAAQ,OAAO,SAAS,CAAC;AAAA,IACtG;AACA,UAAM,UAAU,kBAAkB,IAAI,QAAQ;AAC9C,UAAM,UAAU,kBAAkB,OAAO,SAAS;AAClD,QAAI,CAAC,SAAS,SAAS,OAAO,GAAG;AAC/B,iBAAW,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO,aAAa,UAAU,SAAS,QAAQ,QAAQ,CAAC;AAAA,IAC1F;AAAA,EACF;AAEA,SAAO,EAAE,YAAY,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,kBAAkB;AACvF;AASO,SAAS,mBACd,MACA,SACsB;AACtB,QAAM,cAAc,SAAS,QAAQ,CAAC;AACtC,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,QAAI,CAAC,IAAI,eAAgB,QAAO;AAChC,UAAM,MACJ,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,OAAO,EAAE,gBAAgB,IAAI,UAAU,KAC7E,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,GAAG;AAC3C,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO,IAAI;AAAA,MACX,kBAAkB,IAAI;AAAA,MACtB,UAAU,IAAI;AAAA,MACd,aAAa;AAAA,MACb,GAAI,IAAI,aAAa,EAAE,WAAW,IAAI,WAAW,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AACH;AASO,SAAS,qBAAqB,KAA4C;AAC/E,QAAM,SACJ,IAAI,gBAAgB,sBAAsB,SAAS,IAAI,WAAW,WAAW,aAAa;AAC5F,SAAO;AAAA,IACL,KAAK,IAAI;AAAA,IACT,OAAO,IAAI;AAAA,IACX,YAAY,IAAI;AAAA,IAChB;AAAA,IACA,OAAO;AAAA,IACP,OAAO,IAAI;AAAA,IACX,UAAU,kBAAkB,IAAI,SAAS;AAAA,IACzC,SAAS,IAAI;AAAA,IACb,kBAAkB,IAAI;AAAA,IACtB,UAAU,IAAI;AAAA,IACd,YAAY,EAAE,SAAS,IAAI,WAAW,SAAS,kBAAkB,IAAI,WAAW,iBAAiB;AAAA,IACjG,SAAS,IAAI;AAAA,IACb,QAAQ,cAAc,GAAG;AAAA,IACzB,GAAI,IAAI,aAAa,EAAE,WAAW,IAAI,WAAW,IAAI,CAAC;AAAA,EACxD;AACF;AAeO,SAAS,aAAa,MAA+C;AAC1E,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,MAAM;AACtB,QAAI,IAAI,WAAW,IAAI,UAAU,UAAa,IAAI,SAAS,IAAI,GAAG,EAAG;AACrE,QAAI,KAAK,IAAI,GAAG;AAAA,EAClB;AACA,SAAO;AACT;AAEA,IAAM,wBAAwB,CAAC,sBAAsB,kBAAkB,gBAAgB;AAahF,SAAS,qBAAqB,YAAgE;AACnG,SAAO,WAAW,IAAI,CAAC,eAAe;AACpC,UAAM,MAAM,qBAAqB,UAAU;AAC3C,UAAM,aAAa,sBAAsB,KAAK,CAAC,MAAM,WAAW,IAAI,WAAW,CAAC,CAAC;AACjF,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,aAAa,aAAa;AAAA,MAClC,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAc,KAA2C;AAChE,QAAM,SAA4B,CAAC;AACnC,QAAM,QAAQ,IAAI;AAClB,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,YAAY,IAAI,WAAW;AACjC,MAAI,YAAY,UAAW,QAAO,cAAc;AAEhD,QAAM,UAAW,cAAc,UAAU,CAAC,IAAI,WAAa,cAAc,WAAW,IAAI;AACxF,MAAI,YAAY,UAAU,SAAS;AACjC,WAAO,UAAU;AAAA,MACf,UAAU;AAAA,MACV,cAAc,CAAC;AAAA,MACf,kBAAkB,YAAY,SAAS,QAAQ;AAAA,IACjD;AAAA,EACF;AACA,MAAI,YAAY,SAAS;AAGvB,WAAO,QAAQ,EAAE,UAAU,MAAM,kBAAkB,MAAM;AAAA,EAC3D,WAAW,cAAc,SAAS;AAGhC,WAAO,QAAQ,IAAI,UAAU,EAAE,UAAU,KAAK,IAAI,EAAE,UAAU,OAAO,aAAa,KAAK;AAAA,EACzF;AACA,SAAO;AACT;AAeO,SAAS,qBAAqB,UAAkB,aAA6B;AAClF,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,EAAE;AACnE,MAAI,MAAM;AACV,aAAW,KAAK,OAAO;AACrB,UAAM,OACH,KAAK,MAAQ,KAAK,MAAU,KAAK,MAAQ,KAAK,MAAU,KAAK,MAAQ,KAAK,OAAS,MAAM;AAC5F,WAAO,OAAO,OAAO,aAAa,CAAC,IAAI,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;AAKO,IAAM,uBAA0C;AAAA,EACrD;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AACpG;AACA,IAAM,cAAc,IAAI,IAAI,qBAAqB,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAcrE,SAAS,mBAAmB,KAAqB;AACtD,SAAO,IACJ,MAAM,UAAU,EAChB,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,EAChC;AAAA,IAAI,CAAC,SACJ,YAAY,IAAI,KAAK,YAAY,CAAC,IAC9B,KAAK,YAAY,IACjB,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY;AAAA,EAC/D,EACC,KAAK,GAAG;AACb;AAKO,SAAS,YAAY,GAAsC;AAChE,MAAI,MAAM,QAAQ,MAAM,UAAa,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AACjE,QAAM,MAAM,KAAK,IAAI,CAAC;AACtB,QAAM,OAAO,CAAC,MAAc,EAAE,QAAQ,QAAQ,EAAE;AAChD,MAAI,MAAM,IAAM,QAAO,OAAO,KAAK,MAAM,CAAC,CAAC;AAC3C,MAAI,MAAM,IAAS,QAAO,GAAG,MAAM,IAAI,KAAM,QAAQ,CAAC,CAAC,CAAC;AACxD,MAAI,MAAM,IAAW,QAAO,GAAG,KAAK,MAAM,IAAI,GAAI,CAAC;AACnD,SAAO,GAAG,MAAM,IAAI,KAAW,QAAQ,CAAC,CAAC,CAAC;AAC5C;","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 * A client row's delivery: the server's three, plus `\"server\"` — an included\n * value the server resolves itself (`isServerResolved`), whose delivery the\n * client does not claim. Never on the wire or in a receipt.\n */\nexport type ContextRowDelivery = ContextDelivery | \"server\";\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 /**\n * `\"model\"`: nothing reached a model that reads no context. `\"self_check\"`:\n * the server's post-render safety check stripped it (RULES.md §4/§5) —\n * the row stays, `delivery: \"off\"`.\n */\n blocked_by: ContextBlockedBy | null;\n /**\n * Set when the server did not keep the value under its own key — still a\n * row, never missing (RULES.md §5): `\"expanded\"` (a surroundings envelope\n * became one row per part), `\"directive\"` (a reserved directive the server\n * acted on), `\"renamed\"` (an earlier key spelling, held under the stable\n * key), `\"unaccounted\"` (a server path removed it with no reason — filed as\n * a defect). Absent from an older server.\n */\n consumed_as?: ContextConsumedAs | null;\n /** The keys (each with its own row) that now carry what this value held. */\n consumed_into?: string[];\n /**\n * What the model READ for this value, verbatim from the rendered context\n * block (its own element: the whole value inline, or its on-request entry).\n * Absent/null when it reached no block (off, consumed) or from an older server.\n */\n delivered?: ContextDeliveredText | null;\n /** On-request values the server holds as text: what the `context` tool returns. */\n on_request?: ContextDeliveredText | null;\n}\n\n/** Text the model actually read for one value (receipt `delivered` / `on_request`). */\nexport interface ContextDeliveredText {\n /** Cut at the receipt's cap; `chars` is the whole length. */\n text: string;\n chars: number;\n truncated: boolean;\n /** SHA-256 hex of the whole text (UTF-8). */\n sha256: string;\n}\n\n/** How the server consumed a value it did not keep under its own key (receipt `consumed_as`). */\nexport type ContextConsumedAs = \"expanded\" | \"directive\" | \"renamed\" | \"unaccounted\";\n\n/**\n * The reserved keys the server never keeps under their own name, and what it\n * does with each — the receipt rows say so (`consumed_as`). The same table is\n * the parity corpus's `consumed_key_cases` (aidream `RESERVED_CONSUMED_KEYS`).\n */\nexport const RESERVED_CONSUMED_KEYS: Readonly<Record<string, ContextConsumedAs>> = {\n surface_chain: \"expanded\",\n window_forms: \"expanded\",\n __table_access: \"directive\",\n __google_files: \"directive\",\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/** Why the server kept a value from the model on this turn (receipt `blocked_by`). */\nexport type ContextBlockedBy = \"model\" | \"self_check\";\n\n/** A client's resolved row: what its table renders and what `buildContextWire` sends. */\nexport interface ResolvedContextRow extends Omit<ContextRuleResult, \"delivery\"> {\n delivery: ContextRowDelivery;\n /**\n * The server resolves this value (a resource reference, a `*_id` UUID): size\n * and delivery are unknown (`chars: null`, `delivery: \"server\"` when\n * included), and its default limit is not predicted. Include and the\n * person's own rule still resolve normally.\n */\n serverResolved?: boolean;\n /** Display-only: chars / limit / delivery were filled from a receipt (`applyReceiptToRows`). */\n fromReceipt?: boolean;\n /** From a receipt: why the server kept this value from the model this turn. */\n blockedBy?: ContextBlockedBy;\n /**\n * Display-only, from a receipt: what the model read for this value. When set,\n * a detail view shows THIS — never `value`, the client's pre-send copy.\n */\n delivered?: ContextDeliveredText;\n /** Display-only, from a receipt: what the `context` tool returns for it. */\n onRequest?: ContextDeliveredText;\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/**\n * The person's rule for one key, drift-proof (identical to aidream's\n * `lookup_user_rule_for`). While a primary surface P is in play, every key\n * filed under `_default` OR under P resolves to the same per-field merge\n * `{ ..._default[key], ...rows[P][key] }` (P wins) — whichever surface the\n * client's or the server's manifest says \"declares\" the key. A code manifest\n * and the DB manifest can disagree (sync drift); this makes the person's rule\n * land the same on both sides anyway. A surroundings key (any other surface)\n * keeps its own surface: `{ ..._default[key], ...rows[surfaceKey][key] }`.\n * With no primary surface this is exactly `lookupSavedRule`.\n *\n * Saving is unchanged: page values save to P, everything else to `_default`.\n */\nexport function lookupSavedRuleFor(\n rows: SavedContextRuleRows | null | undefined,\n primarySurface: string | null | undefined,\n surfaceKey: string,\n key: string,\n): SavedContextRule | null {\n const primary = primarySurface && primarySurface !== DEFAULT_SURFACE_KEY ? primarySurface : null;\n if (primary && (surfaceKey === DEFAULT_SURFACE_KEY || surfaceKey === primary)) {\n return lookupSavedRule(rows, primary, key);\n }\n return lookupSavedRule(rows, surfaceKey, key);\n}\n\n// ── serverDefaultMax — the server's per-key default, read from the wire value ─\n\n/**\n * The keys that make a dict the rich envelope form — exactly aidream\n * `context_objects._RICH_FORM_KEYS`. A dict with ANY other key is raw content\n * on the server, `content` key or not.\n */\nexport const RICH_ENVELOPE_KEYS: ReadonlySet<string> = 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\n/**\n * A UUID as the server's `_uuid` (Python `UUID(value.strip())`) accepts it:\n * hyphens optional, `{…}` braces and `urn:uuid:` prefixes allowed.\n */\nfunction isUuidLike(value: string): boolean {\n const hex = value\n .trim()\n .replace(/urn:/gi, \"\")\n .replace(/uuid:/gi, \"\")\n .replace(/^\\{+|\\}+$/g, \"\")\n .replace(/-/g, \"\");\n return /^[0-9a-f]{32}$/i.test(hex);\n}\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 isUuidLike(id)\n );\n}\n\nfunction hasOwn(o: Record<string, unknown>, k: string): boolean {\n return Object.prototype.hasOwnProperty.call(o, k);\n}\n\nfunction onlyRichKeys(o: Record<string, unknown>): boolean {\n return Object.keys(o).every((k) => RICH_ENVELOPE_KEYS.has(k));\n}\n\nfunction hasLazySource(o: Record<string, unknown>): boolean {\n return isPlainObject(o.source) && typeof o.source.kind === \"string\";\n}\n\n/**\n * A rich envelope exactly as the server reads one (aidream\n * `_split_rich_context_value`): a plain object with its own `content` whose\n * every key is in `RICH_ENVELOPE_KEYS`. A record that merely HAS `content`\n * (a note `{ id, title, content }`) is raw content, not an envelope.\n */\nexport function isRichEnvelope(value: unknown): value is Record<string, unknown> {\n return isPlainObject(value) && hasOwn(value, \"content\") && onlyRichKeys(value);\n}\n\n/** The source-only lazy pointer form: rich keys only, no `content`, a `source` with a `kind`. */\nfunction isLazyPointer(value: unknown): value is Record<string, unknown> {\n return isPlainObject(value) && !hasOwn(value, \"content\") && onlyRichKeys(value) && hasLazySource(value);\n}\n\n/**\n * Split one wire value into `{ content, overrides }` exactly as aidream\n * `_split_rich_context_value`: a rich envelope or lazy pointer → its `content`\n * (null when absent) and every other key; anything else → the whole value as\n * content, no overrides.\n */\nexport function splitContextValue(value: unknown): {\n content: unknown;\n overrides: Record<string, unknown>;\n} {\n if (isRichEnvelope(value) || isLazyPointer(value)) {\n const { content = null, ...overrides } = value;\n return { content, overrides };\n }\n return { content: value, overrides: {} };\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 (!isRichEnvelope(value) && !isLazyPointer(value)) return null;\n if (isValidLimit(value.default_max_inline_chars)) return value.default_max_inline_chars;\n if (hasLazySource(value) && (value.content === null || value.content === undefined)) {\n const src = value.source as Record<string, unknown>;\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 /** The page in play; makes the rule lookup drift-proof (`lookupSavedRuleFor`). */\n primarySurface?: string | null,\n): ResolvedContextRow {\n const surfaceKey = source.surfaceKey ?? DEFAULT_SURFACE_KEY;\n const serverResolved = isServerResolved(source.key, source.value);\n const chars = serverResolved\n ? null\n : source.chars !== undefined\n ? source.chars\n : unresolvedSize(source.value)\n ? null\n : contextValueChars(splitContextValue(source.value).content);\n const userRule = lookupSavedRuleFor(savedRows, primarySurface, surfaceKey, source.key);\n const layers: ContextRuleLayers = { ...(source.layers ?? {}) };\n if (!serverResolved && (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 delivery: serverResolved && result.include ? \"server\" : result.delivery,\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 if (serverResolved) row.serverResolved = true;\n return row;\n}\n\n// ── buildContextWire — the ONLY producer of a request's `context` ──────────\n\n/**\n * Size the server cannot know yet: a lazy pointer (or an envelope with a lazy\n * source and null content), or a resource reference it will promote to one.\n */\nfunction unresolvedSize(value: unknown): boolean {\n if (!isPlainObject(value)) return false;\n if (isResourceRef(value)) return true;\n return (\n (isRichEnvelope(value) || isLazyPointer(value)) &&\n hasLazySource(value) &&\n (value.content === null || value.content === undefined)\n );\n}\n\nconst KIND_RESOURCE_REF = \"resource_ref\";\n\n/**\n * Values the server acts on by their top-level shape in `request.context`\n * (aidream `normalize_resource_context` → `parse_resource_reference`, which\n * reads ONLY top-level values):\n *\n * - a resource reference — any object whose `__kind` is `\"resource_ref\"`\n * (its own promote/exclude/overlay policy rides inside it);\n * - a bare UUID string under a conventional `*_id` key (`file_id`,\n * `attached_file_id`, `current_note_id`, …), which the server promotes when\n * the noun is a registered resource type. The registry lives on the server,\n * so every `*_id` key with a UUID passes through; for an unregistered noun\n * the server reads the bare string exactly as it would read it wrapped;\n * - a reserved key the server consumes by shape (`RESERVED_CONSUMED_KEYS`:\n * `surface_chain`, `window_forms`, `__table_access`, `__google_files`) —\n * wrapped, the server reads `{content, label}` instead of its list or grant\n * and drops it.\n *\n * These ship exactly as-is: no label, type, description or limit added.\n */\nexport function isWireDirective(key: string, value: unknown): boolean {\n if (Object.hasOwn(RESERVED_CONSUMED_KEYS, key)) return true;\n if (isPlainObject(value)) return value.__kind === KIND_RESOURCE_REF;\n return (\n typeof value === \"string\" &&\n isIdKey(key) &&\n isUuidLike(value)\n );\n}\n\n/** The server's key rule for promoting a bare id (`_resource_type_for_key`): a `*_id` key. */\nfunction isIdKey(key: string): boolean {\n return key.trim().toLowerCase().endsWith(\"_id\");\n}\n\n/**\n * A value whose size, default limit and delivery the SERVER decides by\n * resolving it — so the client must not predict them:\n *\n * - a top-level resource reference (`__kind: \"resource_ref\"`), bare or as a\n * rich envelope's `content`;\n * - a UUID string under a `*_id` key, bare or as a rich envelope's `content`.\n *\n * The server only promotes an id when its noun is in the resource registry\n * (aidream `_resource_type_for_key`), which the client cannot see, so every\n * `*_id` UUID counts. This is a deliberate superset: on a value the server\n * does not promote, the client only stops predicting — it never makes a wrong\n * promise.\n */\nexport function isServerResolved(key: string, value: unknown): boolean {\n const inner = isRichEnvelope(value) ? value.content : value;\n if (isPlainObject(inner)) return inner.__kind === KIND_RESOURCE_REF;\n return typeof inner === \"string\" && isIdKey(key) && isUuidLike(inner);\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 (`isRichEnvelope`, e.g. a\n * working document carrying `source`/`mutable`/`persist`, or a lazy pointer)\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 // A row with no value (a receipt-built display row) has nothing to send.\n if (row.value === undefined) 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 // A record that merely has `content` is NOT rich: it is wrapped whole, or\n // the server would read it as raw content and drop the envelope fields.\n // A top-level directive the server acts on by SHAPE ships exactly as-is:\n // wrapped, it becomes plain JSON and silently stops resolving.\n if (isWireDirective(row.key, row.value)) {\n wire[row.key] = row.value;\n continue;\n }\n const rich = isRichEnvelope(row.value) || isLazyPointer(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 * `\"self_check\"`: a real difference from what was shown — the server's\n * safety check stripped the value before the model call. Say \"removed by\n * the server's safety check\", not \"the screen was wrong\".\n */\n reason?: \"self_check\";\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. A row the server consumed\n * (`consumed_as`: expanded / directive / renamed) is compared on `include`\n * and `user_rule` only — its parts carry their own rows; an `\"unaccounted\"`\n * row, or an included envelope `\"expanded\"` into no parts, is a `missing`\n * mismatch.\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 // A consumed value (expanded / directive / renamed) was not delivered as\n // itself: its parts carry their own rows, limits and deliveries. Only\n // include and the person's rule are this key's own promise.\n const consumed = Boolean(actual.consumed_as);\n // A server path removed the value with no reason: kept as a row so it is\n // never silently missing, and still a broken promise on this screen.\n if (actual.consumed_as === \"unaccounted\" && exp.include) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: \"unaccounted\" });\n }\n // An envelope the screen showed as sent that the server expanded into no\n // parts delivered nothing (a window whose fields were all unreadable):\n // \"expanded\" alone would pass it silently.\n if (\n actual.consumed_as === \"expanded\" &&\n actual.include &&\n exp.include &&\n (actual.consumed_into ?? []).length === 0\n ) {\n mismatches.push({ key: exp.key, field: \"missing\", expected: exp.delivery, actual: \"expanded_into_nothing\" });\n }\n // A server-resolved value's limit is only the client's to promise when the\n // person set one; its default-derived limit and delivery are the server's.\n const promisesLimit =\n !consumed && (!exp.serverResolved || exp.userRule?.max_inline_chars !== undefined);\n if (promisesLimit && 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 // The safety check stripped a value the person included: always a\n // mismatch (server-resolved or not), tagged with its reason.\n const selfChecked = actual.blocked_by === \"self_check\" && exp.include;\n if (selfChecked) {\n mismatches.push({\n key: exp.key,\n field: \"delivery\",\n expected: exp.delivery,\n actual: actual.delivery,\n reason: \"self_check\",\n });\n }\n if (\n !selfChecked &&\n !consumed &&\n modelReadsContext &&\n !exp.serverResolved &&\n exp.delivery !== \"server\" &&\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/**\n * For DISPLAY only: fill each server-resolved row's `chars`,\n * `max_inline_chars` and `delivery` from the latest receipt row for the same\n * key (key + surface key, then key), marking it `fromReceipt`. Other rows are\n * returned unchanged. Never compare these rows — `compareReceipt` takes the\n * rows the client resolved before sending.\n */\nexport function applyReceiptToRows(\n rows: readonly ResolvedContextRow[],\n receipt: ContextReceipt | null | undefined,\n): ResolvedContextRow[] {\n const receiptRows = receipt?.rows ?? [];\n return rows.map((row) => {\n const hit =\n receiptRows.find((r) => r.key === row.key && r.surface_key === row.surfaceKey) ??\n receiptRows.find((r) => r.key === row.key);\n if (!hit) return row;\n // What the model read is the server's to report for EVERY value — a value the\n // server replaced or reshaped must never show as the client's copy.\n const shown = { ...row, ...deliveredFields(hit) };\n if (!row.serverResolved) return shown;\n return {\n ...shown,\n chars: hit.chars,\n max_inline_chars: hit.max_inline_chars,\n delivery: hit.delivery,\n fromReceipt: true,\n ...(hit.blocked_by ? { blockedBy: hit.blocked_by } : {}),\n };\n });\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 layers: receiptLayers(row),\n ...(row.blocked_by ? { blockedBy: row.blocked_by } : {}),\n ...deliveredFields(row),\n };\n}\n\n/** A receipt row's `delivered` / `on_request` as display fields (absent when not sent). */\nexport function deliveredFields(\n row: Pick<ContextReceiptRow, \"delivered\" | \"on_request\">,\n): Pick<ResolvedContextRow, \"delivered\" | \"onRequest\"> {\n return {\n ...(row.delivered ? { delivered: row.delivered } : {}),\n ...(row.on_request ? { onRequest: row.on_request } : {}),\n };\n}\n\n/**\n * Rebuild the non-person layers from what the server applied, so a read-only\n * table shows the applied limit as the inherited value and marks the layer\n * that decided it. The limit goes on the layer named by\n * `decided_by.max_inline_chars` (`you` is carried by `userRule`); include is\n * rebuilt the same way for a page `auto_context` off or the agent's kill switch.\n */\n/**\n * Keys the client has a value for but is NOT sending this turn (excluded by\n * the page, the agent or the person's rule). Clients send this list as the\n * request's `context_withheld`, so the receipt's `origin: \"rule\"` rows are\n * limited to keys actually withheld on this page. Deduplicated, in row order.\n */\nexport function withheldKeys(rows: readonly ResolvedContextRow[]): string[] {\n const out: string[] = [];\n for (const row of rows) {\n if (row.include || row.value === undefined || out.includes(row.key)) continue;\n out.push(row.key);\n }\n return out;\n}\n\nconst DOCUMENT_KEY_PREFIXES = [\"attached_document_\", \"attached_file_\", \"resource_file_\"];\n\n/**\n * Receipt rows the client never sent (`compareReceipt(...).systemRows`:\n * server-seeded attachments, scope seeds, saved off-rules…) as table rows, so\n * a person can see and govern them — the normal Include switch and Inline max\n * apply. Documents (`attached_document_*`, `attached_file_*`,\n * `resource_file_*`) group as \"attached\", everything else as \"system\". The\n * server resolved them (`serverResolved`), values come from the receipt\n * (`fromReceipt`), and `surfaceKey` is the row's own `surface_key` — so a rule\n * saved for one writes to `_default` unless the server filed it under a page.\n * `value` is undefined: `buildContextWire` never sends these rows.\n */\nexport function systemRowsToResolved(systemRows: readonly ContextReceiptRow[]): ResolvedContextRow[] {\n return systemRows.map((receiptRow) => {\n const row = receiptRowToResolved(receiptRow);\n const isDocument = DOCUMENT_KEY_PREFIXES.some((p) => receiptRow.key.startsWith(p));\n return {\n ...row,\n origin: isDocument ? \"attached\" : \"system\",\n serverResolved: true,\n fromReceipt: true,\n };\n });\n}\n\nfunction receiptLayers(row: ContextReceiptRow): ContextRuleLayers {\n const layers: ContextRuleLayers = {};\n const limit = row.max_inline_chars;\n const limitBy = row.decided_by.max_inline_chars;\n const includeBy = row.decided_by.include;\n if (limitBy === \"default\") layers.default_max = limit;\n // Page auto_context off: the page dropped it, or the agent re-admitted it.\n const pageOff = (includeBy === \"page\" && !row.include) || (includeBy === \"agent\" && row.include);\n if (limitBy === \"page\" || pageOff) {\n layers.surface = {\n declared: true,\n auto_context: !pageOff,\n max_inline_chars: limitBy === \"page\" ? limit : null,\n };\n }\n if (limitBy === \"agent\") {\n // An agent limit means the key is declared; a declared key the agent decided to include\n // was re-admitted past a page auto_context off. (The kill switch never closes a declared key.)\n layers.agent = { declared: true, max_inline_chars: limit };\n } else if (includeBy === \"agent\") {\n // Off = the kill switch closing an undeclared key; on = a declaration re-admitting a\n // page auto_context-off value.\n layers.agent = row.include ? { declared: true } : { declared: false, kill_switch: true };\n }\n return layers;\n}\n\n// ── attachmentContextKey — the context key of a conversation attachment ─────\n\n/**\n * The context key the server files a conversation attachment under, derivable\n * by every client from `GET /conversations/{id}/attachments` (aidream\n * `conversation_attachments/briefing.py`; corpus `attachment_key_cases`):\n *\n * `attached_resource_` + encode(`${provider}:${resourceRef}`)\n *\n * where encode keeps ASCII letters, digits and `-`, and writes every other\n * UTF-8 byte as `_` + two lowercase hex digits (`_` itself is `_5f`). Unique\n * across providers, case-preserving (Drive ids), stable.\n */\nexport function attachmentContextKey(provider: string, resourceRef: string): string {\n const bytes = new TextEncoder().encode(`${provider}:${resourceRef}`);\n let out = \"attached_resource_\";\n for (const b of bytes) {\n const keep =\n (b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x5a) || (b >= 0x61 && b <= 0x7a) || b === 0x2d;\n out += keep ? String.fromCharCode(b) : `_${b.toString(16).padStart(2, \"0\")}`;\n }\n return out;\n}\n\n// ── humanizeContextKey — the one key-to-words rule (client and server) ──────\n\n/** Words always written in capitals when they are a whole word of a key. */\nexport const CONTEXT_KEY_ACRONYMS: readonly string[] = [\n \"ID\", \"URL\", \"PDF\", \"HTML\", \"JSON\", \"API\", \"AI\", \"UI\", \"CSV\", \"SMS\", \"SEO\", \"LLM\", \"MCP\", \"UUID\", \"ISO\",\n];\nconst ACRONYM_SET = new Set(CONTEXT_KEY_ACRONYMS.map((a) => a.toLowerCase()));\n\n/**\n * A context key in words — the label of a value no page or agent named. The\n * rule (aidream `humanize_context_key` runs the same corpus `humanize_cases`):\n *\n * 1. split on runs of `_`, `-` and whitespace; drop empty parts;\n * 2. a part that is, case-insensitively, one of `CONTEXT_KEY_ACRONYMS` is\n * upper-cased; any other part gets its first character upper-cased and the\n * rest lower-cased (acronyms are whole parts only: `url2pdf` → `Url2pdf`);\n * 3. join with single spaces.\n *\n * `note_id` → `Note ID`, `current_note_published_to_web` → `Current Note Published To Web`.\n */\nexport function humanizeContextKey(key: string): string {\n return key\n .split(/[_\\-\\s]+/)\n .filter((part) => part.length > 0)\n .map((part) =>\n ACRONYM_SET.has(part.toLowerCase())\n ? part.toUpperCase()\n : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),\n )\n .join(\" \");\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":[]}
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // context/react/index.ts
32
32
  var react_exports = {};
33
33
  __export(react_exports, {
34
+ ContextDeliveredValue: () => ContextDeliveredValue,
34
35
  ContextRulesChip: () => ContextRulesChip,
35
36
  ContextRulesPanelBody: () => ContextRulesPanelBody,
36
37
  ContextRulesTable: () => ContextRulesTable,
@@ -739,8 +740,44 @@ function ContextRulesChip({
739
740
 
740
741
  // context/react/ContextRulesPanelBody.tsx
741
742
  var React3 = __toESM(require("react"), 1);
743
+ var import_design_system5 = require("@ai-matrx/design-system");
744
+
745
+ // context/react/ContextDeliveredValue.tsx
742
746
  var import_design_system4 = require("@ai-matrx/design-system");
743
747
  var import_jsx_runtime5 = require("react/jsx-runtime");
748
+ function ContextDeliveredValue({
749
+ row,
750
+ className
751
+ }) {
752
+ if (!row.delivered && !row.onRequest) return null;
753
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: (0, import_design_system4.cn)("flex flex-col gap-3", className), children: [
754
+ row.delivered ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DeliveredBlock, { title: "Agent received", text: row.delivered }) : null,
755
+ row.onRequest ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DeliveredBlock, { title: "Returned on request", text: row.onRequest }) : null
756
+ ] });
757
+ }
758
+ function DeliveredBlock({ title, text }) {
759
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "flex flex-col gap-1", "aria-label": title, children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-baseline gap-2 text-xs", children: [
761
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-medium", children: title }),
762
+ text.truncated ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tabular-nums text-muted-foreground", children: `First ${text.text.length.toLocaleString()} of ${text.chars.toLocaleString()} chars` }) : null
763
+ ] }),
764
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
765
+ "pre",
766
+ {
767
+ "data-testid": "context-delivered-text",
768
+ className: (0, import_design_system4.cn)(
769
+ "max-h-[min(50dvh,22rem)] cursor-text select-text overflow-auto",
770
+ "whitespace-pre-wrap break-words rounded border border-border",
771
+ "bg-muted/40 p-2 font-mono text-[11px] text-foreground"
772
+ ),
773
+ children: text.text
774
+ }
775
+ )
776
+ ] });
777
+ }
778
+
779
+ // context/react/ContextRulesPanelBody.tsx
780
+ var import_jsx_runtime6 = require("react/jsx-runtime");
744
781
  function layerCells(row) {
745
782
  const { surface, agent, default_max } = row.layers ?? {};
746
783
  const declared = agent?.declared === true;
@@ -763,38 +800,38 @@ function layerCells(row) {
763
800
  var LAYERS = ["default", "page", "agent", "you"];
764
801
  function LayerTable({ row }) {
765
802
  const cells = layerCells(row);
766
- const cell = (text, winning) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
803
+ const cell = (text, winning) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
767
804
  "span",
768
805
  {
769
806
  role: "cell",
770
807
  "data-winning": winning ? "true" : void 0,
771
- className: (0, import_design_system4.cn)(
808
+ className: (0, import_design_system5.cn)(
772
809
  "rounded px-1.5 py-0.5 text-right tabular-nums",
773
810
  winning ? "bg-primary/10 font-medium text-foreground" : "text-muted-foreground"
774
811
  ),
775
812
  children: text
776
813
  }
777
814
  );
778
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { role: "table", "aria-label": "Layers", className: "text-xs", children: [
779
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
815
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "table", "aria-label": "Layers", className: "text-xs", children: [
816
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
780
817
  "div",
781
818
  {
782
819
  role: "row",
783
820
  className: "grid h-7 grid-cols-[1fr_5rem_5rem] items-center gap-x-2 border-b border-border font-medium text-muted-foreground",
784
821
  children: [
785
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "columnheader", children: "Layer" }),
786
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "columnheader", className: "text-right", children: "Include" }),
787
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "columnheader", className: "text-right", children: "Inline max" })
822
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "columnheader", children: "Layer" }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "columnheader", className: "text-right", children: "Include" }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "columnheader", className: "text-right", children: "Inline max" })
788
825
  ]
789
826
  }
790
827
  ),
791
- LAYERS.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
828
+ LAYERS.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
792
829
  "div",
793
830
  {
794
831
  role: "row",
795
832
  className: "grid h-7 grid-cols-[1fr_5rem_5rem] items-center gap-x-2 border-b border-border/50",
796
833
  children: [
797
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "cell", children: LAYER_LABEL[layer] }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "cell", children: LAYER_LABEL[layer] }),
798
835
  cell(cells[layer].include, row.decided_by.include === layer),
799
836
  cell(cells[layer].limit, row.decided_by.max_inline_chars === layer)
800
837
  ]
@@ -812,19 +849,19 @@ function RowDetail({
812
849
  renderDetail,
813
850
  comfortable
814
851
  }) {
815
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 p-3", children: [
816
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 items-baseline gap-2", children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "min-w-0 truncate text-sm font-medium", children: row.label }),
818
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: `${formatChars(row.chars)} chars \xB7 ${DELIVERY_LABEL[row.delivery]}` })
852
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-3 p-3", children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex min-w-0 items-baseline gap-2", children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "min-w-0 truncate text-sm font-medium", children: row.label }),
855
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: `${formatChars(row.chars)} chars \xB7 ${DELIVERY_LABEL[row.delivery]}` })
819
856
  ] }),
820
- mismatches.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start gap-1.5 rounded-md border border-warning/40 bg-warning/10 px-2 py-1.5 text-xs text-warning", children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TriangleAlertIcon, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
822
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: describeMismatches(mismatches) })
857
+ mismatches.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start gap-1.5 rounded-md border border-warning/40 bg-warning/10 px-2 py-1.5 text-xs text-warning", children: [
858
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TriangleAlertIcon, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: describeMismatches(mismatches) })
823
860
  ] }) : null,
824
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2 text-xs", children: [
825
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-muted-foreground", children: "Include" }),
826
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: (0, import_design_system4.cn)("flex items-center", comfortable ? "h-11" : "h-7"), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
827
- import_design_system4.Switch,
861
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2 text-xs", children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-muted-foreground", children: "Include" }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: (0, import_design_system5.cn)("flex items-center", comfortable ? "h-11" : "h-7"), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
864
+ import_design_system5.Switch,
828
865
  {
829
866
  size: "sm",
830
867
  "aria-label": `Include ${row.label}`,
@@ -833,8 +870,8 @@ function RowDetail({
833
870
  onCheckedChange: (next) => onChange(row.key, row.surfaceKey, withInclude(row, next))
834
871
  }
835
872
  ) }),
836
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-muted-foreground", children: "Inline max" }),
837
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
873
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-muted-foreground", children: "Inline max" }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
838
875
  InlineMaxInput,
839
876
  {
840
877
  label: row.label,
@@ -848,7 +885,8 @@ function RowDetail({
848
885
  }
849
886
  ) })
850
887
  ] }),
851
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LayerTable, { row }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LayerTable, { row }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ContextDeliveredValue, { row }),
852
890
  renderDetail ? renderDetail(row) : null
853
891
  ] });
854
892
  }
@@ -875,21 +913,21 @@ function ContextRulesPanelBody({
875
913
  const visible = q ? rows.filter((r) => r.label.toLowerCase().includes(q) || r.key.toLowerCase().includes(q)) : rows;
876
914
  const selected = selectedKey ? rows.find((r) => r.key === selectedKey) ?? null : null;
877
915
  const comfortable = !!isMobile;
878
- const search = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative shrink-0 border-b border-border p-2", children: [
879
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchIcon, { className: "pointer-events-none absolute left-4 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
880
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
881
- import_design_system4.Input,
916
+ const search = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative shrink-0 border-b border-border p-2", children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SearchIcon, { className: "pointer-events-none absolute left-4 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
919
+ import_design_system5.Input,
882
920
  {
883
921
  type: "search",
884
922
  "aria-label": "Search context",
885
923
  placeholder: "Search",
886
924
  value: query,
887
925
  onChange: (e) => setQuery(e.target.value),
888
- className: (0, import_design_system4.cn)("pl-7 shadow-none", comfortable ? "h-11 text-base" : "h-8 text-xs")
926
+ className: (0, import_design_system5.cn)("pl-7 shadow-none", comfortable ? "h-11 text-base" : "h-8 text-xs")
889
927
  }
890
928
  )
891
929
  ] });
892
- const table = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
930
+ const table = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
893
931
  ContextRulesTable,
894
932
  {
895
933
  rows: visible,
@@ -902,7 +940,7 @@ function ContextRulesPanelBody({
902
940
  ...mismatches ? { mismatches } : {}
903
941
  }
904
942
  ) });
905
- const detail = selected ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
943
+ const detail = selected ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
906
944
  RowDetail,
907
945
  {
908
946
  row: selected,
@@ -915,31 +953,31 @@ function ContextRulesPanelBody({
915
953
  }
916
954
  ) : null;
917
955
  if (isMobile) {
918
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.TooltipProvider, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: (0, import_design_system4.cn)("flex h-full min-h-0 flex-col", className), children: selected ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
919
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex h-12 shrink-0 items-center border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
956
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.TooltipProvider, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: (0, import_design_system5.cn)("flex h-full min-h-0 flex-col", className), children: selected ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-12 shrink-0 items-center border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
920
958
  "button",
921
959
  {
922
960
  type: "button",
923
961
  onClick: () => select(null),
924
962
  className: "inline-flex h-11 items-center gap-1 rounded-md px-2 text-sm text-muted-foreground hover:text-foreground",
925
963
  children: [
926
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronLeftIcon, { className: "h-4 w-4" }),
964
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronLeftIcon, { className: "h-4 w-4" }),
927
965
  "Back"
928
966
  ]
929
967
  }
930
968
  ) }),
931
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: detail })
932
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
969
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: detail })
970
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
933
971
  search,
934
972
  table
935
973
  ] }) }) });
936
974
  }
937
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.TooltipProvider, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: (0, import_design_system4.cn)("flex h-full min-h-0", className), children: [
938
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("aside", { className: "flex w-[22rem] shrink-0 flex-col border-r border-border", children: [
975
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.TooltipProvider, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: (0, import_design_system5.cn)("flex h-full min-h-0", className), children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("aside", { className: "flex w-[22rem] shrink-0 flex-col border-r border-border", children: [
939
977
  search,
940
978
  table
941
979
  ] }),
942
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: "min-h-0 min-w-0 flex-1 overflow-y-auto", children: detail ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex h-full items-center justify-center text-xs text-muted-foreground", children: "Select an item" }) })
980
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("section", { className: "min-h-0 min-w-0 flex-1 overflow-y-auto", children: detail ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-full items-center justify-center text-xs text-muted-foreground", children: "Select an item" }) })
943
981
  ] }) });
944
982
  }
945
983
  //# sourceMappingURL=index.cjs.map