@genesislcap/ai-assistant 15.10.6 → 15.11.0

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.
@@ -1983,6 +1983,34 @@
1983
1983
  ],
1984
1984
  "description": "Feed (or clear) one vendor's AI-spend figures for the settings-modal budget\nmeter — the numeric companion to\nFoundationAiAssistant.setVendorBlocked, and like it a host-driven\nwrite: the element meters nothing itself.\n\nThe meter renders a row for exactly the vendors that currently HAVE\nfigures. An **unlimited** vendor (the budget endpoint reports\n`unlimited: true` / `limitUsd: null`) is expressed by never feeding it —\nthere is deliberately no \"unlimited\" figure shape, because a bar with no\ncap has nothing truthful to fill to. `spentUsd` MAY exceed `budgetUsd`\n(the last allowed call can overshoot the cap): the row's text shows the\nreal figures and only the bar clamps.\n\nReturns `false` — and warns once, not per call — instead of throwing when\n`vendor` is not a budgeted vendor (`BUDGETED_VENDORS`, the proxy's own\nmetered list) or either figure is not a finite number `>= 0`. Invalid input\nchanges no state. Idempotent: re-feeding a vendor's current figures is a\nno-op, so hosts can re-feed unconditionally after every turn.\n\n`false` ALSO means \"not stored, because the element is not ready yet\": a\nfeed that lands before the element has its session store (a host calling\nahead of the first append, or during a pop-out remount) stores nothing and\nsays so, rather than reporting a write that never happened. That case is\ntransient and needs no host handling — the documented per-turn re-feed is\nwhat recovers it — so it is deliberately NOT warned about; the warn-once\nchannel is for garbage input only.\n\n`figures` is nullish-checked at run time, so `undefined` behaves exactly\nlike `null` and CLEARS that vendor's row. Untyped JS hosts feed this\nstraight from `GET /api/budget`, where a per-vendor lookup that misses\nyields `undefined`, not `null` — and \"no figures\" coherently means \"no\nrow\". The `| null` parameter type is unchanged for typed callers; the\nruntime is deliberately the more forgiving of the two.\n\nThe intended feed is the platform budget endpoint:\n\n```ts\n// `vendors` is an OBJECT keyed by vendor id, not an array.\nconst { vendors } = await (await fetch('/api/budget')).json();\nfor (const v of Object.values(vendors)) {\n if (v.unlimited || v.limitUsd == null) {\n assistantEl.setVendorBudget(v.vendor, null); // clear stale figures after a cap is lifted\n continue;\n }\n assistantEl.setVendorBudget(v.vendor, { budgetUsd: v.limitUsd, spentUsd: v.spentUsd });\n}\n```\n\nRe-run it on mount and after each turn. Scope and lifetime follow the rest\nof the session slice (per `stateKey`, survives pop-in/out and \"Clear\" /\n\"New chat\") — except that the figures are NOT persisted to the session\nsnapshot: the server is authoritative and the host re-feeds at boot, so a\npersisted copy could only show stale dollars after a reload."
1985
1985
  },
1986
+ {
1987
+ "kind": "field",
1988
+ "name": "budgetMeterLastFedAt",
1989
+ "type": {
1990
+ "text": "number | null"
1991
+ },
1992
+ "default": "null",
1993
+ "description": "When budget figures last arrived — from the host's FoundationAiAssistant.setVendorBudget\nor the wall latch's own feed — as epoch ms; `null` until the first feed.\n\nELEMENT state, not slice state, on purpose: the reducer is idempotent by\nvalue (an unchanged re-feed must not re-render the meter rows), but \"when\nwas this fetched\" must move on every feed, changed figures or not — a\nrefresh that confirms the same dollars is still a refresh. Only the\ntoolbar label observes this, so a stamp re-renders one span, not the rows."
1994
+ },
1995
+ {
1996
+ "kind": "field",
1997
+ "name": "settingsBudgetUpdatedLabel",
1998
+ "type": {
1999
+ "text": "string | undefined"
2000
+ },
2001
+ "description": "\"Updated HH:MM:SS\" for the meter toolbar, or `undefined` before any feed.",
2002
+ "readonly": true
2003
+ },
2004
+ {
2005
+ "kind": "method",
2006
+ "name": "requestBudgetRefresh",
2007
+ "return": {
2008
+ "type": {
2009
+ "text": "void"
2010
+ }
2011
+ },
2012
+ "description": "Ask the host to re-fetch budget figures now (GENC-1464 refresh affordance).\n\nThe element cannot fetch — the meter is host-fed by design — so this emits\n`budget-refresh-requested` and the host answers with the same fetch that\nalready feeds FoundationAiAssistant.setVendorBudget (Create's handler coalesces in-flight\nfetches, so a spammed button costs one request). Fired by the meter's\nrefresh button and on settings-modal open, so the figures are fresh exactly\nwhen someone is looking at them. Fires unconditionally: a host without a\nlistener simply keeps the per-turn cadence."
2013
+ },
1986
2014
  {
1987
2015
  "kind": "method",
1988
2016
  "name": "isVendorSwept",
@@ -4225,6 +4253,12 @@
4225
4253
  }
4226
4254
  ],
4227
4255
  "events": [
4256
+ {
4257
+ "name": "budget-refresh-requested",
4258
+ "type": {
4259
+ "text": "CustomEvent"
4260
+ }
4261
+ },
4228
4262
  {
4229
4263
  "type": {
4230
4264
  "text": "CustomEvent"
@@ -5217,2016 +5251,2016 @@
5217
5251
  },
5218
5252
  {
5219
5253
  "kind": "javascript-module",
5220
- "path": "src/utils/animated-panel-toggle.ts",
5221
- "declarations": [],
5222
- "exports": []
5223
- },
5224
- {
5225
- "kind": "javascript-module",
5226
- "path": "src/utils/animation-exclusivity.ts",
5227
- "declarations": [],
5228
- "exports": []
5229
- },
5230
- {
5231
- "kind": "javascript-module",
5232
- "path": "src/utils/banked-usage-baselines.ts",
5233
- "declarations": [],
5234
- "exports": []
5235
- },
5236
- {
5237
- "kind": "javascript-module",
5238
- "path": "src/utils/collect-session-models.ts",
5254
+ "path": "src/components/activity-halo/activity-halo.ts",
5239
5255
  "declarations": [
5240
5256
  {
5241
- "kind": "function",
5242
- "name": "collectSessionModels",
5243
- "return": {
5244
- "type": {
5245
- "text": "CostSessionModelEntry[]"
5246
- }
5247
- },
5248
- "parameters": [
5257
+ "kind": "class",
5258
+ "description": "Wrapper component that shows an `<ai-halo-overlay>` when the AI assistant\nis executing one of the declared tool names.",
5259
+ "name": "AiActivityHalo",
5260
+ "members": [
5249
5261
  {
5250
- "name": "messages",
5262
+ "kind": "field",
5263
+ "name": "tools",
5251
5264
  "type": {
5252
- "text": "readonly ChatMessage[]"
5253
- }
5265
+ "text": "string"
5266
+ },
5267
+ "default": "''",
5268
+ "description": "Comma-separated list of tool names this element reacts to."
5254
5269
  },
5255
5270
  {
5256
- "name": "providerStatuses",
5257
- "default": "[]",
5271
+ "kind": "field",
5272
+ "name": "minShowTime",
5258
5273
  "type": {
5259
- "text": "readonly AIProviderRegistryStatusEntry[]"
5260
- }
5261
- }
5262
- ],
5263
- "description": "Unique models used across a message list (includes sub-agent traces)."
5264
- }
5265
- ],
5266
- "exports": [
5267
- {
5268
- "kind": "js",
5269
- "name": "collectSessionModels",
5270
- "declaration": {
5271
- "name": "collectSessionModels",
5272
- "module": "src/utils/collect-session-models.ts"
5273
- }
5274
- }
5275
- ]
5276
- },
5277
- {
5278
- "kind": "javascript-module",
5279
- "path": "src/utils/condense-history.ts",
5280
- "declarations": [
5281
- {
5282
- "kind": "variable",
5283
- "name": "CONDENSE_MIN_CHARS",
5284
- "type": {
5285
- "text": "number"
5286
- },
5287
- "default": "1000",
5288
- "description": "Below this serialized size a tool payload isn't worth condensing — the stub\nwould save little and cost a breadcrumb. A driver-level floor so the public\n`condenseWhen` API needs no per-policy threshold field."
5289
- },
5290
- {
5291
- "kind": "variable",
5292
- "name": "CONDENSED_ARGS_KEY",
5293
- "type": {
5294
- "text": "string"
5295
- },
5296
- "default": "'condensed'",
5297
- "description": "Key the collapsed args are stored under — a tool-call's args must stay a Record."
5298
- },
5299
- {
5300
- "kind": "function",
5301
- "name": "applyCondensation",
5302
- "return": {
5303
- "type": {
5304
- "text": "ChatMessage[]"
5305
- }
5306
- },
5307
- "parameters": [
5274
+ "text": "number"
5275
+ },
5276
+ "default": "600",
5277
+ "description": "Minimum time in ms the halo stays visible after activation."
5278
+ },
5308
5279
  {
5309
- "name": "history",
5280
+ "kind": "field",
5281
+ "name": "unsubBus",
5310
5282
  "type": {
5311
- "text": "ChatMessage[]"
5283
+ "text": "() => void | undefined"
5312
5284
  },
5313
- "description": "The to-model history copy to rewrite (not mutated)."
5285
+ "privacy": "private"
5314
5286
  },
5315
5287
  {
5316
- "name": "policies",
5288
+ "kind": "field",
5289
+ "name": "hideTimeout",
5317
5290
  "type": {
5318
- "text": "Map<string, RegisteredCondensePolicy>"
5291
+ "text": "ReturnType<typeof setTimeout> | undefined"
5319
5292
  },
5320
- "description": "The driver's registry, keyed by tool-call id. Its entries'\n`reported*` flags are flipped as payloads first collapse."
5293
+ "privacy": "private"
5321
5294
  },
5322
5295
  {
5323
- "name": "ctx",
5296
+ "kind": "field",
5297
+ "name": "startTime",
5324
5298
  "type": {
5325
- "text": "CondenseContext"
5299
+ "text": "number"
5326
5300
  },
5327
- "description": "The driver's live clocks (model-call / turn / agent-activation)."
5301
+ "privacy": "private",
5302
+ "default": "0"
5328
5303
  },
5329
5304
  {
5330
- "name": "onCondensed",
5305
+ "kind": "method",
5306
+ "name": "activate",
5307
+ "privacy": "private"
5308
+ },
5309
+ {
5310
+ "kind": "method",
5311
+ "name": "scheduleDeactivate",
5312
+ "privacy": "private"
5313
+ }
5314
+ ],
5315
+ "attributes": [
5316
+ {
5317
+ "name": "tools",
5331
5318
  "type": {
5332
- "text": "(detail: CondensedEventDetail) => void"
5319
+ "text": "string"
5333
5320
  },
5334
- "description": "Invoked once per payload at its full→stub transition."
5321
+ "default": "''",
5322
+ "description": "Comma-separated list of tool names this element reacts to.",
5323
+ "fieldName": "tools"
5324
+ },
5325
+ {
5326
+ "name": "min-show-time",
5327
+ "type": {
5328
+ "text": "number"
5329
+ },
5330
+ "default": "DEFAULT_MIN_SHOW_TIME",
5331
+ "description": "Minimum time in ms the halo stays visible after activation.",
5332
+ "resolveInitializer": {
5333
+ "module": "src/components/activity-halo/activity-halo.ts"
5334
+ },
5335
+ "fieldName": "minShowTime"
5335
5336
  }
5336
5337
  ],
5337
- "description": "Collapse stale tool payloads (declared via `condenseWhen`) out of the\nmodel-bound history. Pure over the `history` array — it emits new message\nobjects and never mutates the input messages (mirroring the agent-masking\ntransform). It DOES, however, write two latches onto the matching `policies`\nentry the first time a payload collapses, both because the collapse itself\nre-runs before every provider call: the `reported*` flag (the report-once gate\nthat keeps `onCondensed` firing once per (tool call, payload)) and\n`firedTrigger` (which pins the reason so a reason-bearing stub is rendered\nonce and never rewritten).\n\nTriggers (a policy may list several via `on: Trigger[]` — the FIRST to fire\ncollapses the payload; all are monotonic, so the collapse never reverts):\n- `superseded` — among all registered calls sharing a `by` key, the LAST in\n history order survives; every earlier one's targeted payload collapses. A\n re-call with the same key becomes the new survivor and re-arms the rest.\n- `age` — the result is first visible one model-call after the call, so `turns`\n is how many model-calls may see the full result before it collapses\n (fires when `modelCall − callModelCall > turns`): `turns: 1` is seen once,\n then collapses. The clock is monotonic across turns.\n- `turnEnd` — collapses once the turn (`sendMessage`) that made the call has\n ended (fires when `turn > callTurn`): full for the rest of that request,\n gone on every later one.\n- `agentEnd` — collapses once the agent activation that made the call has ended\n (a swap to another agent, or `releaseAgent`/`completeSubAgent`): kept across\n all of the agent's turns, dropped when its flow finishes.\n- `phaseEnd` — collapses once the app advances its phase epoch via `endPhase()`\n after the call (a sub-`agentEnd` boundary the author declares), OR at\n `agentEnd` as a backstop if no boundary is ever declared.\n\nThe tool-call/result envelope is never removed (providers reject orphaned\ncalls/results) — only the args object or the result content is replaced."
5338
+ "superclass": {
5339
+ "name": "GenesisElement",
5340
+ "package": "@genesislcap/web-core"
5341
+ },
5342
+ "tagName": "ai-activity-halo",
5343
+ "customElement": true
5338
5344
  }
5339
5345
  ],
5340
5346
  "exports": [
5341
5347
  {
5342
5348
  "kind": "js",
5343
- "name": "CONDENSE_MIN_CHARS",
5349
+ "name": "AiActivityHalo",
5344
5350
  "declaration": {
5345
- "name": "CONDENSE_MIN_CHARS",
5346
- "module": "src/utils/condense-history.ts"
5351
+ "name": "AiActivityHalo",
5352
+ "module": "src/components/activity-halo/activity-halo.ts"
5347
5353
  }
5348
5354
  },
5349
5355
  {
5350
- "kind": "js",
5351
- "name": "CONDENSED_ARGS_KEY",
5356
+ "kind": "custom-element-definition",
5357
+ "name": "ai-activity-halo",
5352
5358
  "declaration": {
5353
- "name": "CONDENSED_ARGS_KEY",
5354
- "module": "src/utils/condense-history.ts"
5359
+ "name": "AiActivityHalo",
5360
+ "module": "src/components/activity-halo/activity-halo.ts"
5355
5361
  }
5356
- },
5362
+ }
5363
+ ]
5364
+ },
5365
+ {
5366
+ "kind": "javascript-module",
5367
+ "path": "src/components/agent-picker/agent-picker.constants.ts",
5368
+ "declarations": [
5369
+ {
5370
+ "kind": "variable",
5371
+ "name": "AGENT_PICKER_AUTO_VALUE",
5372
+ "type": {
5373
+ "text": "string"
5374
+ },
5375
+ "default": "'__auto__'",
5376
+ "description": "Sentinel value used by the segmented control / select to represent \"Auto\"."
5377
+ }
5378
+ ],
5379
+ "exports": [
5357
5380
  {
5358
5381
  "kind": "js",
5359
- "name": "applyCondensation",
5382
+ "name": "AGENT_PICKER_AUTO_VALUE",
5360
5383
  "declaration": {
5361
- "name": "applyCondensation",
5362
- "module": "src/utils/condense-history.ts"
5384
+ "name": "AGENT_PICKER_AUTO_VALUE",
5385
+ "module": "src/components/agent-picker/agent-picker.constants.ts"
5363
5386
  }
5364
5387
  }
5365
5388
  ]
5366
5389
  },
5367
5390
  {
5368
5391
  "kind": "javascript-module",
5369
- "path": "src/utils/cost-session-history.ts",
5392
+ "path": "src/components/agent-picker/agent-picker.styles.ts",
5370
5393
  "declarations": [
5371
5394
  {
5372
- "kind": "function",
5373
- "name": "costHistoryStorageKey",
5374
- "return": {
5375
- "type": {
5376
- "text": "string"
5377
- }
5378
- },
5379
- "parameters": [
5380
- {
5381
- "name": "scope",
5382
- "type": {
5383
- "text": "string"
5384
- }
5385
- }
5386
- ],
5387
- "description": "localStorage key for a host-scoped cost history list."
5388
- },
5395
+ "kind": "variable",
5396
+ "name": "styles",
5397
+ "default": "css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .picker {\n position: relative;\n padding: 6px calc(var(--design-unit) * 3px);\n background-color: var(--neutral-layer-2);\n }\n\n /* Clipping wrapper. The inner segmented-control is naturally inline-flex\n and auto-sizes to its segments, so it would overflow the panel without\n ever reporting scrollWidth > clientWidth. By giving the wrapper a\n constrained width (flex+min-width:0) and overflow:hidden, the wrapper's\n scrollWidth reflects the segments' natural width while clientWidth is\n the available width — that's what we measure to decide on the fallback. */\n .segmented-row {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n }\n\n /* When the fallback select is showing, take the segmented row out of layout\n but keep it in the DOM so the ResizeObserver can re-measure when the\n container grows back and we can swap back to segmented-control. */\n .picker[data-effective='select'] .segmented-row {\n position: absolute;\n inset: 6px calc(var(--design-unit) * 3px);\n visibility: hidden;\n pointer-events: none;\n }\n\n .segmented-control {\n flex: 0 0 auto;\n }\n\n .select-fallback {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .select-label {\n font-family: var(--body-font);\n font-size: 0.8em;\n color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));\n }\n\n .select-control {\n flex: 1 1 auto;\n min-width: 0;\n padding: 4px 8px;\n background: var(--neutral-layer-3);\n color: var(--neutral-foreground-rest);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: 4px;\n font-family: var(--body-font);\n font-size: 0.85em;\n cursor: pointer;\n }\n\n .select-control:focus-visible {\n outline: 2px solid var(--accent-fill-rest);\n outline-offset: 1px;\n }\n`"
5398
+ }
5399
+ ],
5400
+ "exports": [
5389
5401
  {
5390
- "kind": "function",
5391
- "name": "loadCostSessionHistory",
5392
- "return": {
5393
- "type": {
5394
- "text": "CostSessionRecord[]"
5395
- }
5396
- },
5397
- "parameters": [
5398
- {
5399
- "name": "scope",
5400
- "type": {
5401
- "text": "string"
5402
- }
5403
- }
5404
- ],
5405
- "description": "Load persisted cost sessions for a scope, in whatever order they were stored — this applies no\nordering of its own. Display order is the caller's business; see `sortRecordsByRecency`."
5406
- },
5402
+ "kind": "js",
5403
+ "name": "styles",
5404
+ "declaration": {
5405
+ "name": "styles",
5406
+ "module": "src/components/agent-picker/agent-picker.styles.ts"
5407
+ }
5408
+ }
5409
+ ]
5410
+ },
5411
+ {
5412
+ "kind": "javascript-module",
5413
+ "path": "src/components/agent-picker/agent-picker.template.ts",
5414
+ "declarations": [],
5415
+ "exports": []
5416
+ },
5417
+ {
5418
+ "kind": "javascript-module",
5419
+ "path": "src/components/agent-picker/agent-picker.ts",
5420
+ "declarations": [
5407
5421
  {
5408
- "kind": "function",
5409
- "name": "saveCostSessionHistory",
5410
- "return": {
5411
- "type": {
5412
- "text": "void"
5413
- }
5414
- },
5415
- "parameters": [
5422
+ "kind": "class",
5423
+ "description": "User-facing agent picker rendered above the chat input.\n\nRenders `Auto` plus each top-level agent that has `manualSelection.enabled`.\nSub-agents and non-selectable specialists are excluded. Hidden when fewer\nthan two agents are configured or none are manually selectable.",
5424
+ "name": "AgentPicker",
5425
+ "members": [
5416
5426
  {
5417
- "name": "scope",
5427
+ "kind": "field",
5428
+ "name": "designSystemPrefix",
5418
5429
  "type": {
5419
5430
  "text": "string"
5420
- }
5431
+ },
5432
+ "default": "'rapid'",
5433
+ "description": "Design-system tag prefix, e.g. `'rapid'` for `rapid-segmented-control`."
5421
5434
  },
5422
5435
  {
5423
- "name": "records",
5436
+ "kind": "field",
5437
+ "name": "mode",
5424
5438
  "type": {
5425
- "text": "CostSessionRecord[]"
5426
- }
5427
- }
5428
- ],
5429
- "description": "Replace the persisted list for a scope."
5430
- },
5431
- {
5432
- "kind": "function",
5433
- "name": "upsertRecord",
5434
- "return": {
5435
- "type": {
5436
- "text": "CostSessionRecord[]"
5437
- }
5438
- },
5439
- "parameters": [
5439
+ "text": "AgentPickerMode"
5440
+ },
5441
+ "default": "'disabled'",
5442
+ "description": "Picker variant, sourced from the assistant's `chatConfig.agentPicker`."
5443
+ },
5440
5444
  {
5441
- "name": "records",
5445
+ "kind": "field",
5446
+ "name": "agents",
5442
5447
  "type": {
5443
- "text": "readonly CostSessionRecord[]"
5444
- }
5448
+ "text": "AgentConfig[]"
5449
+ },
5450
+ "default": "[]",
5451
+ "description": "Top-level agents passed to the assistant."
5445
5452
  },
5446
5453
  {
5447
- "name": "record",
5454
+ "kind": "field",
5455
+ "name": "pinnedAgentName",
5448
5456
  "type": {
5449
- "text": "CostSessionRecord"
5450
- }
5451
- }
5452
- ],
5453
- "description": "Pure upsert — `records` with `record` replacing any row sharing its `projectKey`, or\nprepended when there is none.\n\nAn upsert, not an append, because a row stands for a **project's lifetime usage** rather\nthan a visit to it. Appending cut a fresh row per page-hide, so one project accumulated\na stack of near-identical rows and its spend was counted once per refresh by anything\nsumming the list.\n\nThe resulting array order is incidental — it is insertion order, so a project returned to\nstays where it first landed. Do not read it as recency; `sortRecordsByRecency` is what the\nUsage tab renders through.\n\nStorage-agnostic on purpose: the same merge serves the synchronous `localStorage` path\nand the async provider-backed one, so the two can never drift on what \"upsert\" means.\nThe caller owns persistence — see the assistant's `persistCostHistory`."
5454
- },
5455
- {
5456
- "kind": "function",
5457
- "name": "sortRecordsByRecency",
5458
- "return": {
5459
- "type": {
5460
- "text": "CostSessionRecord[]"
5461
- }
5462
- },
5463
- "parameters": [
5457
+ "text": "string | null"
5458
+ },
5459
+ "default": "null",
5460
+ "description": "Currently pinned agent name, or `null` for Auto."
5461
+ },
5464
5462
  {
5465
- "name": "records",
5463
+ "kind": "field",
5464
+ "name": "resizeObserver",
5466
5465
  "type": {
5467
- "text": "readonly CostSessionRecord[]"
5468
- }
5469
- }
5470
- ],
5471
- "description": "Rows most-recently-worked-on first, as a new array.\n\nApplied at read time rather than baked into the stored order, so it covers rows that came from\na host provider as well as from `localStorage`, and so no write path has to maintain it.\n\n`updatedAt` is ISO-8601, which sorts lexicographically — no date parsing, and no dependence on\nthe strings being valid dates. Sorting by it rather than by insertion order is what makes the\nlist track use: the active project moves to the top on its first upsert and stays there, since\nits row is rewritten on every transcript change."
5472
- },
5473
- {
5474
- "kind": "function",
5475
- "name": "resolveBankedUsage",
5476
- "return": {
5477
- "type": {
5478
- "text": "AggregateUsage"
5479
- }
5480
- },
5481
- "parameters": [
5466
+ "text": "ResizeObserver | undefined"
5467
+ },
5468
+ "privacy": "private"
5469
+ },
5482
5470
  {
5483
- "name": "existing",
5471
+ "kind": "field",
5472
+ "name": "selectableAgents",
5484
5473
  "type": {
5485
- "text": "CostSessionRecord | undefined"
5486
- }
5474
+ "text": "AgentConfig[]"
5475
+ },
5476
+ "description": "Top-level agents that opted in to manual selection.",
5477
+ "readonly": true
5487
5478
  },
5488
5479
  {
5489
- "name": "transcriptIsAuthoritative",
5480
+ "kind": "field",
5481
+ "name": "visible",
5490
5482
  "type": {
5491
5483
  "text": "boolean"
5484
+ },
5485
+ "description": "Whether the picker should render at all.",
5486
+ "readonly": true
5487
+ },
5488
+ {
5489
+ "kind": "field",
5490
+ "name": "effectiveMode",
5491
+ "type": {
5492
+ "text": "'select' | 'segmented-control'"
5493
+ },
5494
+ "description": "The variant actually rendered: `'select'` if user-configured or if the\nsegmented row currently overflows; `'segmented-control'` otherwise.",
5495
+ "readonly": true
5496
+ },
5497
+ {
5498
+ "kind": "method",
5499
+ "name": "agentsChanged",
5500
+ "return": {
5501
+ "type": {
5502
+ "text": "void"
5503
+ }
5504
+ },
5505
+ "description": "Re-measure when agents change — newly added selectable agents may push the\nsegmented row past the container's width."
5506
+ },
5507
+ {
5508
+ "kind": "method",
5509
+ "name": "modeChanged",
5510
+ "return": {
5511
+ "type": {
5512
+ "text": "void"
5513
+ }
5492
5514
  }
5493
- }
5494
- ],
5495
- "description": "The spend a project's next row must carry over from before its current transcript.\n\n`transcriptIsAuthoritative` means the open transcript already accounts for this project's\nhistory — true exactly when a session snapshot is restored on load. The two branches are\nnot interchangeable, and picking the wrong one is silently destructive:\n\n- Authoritative: carry the previous row's own `banked` forward. Taking `usage` instead would\n re-add the restored history on every reload, the compounding double-count this replaced.\n- Not authoritative: the whole previous total becomes banked, because nothing in the fresh\n transcript accounts for it. Taking `banked` instead would rewrite the row down to the\n current page-load and drop the project's earlier spend.\n\nNote both branches are idempotent — the result never includes the current transcript — which\nis what lets two assistant instances sharing a session write the same row."
5496
- },
5497
- {
5498
- "kind": "function",
5499
- "name": "clearCostSessionHistory",
5500
- "return": {
5501
- "type": {
5502
- "text": "void"
5503
- }
5504
- },
5505
- "parameters": [
5515
+ },
5506
5516
  {
5507
- "name": "scope",
5517
+ "kind": "method",
5518
+ "name": "measureOverflow",
5519
+ "privacy": "private",
5520
+ "return": {
5521
+ "type": {
5522
+ "text": "void"
5523
+ }
5524
+ },
5525
+ "description": "Measures whether the segmented row's natural content width exceeds the\ncontainer's visible width. Bails when the row isn't currently rendered\n(e.g. when `mode !== 'segmented-control'`)."
5526
+ },
5527
+ {
5528
+ "kind": "method",
5529
+ "name": "selectAgent",
5530
+ "return": {
5531
+ "type": {
5532
+ "text": "void"
5533
+ }
5534
+ },
5535
+ "parameters": [
5536
+ {
5537
+ "name": "name",
5538
+ "type": {
5539
+ "text": "string | null"
5540
+ }
5541
+ }
5542
+ ]
5543
+ },
5544
+ {
5545
+ "kind": "method",
5546
+ "name": "selectByValue",
5547
+ "return": {
5548
+ "type": {
5549
+ "text": "void"
5550
+ }
5551
+ },
5552
+ "parameters": [
5553
+ {
5554
+ "name": "value",
5555
+ "type": {
5556
+ "text": "string"
5557
+ }
5558
+ }
5559
+ ],
5560
+ "description": "Maps a value from the segmented-control or select (which uses\nAGENT_PICKER_AUTO_VALUE for Auto) back to the `string | null` form\nexpected by `selectAgent`."
5561
+ },
5562
+ {
5563
+ "kind": "field",
5564
+ "name": "currentValue",
5508
5565
  "type": {
5509
5566
  "text": "string"
5510
- }
5567
+ },
5568
+ "description": "Current pinned agent name as a value the segmented-control / select can bind to.",
5569
+ "readonly": true
5511
5570
  }
5512
5571
  ],
5513
- "description": "Clear all persisted records for a scope."
5514
- },
5515
- {
5516
- "kind": "function",
5517
- "name": "formatCostSessionDate",
5518
- "return": {
5519
- "type": {
5520
- "text": "string"
5572
+ "events": [
5573
+ {
5574
+ "description": "Fired when the user changes selection with the agent name or `null` for Auto. detail: `string | null`",
5575
+ "name": "agent-pinned"
5521
5576
  }
5522
- },
5523
- "parameters": [
5577
+ ],
5578
+ "attributes": [
5524
5579
  {
5525
- "name": "iso",
5580
+ "name": "design-system-prefix",
5526
5581
  "type": {
5527
5582
  "text": "string"
5528
- }
5529
- }
5530
- ],
5531
- "description": "Display date for a history row (e.g. \"Jun 14, 2026\")."
5532
- },
5533
- {
5534
- "kind": "function",
5535
- "name": "isCostSessionRecord",
5536
- "return": {
5537
- "type": {
5538
- "text": "value is CostSessionRecord"
5539
- }
5540
- },
5541
- "parameters": [
5542
- {
5543
- "name": "value",
5544
- "type": {
5545
- "text": "unknown"
5546
- }
5583
+ },
5584
+ "default": "'rapid'",
5585
+ "description": "Design-system tag prefix, e.g. `'rapid'` for `rapid-segmented-control`.",
5586
+ "fieldName": "designSystemPrefix"
5547
5587
  }
5548
5588
  ],
5549
- "description": "Whether a value is a usable row. Exported because rows arrive by two routes and BOTH need it:\n`loadCostSessionHistory` filters the `localStorage` path here, and the assistant filters what a\nhost provider returns. A row that skips this check is not merely ignored downstream — a\nhalf-shaped `usage` sums to `NaN` through every total, and a missing `updatedAt` throws inside\nthe sort that orders the list, during render."
5589
+ "superclass": {
5590
+ "name": "GenesisElement",
5591
+ "package": "@genesislcap/web-core"
5592
+ },
5593
+ "tagName": "foundation-ai-agent-picker",
5594
+ "customElement": true
5550
5595
  }
5551
5596
  ],
5552
5597
  "exports": [
5553
5598
  {
5554
5599
  "kind": "js",
5555
- "name": "costHistoryStorageKey",
5600
+ "name": "AGENT_PICKER_AUTO_VALUE",
5556
5601
  "declaration": {
5557
- "name": "costHistoryStorageKey",
5558
- "module": "src/utils/cost-session-history.ts"
5602
+ "name": "AGENT_PICKER_AUTO_VALUE",
5603
+ "module": "src/components/agent-picker/agent-picker.ts"
5559
5604
  }
5560
5605
  },
5561
5606
  {
5562
5607
  "kind": "js",
5563
- "name": "loadCostSessionHistory",
5608
+ "name": "AgentPicker",
5564
5609
  "declaration": {
5565
- "name": "loadCostSessionHistory",
5566
- "module": "src/utils/cost-session-history.ts"
5610
+ "name": "AgentPicker",
5611
+ "module": "src/components/agent-picker/agent-picker.ts"
5567
5612
  }
5568
5613
  },
5569
5614
  {
5570
- "kind": "js",
5571
- "name": "saveCostSessionHistory",
5615
+ "kind": "custom-element-definition",
5616
+ "name": "foundation-ai-agent-picker",
5572
5617
  "declaration": {
5573
- "name": "saveCostSessionHistory",
5574
- "module": "src/utils/cost-session-history.ts"
5618
+ "name": "AgentPicker",
5619
+ "module": "src/components/agent-picker/agent-picker.ts"
5575
5620
  }
5576
- },
5621
+ }
5622
+ ]
5623
+ },
5624
+ {
5625
+ "kind": "javascript-module",
5626
+ "path": "src/components/agent-picker/index.ts",
5627
+ "declarations": [],
5628
+ "exports": [
5577
5629
  {
5578
5630
  "kind": "js",
5579
- "name": "upsertRecord",
5631
+ "name": "*",
5580
5632
  "declaration": {
5581
- "name": "upsertRecord",
5582
- "module": "src/utils/cost-session-history.ts"
5633
+ "name": "*",
5634
+ "package": "./agent-picker"
5583
5635
  }
5584
- },
5636
+ }
5637
+ ]
5638
+ },
5639
+ {
5640
+ "kind": "javascript-module",
5641
+ "path": "src/components/ai-driver/ai-driver.ts",
5642
+ "declarations": [],
5643
+ "exports": []
5644
+ },
5645
+ {
5646
+ "kind": "javascript-module",
5647
+ "path": "src/components/ai-driver/index.ts",
5648
+ "declarations": [],
5649
+ "exports": [
5585
5650
  {
5586
5651
  "kind": "js",
5587
- "name": "sortRecordsByRecency",
5652
+ "name": "AiDriver",
5588
5653
  "declaration": {
5589
- "name": "sortRecordsByRecency",
5590
- "module": "src/utils/cost-session-history.ts"
5654
+ "name": "AiDriver",
5655
+ "module": "./ai-driver"
5591
5656
  }
5592
- },
5657
+ }
5658
+ ]
5659
+ },
5660
+ {
5661
+ "kind": "javascript-module",
5662
+ "path": "src/components/chat-bubble/chat-bubble.styles.ts",
5663
+ "declarations": [
5593
5664
  {
5594
- "kind": "js",
5595
- "name": "resolveBankedUsage",
5596
- "declaration": {
5597
- "name": "resolveBankedUsage",
5598
- "module": "src/utils/cost-session-history.ts"
5599
- }
5600
- },
5665
+ "kind": "variable",
5666
+ "name": "styles",
5667
+ "default": "css`\n :host {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 9999;\n overflow: visible;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .bubble {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n cursor: grab;\n pointer-events: all;\n overflow: visible;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n transition:\n transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.25s ease;\n background: linear-gradient(135deg, #0e1628 0%, #192340 100%);\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n color: white;\n }\n\n .bubble img {\n width: 62px;\n height: 62px;\n border-radius: 50%;\n object-fit: cover;\n pointer-events: none;\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n }\n\n .bubble svg {\n pointer-events: none;\n }\n\n .bubble:hover,\n .bubble.hovered {\n transform: scale(1.18);\n }\n\n .bubble:hover img,\n .bubble.hovered img {\n box-shadow:\n 0 6px 32px rgb(0 100 255 / 55%),\n 0 0 0 3px rgb(0 120 255 / 35%);\n }\n\n .bubble:active {\n cursor: grabbing;\n }\n\n .tooltip {\n position: absolute;\n bottom: calc(100% + 10px);\n background: linear-gradient(135deg, #0078ff 0%, #0050d0 100%);\n color: #fff;\n font-size: 0.78em;\n font-weight: 600;\n letter-spacing: 0.03em;\n white-space: nowrap;\n padding: 5px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 14px rgb(0 120 255 / 45%);\n pointer-events: none;\n opacity: 0%;\n translate: 0 4px;\n transition:\n opacity 0.18s ease,\n translate 0.18s ease;\n }\n\n .tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: var(--arrow-left, 50%);\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #0050d0;\n }\n\n .bubble:hover .tooltip,\n .bubble.hovered .tooltip {\n opacity: 100%;\n translate: 0 0;\n }\n\n .pulse-ring {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n border: 2px solid rgb(0 120 255 / 50%);\n animation: pulse 2.5s ease-out infinite;\n pointer-events: none;\n }\n\n @keyframes pulse {\n 0% {\n transform: scale(1);\n opacity: 70%;\n }\n\n 70% {\n transform: scale(1.5);\n opacity: 0%;\n }\n\n 100% {\n transform: scale(1.5);\n opacity: 0%;\n }\n }\n\n .dialog {\n position: absolute;\n box-sizing: border-box;\n width: 400px;\n height: 520px;\n min-width: 300px;\n min-height: 300px;\n max-width: 50vw;\n max-height: calc(100vh - 40px);\n background: var(--neutral-layer-1);\n border-radius: 14px;\n box-shadow:\n 0 12px 50px rgb(0 0 0 / 55%),\n 0 0 0 1px rgb(255 255 255 / 6%);\n pointer-events: all;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n resize: both;\n border: 1px solid var(--neutral-stroke-rest);\n animation: dialog-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes dialog-in {\n from {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n\n to {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n }\n\n @keyframes dialog-out {\n from {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n\n to {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n }\n\n .dialog.closing {\n animation: dialog-out 0.2s cubic-bezier(0.4, 0, 0.6, 1) forwards;\n }\n\n .dialog-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n min-height: 0;\n }\n\n .dialog-content ::slotted(*) {\n display: flex;\n flex: 1;\n width: 100%;\n min-height: 0;\n }\n`"
5668
+ }
5669
+ ],
5670
+ "exports": [
5601
5671
  {
5602
5672
  "kind": "js",
5603
- "name": "clearCostSessionHistory",
5673
+ "name": "styles",
5604
5674
  "declaration": {
5605
- "name": "clearCostSessionHistory",
5606
- "module": "src/utils/cost-session-history.ts"
5675
+ "name": "styles",
5676
+ "module": "src/components/chat-bubble/chat-bubble.styles.ts"
5607
5677
  }
5608
- },
5678
+ }
5679
+ ]
5680
+ },
5681
+ {
5682
+ "kind": "javascript-module",
5683
+ "path": "src/components/chat-bubble/chat-bubble.template.ts",
5684
+ "declarations": [
5609
5685
  {
5610
- "kind": "js",
5611
- "name": "formatCostSessionDate",
5612
- "declaration": {
5613
- "name": "formatCostSessionDate",
5614
- "module": "src/utils/cost-session-history.ts"
5686
+ "kind": "function",
5687
+ "name": "AiChatBubbleTemplate",
5688
+ "return": {
5689
+ "type": {
5690
+ "text": "ViewTemplate<AiChatBubble>"
5691
+ }
5615
5692
  }
5616
- },
5693
+ }
5694
+ ],
5695
+ "exports": [
5617
5696
  {
5618
5697
  "kind": "js",
5619
- "name": "isCostSessionRecord",
5698
+ "name": "AiChatBubbleTemplate",
5620
5699
  "declaration": {
5621
- "name": "isCostSessionRecord",
5622
- "module": "src/utils/cost-session-history.ts"
5700
+ "name": "AiChatBubbleTemplate",
5701
+ "module": "src/components/chat-bubble/chat-bubble.template.ts"
5623
5702
  }
5624
5703
  }
5625
5704
  ]
5626
5705
  },
5627
5706
  {
5628
5707
  "kind": "javascript-module",
5629
- "path": "src/utils/derive-cost-session-title.ts",
5708
+ "path": "src/components/chat-bubble/chat-bubble.ts",
5630
5709
  "declarations": [
5631
5710
  {
5632
- "kind": "variable",
5633
- "name": "COST_SESSION_TITLE_MAX_WORDS",
5634
- "type": {
5635
- "text": "number"
5636
- },
5637
- "default": "10",
5638
- "description": "Max words kept from the first user turn when deriving a session title."
5639
- },
5640
- {
5641
- "kind": "variable",
5642
- "name": "COST_SESSION_TITLE_MAX_CHARS",
5643
- "type": {
5644
- "text": "number"
5645
- },
5646
- "default": "60",
5647
- "description": "Hard cap on derived title length (ellipsis included)."
5648
- },
5649
- {
5650
- "kind": "function",
5651
- "name": "getChatMessageText",
5652
- "return": {
5653
- "type": {
5654
- "text": "string"
5655
- }
5656
- },
5657
- "parameters": [
5711
+ "kind": "class",
5712
+ "description": "Floating chat bubble component that provides a draggable orb and dialog panel\nfor embedding an AI assistant without a full-page layout.",
5713
+ "name": "AiChatBubble",
5714
+ "members": [
5658
5715
  {
5659
- "name": "content",
5716
+ "kind": "field",
5717
+ "name": "title",
5660
5718
  "type": {
5661
- "text": "ChatMessage['content']"
5719
+ "text": "string"
5720
+ },
5721
+ "default": "'AI Assistant'"
5722
+ },
5723
+ {
5724
+ "kind": "field",
5725
+ "name": "imageSrc",
5726
+ "type": {
5727
+ "text": "string | undefined"
5662
5728
  }
5663
- }
5664
- ],
5665
- "description": "Plain text from a chat message `content` field."
5666
- },
5667
- {
5668
- "kind": "function",
5669
- "name": "truncateCostSessionTitle",
5670
- "return": {
5671
- "type": {
5672
- "text": "string"
5673
- }
5674
- },
5675
- "parameters": [
5729
+ },
5676
5730
  {
5677
- "name": "text",
5731
+ "kind": "field",
5732
+ "name": "designSystemPrefix",
5678
5733
  "type": {
5679
5734
  "text": "string"
5680
- }
5681
- }
5682
- ],
5683
- "description": "Trim a raw user phrase to a display-friendly session title: first sentence,\nthen word cap, then character cap."
5684
- },
5685
- {
5686
- "kind": "function",
5687
- "name": "deriveCostSessionTitleFromMessages",
5688
- "return": {
5689
- "type": {
5690
- "text": "string | undefined"
5691
- }
5692
- },
5693
- "parameters": [
5735
+ },
5736
+ "default": "'rapid'"
5737
+ },
5694
5738
  {
5695
- "name": "messages",
5739
+ "kind": "field",
5740
+ "name": "bubbleLeft",
5696
5741
  "type": {
5697
- "text": "readonly ChatMessage[]"
5698
- }
5699
- }
5700
- ],
5701
- "description": "First non-empty user message in the thread, shortened for cost history."
5702
- },
5703
- {
5704
- "kind": "function",
5705
- "name": "resolveCostSessionTitle",
5706
- "return": {
5707
- "type": {
5708
- "text": "string"
5709
- }
5710
- },
5711
- "parameters": [
5742
+ "text": "number"
5743
+ },
5744
+ "default": "0",
5745
+ "description": "Current left position of the bubble orb (px from viewport left). @internal"
5746
+ },
5712
5747
  {
5713
- "name": "input",
5748
+ "kind": "field",
5749
+ "name": "bubbleTop",
5714
5750
  "type": {
5715
- "text": "ResolveCostSessionTitleInput"
5716
- }
5717
- }
5718
- ],
5719
- "description": "Resolve the label stored on a finalized cost session record."
5720
- }
5721
- ],
5722
- "exports": [
5723
- {
5724
- "kind": "js",
5725
- "name": "COST_SESSION_TITLE_MAX_WORDS",
5726
- "declaration": {
5727
- "name": "COST_SESSION_TITLE_MAX_WORDS",
5728
- "module": "src/utils/derive-cost-session-title.ts"
5729
- }
5730
- },
5731
- {
5732
- "kind": "js",
5733
- "name": "COST_SESSION_TITLE_MAX_CHARS",
5734
- "declaration": {
5735
- "name": "COST_SESSION_TITLE_MAX_CHARS",
5736
- "module": "src/utils/derive-cost-session-title.ts"
5737
- }
5738
- },
5739
- {
5740
- "kind": "js",
5741
- "name": "getChatMessageText",
5742
- "declaration": {
5743
- "name": "getChatMessageText",
5744
- "module": "src/utils/derive-cost-session-title.ts"
5745
- }
5746
- },
5747
- {
5748
- "kind": "js",
5749
- "name": "truncateCostSessionTitle",
5750
- "declaration": {
5751
- "name": "truncateCostSessionTitle",
5752
- "module": "src/utils/derive-cost-session-title.ts"
5753
- }
5754
- },
5755
- {
5756
- "kind": "js",
5757
- "name": "deriveCostSessionTitleFromMessages",
5758
- "declaration": {
5759
- "name": "deriveCostSessionTitleFromMessages",
5760
- "module": "src/utils/derive-cost-session-title.ts"
5761
- }
5762
- },
5763
- {
5764
- "kind": "js",
5765
- "name": "resolveCostSessionTitle",
5766
- "declaration": {
5767
- "name": "resolveCostSessionTitle",
5768
- "module": "src/utils/derive-cost-session-title.ts"
5769
- }
5770
- }
5771
- ]
5772
- },
5773
- {
5774
- "kind": "javascript-module",
5775
- "path": "src/utils/flatten-sub-agent-messages.ts",
5776
- "declarations": [],
5777
- "exports": []
5778
- },
5779
- {
5780
- "kind": "javascript-module",
5781
- "path": "src/utils/format-usd.ts",
5782
- "declarations": [
5783
- {
5784
- "kind": "function",
5785
- "name": "formatUsdAmount",
5786
- "return": {
5787
- "type": {
5788
- "text": "string"
5789
- }
5790
- },
5791
- "parameters": [
5751
+ "text": "number"
5752
+ },
5753
+ "default": "0",
5754
+ "description": "Current top position of the bubble orb (px from viewport top). @internal"
5755
+ },
5792
5756
  {
5793
- "name": "value",
5757
+ "kind": "field",
5758
+ "name": "dialogLeft",
5794
5759
  "type": {
5795
5760
  "text": "number"
5796
- }
5797
- }
5798
- ],
5799
- "description": "The bare amount, no currency sign — `(12.3) => \"12.30\"`.\n\nExists for the settings modal's cost blocks, where the `$` is a separately\nstyled `<span class=\"prefix\">` and folding it into the text would change the\nDOM those styles target."
5800
- },
5801
- {
5802
- "kind": "function",
5803
- "name": "formatUsd",
5804
- "return": {
5805
- "type": {
5806
- "text": "string"
5807
- }
5808
- },
5809
- "parameters": [
5761
+ },
5762
+ "default": "0",
5763
+ "description": "Current left position of the dialog panel (px from viewport left). @internal"
5764
+ },
5810
5765
  {
5811
- "name": "value",
5766
+ "kind": "field",
5767
+ "name": "dialogTop",
5812
5768
  "type": {
5813
5769
  "text": "number"
5814
- }
5815
- }
5816
- ],
5817
- "description": "A signed USD figure — `(12.3) => \"$12.30\"`."
5818
- }
5819
- ],
5820
- "exports": [
5821
- {
5822
- "kind": "js",
5823
- "name": "formatUsdAmount",
5824
- "declaration": {
5825
- "name": "formatUsdAmount",
5826
- "module": "src/utils/format-usd.ts"
5827
- }
5828
- },
5829
- {
5830
- "kind": "js",
5831
- "name": "formatUsd",
5832
- "declaration": {
5833
- "name": "formatUsd",
5834
- "module": "src/utils/format-usd.ts"
5835
- }
5836
- }
5837
- ]
5838
- },
5839
- {
5840
- "kind": "javascript-module",
5841
- "path": "src/utils/history-transform.ts",
5842
- "declarations": [
5843
- {
5844
- "kind": "function",
5845
- "name": "transformHistoryForAgent",
5846
- "return": {
5847
- "type": {
5848
- "text": "ChatMessage[]"
5849
- }
5850
- },
5851
- "parameters": [
5770
+ },
5771
+ "default": "0",
5772
+ "description": "Current top position of the dialog panel (px from viewport top). @internal"
5773
+ },
5852
5774
  {
5853
- "name": "history",
5775
+ "kind": "field",
5776
+ "name": "dialogWidth",
5854
5777
  "type": {
5855
- "text": "ChatMessage[]"
5856
- }
5778
+ "text": "number"
5779
+ },
5780
+ "default": "400",
5781
+ "description": "Current rendered width of the dialog panel (px). @internal"
5857
5782
  },
5858
5783
  {
5859
- "name": "agentName",
5784
+ "kind": "field",
5785
+ "name": "dialogHeight",
5860
5786
  "type": {
5861
- "text": "string"
5862
- }
5863
- }
5864
- ],
5865
- "description": "Prepares history for the LLM only: masks tool call args and results from other\nagents so the active specialist is not confused by tools it does not have.\nCanonical history in `ChatDriver` stays unmasked for UI and logging."
5866
- },
5867
- {
5868
- "kind": "function",
5869
- "name": "applyHistoryCap",
5870
- "return": {
5871
- "type": {
5872
- "text": "ChatMessage[]"
5873
- }
5874
- },
5875
- "parameters": [
5787
+ "text": "number"
5788
+ },
5789
+ "default": "520",
5790
+ "description": "Current rendered height of the dialog panel (px). @internal"
5791
+ },
5876
5792
  {
5877
- "name": "history",
5793
+ "kind": "field",
5794
+ "name": "dialogOpen",
5878
5795
  "type": {
5879
- "text": "readonly ChatMessage[]"
5880
- }
5796
+ "text": "boolean"
5797
+ },
5798
+ "default": "false",
5799
+ "description": "Whether the dialog panel is currently open. @internal"
5881
5800
  },
5882
5801
  {
5883
- "name": "cap",
5802
+ "kind": "field",
5803
+ "name": "hovered",
5884
5804
  "type": {
5885
- "text": "number"
5886
- }
5887
- }
5888
- ],
5889
- "description": "Applies a history cap for sub-agent context: the last `cap` messages are passed\nverbatim; older messages have their tool payloads masked."
5890
- },
5891
- {
5892
- "kind": "variable",
5893
- "name": "COMPACT_KEEP_RECENT_MESSAGES",
5894
- "type": {
5895
- "text": "number"
5896
- },
5897
- "default": "4",
5898
- "description": "Recent messages kept verbatim when compacting; everything earlier is summarized."
5899
- },
5900
- {
5901
- "kind": "variable",
5902
- "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
5903
- "type": {
5904
- "text": "number"
5905
- },
5906
- "default": "4",
5907
- "description": "Don't bother compacting unless at least this many messages would be summarized."
5908
- },
5909
- {
5910
- "kind": "function",
5911
- "name": "findCompactionCut",
5912
- "return": {
5913
- "type": {
5914
- "text": "number | null"
5915
- }
5916
- },
5917
- "parameters": [
5805
+ "text": "boolean"
5806
+ },
5807
+ "default": "false",
5808
+ "description": "Whether the bubble orb is currently hovered. @internal"
5809
+ },
5918
5810
  {
5919
- "name": "history",
5811
+ "kind": "field",
5812
+ "name": "unsubBus",
5920
5813
  "type": {
5921
- "text": "readonly ChatMessage[]"
5922
- }
5923
- }
5924
- ],
5925
- "description": "Index at which the verbatim tail begins — everything before it gets summarized.\nThe tail starts on a clean turn boundary (a `user` message): a user message\nnever appears between a `tool_use` and its `tool_result`, so cutting there\nguarantees the summarized region is tool-pair-balanced (compaction only runs\nwhen idle, so history is turn-complete). Returns `null` when there is nothing\nworth compacting (too short, or no earlier user boundary) — e.g. a freshly\nrestored session whose only user turn is the current one."
5926
- },
5927
- {
5928
- "kind": "function",
5929
- "name": "buildCompactionSummaryPrompt",
5930
- "return": {
5931
- "type": {
5932
- "text": "{\n systemPrompt: string;\n userMessage: string;\n}"
5933
- }
5934
- },
5935
- "parameters": [
5814
+ "text": "() => void | undefined"
5815
+ },
5816
+ "privacy": "private"
5817
+ },
5936
5818
  {
5937
- "name": "toCompact",
5819
+ "kind": "field",
5820
+ "name": "injectedCloseBtn",
5938
5821
  "type": {
5939
- "text": "readonly ChatMessage[]"
5940
- }
5941
- }
5942
- ],
5943
- "description": "Build the one-shot summarizer prompt for a run of older messages (used with\n`AIProvider.prompt`). Mirrors the `<conversation_history>` block convention\nthat suggestions use. Any existing `compacted-summary` at the head is folded\nin (rendered as prior-summary context), so rolling re-compaction produces one\ncombined summary."
5944
- },
5945
- {
5946
- "kind": "function",
5947
- "name": "normalizeForProvider",
5948
- "return": {
5949
- "type": {
5950
- "text": "ChatMessage[]"
5951
- }
5952
- },
5953
- "parameters": [
5822
+ "text": "Element | undefined"
5823
+ },
5824
+ "privacy": "private"
5825
+ },
5954
5826
  {
5955
- "name": "history",
5827
+ "kind": "field",
5828
+ "name": "resizeObserver",
5956
5829
  "type": {
5957
- "text": "readonly ChatMessage[]"
5958
- }
5959
- }
5960
- ],
5961
- "description": "Normalize history for a provider request (GENC-1351 §5.7): translate each\n`compacted-summary` message into a `user` message that wraps its summary in a\n`<conversation_summary>` block, so the raw display-only role never reaches a\ntransport. Other roles pass through untouched. Pure — returns a new array.\n\nApplied at the request-build chokepoint *before* per-agent masking, so the\nguarantee holds for every provider/transport regardless of any optional\nper-agent history transform."
5962
- },
5963
- {
5964
- "kind": "function",
5965
- "name": "compactionDividerLabel",
5966
- "return": {
5967
- "type": {
5968
- "text": "string"
5969
- }
5970
- },
5971
- "parameters": [
5830
+ "text": "ResizeObserver | undefined"
5831
+ },
5832
+ "privacy": "private"
5833
+ },
5972
5834
  {
5973
- "name": "m",
5835
+ "kind": "field",
5836
+ "name": "dragging",
5974
5837
  "type": {
5975
- "text": "ChatMessage"
5976
- }
5977
- }
5978
- ],
5979
- "description": "Human-facing divider label for a `compacted-summary` message."
5980
- }
5981
- ],
5982
- "exports": [
5983
- {
5984
- "kind": "js",
5985
- "name": "transformHistoryForAgent",
5986
- "declaration": {
5987
- "name": "transformHistoryForAgent",
5988
- "module": "src/utils/history-transform.ts"
5989
- }
5990
- },
5991
- {
5992
- "kind": "js",
5993
- "name": "applyHistoryCap",
5994
- "declaration": {
5995
- "name": "applyHistoryCap",
5996
- "module": "src/utils/history-transform.ts"
5997
- }
5998
- },
5999
- {
6000
- "kind": "js",
6001
- "name": "COMPACT_KEEP_RECENT_MESSAGES",
6002
- "declaration": {
6003
- "name": "COMPACT_KEEP_RECENT_MESSAGES",
6004
- "module": "src/utils/history-transform.ts"
6005
- }
6006
- },
6007
- {
6008
- "kind": "js",
6009
- "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
6010
- "declaration": {
6011
- "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
6012
- "module": "src/utils/history-transform.ts"
6013
- }
6014
- },
6015
- {
6016
- "kind": "js",
6017
- "name": "findCompactionCut",
6018
- "declaration": {
6019
- "name": "findCompactionCut",
6020
- "module": "src/utils/history-transform.ts"
6021
- }
6022
- },
6023
- {
6024
- "kind": "js",
6025
- "name": "buildCompactionSummaryPrompt",
6026
- "declaration": {
6027
- "name": "buildCompactionSummaryPrompt",
6028
- "module": "src/utils/history-transform.ts"
6029
- }
6030
- },
6031
- {
6032
- "kind": "js",
6033
- "name": "normalizeForProvider",
6034
- "declaration": {
6035
- "name": "normalizeForProvider",
6036
- "module": "src/utils/history-transform.ts"
6037
- }
6038
- },
6039
- {
6040
- "kind": "js",
6041
- "name": "compactionDividerLabel",
6042
- "declaration": {
6043
- "name": "compactionDividerLabel",
6044
- "module": "src/utils/history-transform.ts"
6045
- }
6046
- }
6047
- ]
6048
- },
6049
- {
6050
- "kind": "javascript-module",
6051
- "path": "src/utils/index.ts",
6052
- "declarations": [],
6053
- "exports": [
6054
- {
6055
- "kind": "js",
6056
- "name": "*",
6057
- "declaration": {
6058
- "name": "*",
6059
- "package": "./animated-panel-toggle"
6060
- }
6061
- },
6062
- {
6063
- "kind": "js",
6064
- "name": "*",
6065
- "declaration": {
6066
- "name": "*",
6067
- "package": "./logger"
6068
- }
6069
- },
6070
- {
6071
- "kind": "js",
6072
- "name": "*",
6073
- "declaration": {
6074
- "name": "*",
6075
- "package": "./tool-fold"
6076
- }
6077
- }
6078
- ]
6079
- },
6080
- {
6081
- "kind": "javascript-module",
6082
- "path": "src/utils/logger.ts",
6083
- "declarations": [
6084
- {
6085
- "kind": "variable",
6086
- "name": "logger"
6087
- }
6088
- ],
6089
- "exports": [
6090
- {
6091
- "kind": "js",
6092
- "name": "logger",
6093
- "declaration": {
6094
- "name": "logger",
6095
- "module": "src/utils/logger.ts"
6096
- }
6097
- }
6098
- ]
6099
- },
6100
- {
6101
- "kind": "javascript-module",
6102
- "path": "src/utils/message-partition.ts",
6103
- "declarations": [
6104
- {
6105
- "kind": "function",
6106
- "name": "isSessionNoticeMarker",
6107
- "return": {
6108
- "type": {
6109
- "text": "boolean"
6110
- }
6111
- },
6112
- "parameters": [
5838
+ "text": "boolean"
5839
+ },
5840
+ "privacy": "private",
5841
+ "default": "false"
5842
+ },
6113
5843
  {
6114
- "name": "m",
5844
+ "kind": "field",
5845
+ "name": "dragTarget",
6115
5846
  "type": {
6116
- "text": "ChatMessage"
6117
- }
6118
- }
6119
- ],
6120
- "description": "Either restore notice — both display-only, stripped before persist. @internal"
6121
- },
6122
- {
6123
- "kind": "function",
6124
- "name": "trailingInteractionRow",
6125
- "return": {
6126
- "type": {
6127
- "text": "ChatMessage[]"
6128
- }
6129
- },
6130
- "parameters": [
5847
+ "text": "'bubble' | 'dialog' | null"
5848
+ },
5849
+ "privacy": "private",
5850
+ "default": "null"
5851
+ },
6131
5852
  {
6132
- "name": "messages",
5853
+ "kind": "field",
5854
+ "name": "startMouseX",
6133
5855
  "type": {
6134
- "text": "ChatMessage[]"
6135
- }
6136
- }
6137
- ],
6138
- "description": "The trailing interaction to render in a pinned slot, separate from the\nfiltered list. Empty unless the most recent message carries an interaction.\n\nPinning the trailing interaction keeps its widget out of the list's\nreconciliation: FAST's `repeat` has no key function and reconciles by array\nindex, so re-filtering the list (a cog-menu toggle) would otherwise rebind an\nexisting row to a different message and re-create the live widget — discarding\nany in-progress input. The slot's binding reads only `messages` and returns\nthe same message object across toggles, so the widget's DOM is preserved.\n\nIt is the *trailing* message (resolved or not), not \"unresolved only\", so that\nanswering an interaction leaves it in the slot — `resolved` is forwarded in\nplace with no re-render. It rejoins the list naturally once a newer message\narrives and it is no longer last."
6139
- },
6140
- {
6141
- "kind": "function",
6142
- "name": "filterVisibleMessages",
6143
- "return": {
6144
- "type": {
6145
- "text": "ChatMessage[]"
6146
- }
6147
- },
6148
- "parameters": [
5856
+ "text": "number"
5857
+ },
5858
+ "privacy": "private",
5859
+ "default": "0"
5860
+ },
6149
5861
  {
6150
- "name": "messages",
5862
+ "kind": "field",
5863
+ "name": "startMouseY",
6151
5864
  "type": {
6152
- "text": "ChatMessage[]"
6153
- }
5865
+ "text": "number"
5866
+ },
5867
+ "privacy": "private",
5868
+ "default": "0"
6154
5869
  },
6155
5870
  {
6156
- "name": "toggles",
6157
- "type": {
6158
- "text": "MessageFilterState"
6159
- }
6160
- }
6161
- ],
6162
- "description": "Filters messages for the scrolling list per the current cog-menu toggles.\n\nThe trailing interaction (see `trailingInteractionRow`) must be removed\nbefore calling, as it is rendered separately."
6163
- }
6164
- ],
6165
- "exports": [
6166
- {
6167
- "kind": "js",
6168
- "name": "isSessionNoticeMarker",
6169
- "declaration": {
6170
- "name": "isSessionNoticeMarker",
6171
- "module": "src/utils/message-partition.ts"
6172
- }
6173
- },
6174
- {
6175
- "kind": "js",
6176
- "name": "trailingInteractionRow",
6177
- "declaration": {
6178
- "name": "trailingInteractionRow",
6179
- "module": "src/utils/message-partition.ts"
6180
- }
6181
- },
6182
- {
6183
- "kind": "js",
6184
- "name": "filterVisibleMessages",
6185
- "declaration": {
6186
- "name": "filterVisibleMessages",
6187
- "module": "src/utils/message-partition.ts"
6188
- }
6189
- }
6190
- ]
6191
- },
6192
- {
6193
- "kind": "javascript-module",
6194
- "path": "src/utils/resolve-cost-history-config.ts",
6195
- "declarations": [
6196
- {
6197
- "kind": "function",
6198
- "name": "resolveCostHistoryConfig",
6199
- "return": {
6200
- "type": {
6201
- "text": "ResolvedCostHistoryConfig"
6202
- }
6203
- },
6204
- "parameters": [
6205
- {
6206
- "name": "chatConfig",
6207
- "type": {
6208
- "text": "ChatConfig"
6209
- }
6210
- }
6211
- ],
6212
- "description": "Resolve `chatConfig.costHistory` with defaults applied."
6213
- }
6214
- ],
6215
- "exports": [
6216
- {
6217
- "kind": "js",
6218
- "name": "resolveCostHistoryConfig",
6219
- "declaration": {
6220
- "name": "resolveCostHistoryConfig",
6221
- "module": "src/utils/resolve-cost-history-config.ts"
6222
- }
6223
- }
6224
- ]
6225
- },
6226
- {
6227
- "kind": "javascript-module",
6228
- "path": "src/utils/resolve-preference-baseline.ts",
6229
- "declarations": [],
6230
- "exports": []
6231
- },
6232
- {
6233
- "kind": "javascript-module",
6234
- "path": "src/utils/strip-agent-handlers.ts",
6235
- "declarations": [],
6236
- "exports": []
6237
- },
6238
- {
6239
- "kind": "javascript-module",
6240
- "path": "src/utils/sum-costs.ts",
6241
- "declarations": [],
6242
- "exports": []
6243
- },
6244
- {
6245
- "kind": "javascript-module",
6246
- "path": "src/utils/sum-tokens.ts",
6247
- "declarations": [
6248
- {
6249
- "kind": "function",
6250
- "name": "formatCompactTokenCountSafe",
6251
- "return": {
6252
- "type": {
6253
- "text": "string"
6254
- }
6255
- },
6256
- "parameters": [
6257
- {
6258
- "name": "tokens",
5871
+ "kind": "field",
5872
+ "name": "startElemLeft",
6259
5873
  "type": {
6260
5874
  "text": "number"
6261
- }
6262
- }
6263
- ],
6264
- "description": "formatCompactTokenCount passed through DOMPurify for `:innerHTML` bindings."
6265
- }
6266
- ],
6267
- "exports": [
6268
- {
6269
- "kind": "js",
6270
- "name": "formatCompactTokenCountSafe",
6271
- "declaration": {
6272
- "name": "formatCompactTokenCountSafe",
6273
- "module": "src/utils/sum-tokens.ts"
6274
- }
6275
- }
6276
- ]
6277
- },
6278
- {
6279
- "kind": "javascript-module",
6280
- "path": "src/utils/sum-usage.ts",
6281
- "declarations": [],
6282
- "exports": []
6283
- },
6284
- {
6285
- "kind": "javascript-module",
6286
- "path": "src/utils/tool-fold.ts",
6287
- "declarations": [
6288
- {
6289
- "kind": "function",
6290
- "name": "createToolFold",
6291
- "return": {
6292
- "type": {
6293
- "text": "ToolFoldResult"
6294
- }
6295
- },
6296
- "parameters": [
6297
- {
6298
- "name": "config",
6299
- "type": {
6300
- "text": "{\n /** Facade tool name — used as the tool name the model calls to open this fold. */\n name: string;\n /**\n * Description shown to the model for the facade tool.\n * If omitted, auto-generated from the inner tool names:\n * \"Contains: tool_a, tool_b. Invoke to access these tools.\"\n */\n description?: string;\n /**\n * Optional usage guidance injected into the fold-open response message.\n * Not included in the facade description — only visible once the fold is open.\n */\n usageNotes?: string;\n /** Inner tool definitions. Same type as `AgentConfig.toolDefinitions`. */\n tools: ChatToolDefinition[];\n /** Inner tool handlers. Same type as `AgentConfig.toolHandlers`. */\n handlers: ChatToolHandlers;\n /**\n * When true (default), all other tools are removed when this fold opens.\n * When false, sibling tools remain available alongside the inner tools.\n */\n exclusive?: boolean;\n}"
6301
- }
6302
- }
6303
- ],
6304
- "description": "Creates a tool fold — a facade that hides a group of related tools behind a single\nnamed entry, revealing them progressively when the model invokes the facade.\n\nThe `tools` and `handlers` parameters accept the exact same types as `AgentConfig.toolDefinitions`\nand `AgentConfig.toolHandlers`, so existing tool creation functions can be passed in unchanged."
6305
- },
6306
- {
6307
- "kind": "function",
6308
- "name": "expandToolTree",
6309
- "return": {
6310
- "type": {
6311
- "text": "ToolTreeNode[]"
6312
- }
6313
- },
6314
- "parameters": [
6315
- {
6316
- "name": "definitions",
6317
- "type": {
6318
- "text": "ChatToolDefinition[]"
6319
- }
5875
+ },
5876
+ "privacy": "private",
5877
+ "default": "0"
6320
5878
  },
6321
5879
  {
6322
- "name": "handlers",
5880
+ "kind": "field",
5881
+ "name": "startElemTop",
6323
5882
  "type": {
6324
- "text": "ChatToolHandlers"
6325
- }
6326
- }
6327
- ],
6328
- "description": "Expands a flat list of tool definitions into a nested tree by resolving fold\nmetadata from the corresponding handlers. Use this for debug output so the\nfull tool hierarchy is visible rather than just the top-level facade names."
6329
- }
6330
- ],
6331
- "exports": [
6332
- {
6333
- "kind": "js",
6334
- "name": "createToolFold",
6335
- "declaration": {
6336
- "name": "createToolFold",
6337
- "module": "src/utils/tool-fold.ts"
6338
- }
6339
- },
6340
- {
6341
- "kind": "js",
6342
- "name": "expandToolTree",
6343
- "declaration": {
6344
- "name": "expandToolTree",
6345
- "module": "src/utils/tool-fold.ts"
6346
- }
6347
- }
6348
- ]
6349
- },
6350
- {
6351
- "kind": "javascript-module",
6352
- "path": "src/utils/with-timeout.ts",
6353
- "declarations": [],
6354
- "exports": []
6355
- },
6356
- {
6357
- "kind": "javascript-module",
6358
- "path": "src/components/activity-halo/activity-halo.ts",
6359
- "declarations": [
6360
- {
6361
- "kind": "class",
6362
- "description": "Wrapper component that shows an `<ai-halo-overlay>` when the AI assistant\nis executing one of the declared tool names.",
6363
- "name": "AiActivityHalo",
6364
- "members": [
5883
+ "text": "number"
5884
+ },
5885
+ "privacy": "private",
5886
+ "default": "0"
5887
+ },
6365
5888
  {
6366
5889
  "kind": "field",
6367
- "name": "tools",
5890
+ "name": "hasDragged",
6368
5891
  "type": {
6369
- "text": "string"
5892
+ "text": "boolean"
6370
5893
  },
6371
- "default": "''",
6372
- "description": "Comma-separated list of tool names this element reacts to."
5894
+ "privacy": "private",
5895
+ "default": "false"
6373
5896
  },
6374
5897
  {
6375
5898
  "kind": "field",
6376
- "name": "minShowTime",
5899
+ "name": "latestMouseX",
6377
5900
  "type": {
6378
5901
  "text": "number"
6379
5902
  },
6380
- "default": "600",
6381
- "description": "Minimum time in ms the halo stays visible after activation."
5903
+ "privacy": "private",
5904
+ "default": "0"
6382
5905
  },
6383
5906
  {
6384
5907
  "kind": "field",
6385
- "name": "unsubBus",
5908
+ "name": "latestMouseY",
6386
5909
  "type": {
6387
- "text": "() => void | undefined"
5910
+ "text": "number"
6388
5911
  },
6389
- "privacy": "private"
5912
+ "privacy": "private",
5913
+ "default": "0"
6390
5914
  },
6391
5915
  {
6392
5916
  "kind": "field",
6393
- "name": "hideTimeout",
5917
+ "name": "rafHandle",
6394
5918
  "type": {
6395
- "text": "ReturnType<typeof setTimeout> | undefined"
5919
+ "text": "number | undefined"
6396
5920
  },
6397
5921
  "privacy": "private"
6398
5922
  },
6399
5923
  {
6400
5924
  "kind": "field",
6401
- "name": "startTime",
5925
+ "name": "closeAbort",
6402
5926
  "type": {
6403
- "text": "number"
5927
+ "text": "AbortController | undefined"
6404
5928
  },
6405
- "privacy": "private",
6406
- "default": "0"
5929
+ "privacy": "private"
6407
5930
  },
6408
5931
  {
6409
5932
  "kind": "method",
6410
- "name": "activate",
6411
- "privacy": "private"
5933
+ "name": "onBubbleMouseEnter"
6412
5934
  },
6413
5935
  {
6414
5936
  "kind": "method",
6415
- "name": "scheduleDeactivate",
6416
- "privacy": "private"
6417
- }
6418
- ],
6419
- "attributes": [
5937
+ "name": "onBubbleMouseLeave"
5938
+ },
6420
5939
  {
6421
- "name": "tools",
6422
- "type": {
6423
- "text": "string"
6424
- },
6425
- "default": "''",
6426
- "description": "Comma-separated list of tool names this element reacts to.",
6427
- "fieldName": "tools"
5940
+ "kind": "method",
5941
+ "name": "dialogOpenChanged",
5942
+ "parameters": [
5943
+ {
5944
+ "name": "_",
5945
+ "type": {
5946
+ "text": "boolean"
5947
+ }
5948
+ },
5949
+ {
5950
+ "name": "next",
5951
+ "type": {
5952
+ "text": "boolean"
5953
+ }
5954
+ }
5955
+ ]
6428
5956
  },
6429
5957
  {
6430
- "name": "min-show-time",
5958
+ "kind": "method",
5959
+ "name": "dialogContentChanged",
5960
+ "parameters": [
5961
+ {
5962
+ "name": "_",
5963
+ "type": {
5964
+ "text": "HTMLElement[]"
5965
+ }
5966
+ },
5967
+ {
5968
+ "name": "next",
5969
+ "type": {
5970
+ "text": "HTMLElement[]"
5971
+ }
5972
+ }
5973
+ ]
5974
+ },
5975
+ {
5976
+ "kind": "method",
5977
+ "name": "injectCloseButton",
5978
+ "privacy": "private",
5979
+ "parameters": [
5980
+ {
5981
+ "name": "assistant",
5982
+ "type": {
5983
+ "text": "Element"
5984
+ }
5985
+ }
5986
+ ]
5987
+ },
5988
+ {
5989
+ "kind": "method",
5990
+ "name": "onBubbleMouseDown",
5991
+ "parameters": [
5992
+ {
5993
+ "name": "e",
5994
+ "type": {
5995
+ "text": "MouseEvent"
5996
+ }
5997
+ }
5998
+ ]
5999
+ },
6000
+ {
6001
+ "kind": "method",
6002
+ "name": "onDialogHeaderMouseDown",
6003
+ "parameters": [
6004
+ {
6005
+ "name": "e",
6006
+ "type": {
6007
+ "text": "CustomEvent<{ clientX: number; clientY: number }>"
6008
+ }
6009
+ }
6010
+ ]
6011
+ },
6012
+ {
6013
+ "kind": "field",
6014
+ "name": "onCloseBtnClick",
6015
+ "privacy": "private",
6016
+ "readonly": true
6017
+ },
6018
+ {
6019
+ "kind": "method",
6020
+ "name": "cancelClose",
6021
+ "privacy": "private"
6022
+ },
6023
+ {
6024
+ "kind": "method",
6025
+ "name": "startCloseDialog",
6026
+ "privacy": "private"
6027
+ },
6028
+ {
6029
+ "kind": "field",
6030
+ "name": "onDocMouseMove",
6031
+ "privacy": "private"
6032
+ },
6033
+ {
6034
+ "kind": "field",
6035
+ "name": "applyDragFrame",
6036
+ "privacy": "private"
6037
+ },
6038
+ {
6039
+ "kind": "field",
6040
+ "name": "onDocMouseUp",
6041
+ "privacy": "private"
6042
+ },
6043
+ {
6044
+ "kind": "method",
6045
+ "name": "onPopout"
6046
+ },
6047
+ {
6048
+ "kind": "method",
6049
+ "name": "onCloseDialog"
6050
+ },
6051
+ {
6052
+ "kind": "field",
6053
+ "name": "tooltipStyle",
6431
6054
  "type": {
6432
- "text": "number"
6055
+ "text": "string"
6433
6056
  },
6434
- "default": "DEFAULT_MIN_SHOW_TIME",
6435
- "description": "Minimum time in ms the halo stays visible after activation.",
6436
- "resolveInitializer": {
6437
- "module": "src/components/activity-halo/activity-halo.ts"
6057
+ "readonly": true
6058
+ }
6059
+ ],
6060
+ "attributes": [
6061
+ {
6062
+ "name": "title",
6063
+ "type": {
6064
+ "text": "string"
6438
6065
  },
6439
- "fieldName": "minShowTime"
6066
+ "default": "'AI Assistant'",
6067
+ "fieldName": "title"
6068
+ },
6069
+ {
6070
+ "name": "image-src",
6071
+ "type": {
6072
+ "text": "string | undefined"
6073
+ },
6074
+ "fieldName": "imageSrc"
6075
+ },
6076
+ {
6077
+ "name": "design-system-prefix",
6078
+ "type": {
6079
+ "text": "string"
6080
+ },
6081
+ "default": "'rapid'",
6082
+ "fieldName": "designSystemPrefix"
6440
6083
  }
6441
6084
  ],
6442
6085
  "superclass": {
6443
6086
  "name": "GenesisElement",
6444
6087
  "package": "@genesislcap/web-core"
6445
6088
  },
6446
- "tagName": "ai-activity-halo",
6089
+ "tagName": "foundation-ai-chat-bubble",
6447
6090
  "customElement": true
6448
6091
  }
6449
6092
  ],
6450
6093
  "exports": [
6451
6094
  {
6452
6095
  "kind": "js",
6453
- "name": "AiActivityHalo",
6096
+ "name": "AiChatBubble",
6454
6097
  "declaration": {
6455
- "name": "AiActivityHalo",
6456
- "module": "src/components/activity-halo/activity-halo.ts"
6098
+ "name": "AiChatBubble",
6099
+ "module": "src/components/chat-bubble/chat-bubble.ts"
6457
6100
  }
6458
6101
  },
6459
6102
  {
6460
6103
  "kind": "custom-element-definition",
6461
- "name": "ai-activity-halo",
6104
+ "name": "foundation-ai-chat-bubble",
6462
6105
  "declaration": {
6463
- "name": "AiActivityHalo",
6464
- "module": "src/components/activity-halo/activity-halo.ts"
6106
+ "name": "AiChatBubble",
6107
+ "module": "src/components/chat-bubble/chat-bubble.ts"
6465
6108
  }
6466
6109
  }
6467
6110
  ]
6468
6111
  },
6469
6112
  {
6470
6113
  "kind": "javascript-module",
6471
- "path": "src/components/agent-picker/agent-picker.constants.ts",
6472
- "declarations": [
6473
- {
6474
- "kind": "variable",
6475
- "name": "AGENT_PICKER_AUTO_VALUE",
6476
- "type": {
6477
- "text": "string"
6478
- },
6479
- "default": "'__auto__'",
6480
- "description": "Sentinel value used by the segmented control / select to represent \"Auto\"."
6481
- }
6482
- ],
6114
+ "path": "src/components/chat-bubble/index.ts",
6115
+ "declarations": [],
6483
6116
  "exports": [
6484
6117
  {
6485
6118
  "kind": "js",
6486
- "name": "AGENT_PICKER_AUTO_VALUE",
6119
+ "name": "*",
6487
6120
  "declaration": {
6488
- "name": "AGENT_PICKER_AUTO_VALUE",
6489
- "module": "src/components/agent-picker/agent-picker.constants.ts"
6121
+ "name": "*",
6122
+ "package": "./chat-bubble"
6490
6123
  }
6491
6124
  }
6492
6125
  ]
6493
6126
  },
6494
6127
  {
6495
6128
  "kind": "javascript-module",
6496
- "path": "src/components/agent-picker/agent-picker.styles.ts",
6129
+ "path": "src/utils/animated-panel-toggle.ts",
6130
+ "declarations": [],
6131
+ "exports": []
6132
+ },
6133
+ {
6134
+ "kind": "javascript-module",
6135
+ "path": "src/utils/animation-exclusivity.ts",
6136
+ "declarations": [],
6137
+ "exports": []
6138
+ },
6139
+ {
6140
+ "kind": "javascript-module",
6141
+ "path": "src/utils/banked-usage-baselines.ts",
6142
+ "declarations": [],
6143
+ "exports": []
6144
+ },
6145
+ {
6146
+ "kind": "javascript-module",
6147
+ "path": "src/utils/collect-session-models.ts",
6497
6148
  "declarations": [
6498
6149
  {
6499
- "kind": "variable",
6500
- "name": "styles",
6501
- "default": "css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .picker {\n position: relative;\n padding: 6px calc(var(--design-unit) * 3px);\n background-color: var(--neutral-layer-2);\n }\n\n /* Clipping wrapper. The inner segmented-control is naturally inline-flex\n and auto-sizes to its segments, so it would overflow the panel without\n ever reporting scrollWidth > clientWidth. By giving the wrapper a\n constrained width (flex+min-width:0) and overflow:hidden, the wrapper's\n scrollWidth reflects the segments' natural width while clientWidth is\n the available width — that's what we measure to decide on the fallback. */\n .segmented-row {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n }\n\n /* When the fallback select is showing, take the segmented row out of layout\n but keep it in the DOM so the ResizeObserver can re-measure when the\n container grows back and we can swap back to segmented-control. */\n .picker[data-effective='select'] .segmented-row {\n position: absolute;\n inset: 6px calc(var(--design-unit) * 3px);\n visibility: hidden;\n pointer-events: none;\n }\n\n .segmented-control {\n flex: 0 0 auto;\n }\n\n .select-fallback {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .select-label {\n font-family: var(--body-font);\n font-size: 0.8em;\n color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));\n }\n\n .select-control {\n flex: 1 1 auto;\n min-width: 0;\n padding: 4px 8px;\n background: var(--neutral-layer-3);\n color: var(--neutral-foreground-rest);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: 4px;\n font-family: var(--body-font);\n font-size: 0.85em;\n cursor: pointer;\n }\n\n .select-control:focus-visible {\n outline: 2px solid var(--accent-fill-rest);\n outline-offset: 1px;\n }\n`"
6150
+ "kind": "function",
6151
+ "name": "collectSessionModels",
6152
+ "return": {
6153
+ "type": {
6154
+ "text": "CostSessionModelEntry[]"
6155
+ }
6156
+ },
6157
+ "parameters": [
6158
+ {
6159
+ "name": "messages",
6160
+ "type": {
6161
+ "text": "readonly ChatMessage[]"
6162
+ }
6163
+ },
6164
+ {
6165
+ "name": "providerStatuses",
6166
+ "default": "[]",
6167
+ "type": {
6168
+ "text": "readonly AIProviderRegistryStatusEntry[]"
6169
+ }
6170
+ }
6171
+ ],
6172
+ "description": "Unique models used across a message list (includes sub-agent traces)."
6502
6173
  }
6503
6174
  ],
6504
6175
  "exports": [
6505
6176
  {
6506
6177
  "kind": "js",
6507
- "name": "styles",
6178
+ "name": "collectSessionModels",
6508
6179
  "declaration": {
6509
- "name": "styles",
6510
- "module": "src/components/agent-picker/agent-picker.styles.ts"
6180
+ "name": "collectSessionModels",
6181
+ "module": "src/utils/collect-session-models.ts"
6511
6182
  }
6512
6183
  }
6513
6184
  ]
6514
6185
  },
6515
6186
  {
6516
6187
  "kind": "javascript-module",
6517
- "path": "src/components/agent-picker/agent-picker.template.ts",
6518
- "declarations": [],
6519
- "exports": []
6520
- },
6521
- {
6522
- "kind": "javascript-module",
6523
- "path": "src/components/agent-picker/agent-picker.ts",
6188
+ "path": "src/utils/condense-history.ts",
6524
6189
  "declarations": [
6525
6190
  {
6526
- "kind": "class",
6527
- "description": "User-facing agent picker rendered above the chat input.\n\nRenders `Auto` plus each top-level agent that has `manualSelection.enabled`.\nSub-agents and non-selectable specialists are excluded. Hidden when fewer\nthan two agents are configured or none are manually selectable.",
6528
- "name": "AgentPicker",
6529
- "members": [
6191
+ "kind": "variable",
6192
+ "name": "CONDENSE_MIN_CHARS",
6193
+ "type": {
6194
+ "text": "number"
6195
+ },
6196
+ "default": "1000",
6197
+ "description": "Below this serialized size a tool payload isn't worth condensing — the stub\nwould save little and cost a breadcrumb. A driver-level floor so the public\n`condenseWhen` API needs no per-policy threshold field."
6198
+ },
6199
+ {
6200
+ "kind": "variable",
6201
+ "name": "CONDENSED_ARGS_KEY",
6202
+ "type": {
6203
+ "text": "string"
6204
+ },
6205
+ "default": "'condensed'",
6206
+ "description": "Key the collapsed args are stored under — a tool-call's args must stay a Record."
6207
+ },
6208
+ {
6209
+ "kind": "function",
6210
+ "name": "applyCondensation",
6211
+ "return": {
6212
+ "type": {
6213
+ "text": "ChatMessage[]"
6214
+ }
6215
+ },
6216
+ "parameters": [
6530
6217
  {
6531
- "kind": "field",
6532
- "name": "designSystemPrefix",
6218
+ "name": "history",
6533
6219
  "type": {
6534
- "text": "string"
6535
- },
6536
- "default": "'rapid'",
6537
- "description": "Design-system tag prefix, e.g. `'rapid'` for `rapid-segmented-control`."
6538
- },
6539
- {
6540
- "kind": "field",
6541
- "name": "mode",
6542
- "type": {
6543
- "text": "AgentPickerMode"
6544
- },
6545
- "default": "'disabled'",
6546
- "description": "Picker variant, sourced from the assistant's `chatConfig.agentPicker`."
6547
- },
6548
- {
6549
- "kind": "field",
6550
- "name": "agents",
6551
- "type": {
6552
- "text": "AgentConfig[]"
6553
- },
6554
- "default": "[]",
6555
- "description": "Top-level agents passed to the assistant."
6556
- },
6557
- {
6558
- "kind": "field",
6559
- "name": "pinnedAgentName",
6560
- "type": {
6561
- "text": "string | null"
6562
- },
6563
- "default": "null",
6564
- "description": "Currently pinned agent name, or `null` for Auto."
6565
- },
6566
- {
6567
- "kind": "field",
6568
- "name": "resizeObserver",
6569
- "type": {
6570
- "text": "ResizeObserver | undefined"
6571
- },
6572
- "privacy": "private"
6573
- },
6574
- {
6575
- "kind": "field",
6576
- "name": "selectableAgents",
6577
- "type": {
6578
- "text": "AgentConfig[]"
6220
+ "text": "ChatMessage[]"
6579
6221
  },
6580
- "description": "Top-level agents that opted in to manual selection.",
6581
- "readonly": true
6222
+ "description": "The to-model history copy to rewrite (not mutated)."
6582
6223
  },
6583
6224
  {
6584
- "kind": "field",
6585
- "name": "visible",
6225
+ "name": "policies",
6586
6226
  "type": {
6587
- "text": "boolean"
6227
+ "text": "Map<string, RegisteredCondensePolicy>"
6588
6228
  },
6589
- "description": "Whether the picker should render at all.",
6590
- "readonly": true
6229
+ "description": "The driver's registry, keyed by tool-call id. Its entries'\n`reported*` flags are flipped as payloads first collapse."
6591
6230
  },
6592
6231
  {
6593
- "kind": "field",
6594
- "name": "effectiveMode",
6232
+ "name": "ctx",
6595
6233
  "type": {
6596
- "text": "'select' | 'segmented-control'"
6597
- },
6598
- "description": "The variant actually rendered: `'select'` if user-configured or if the\nsegmented row currently overflows; `'segmented-control'` otherwise.",
6599
- "readonly": true
6600
- },
6601
- {
6602
- "kind": "method",
6603
- "name": "agentsChanged",
6604
- "return": {
6605
- "type": {
6606
- "text": "void"
6607
- }
6608
- },
6609
- "description": "Re-measure when agents change — newly added selectable agents may push the\nsegmented row past the container's width."
6610
- },
6611
- {
6612
- "kind": "method",
6613
- "name": "modeChanged",
6614
- "return": {
6615
- "type": {
6616
- "text": "void"
6617
- }
6618
- }
6619
- },
6620
- {
6621
- "kind": "method",
6622
- "name": "measureOverflow",
6623
- "privacy": "private",
6624
- "return": {
6625
- "type": {
6626
- "text": "void"
6627
- }
6628
- },
6629
- "description": "Measures whether the segmented row's natural content width exceeds the\ncontainer's visible width. Bails when the row isn't currently rendered\n(e.g. when `mode !== 'segmented-control'`)."
6630
- },
6631
- {
6632
- "kind": "method",
6633
- "name": "selectAgent",
6634
- "return": {
6635
- "type": {
6636
- "text": "void"
6637
- }
6638
- },
6639
- "parameters": [
6640
- {
6641
- "name": "name",
6642
- "type": {
6643
- "text": "string | null"
6644
- }
6645
- }
6646
- ]
6647
- },
6648
- {
6649
- "kind": "method",
6650
- "name": "selectByValue",
6651
- "return": {
6652
- "type": {
6653
- "text": "void"
6654
- }
6234
+ "text": "CondenseContext"
6655
6235
  },
6656
- "parameters": [
6657
- {
6658
- "name": "value",
6659
- "type": {
6660
- "text": "string"
6661
- }
6662
- }
6663
- ],
6664
- "description": "Maps a value from the segmented-control or select (which uses\nAGENT_PICKER_AUTO_VALUE for Auto) back to the `string | null` form\nexpected by `selectAgent`."
6236
+ "description": "The driver's live clocks (model-call / turn / agent-activation)."
6665
6237
  },
6666
6238
  {
6667
- "kind": "field",
6668
- "name": "currentValue",
6669
- "type": {
6670
- "text": "string"
6671
- },
6672
- "description": "Current pinned agent name as a value the segmented-control / select can bind to.",
6673
- "readonly": true
6674
- }
6675
- ],
6676
- "events": [
6677
- {
6678
- "description": "Fired when the user changes selection with the agent name or `null` for Auto. detail: `string | null`",
6679
- "name": "agent-pinned"
6680
- }
6681
- ],
6682
- "attributes": [
6683
- {
6684
- "name": "design-system-prefix",
6239
+ "name": "onCondensed",
6685
6240
  "type": {
6686
- "text": "string"
6241
+ "text": "(detail: CondensedEventDetail) => void"
6687
6242
  },
6688
- "default": "'rapid'",
6689
- "description": "Design-system tag prefix, e.g. `'rapid'` for `rapid-segmented-control`.",
6690
- "fieldName": "designSystemPrefix"
6243
+ "description": "Invoked once per payload at its full→stub transition."
6691
6244
  }
6692
6245
  ],
6693
- "superclass": {
6694
- "name": "GenesisElement",
6695
- "package": "@genesislcap/web-core"
6696
- },
6697
- "tagName": "foundation-ai-agent-picker",
6698
- "customElement": true
6246
+ "description": "Collapse stale tool payloads (declared via `condenseWhen`) out of the\nmodel-bound history. Pure over the `history` array — it emits new message\nobjects and never mutates the input messages (mirroring the agent-masking\ntransform). It DOES, however, write two latches onto the matching `policies`\nentry the first time a payload collapses, both because the collapse itself\nre-runs before every provider call: the `reported*` flag (the report-once gate\nthat keeps `onCondensed` firing once per (tool call, payload)) and\n`firedTrigger` (which pins the reason so a reason-bearing stub is rendered\nonce and never rewritten).\n\nTriggers (a policy may list several via `on: Trigger[]` — the FIRST to fire\ncollapses the payload; all are monotonic, so the collapse never reverts):\n- `superseded` — among all registered calls sharing a `by` key, the LAST in\n history order survives; every earlier one's targeted payload collapses. A\n re-call with the same key becomes the new survivor and re-arms the rest.\n- `age` — the result is first visible one model-call after the call, so `turns`\n is how many model-calls may see the full result before it collapses\n (fires when `modelCall − callModelCall > turns`): `turns: 1` is seen once,\n then collapses. The clock is monotonic across turns.\n- `turnEnd` — collapses once the turn (`sendMessage`) that made the call has\n ended (fires when `turn > callTurn`): full for the rest of that request,\n gone on every later one.\n- `agentEnd` — collapses once the agent activation that made the call has ended\n (a swap to another agent, or `releaseAgent`/`completeSubAgent`): kept across\n all of the agent's turns, dropped when its flow finishes.\n- `phaseEnd` — collapses once the app advances its phase epoch via `endPhase()`\n after the call (a sub-`agentEnd` boundary the author declares), OR at\n `agentEnd` as a backstop if no boundary is ever declared.\n\nThe tool-call/result envelope is never removed (providers reject orphaned\ncalls/results) — only the args object or the result content is replaced."
6699
6247
  }
6700
6248
  ],
6701
6249
  "exports": [
6702
6250
  {
6703
6251
  "kind": "js",
6704
- "name": "AGENT_PICKER_AUTO_VALUE",
6252
+ "name": "CONDENSE_MIN_CHARS",
6705
6253
  "declaration": {
6706
- "name": "AGENT_PICKER_AUTO_VALUE",
6707
- "module": "src/components/agent-picker/agent-picker.ts"
6254
+ "name": "CONDENSE_MIN_CHARS",
6255
+ "module": "src/utils/condense-history.ts"
6708
6256
  }
6709
6257
  },
6710
6258
  {
6711
6259
  "kind": "js",
6712
- "name": "AgentPicker",
6260
+ "name": "CONDENSED_ARGS_KEY",
6713
6261
  "declaration": {
6714
- "name": "AgentPicker",
6715
- "module": "src/components/agent-picker/agent-picker.ts"
6262
+ "name": "CONDENSED_ARGS_KEY",
6263
+ "module": "src/utils/condense-history.ts"
6716
6264
  }
6717
6265
  },
6718
- {
6719
- "kind": "custom-element-definition",
6720
- "name": "foundation-ai-agent-picker",
6721
- "declaration": {
6722
- "name": "AgentPicker",
6723
- "module": "src/components/agent-picker/agent-picker.ts"
6724
- }
6725
- }
6726
- ]
6727
- },
6728
- {
6729
- "kind": "javascript-module",
6730
- "path": "src/components/agent-picker/index.ts",
6731
- "declarations": [],
6732
- "exports": [
6733
- {
6734
- "kind": "js",
6735
- "name": "*",
6736
- "declaration": {
6737
- "name": "*",
6738
- "package": "./agent-picker"
6739
- }
6740
- }
6741
- ]
6742
- },
6743
- {
6744
- "kind": "javascript-module",
6745
- "path": "src/components/ai-driver/ai-driver.ts",
6746
- "declarations": [],
6747
- "exports": []
6748
- },
6749
- {
6750
- "kind": "javascript-module",
6751
- "path": "src/components/ai-driver/index.ts",
6752
- "declarations": [],
6753
- "exports": [
6754
- {
6755
- "kind": "js",
6756
- "name": "AiDriver",
6757
- "declaration": {
6758
- "name": "AiDriver",
6759
- "module": "./ai-driver"
6760
- }
6761
- }
6762
- ]
6763
- },
6764
- {
6765
- "kind": "javascript-module",
6766
- "path": "src/components/chat-bubble/chat-bubble.styles.ts",
6767
- "declarations": [
6768
- {
6769
- "kind": "variable",
6770
- "name": "styles",
6771
- "default": "css`\n :host {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 9999;\n overflow: visible;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .bubble {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n cursor: grab;\n pointer-events: all;\n overflow: visible;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n transition:\n transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.25s ease;\n background: linear-gradient(135deg, #0e1628 0%, #192340 100%);\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n color: white;\n }\n\n .bubble img {\n width: 62px;\n height: 62px;\n border-radius: 50%;\n object-fit: cover;\n pointer-events: none;\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n }\n\n .bubble svg {\n pointer-events: none;\n }\n\n .bubble:hover,\n .bubble.hovered {\n transform: scale(1.18);\n }\n\n .bubble:hover img,\n .bubble.hovered img {\n box-shadow:\n 0 6px 32px rgb(0 100 255 / 55%),\n 0 0 0 3px rgb(0 120 255 / 35%);\n }\n\n .bubble:active {\n cursor: grabbing;\n }\n\n .tooltip {\n position: absolute;\n bottom: calc(100% + 10px);\n background: linear-gradient(135deg, #0078ff 0%, #0050d0 100%);\n color: #fff;\n font-size: 0.78em;\n font-weight: 600;\n letter-spacing: 0.03em;\n white-space: nowrap;\n padding: 5px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 14px rgb(0 120 255 / 45%);\n pointer-events: none;\n opacity: 0%;\n translate: 0 4px;\n transition:\n opacity 0.18s ease,\n translate 0.18s ease;\n }\n\n .tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: var(--arrow-left, 50%);\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #0050d0;\n }\n\n .bubble:hover .tooltip,\n .bubble.hovered .tooltip {\n opacity: 100%;\n translate: 0 0;\n }\n\n .pulse-ring {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n border: 2px solid rgb(0 120 255 / 50%);\n animation: pulse 2.5s ease-out infinite;\n pointer-events: none;\n }\n\n @keyframes pulse {\n 0% {\n transform: scale(1);\n opacity: 70%;\n }\n\n 70% {\n transform: scale(1.5);\n opacity: 0%;\n }\n\n 100% {\n transform: scale(1.5);\n opacity: 0%;\n }\n }\n\n .dialog {\n position: absolute;\n box-sizing: border-box;\n width: 400px;\n height: 520px;\n min-width: 300px;\n min-height: 300px;\n max-width: 50vw;\n max-height: calc(100vh - 40px);\n background: var(--neutral-layer-1);\n border-radius: 14px;\n box-shadow:\n 0 12px 50px rgb(0 0 0 / 55%),\n 0 0 0 1px rgb(255 255 255 / 6%);\n pointer-events: all;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n resize: both;\n border: 1px solid var(--neutral-stroke-rest);\n animation: dialog-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes dialog-in {\n from {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n\n to {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n }\n\n @keyframes dialog-out {\n from {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n\n to {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n }\n\n .dialog.closing {\n animation: dialog-out 0.2s cubic-bezier(0.4, 0, 0.6, 1) forwards;\n }\n\n .dialog-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n min-height: 0;\n }\n\n .dialog-content ::slotted(*) {\n display: flex;\n flex: 1;\n width: 100%;\n min-height: 0;\n }\n`"
6772
- }
6773
- ],
6774
- "exports": [
6775
6266
  {
6776
6267
  "kind": "js",
6777
- "name": "styles",
6268
+ "name": "applyCondensation",
6778
6269
  "declaration": {
6779
- "name": "styles",
6780
- "module": "src/components/chat-bubble/chat-bubble.styles.ts"
6270
+ "name": "applyCondensation",
6271
+ "module": "src/utils/condense-history.ts"
6781
6272
  }
6782
6273
  }
6783
6274
  ]
6784
6275
  },
6785
6276
  {
6786
6277
  "kind": "javascript-module",
6787
- "path": "src/components/chat-bubble/chat-bubble.template.ts",
6278
+ "path": "src/utils/cost-session-history.ts",
6788
6279
  "declarations": [
6789
6280
  {
6790
6281
  "kind": "function",
6791
- "name": "AiChatBubbleTemplate",
6282
+ "name": "costHistoryStorageKey",
6792
6283
  "return": {
6793
6284
  "type": {
6794
- "text": "ViewTemplate<AiChatBubble>"
6285
+ "text": "string"
6795
6286
  }
6796
- }
6797
- }
6798
- ],
6799
- "exports": [
6800
- {
6801
- "kind": "js",
6802
- "name": "AiChatBubbleTemplate",
6803
- "declaration": {
6804
- "name": "AiChatBubbleTemplate",
6805
- "module": "src/components/chat-bubble/chat-bubble.template.ts"
6806
- }
6807
- }
6808
- ]
6809
- },
6810
- {
6811
- "kind": "javascript-module",
6812
- "path": "src/components/chat-bubble/chat-bubble.ts",
6813
- "declarations": [
6814
- {
6815
- "kind": "class",
6816
- "description": "Floating chat bubble component that provides a draggable orb and dialog panel\nfor embedding an AI assistant without a full-page layout.",
6817
- "name": "AiChatBubble",
6818
- "members": [
6287
+ },
6288
+ "parameters": [
6819
6289
  {
6820
- "kind": "field",
6821
- "name": "title",
6290
+ "name": "scope",
6822
6291
  "type": {
6823
6292
  "text": "string"
6824
- },
6825
- "default": "'AI Assistant'"
6826
- },
6827
- {
6828
- "kind": "field",
6829
- "name": "imageSrc",
6830
- "type": {
6831
- "text": "string | undefined"
6832
6293
  }
6833
- },
6294
+ }
6295
+ ],
6296
+ "description": "localStorage key for a host-scoped cost history list."
6297
+ },
6298
+ {
6299
+ "kind": "function",
6300
+ "name": "loadCostSessionHistory",
6301
+ "return": {
6302
+ "type": {
6303
+ "text": "CostSessionRecord[]"
6304
+ }
6305
+ },
6306
+ "parameters": [
6834
6307
  {
6835
- "kind": "field",
6836
- "name": "designSystemPrefix",
6308
+ "name": "scope",
6837
6309
  "type": {
6838
6310
  "text": "string"
6839
- },
6840
- "default": "'rapid'"
6841
- },
6842
- {
6843
- "kind": "field",
6844
- "name": "bubbleLeft",
6845
- "type": {
6846
- "text": "number"
6847
- },
6848
- "default": "0",
6849
- "description": "Current left position of the bubble orb (px from viewport left). @internal"
6850
- },
6311
+ }
6312
+ }
6313
+ ],
6314
+ "description": "Load persisted cost sessions for a scope, in whatever order they were stored — this applies no\nordering of its own. Display order is the caller's business; see `sortRecordsByRecency`."
6315
+ },
6316
+ {
6317
+ "kind": "function",
6318
+ "name": "saveCostSessionHistory",
6319
+ "return": {
6320
+ "type": {
6321
+ "text": "void"
6322
+ }
6323
+ },
6324
+ "parameters": [
6851
6325
  {
6852
- "kind": "field",
6853
- "name": "bubbleTop",
6326
+ "name": "scope",
6854
6327
  "type": {
6855
- "text": "number"
6856
- },
6857
- "default": "0",
6858
- "description": "Current top position of the bubble orb (px from viewport top). @internal"
6328
+ "text": "string"
6329
+ }
6859
6330
  },
6860
6331
  {
6861
- "kind": "field",
6862
- "name": "dialogLeft",
6332
+ "name": "records",
6863
6333
  "type": {
6864
- "text": "number"
6865
- },
6866
- "default": "0",
6867
- "description": "Current left position of the dialog panel (px from viewport left). @internal"
6868
- },
6334
+ "text": "CostSessionRecord[]"
6335
+ }
6336
+ }
6337
+ ],
6338
+ "description": "Replace the persisted list for a scope."
6339
+ },
6340
+ {
6341
+ "kind": "function",
6342
+ "name": "upsertRecord",
6343
+ "return": {
6344
+ "type": {
6345
+ "text": "CostSessionRecord[]"
6346
+ }
6347
+ },
6348
+ "parameters": [
6869
6349
  {
6870
- "kind": "field",
6871
- "name": "dialogTop",
6350
+ "name": "records",
6872
6351
  "type": {
6873
- "text": "number"
6874
- },
6875
- "default": "0",
6876
- "description": "Current top position of the dialog panel (px from viewport top). @internal"
6352
+ "text": "readonly CostSessionRecord[]"
6353
+ }
6877
6354
  },
6878
6355
  {
6879
- "kind": "field",
6880
- "name": "dialogWidth",
6356
+ "name": "record",
6881
6357
  "type": {
6882
- "text": "number"
6883
- },
6884
- "default": "400",
6885
- "description": "Current rendered width of the dialog panel (px). @internal"
6886
- },
6358
+ "text": "CostSessionRecord"
6359
+ }
6360
+ }
6361
+ ],
6362
+ "description": "Pure upsert — `records` with `record` replacing any row sharing its `projectKey`, or\nprepended when there is none.\n\nAn upsert, not an append, because a row stands for a **project's lifetime usage** rather\nthan a visit to it. Appending cut a fresh row per page-hide, so one project accumulated\na stack of near-identical rows and its spend was counted once per refresh by anything\nsumming the list.\n\nThe resulting array order is incidental — it is insertion order, so a project returned to\nstays where it first landed. Do not read it as recency; `sortRecordsByRecency` is what the\nUsage tab renders through.\n\nStorage-agnostic on purpose: the same merge serves the synchronous `localStorage` path\nand the async provider-backed one, so the two can never drift on what \"upsert\" means.\nThe caller owns persistence — see the assistant's `persistCostHistory`."
6363
+ },
6364
+ {
6365
+ "kind": "function",
6366
+ "name": "sortRecordsByRecency",
6367
+ "return": {
6368
+ "type": {
6369
+ "text": "CostSessionRecord[]"
6370
+ }
6371
+ },
6372
+ "parameters": [
6887
6373
  {
6888
- "kind": "field",
6889
- "name": "dialogHeight",
6374
+ "name": "records",
6890
6375
  "type": {
6891
- "text": "number"
6892
- },
6893
- "default": "520",
6894
- "description": "Current rendered height of the dialog panel (px). @internal"
6895
- },
6376
+ "text": "readonly CostSessionRecord[]"
6377
+ }
6378
+ }
6379
+ ],
6380
+ "description": "Rows most-recently-worked-on first, as a new array.\n\nApplied at read time rather than baked into the stored order, so it covers rows that came from\na host provider as well as from `localStorage`, and so no write path has to maintain it.\n\n`updatedAt` is ISO-8601, which sorts lexicographically — no date parsing, and no dependence on\nthe strings being valid dates. Sorting by it rather than by insertion order is what makes the\nlist track use: the active project moves to the top on its first upsert and stays there, since\nits row is rewritten on every transcript change."
6381
+ },
6382
+ {
6383
+ "kind": "function",
6384
+ "name": "resolveBankedUsage",
6385
+ "return": {
6386
+ "type": {
6387
+ "text": "AggregateUsage"
6388
+ }
6389
+ },
6390
+ "parameters": [
6896
6391
  {
6897
- "kind": "field",
6898
- "name": "dialogOpen",
6392
+ "name": "existing",
6899
6393
  "type": {
6900
- "text": "boolean"
6901
- },
6902
- "default": "false",
6903
- "description": "Whether the dialog panel is currently open. @internal"
6394
+ "text": "CostSessionRecord | undefined"
6395
+ }
6904
6396
  },
6905
6397
  {
6906
- "kind": "field",
6907
- "name": "hovered",
6398
+ "name": "transcriptIsAuthoritative",
6908
6399
  "type": {
6909
6400
  "text": "boolean"
6910
- },
6911
- "default": "false",
6912
- "description": "Whether the bubble orb is currently hovered. @internal"
6913
- },
6914
- {
6915
- "kind": "field",
6916
- "name": "unsubBus",
6917
- "type": {
6918
- "text": "() => void | undefined"
6919
- },
6920
- "privacy": "private"
6921
- },
6922
- {
6923
- "kind": "field",
6924
- "name": "injectedCloseBtn",
6925
- "type": {
6926
- "text": "Element | undefined"
6927
- },
6928
- "privacy": "private"
6929
- },
6930
- {
6931
- "kind": "field",
6932
- "name": "resizeObserver",
6933
- "type": {
6934
- "text": "ResizeObserver | undefined"
6935
- },
6936
- "privacy": "private"
6937
- },
6401
+ }
6402
+ }
6403
+ ],
6404
+ "description": "The spend a project's next row must carry over from before its current transcript.\n\n`transcriptIsAuthoritative` means the open transcript already accounts for this project's\nhistory — true exactly when a session snapshot is restored on load. The two branches are\nnot interchangeable, and picking the wrong one is silently destructive:\n\n- Authoritative: carry the previous row's own `banked` forward. Taking `usage` instead would\n re-add the restored history on every reload, the compounding double-count this replaced.\n- Not authoritative: the whole previous total becomes banked, because nothing in the fresh\n transcript accounts for it. Taking `banked` instead would rewrite the row down to the\n current page-load and drop the project's earlier spend.\n\nNote both branches are idempotent — the result never includes the current transcript — which\nis what lets two assistant instances sharing a session write the same row."
6405
+ },
6406
+ {
6407
+ "kind": "function",
6408
+ "name": "clearCostSessionHistory",
6409
+ "return": {
6410
+ "type": {
6411
+ "text": "void"
6412
+ }
6413
+ },
6414
+ "parameters": [
6938
6415
  {
6939
- "kind": "field",
6940
- "name": "dragging",
6416
+ "name": "scope",
6941
6417
  "type": {
6942
- "text": "boolean"
6943
- },
6944
- "privacy": "private",
6945
- "default": "false"
6946
- },
6418
+ "text": "string"
6419
+ }
6420
+ }
6421
+ ],
6422
+ "description": "Clear all persisted records for a scope."
6423
+ },
6424
+ {
6425
+ "kind": "function",
6426
+ "name": "formatCostSessionDate",
6427
+ "return": {
6428
+ "type": {
6429
+ "text": "string"
6430
+ }
6431
+ },
6432
+ "parameters": [
6947
6433
  {
6948
- "kind": "field",
6949
- "name": "dragTarget",
6434
+ "name": "iso",
6950
6435
  "type": {
6951
- "text": "'bubble' | 'dialog' | null"
6952
- },
6953
- "privacy": "private",
6954
- "default": "null"
6955
- },
6436
+ "text": "string"
6437
+ }
6438
+ }
6439
+ ],
6440
+ "description": "Display date for a history row (e.g. \"Jun 14, 2026\")."
6441
+ },
6442
+ {
6443
+ "kind": "function",
6444
+ "name": "isCostSessionRecord",
6445
+ "return": {
6446
+ "type": {
6447
+ "text": "value is CostSessionRecord"
6448
+ }
6449
+ },
6450
+ "parameters": [
6956
6451
  {
6957
- "kind": "field",
6958
- "name": "startMouseX",
6452
+ "name": "value",
6959
6453
  "type": {
6960
- "text": "number"
6961
- },
6962
- "privacy": "private",
6963
- "default": "0"
6454
+ "text": "unknown"
6455
+ }
6456
+ }
6457
+ ],
6458
+ "description": "Whether a value is a usable row. Exported because rows arrive by two routes and BOTH need it:\n`loadCostSessionHistory` filters the `localStorage` path here, and the assistant filters what a\nhost provider returns. A row that skips this check is not merely ignored downstream — a\nhalf-shaped `usage` sums to `NaN` through every total, and a missing `updatedAt` throws inside\nthe sort that orders the list, during render."
6459
+ }
6460
+ ],
6461
+ "exports": [
6462
+ {
6463
+ "kind": "js",
6464
+ "name": "costHistoryStorageKey",
6465
+ "declaration": {
6466
+ "name": "costHistoryStorageKey",
6467
+ "module": "src/utils/cost-session-history.ts"
6468
+ }
6469
+ },
6470
+ {
6471
+ "kind": "js",
6472
+ "name": "loadCostSessionHistory",
6473
+ "declaration": {
6474
+ "name": "loadCostSessionHistory",
6475
+ "module": "src/utils/cost-session-history.ts"
6476
+ }
6477
+ },
6478
+ {
6479
+ "kind": "js",
6480
+ "name": "saveCostSessionHistory",
6481
+ "declaration": {
6482
+ "name": "saveCostSessionHistory",
6483
+ "module": "src/utils/cost-session-history.ts"
6484
+ }
6485
+ },
6486
+ {
6487
+ "kind": "js",
6488
+ "name": "upsertRecord",
6489
+ "declaration": {
6490
+ "name": "upsertRecord",
6491
+ "module": "src/utils/cost-session-history.ts"
6492
+ }
6493
+ },
6494
+ {
6495
+ "kind": "js",
6496
+ "name": "sortRecordsByRecency",
6497
+ "declaration": {
6498
+ "name": "sortRecordsByRecency",
6499
+ "module": "src/utils/cost-session-history.ts"
6500
+ }
6501
+ },
6502
+ {
6503
+ "kind": "js",
6504
+ "name": "resolveBankedUsage",
6505
+ "declaration": {
6506
+ "name": "resolveBankedUsage",
6507
+ "module": "src/utils/cost-session-history.ts"
6508
+ }
6509
+ },
6510
+ {
6511
+ "kind": "js",
6512
+ "name": "clearCostSessionHistory",
6513
+ "declaration": {
6514
+ "name": "clearCostSessionHistory",
6515
+ "module": "src/utils/cost-session-history.ts"
6516
+ }
6517
+ },
6518
+ {
6519
+ "kind": "js",
6520
+ "name": "formatCostSessionDate",
6521
+ "declaration": {
6522
+ "name": "formatCostSessionDate",
6523
+ "module": "src/utils/cost-session-history.ts"
6524
+ }
6525
+ },
6526
+ {
6527
+ "kind": "js",
6528
+ "name": "isCostSessionRecord",
6529
+ "declaration": {
6530
+ "name": "isCostSessionRecord",
6531
+ "module": "src/utils/cost-session-history.ts"
6532
+ }
6533
+ }
6534
+ ]
6535
+ },
6536
+ {
6537
+ "kind": "javascript-module",
6538
+ "path": "src/utils/derive-cost-session-title.ts",
6539
+ "declarations": [
6540
+ {
6541
+ "kind": "variable",
6542
+ "name": "COST_SESSION_TITLE_MAX_WORDS",
6543
+ "type": {
6544
+ "text": "number"
6545
+ },
6546
+ "default": "10",
6547
+ "description": "Max words kept from the first user turn when deriving a session title."
6548
+ },
6549
+ {
6550
+ "kind": "variable",
6551
+ "name": "COST_SESSION_TITLE_MAX_CHARS",
6552
+ "type": {
6553
+ "text": "number"
6554
+ },
6555
+ "default": "60",
6556
+ "description": "Hard cap on derived title length (ellipsis included)."
6557
+ },
6558
+ {
6559
+ "kind": "function",
6560
+ "name": "getChatMessageText",
6561
+ "return": {
6562
+ "type": {
6563
+ "text": "string"
6564
+ }
6565
+ },
6566
+ "parameters": [
6567
+ {
6568
+ "name": "content",
6569
+ "type": {
6570
+ "text": "ChatMessage['content']"
6571
+ }
6572
+ }
6573
+ ],
6574
+ "description": "Plain text from a chat message `content` field."
6575
+ },
6576
+ {
6577
+ "kind": "function",
6578
+ "name": "truncateCostSessionTitle",
6579
+ "return": {
6580
+ "type": {
6581
+ "text": "string"
6582
+ }
6583
+ },
6584
+ "parameters": [
6585
+ {
6586
+ "name": "text",
6587
+ "type": {
6588
+ "text": "string"
6589
+ }
6590
+ }
6591
+ ],
6592
+ "description": "Trim a raw user phrase to a display-friendly session title: first sentence,\nthen word cap, then character cap."
6593
+ },
6594
+ {
6595
+ "kind": "function",
6596
+ "name": "deriveCostSessionTitleFromMessages",
6597
+ "return": {
6598
+ "type": {
6599
+ "text": "string | undefined"
6600
+ }
6601
+ },
6602
+ "parameters": [
6603
+ {
6604
+ "name": "messages",
6605
+ "type": {
6606
+ "text": "readonly ChatMessage[]"
6607
+ }
6608
+ }
6609
+ ],
6610
+ "description": "First non-empty user message in the thread, shortened for cost history."
6611
+ },
6612
+ {
6613
+ "kind": "function",
6614
+ "name": "resolveCostSessionTitle",
6615
+ "return": {
6616
+ "type": {
6617
+ "text": "string"
6618
+ }
6619
+ },
6620
+ "parameters": [
6621
+ {
6622
+ "name": "input",
6623
+ "type": {
6624
+ "text": "ResolveCostSessionTitleInput"
6625
+ }
6626
+ }
6627
+ ],
6628
+ "description": "Resolve the label stored on a finalized cost session record."
6629
+ }
6630
+ ],
6631
+ "exports": [
6632
+ {
6633
+ "kind": "js",
6634
+ "name": "COST_SESSION_TITLE_MAX_WORDS",
6635
+ "declaration": {
6636
+ "name": "COST_SESSION_TITLE_MAX_WORDS",
6637
+ "module": "src/utils/derive-cost-session-title.ts"
6638
+ }
6639
+ },
6640
+ {
6641
+ "kind": "js",
6642
+ "name": "COST_SESSION_TITLE_MAX_CHARS",
6643
+ "declaration": {
6644
+ "name": "COST_SESSION_TITLE_MAX_CHARS",
6645
+ "module": "src/utils/derive-cost-session-title.ts"
6646
+ }
6647
+ },
6648
+ {
6649
+ "kind": "js",
6650
+ "name": "getChatMessageText",
6651
+ "declaration": {
6652
+ "name": "getChatMessageText",
6653
+ "module": "src/utils/derive-cost-session-title.ts"
6654
+ }
6655
+ },
6656
+ {
6657
+ "kind": "js",
6658
+ "name": "truncateCostSessionTitle",
6659
+ "declaration": {
6660
+ "name": "truncateCostSessionTitle",
6661
+ "module": "src/utils/derive-cost-session-title.ts"
6662
+ }
6663
+ },
6664
+ {
6665
+ "kind": "js",
6666
+ "name": "deriveCostSessionTitleFromMessages",
6667
+ "declaration": {
6668
+ "name": "deriveCostSessionTitleFromMessages",
6669
+ "module": "src/utils/derive-cost-session-title.ts"
6670
+ }
6671
+ },
6672
+ {
6673
+ "kind": "js",
6674
+ "name": "resolveCostSessionTitle",
6675
+ "declaration": {
6676
+ "name": "resolveCostSessionTitle",
6677
+ "module": "src/utils/derive-cost-session-title.ts"
6678
+ }
6679
+ }
6680
+ ]
6681
+ },
6682
+ {
6683
+ "kind": "javascript-module",
6684
+ "path": "src/utils/flatten-sub-agent-messages.ts",
6685
+ "declarations": [],
6686
+ "exports": []
6687
+ },
6688
+ {
6689
+ "kind": "javascript-module",
6690
+ "path": "src/utils/format-usd.ts",
6691
+ "declarations": [
6692
+ {
6693
+ "kind": "function",
6694
+ "name": "formatUsdAmount",
6695
+ "return": {
6696
+ "type": {
6697
+ "text": "string"
6698
+ }
6699
+ },
6700
+ "parameters": [
6701
+ {
6702
+ "name": "value",
6703
+ "type": {
6704
+ "text": "number"
6705
+ }
6706
+ }
6707
+ ],
6708
+ "description": "The bare amount, no currency sign — `(12.3) => \"12.30\"`.\n\nExists for the settings modal's cost blocks, where the `$` is a separately\nstyled `<span class=\"prefix\">` and folding it into the text would change the\nDOM those styles target."
6709
+ },
6710
+ {
6711
+ "kind": "function",
6712
+ "name": "formatUsd",
6713
+ "return": {
6714
+ "type": {
6715
+ "text": "string"
6716
+ }
6717
+ },
6718
+ "parameters": [
6719
+ {
6720
+ "name": "value",
6721
+ "type": {
6722
+ "text": "number"
6723
+ }
6724
+ }
6725
+ ],
6726
+ "description": "A signed USD figure — `(12.3) => \"$12.30\"`."
6727
+ }
6728
+ ],
6729
+ "exports": [
6730
+ {
6731
+ "kind": "js",
6732
+ "name": "formatUsdAmount",
6733
+ "declaration": {
6734
+ "name": "formatUsdAmount",
6735
+ "module": "src/utils/format-usd.ts"
6736
+ }
6737
+ },
6738
+ {
6739
+ "kind": "js",
6740
+ "name": "formatUsd",
6741
+ "declaration": {
6742
+ "name": "formatUsd",
6743
+ "module": "src/utils/format-usd.ts"
6744
+ }
6745
+ }
6746
+ ]
6747
+ },
6748
+ {
6749
+ "kind": "javascript-module",
6750
+ "path": "src/utils/history-transform.ts",
6751
+ "declarations": [
6752
+ {
6753
+ "kind": "function",
6754
+ "name": "transformHistoryForAgent",
6755
+ "return": {
6756
+ "type": {
6757
+ "text": "ChatMessage[]"
6758
+ }
6759
+ },
6760
+ "parameters": [
6761
+ {
6762
+ "name": "history",
6763
+ "type": {
6764
+ "text": "ChatMessage[]"
6765
+ }
6964
6766
  },
6965
6767
  {
6966
- "kind": "field",
6967
- "name": "startMouseY",
6768
+ "name": "agentName",
6769
+ "type": {
6770
+ "text": "string"
6771
+ }
6772
+ }
6773
+ ],
6774
+ "description": "Prepares history for the LLM only: masks tool call args and results from other\nagents so the active specialist is not confused by tools it does not have.\nCanonical history in `ChatDriver` stays unmasked for UI and logging."
6775
+ },
6776
+ {
6777
+ "kind": "function",
6778
+ "name": "applyHistoryCap",
6779
+ "return": {
6780
+ "type": {
6781
+ "text": "ChatMessage[]"
6782
+ }
6783
+ },
6784
+ "parameters": [
6785
+ {
6786
+ "name": "history",
6968
6787
  "type": {
6969
- "text": "number"
6970
- },
6971
- "privacy": "private",
6972
- "default": "0"
6788
+ "text": "readonly ChatMessage[]"
6789
+ }
6973
6790
  },
6974
6791
  {
6975
- "kind": "field",
6976
- "name": "startElemLeft",
6792
+ "name": "cap",
6977
6793
  "type": {
6978
6794
  "text": "number"
6979
- },
6980
- "privacy": "private",
6981
- "default": "0"
6982
- },
6795
+ }
6796
+ }
6797
+ ],
6798
+ "description": "Applies a history cap for sub-agent context: the last `cap` messages are passed\nverbatim; older messages have their tool payloads masked."
6799
+ },
6800
+ {
6801
+ "kind": "variable",
6802
+ "name": "COMPACT_KEEP_RECENT_MESSAGES",
6803
+ "type": {
6804
+ "text": "number"
6805
+ },
6806
+ "default": "4",
6807
+ "description": "Recent messages kept verbatim when compacting; everything earlier is summarized."
6808
+ },
6809
+ {
6810
+ "kind": "variable",
6811
+ "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
6812
+ "type": {
6813
+ "text": "number"
6814
+ },
6815
+ "default": "4",
6816
+ "description": "Don't bother compacting unless at least this many messages would be summarized."
6817
+ },
6818
+ {
6819
+ "kind": "function",
6820
+ "name": "findCompactionCut",
6821
+ "return": {
6822
+ "type": {
6823
+ "text": "number | null"
6824
+ }
6825
+ },
6826
+ "parameters": [
6983
6827
  {
6984
- "kind": "field",
6985
- "name": "startElemTop",
6828
+ "name": "history",
6986
6829
  "type": {
6987
- "text": "number"
6988
- },
6989
- "privacy": "private",
6990
- "default": "0"
6991
- },
6830
+ "text": "readonly ChatMessage[]"
6831
+ }
6832
+ }
6833
+ ],
6834
+ "description": "Index at which the verbatim tail begins — everything before it gets summarized.\nThe tail starts on a clean turn boundary (a `user` message): a user message\nnever appears between a `tool_use` and its `tool_result`, so cutting there\nguarantees the summarized region is tool-pair-balanced (compaction only runs\nwhen idle, so history is turn-complete). Returns `null` when there is nothing\nworth compacting (too short, or no earlier user boundary) — e.g. a freshly\nrestored session whose only user turn is the current one."
6835
+ },
6836
+ {
6837
+ "kind": "function",
6838
+ "name": "buildCompactionSummaryPrompt",
6839
+ "return": {
6840
+ "type": {
6841
+ "text": "{\n systemPrompt: string;\n userMessage: string;\n}"
6842
+ }
6843
+ },
6844
+ "parameters": [
6992
6845
  {
6993
- "kind": "field",
6994
- "name": "hasDragged",
6846
+ "name": "toCompact",
6995
6847
  "type": {
6996
- "text": "boolean"
6997
- },
6998
- "privacy": "private",
6999
- "default": "false"
7000
- },
6848
+ "text": "readonly ChatMessage[]"
6849
+ }
6850
+ }
6851
+ ],
6852
+ "description": "Build the one-shot summarizer prompt for a run of older messages (used with\n`AIProvider.prompt`). Mirrors the `<conversation_history>` block convention\nthat suggestions use. Any existing `compacted-summary` at the head is folded\nin (rendered as prior-summary context), so rolling re-compaction produces one\ncombined summary."
6853
+ },
6854
+ {
6855
+ "kind": "function",
6856
+ "name": "normalizeForProvider",
6857
+ "return": {
6858
+ "type": {
6859
+ "text": "ChatMessage[]"
6860
+ }
6861
+ },
6862
+ "parameters": [
7001
6863
  {
7002
- "kind": "field",
7003
- "name": "latestMouseX",
6864
+ "name": "history",
7004
6865
  "type": {
7005
- "text": "number"
7006
- },
7007
- "privacy": "private",
7008
- "default": "0"
7009
- },
6866
+ "text": "readonly ChatMessage[]"
6867
+ }
6868
+ }
6869
+ ],
6870
+ "description": "Normalize history for a provider request (GENC-1351 §5.7): translate each\n`compacted-summary` message into a `user` message that wraps its summary in a\n`<conversation_summary>` block, so the raw display-only role never reaches a\ntransport. Other roles pass through untouched. Pure — returns a new array.\n\nApplied at the request-build chokepoint *before* per-agent masking, so the\nguarantee holds for every provider/transport regardless of any optional\nper-agent history transform."
6871
+ },
6872
+ {
6873
+ "kind": "function",
6874
+ "name": "compactionDividerLabel",
6875
+ "return": {
6876
+ "type": {
6877
+ "text": "string"
6878
+ }
6879
+ },
6880
+ "parameters": [
6881
+ {
6882
+ "name": "m",
6883
+ "type": {
6884
+ "text": "ChatMessage"
6885
+ }
6886
+ }
6887
+ ],
6888
+ "description": "Human-facing divider label for a `compacted-summary` message."
6889
+ }
6890
+ ],
6891
+ "exports": [
6892
+ {
6893
+ "kind": "js",
6894
+ "name": "transformHistoryForAgent",
6895
+ "declaration": {
6896
+ "name": "transformHistoryForAgent",
6897
+ "module": "src/utils/history-transform.ts"
6898
+ }
6899
+ },
6900
+ {
6901
+ "kind": "js",
6902
+ "name": "applyHistoryCap",
6903
+ "declaration": {
6904
+ "name": "applyHistoryCap",
6905
+ "module": "src/utils/history-transform.ts"
6906
+ }
6907
+ },
6908
+ {
6909
+ "kind": "js",
6910
+ "name": "COMPACT_KEEP_RECENT_MESSAGES",
6911
+ "declaration": {
6912
+ "name": "COMPACT_KEEP_RECENT_MESSAGES",
6913
+ "module": "src/utils/history-transform.ts"
6914
+ }
6915
+ },
6916
+ {
6917
+ "kind": "js",
6918
+ "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
6919
+ "declaration": {
6920
+ "name": "COMPACT_MIN_MESSAGES_TO_COMPACT",
6921
+ "module": "src/utils/history-transform.ts"
6922
+ }
6923
+ },
6924
+ {
6925
+ "kind": "js",
6926
+ "name": "findCompactionCut",
6927
+ "declaration": {
6928
+ "name": "findCompactionCut",
6929
+ "module": "src/utils/history-transform.ts"
6930
+ }
6931
+ },
6932
+ {
6933
+ "kind": "js",
6934
+ "name": "buildCompactionSummaryPrompt",
6935
+ "declaration": {
6936
+ "name": "buildCompactionSummaryPrompt",
6937
+ "module": "src/utils/history-transform.ts"
6938
+ }
6939
+ },
6940
+ {
6941
+ "kind": "js",
6942
+ "name": "normalizeForProvider",
6943
+ "declaration": {
6944
+ "name": "normalizeForProvider",
6945
+ "module": "src/utils/history-transform.ts"
6946
+ }
6947
+ },
6948
+ {
6949
+ "kind": "js",
6950
+ "name": "compactionDividerLabel",
6951
+ "declaration": {
6952
+ "name": "compactionDividerLabel",
6953
+ "module": "src/utils/history-transform.ts"
6954
+ }
6955
+ }
6956
+ ]
6957
+ },
6958
+ {
6959
+ "kind": "javascript-module",
6960
+ "path": "src/utils/index.ts",
6961
+ "declarations": [],
6962
+ "exports": [
6963
+ {
6964
+ "kind": "js",
6965
+ "name": "*",
6966
+ "declaration": {
6967
+ "name": "*",
6968
+ "package": "./animated-panel-toggle"
6969
+ }
6970
+ },
6971
+ {
6972
+ "kind": "js",
6973
+ "name": "*",
6974
+ "declaration": {
6975
+ "name": "*",
6976
+ "package": "./logger"
6977
+ }
6978
+ },
6979
+ {
6980
+ "kind": "js",
6981
+ "name": "*",
6982
+ "declaration": {
6983
+ "name": "*",
6984
+ "package": "./tool-fold"
6985
+ }
6986
+ }
6987
+ ]
6988
+ },
6989
+ {
6990
+ "kind": "javascript-module",
6991
+ "path": "src/utils/logger.ts",
6992
+ "declarations": [
6993
+ {
6994
+ "kind": "variable",
6995
+ "name": "logger"
6996
+ }
6997
+ ],
6998
+ "exports": [
6999
+ {
7000
+ "kind": "js",
7001
+ "name": "logger",
7002
+ "declaration": {
7003
+ "name": "logger",
7004
+ "module": "src/utils/logger.ts"
7005
+ }
7006
+ }
7007
+ ]
7008
+ },
7009
+ {
7010
+ "kind": "javascript-module",
7011
+ "path": "src/utils/message-partition.ts",
7012
+ "declarations": [
7013
+ {
7014
+ "kind": "function",
7015
+ "name": "isSessionNoticeMarker",
7016
+ "return": {
7017
+ "type": {
7018
+ "text": "boolean"
7019
+ }
7020
+ },
7021
+ "parameters": [
7010
7022
  {
7011
- "kind": "field",
7012
- "name": "latestMouseY",
7023
+ "name": "m",
7013
7024
  "type": {
7014
- "text": "number"
7015
- },
7016
- "privacy": "private",
7017
- "default": "0"
7018
- },
7025
+ "text": "ChatMessage"
7026
+ }
7027
+ }
7028
+ ],
7029
+ "description": "Either restore notice — both display-only, stripped before persist. @internal"
7030
+ },
7031
+ {
7032
+ "kind": "function",
7033
+ "name": "trailingInteractionRow",
7034
+ "return": {
7035
+ "type": {
7036
+ "text": "ChatMessage[]"
7037
+ }
7038
+ },
7039
+ "parameters": [
7019
7040
  {
7020
- "kind": "field",
7021
- "name": "rafHandle",
7041
+ "name": "messages",
7022
7042
  "type": {
7023
- "text": "number | undefined"
7024
- },
7025
- "privacy": "private"
7026
- },
7043
+ "text": "ChatMessage[]"
7044
+ }
7045
+ }
7046
+ ],
7047
+ "description": "The trailing interaction to render in a pinned slot, separate from the\nfiltered list. Empty unless the most recent message carries an interaction.\n\nPinning the trailing interaction keeps its widget out of the list's\nreconciliation: FAST's `repeat` has no key function and reconciles by array\nindex, so re-filtering the list (a cog-menu toggle) would otherwise rebind an\nexisting row to a different message and re-create the live widget — discarding\nany in-progress input. The slot's binding reads only `messages` and returns\nthe same message object across toggles, so the widget's DOM is preserved.\n\nIt is the *trailing* message (resolved or not), not \"unresolved only\", so that\nanswering an interaction leaves it in the slot — `resolved` is forwarded in\nplace with no re-render. It rejoins the list naturally once a newer message\narrives and it is no longer last."
7048
+ },
7049
+ {
7050
+ "kind": "function",
7051
+ "name": "filterVisibleMessages",
7052
+ "return": {
7053
+ "type": {
7054
+ "text": "ChatMessage[]"
7055
+ }
7056
+ },
7057
+ "parameters": [
7027
7058
  {
7028
- "kind": "field",
7029
- "name": "closeAbort",
7059
+ "name": "messages",
7030
7060
  "type": {
7031
- "text": "AbortController | undefined"
7032
- },
7033
- "privacy": "private"
7034
- },
7035
- {
7036
- "kind": "method",
7037
- "name": "onBubbleMouseEnter"
7038
- },
7039
- {
7040
- "kind": "method",
7041
- "name": "onBubbleMouseLeave"
7042
- },
7043
- {
7044
- "kind": "method",
7045
- "name": "dialogOpenChanged",
7046
- "parameters": [
7047
- {
7048
- "name": "_",
7049
- "type": {
7050
- "text": "boolean"
7051
- }
7052
- },
7053
- {
7054
- "name": "next",
7055
- "type": {
7056
- "text": "boolean"
7057
- }
7058
- }
7059
- ]
7060
- },
7061
- {
7062
- "kind": "method",
7063
- "name": "dialogContentChanged",
7064
- "parameters": [
7065
- {
7066
- "name": "_",
7067
- "type": {
7068
- "text": "HTMLElement[]"
7069
- }
7070
- },
7071
- {
7072
- "name": "next",
7073
- "type": {
7074
- "text": "HTMLElement[]"
7075
- }
7076
- }
7077
- ]
7078
- },
7079
- {
7080
- "kind": "method",
7081
- "name": "injectCloseButton",
7082
- "privacy": "private",
7083
- "parameters": [
7084
- {
7085
- "name": "assistant",
7086
- "type": {
7087
- "text": "Element"
7088
- }
7089
- }
7090
- ]
7091
- },
7092
- {
7093
- "kind": "method",
7094
- "name": "onBubbleMouseDown",
7095
- "parameters": [
7096
- {
7097
- "name": "e",
7098
- "type": {
7099
- "text": "MouseEvent"
7100
- }
7101
- }
7102
- ]
7103
- },
7104
- {
7105
- "kind": "method",
7106
- "name": "onDialogHeaderMouseDown",
7107
- "parameters": [
7108
- {
7109
- "name": "e",
7110
- "type": {
7111
- "text": "CustomEvent<{ clientX: number; clientY: number }>"
7112
- }
7113
- }
7114
- ]
7115
- },
7116
- {
7117
- "kind": "field",
7118
- "name": "onCloseBtnClick",
7119
- "privacy": "private",
7120
- "readonly": true
7121
- },
7122
- {
7123
- "kind": "method",
7124
- "name": "cancelClose",
7125
- "privacy": "private"
7126
- },
7127
- {
7128
- "kind": "method",
7129
- "name": "startCloseDialog",
7130
- "privacy": "private"
7131
- },
7132
- {
7133
- "kind": "field",
7134
- "name": "onDocMouseMove",
7135
- "privacy": "private"
7136
- },
7137
- {
7138
- "kind": "field",
7139
- "name": "applyDragFrame",
7140
- "privacy": "private"
7141
- },
7142
- {
7143
- "kind": "field",
7144
- "name": "onDocMouseUp",
7145
- "privacy": "private"
7061
+ "text": "ChatMessage[]"
7062
+ }
7146
7063
  },
7147
7064
  {
7148
- "kind": "method",
7149
- "name": "onPopout"
7150
- },
7065
+ "name": "toggles",
7066
+ "type": {
7067
+ "text": "MessageFilterState"
7068
+ }
7069
+ }
7070
+ ],
7071
+ "description": "Filters messages for the scrolling list per the current cog-menu toggles.\n\nThe trailing interaction (see `trailingInteractionRow`) must be removed\nbefore calling, as it is rendered separately."
7072
+ }
7073
+ ],
7074
+ "exports": [
7075
+ {
7076
+ "kind": "js",
7077
+ "name": "isSessionNoticeMarker",
7078
+ "declaration": {
7079
+ "name": "isSessionNoticeMarker",
7080
+ "module": "src/utils/message-partition.ts"
7081
+ }
7082
+ },
7083
+ {
7084
+ "kind": "js",
7085
+ "name": "trailingInteractionRow",
7086
+ "declaration": {
7087
+ "name": "trailingInteractionRow",
7088
+ "module": "src/utils/message-partition.ts"
7089
+ }
7090
+ },
7091
+ {
7092
+ "kind": "js",
7093
+ "name": "filterVisibleMessages",
7094
+ "declaration": {
7095
+ "name": "filterVisibleMessages",
7096
+ "module": "src/utils/message-partition.ts"
7097
+ }
7098
+ }
7099
+ ]
7100
+ },
7101
+ {
7102
+ "kind": "javascript-module",
7103
+ "path": "src/utils/resolve-cost-history-config.ts",
7104
+ "declarations": [
7105
+ {
7106
+ "kind": "function",
7107
+ "name": "resolveCostHistoryConfig",
7108
+ "return": {
7109
+ "type": {
7110
+ "text": "ResolvedCostHistoryConfig"
7111
+ }
7112
+ },
7113
+ "parameters": [
7151
7114
  {
7152
- "kind": "method",
7153
- "name": "onCloseDialog"
7154
- },
7115
+ "name": "chatConfig",
7116
+ "type": {
7117
+ "text": "ChatConfig"
7118
+ }
7119
+ }
7120
+ ],
7121
+ "description": "Resolve `chatConfig.costHistory` with defaults applied."
7122
+ }
7123
+ ],
7124
+ "exports": [
7125
+ {
7126
+ "kind": "js",
7127
+ "name": "resolveCostHistoryConfig",
7128
+ "declaration": {
7129
+ "name": "resolveCostHistoryConfig",
7130
+ "module": "src/utils/resolve-cost-history-config.ts"
7131
+ }
7132
+ }
7133
+ ]
7134
+ },
7135
+ {
7136
+ "kind": "javascript-module",
7137
+ "path": "src/utils/resolve-preference-baseline.ts",
7138
+ "declarations": [],
7139
+ "exports": []
7140
+ },
7141
+ {
7142
+ "kind": "javascript-module",
7143
+ "path": "src/utils/strip-agent-handlers.ts",
7144
+ "declarations": [],
7145
+ "exports": []
7146
+ },
7147
+ {
7148
+ "kind": "javascript-module",
7149
+ "path": "src/utils/sum-costs.ts",
7150
+ "declarations": [],
7151
+ "exports": []
7152
+ },
7153
+ {
7154
+ "kind": "javascript-module",
7155
+ "path": "src/utils/sum-tokens.ts",
7156
+ "declarations": [
7157
+ {
7158
+ "kind": "function",
7159
+ "name": "formatCompactTokenCountSafe",
7160
+ "return": {
7161
+ "type": {
7162
+ "text": "string"
7163
+ }
7164
+ },
7165
+ "parameters": [
7155
7166
  {
7156
- "kind": "field",
7157
- "name": "tooltipStyle",
7167
+ "name": "tokens",
7158
7168
  "type": {
7159
- "text": "string"
7160
- },
7161
- "readonly": true
7169
+ "text": "number"
7170
+ }
7162
7171
  }
7163
7172
  ],
7164
- "attributes": [
7173
+ "description": "formatCompactTokenCount passed through DOMPurify for `:innerHTML` bindings."
7174
+ }
7175
+ ],
7176
+ "exports": [
7177
+ {
7178
+ "kind": "js",
7179
+ "name": "formatCompactTokenCountSafe",
7180
+ "declaration": {
7181
+ "name": "formatCompactTokenCountSafe",
7182
+ "module": "src/utils/sum-tokens.ts"
7183
+ }
7184
+ }
7185
+ ]
7186
+ },
7187
+ {
7188
+ "kind": "javascript-module",
7189
+ "path": "src/utils/sum-usage.ts",
7190
+ "declarations": [],
7191
+ "exports": []
7192
+ },
7193
+ {
7194
+ "kind": "javascript-module",
7195
+ "path": "src/utils/tool-fold.ts",
7196
+ "declarations": [
7197
+ {
7198
+ "kind": "function",
7199
+ "name": "createToolFold",
7200
+ "return": {
7201
+ "type": {
7202
+ "text": "ToolFoldResult"
7203
+ }
7204
+ },
7205
+ "parameters": [
7165
7206
  {
7166
- "name": "title",
7207
+ "name": "config",
7167
7208
  "type": {
7168
- "text": "string"
7169
- },
7170
- "default": "'AI Assistant'",
7171
- "fieldName": "title"
7172
- },
7209
+ "text": "{\n /** Facade tool name — used as the tool name the model calls to open this fold. */\n name: string;\n /**\n * Description shown to the model for the facade tool.\n * If omitted, auto-generated from the inner tool names:\n * \"Contains: tool_a, tool_b. Invoke to access these tools.\"\n */\n description?: string;\n /**\n * Optional usage guidance injected into the fold-open response message.\n * Not included in the facade description — only visible once the fold is open.\n */\n usageNotes?: string;\n /** Inner tool definitions. Same type as `AgentConfig.toolDefinitions`. */\n tools: ChatToolDefinition[];\n /** Inner tool handlers. Same type as `AgentConfig.toolHandlers`. */\n handlers: ChatToolHandlers;\n /**\n * When true (default), all other tools are removed when this fold opens.\n * When false, sibling tools remain available alongside the inner tools.\n */\n exclusive?: boolean;\n}"
7210
+ }
7211
+ }
7212
+ ],
7213
+ "description": "Creates a tool fold — a facade that hides a group of related tools behind a single\nnamed entry, revealing them progressively when the model invokes the facade.\n\nThe `tools` and `handlers` parameters accept the exact same types as `AgentConfig.toolDefinitions`\nand `AgentConfig.toolHandlers`, so existing tool creation functions can be passed in unchanged."
7214
+ },
7215
+ {
7216
+ "kind": "function",
7217
+ "name": "expandToolTree",
7218
+ "return": {
7219
+ "type": {
7220
+ "text": "ToolTreeNode[]"
7221
+ }
7222
+ },
7223
+ "parameters": [
7173
7224
  {
7174
- "name": "image-src",
7225
+ "name": "definitions",
7175
7226
  "type": {
7176
- "text": "string | undefined"
7177
- },
7178
- "fieldName": "imageSrc"
7227
+ "text": "ChatToolDefinition[]"
7228
+ }
7179
7229
  },
7180
7230
  {
7181
- "name": "design-system-prefix",
7231
+ "name": "handlers",
7182
7232
  "type": {
7183
- "text": "string"
7184
- },
7185
- "default": "'rapid'",
7186
- "fieldName": "designSystemPrefix"
7233
+ "text": "ChatToolHandlers"
7234
+ }
7187
7235
  }
7188
7236
  ],
7189
- "superclass": {
7190
- "name": "GenesisElement",
7191
- "package": "@genesislcap/web-core"
7192
- },
7193
- "tagName": "foundation-ai-chat-bubble",
7194
- "customElement": true
7237
+ "description": "Expands a flat list of tool definitions into a nested tree by resolving fold\nmetadata from the corresponding handlers. Use this for debug output so the\nfull tool hierarchy is visible rather than just the top-level facade names."
7195
7238
  }
7196
7239
  ],
7197
7240
  "exports": [
7198
7241
  {
7199
7242
  "kind": "js",
7200
- "name": "AiChatBubble",
7243
+ "name": "createToolFold",
7201
7244
  "declaration": {
7202
- "name": "AiChatBubble",
7203
- "module": "src/components/chat-bubble/chat-bubble.ts"
7245
+ "name": "createToolFold",
7246
+ "module": "src/utils/tool-fold.ts"
7204
7247
  }
7205
7248
  },
7206
7249
  {
7207
- "kind": "custom-element-definition",
7208
- "name": "foundation-ai-chat-bubble",
7250
+ "kind": "js",
7251
+ "name": "expandToolTree",
7209
7252
  "declaration": {
7210
- "name": "AiChatBubble",
7211
- "module": "src/components/chat-bubble/chat-bubble.ts"
7253
+ "name": "expandToolTree",
7254
+ "module": "src/utils/tool-fold.ts"
7212
7255
  }
7213
7256
  }
7214
7257
  ]
7215
7258
  },
7216
7259
  {
7217
7260
  "kind": "javascript-module",
7218
- "path": "src/components/chat-bubble/index.ts",
7261
+ "path": "src/utils/with-timeout.ts",
7219
7262
  "declarations": [],
7220
- "exports": [
7221
- {
7222
- "kind": "js",
7223
- "name": "*",
7224
- "declaration": {
7225
- "name": "*",
7226
- "package": "./chat-bubble"
7227
- }
7228
- }
7229
- ]
7263
+ "exports": []
7230
7264
  },
7231
7265
  {
7232
7266
  "kind": "javascript-module",
@@ -9132,6 +9166,67 @@
9132
9166
  }
9133
9167
  ]
9134
9168
  },
9169
+ {
9170
+ "kind": "javascript-module",
9171
+ "path": "src/components/chat-markdown/chat-markdown.ts",
9172
+ "declarations": [
9173
+ {
9174
+ "kind": "class",
9175
+ "description": "",
9176
+ "name": "AiChatMarkdown",
9177
+ "members": [
9178
+ {
9179
+ "kind": "field",
9180
+ "name": "content",
9181
+ "type": {
9182
+ "text": "string"
9183
+ },
9184
+ "default": "''",
9185
+ "description": "The markdown string to render."
9186
+ }
9187
+ ],
9188
+ "superclass": {
9189
+ "name": "GenesisElement",
9190
+ "package": "@genesislcap/web-core"
9191
+ },
9192
+ "tagName": "ai-chat-markdown",
9193
+ "customElement": true
9194
+ }
9195
+ ],
9196
+ "exports": [
9197
+ {
9198
+ "kind": "js",
9199
+ "name": "AiChatMarkdown",
9200
+ "declaration": {
9201
+ "name": "AiChatMarkdown",
9202
+ "module": "src/components/chat-markdown/chat-markdown.ts"
9203
+ }
9204
+ },
9205
+ {
9206
+ "kind": "custom-element-definition",
9207
+ "name": "ai-chat-markdown",
9208
+ "declaration": {
9209
+ "name": "AiChatMarkdown",
9210
+ "module": "src/components/chat-markdown/chat-markdown.ts"
9211
+ }
9212
+ }
9213
+ ]
9214
+ },
9215
+ {
9216
+ "kind": "javascript-module",
9217
+ "path": "src/components/chat-markdown/index.ts",
9218
+ "declarations": [],
9219
+ "exports": [
9220
+ {
9221
+ "kind": "js",
9222
+ "name": "*",
9223
+ "declaration": {
9224
+ "name": "*",
9225
+ "package": "./chat-markdown"
9226
+ }
9227
+ }
9228
+ ]
9229
+ },
9135
9230
  {
9136
9231
  "kind": "javascript-module",
9137
9232
  "path": "src/components/orchestrating-driver/index.ts",
@@ -10007,7 +10102,7 @@
10007
10102
  {
10008
10103
  "kind": "variable",
10009
10104
  "name": "settingsModalStyles",
10010
- "default": "css`\n /*\n * Section headings come from the shared fragment the package also exports, so a host titling\n * its own slotted section applies the same rule rather than a copy of it.\n */\n ${assistantSettingsSectionTitleStyles}\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n .settings-modal {\n --ai-tier-badge-bg-mix: 14%;\n --ai-tier-badge-text-lighten: 18%;\n }\n\n .settings-modal::part(dialog) {\n max-width: 620px;\n width: calc(100vw - 48px);\n max-height: calc(100vh - 48px);\n padding: 0;\n }\n\n .settings-modal::part(content) {\n display: flex;\n flex-direction: column;\n min-height: 0;\n padding: 0;\n width: 100%;\n }\n\n .settings-modal::part(top) {\n padding: calc(var(--design-unit) * 4px) calc(var(--design-unit) * 5px);\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal::part(bottom) {\n padding: calc(var(--design-unit) * 4px) calc(var(--design-unit) * 5px);\n border-top: 1px solid var(--neutral-stroke-rest);\n border-bottom-left-radius: var(--card-border-radius, calc(var(--control-corner-radius) * 1px));\n border-bottom-right-radius: var(--card-border-radius, calc(var(--control-corner-radius) * 1px));\n background-color: var(--neutral-layer-2);\n }\n\n .settings-modal-header {\n display: flex;\n align-items: center;\n min-width: 0;\n }\n\n .settings-modal-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: calc(var(--design-unit) * 2px);\n color: var(--neutral-foreground-rest);\n }\n\n .settings-modal-content {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1 1 auto;\n }\n\n .settings-modal-tabs::part(tablist) {\n border-bottom: 0;\n padding: 0 calc(var(--design-unit) * 5px);\n column-gap: calc(var(--design-unit) * 5px);\n }\n\n .settings-modal-tabs::part(activeIndicator) {\n height: 2px;\n }\n\n .settings-modal-tab {\n padding: 0;\n font-weight: 600;\n gap: 6px;\n }\n\n .settings-modal-tab[aria-selected='true'] {\n color: var(--neutral-foreground-rest);\n }\n\n .settings-modal-tabs::part(tabpanel) {\n border-top: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal-tab-panel {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1 1 auto;\n padding: 0;\n border-radius: 0;\n }\n\n .settings-modal-tab-panel[hidden] {\n display: none !important;\n }\n\n .settings-modal-body {\n overflow-y: auto;\n padding: 0 calc(var(--design-unit) * 5px) calc(var(--design-unit) * 2px);\n max-height: min(60vh, 520px);\n }\n\n .settings-modal-section {\n padding: calc(var(--design-unit) * 5px) 0;\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal-section:last-child {\n border-bottom: none;\n }\n\n /*\n * Only the spacing. Everything else about this heading comes from the exported fragment\n * above, which the assistant and its hosts share; margin is deliberately not in there,\n * since the gap below a heading depends on the layout around it.\n */\n .ai-settings-section-title {\n margin: 0 0 calc(var(--design-unit) * 3px);\n }\n\n .settings-modal-section-note {\n margin: calc(var(--design-unit) * -2px) 0 calc(var(--design-unit) * 3px);\n font-size: 12px;\n line-height: 1.5;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-modal-section-body {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-toggle-row {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--design-unit) * 3px);\n padding: 0 0 calc(var(--design-unit) * 4px) 0;\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-toggle-row:last-child {\n border-bottom: none;\n padding-bottom: calc(var(--design-unit) * 1px);\n }\n\n .settings-toggle-row.is-stacked {\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n }\n\n /*\n * For rows whose control is a button rather than a switch. The base row top-aligns, which suits\n * a switch (it lands level with the title), but a button is tall enough that top-aligning leaves\n * it above the title-plus-description block it acts on.\n */\n .settings-toggle-row.is-centered {\n align-items: center;\n }\n\n .settings-animation-list {\n display: flex;\n flex-direction: column;\n width: 100%;\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.2px);\n padding: calc(var(--design-unit) * 2px);\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-animation-list .settings-toggle-row {\n border: 0;\n }\n\n .settings-animation-category {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-animation-category-label {\n font-weight: 600;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-toggle-row + .settings-animation-category-label {\n border-top: 1px solid var(--neutral-stroke-rest);\n padding-top: calc(var(--design-unit) * 2px);\n }\n\n .settings-animation-option {\n padding: 0;\n margin: 0;\n }\n\n .settings-animation-option:last-child {\n border-bottom: none;\n padding-bottom: calc(var(--design-unit) * 2px);\n }\n\n .settings-toggle-text {\n flex: 1;\n min-width: 0;\n }\n\n .settings-toggle-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--neutral-foreground-rest);\n }\n\n .settings-toggle-desc {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n line-height: 1.5;\n max-width: 92%;\n }\n\n .settings-toggle-control {\n flex: 0 0 auto;\n min-width: 0;\n }\n\n .settings-toggle-control-wide {\n width: 100%;\n max-width: 280px;\n }\n\n .settings-toggle-row.is-stacked .settings-toggle-desc {\n max-width: none;\n }\n\n .settings-toggle-row.is-stacked .settings-toggle-control-wide {\n width: 100%;\n max-width: none;\n }\n\n /*\n * Sub-heading over the provider toggle group. Unlike the section headings it sits INSIDE\n * .settings-modal-section-body, so the parent's 4-unit flex gap already separates it from the\n * first toggle — and flex does not collapse margins, so the shared rule's 3-unit margin-bottom\n * would add to that gap rather than replace it, leaving the label adrift from the rows it\n * names. Zero the margin and pull back 2 of the 4 gap units, expressed in the same token so it\n * tracks if that gap changes.\n */\n .settings-app-toggles-title {\n margin: 0 0 calc(var(--design-unit) * -2px);\n }\n\n .settings-app-toggle-block {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 1px);\n }\n\n .settings-toggle-hint {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n line-height: 1.4;\n padding-left: calc(var(--design-unit) * 1px);\n font-style: italic;\n }\n\n /*\n * The budget meter (GENC-1464) is the context indicator's money sibling and\n * shares its row anatomy 1:1, so the budget classes JOIN these rules rather\n * than restating them — one place to restyle the header/label/figures shape,\n * and the two indicators cannot drift apart. Design-system tokens only, and\n * unfallbacked: every token here ships a DS default, so a var(--x, y)\n * fallback would be dead code that hides a broken token name (#2411/#2436\n * review rule, pinned by settings-modal.styles.test.ts).\n */\n .settings-context-header,\n .settings-budget-header {\n display: flex;\n justify-content: space-between;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 2px);\n font-size: 12px;\n }\n\n .settings-context-label,\n .settings-budget-label {\n color: var(--neutral-foreground-hint);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n }\n\n .settings-context-tokens,\n .settings-budget-figures {\n color: var(--neutral-foreground-rest);\n }\n\n /*\n * ONE rhythm for every meter row. The outer wrapper holds the context\n * indicator and the budget meter; the inner one holds the budget meter's\n * vendor rows — and both gap by the same 2 units (half the section body's 4)\n * so that visually identical meter rows are evenly spaced whatever mix of\n * them is on screen. Without the wrapper the context row inherits the body's\n * 4-unit gap while the vendor rows keep 2, and three identical-looking rows\n * read as an uneven 4/2/2. The wrapper renders only when at least one meter\n * does — see settingsUsageMetersTemplate for why an always-on one would cost\n * a stray gap.\n */\n .settings-usage-meters,\n .settings-budget-usage {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-current-build,\n .settings-lifetime-usage {\n background: var(--neutral-layer-2);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.5px);\n padding: calc(var(--design-unit) * 4px);\n margin: calc(var(--design-unit) * 3px) 0;\n }\n\n /*\n * Deliberately quieter than the live block: no pulsing badge and no accent, so the eye lands\n * on what is being built now — which is why that block leads the tab and this one follows,\n * under the history heading whose rows it totals.\n */\n .settings-lifetime-usage-top {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n .settings-lifetime-label {\n font-size: 10px;\n line-height: 1;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-current-build-top {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n /*\n * Optically centring these 10px caps against the 14px project name beside them. Flex offers\n * only two alignments and neither lands it, because a box centre and a text baseline are\n * different lines:\n * - align-items: center sits the badge baseline about 1.2px high\n * - align-items: baseline matches baselines, but the caps then read as bottom-aligned\n * against the name's ascenders, so the badge looks low\n * The eye wants the midpoint, so: centre the boxes, then translate down by the ~1px of\n * overshoot. This is a nudge, but it corrects a fixed relationship between two font sizes\n * rather than some neighbouring element's layout, so unlike a margin tuned to a sibling it\n * cannot rot when that sibling changes. In em, so it tracks the badge text.\n *\n * display: block, not inline-flex: a flex container has no text baseline of its own and\n * synthesizes one from its first child, which here is the dot, an empty span. As a block box\n * the badge takes the baseline of its first line, i.e. the text.\n */\n .settings-live-badge {\n display: block;\n font-size: 10px;\n line-height: 1;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--accent-fill-rest);\n white-space: nowrap;\n transform: translateY(0.1em);\n }\n\n .settings-live-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n\n /* Replaces the flex gap the badge no longer has. */\n margin-right: calc(var(--design-unit) * 1px);\n\n /* Centres the dot on the text beside it without disturbing that text's baseline. */\n vertical-align: middle;\n border-radius: 50%;\n background: var(--accent-fill-rest);\n animation: settings-live-pulse 1.6s ease-out infinite;\n }\n\n @keyframes settings-live-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-fill-rest) 55%, transparent);\n }\n\n 70% {\n box-shadow: 0 0 0 7px transparent;\n }\n\n 100% {\n box-shadow: 0 0 0 0 transparent;\n }\n }\n\n .settings-current-build-name {\n font-size: 14px;\n line-height: 1;\n font-weight: 600;\n }\n\n /*\n * Cost and the token buckets side by side: one number in its own box, the four buckets\n * sharing the box next to it. Stretch, so the two are the same height however tall the 22px\n * cost value makes the left one.\n */\n .settings-usage-row {\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n gap: calc(var(--design-unit) * 3px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n .settings-usage-row-tokens {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .settings-build-stat {\n min-width: 0;\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1px);\n padding: calc(var(--design-unit) * 3px);\n }\n\n .settings-build-stat-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-build-stat-value {\n font-size: 22px;\n font-weight: 700;\n margin-top: calc(var(--design-unit) * 1px);\n color: var(--neutral-foreground-rest);\n }\n\n .settings-build-stat-value .prefix,\n .settings-build-stat-value .suffix {\n color: var(--accent-fill-rest);\n }\n\n /*\n * Per-bucket token split, beneath the headline cost. Small paired label/value cells\n * rather than four more stat cards: they are the detail behind the cost above, so they\n * have to read as subordinate to it rather than compete with it. The four buckets ARE\n * the token figure now — there is no merged total anywhere for them to break down.\n */\n .settings-token-breakdown {\n display: flex;\n flex-direction: column;\n\n /*\n * Tight against the buckets below. One design unit rather than two, because each bucket\n * separates its own label from its value by only 2px — at two units the title sat four\n * times further from the bucket labels than they sit from their own numbers, which read\n * as a gap between unrelated blocks instead of a heading over its content.\n */\n gap: calc(var(--design-unit) * 1px);\n margin-bottom: calc(var(--design-unit) * 3px);\n padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1px);\n }\n\n /*\n * Titles the box, because nothing else in it named the unit. Same styling and position as\n * the cost box's label, so two boxes sitting side by side are titled the same way; the\n * hierarchy against the bucket labels comes from position, not a second type size.\n */\n .settings-token-breakdown-label {\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-token-buckets {\n display: flex;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-token-bucket {\n display: flex;\n\n /*\n * min-content (not 0) so a bucket cannot be shrunk narrower than its own unbreakable label.\n * With the label set to nowrap, a zero minimum would overflow it into the next column instead\n * of wrapping — so the container's flex-wrap takes over and moves a bucket to a second row,\n * which is the degradation we want when the modal is dragged narrow.\n */\n min-width: min-content;\n flex-direction: column;\n gap: 2px;\n }\n\n .settings-token-bucket-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n\n /*\n * One line, always. The rate marker is a separate inline box after collapsible whitespace, so\n * a wrappable label gives it a break opportunity of its own — in a narrow column it orphaned\n * onto line 2, which also pushed that bucket's value a line below the other three and broke\n * the row of figures. nowrap removes every break opportunity in the label, the space before\n * the marker included, so no glue character is needed.\n */\n white-space: nowrap;\n }\n\n .settings-token-bucket-value {\n font-size: 13px;\n font-weight: 600;\n color: var(--neutral-foreground-rest);\n }\n\n /*\n * The price-direction marker on a cache bucket's label — down for reads (a fraction of the\n * uncached input rate), up for writes (a premium over it). Three tiers, so a bucket with no\n * marker is the reference rate rather than an unlabelled fourth case.\n *\n * Carried by the label rather than the value, which is what this used to do: cache reads are\n * usually the largest bucket by a wide margin in an agentic session, so the value was muted to\n * stop the biggest number reading as the dominant cost driver. That priced the treatment to the\n * wrong thing — a dimmed 0 (every Gemini cache write) looked disabled next to a normal 0, and a\n * rate is a property of the bucket, not of this turn's figure.\n *\n * Sized down and nudged up rather than tinted: the label is already the hint colour, so a\n * dimmer marker would vanish and a brighter one would out-shout the word it qualifies.\n */\n .settings-token-bucket-rate {\n font-size: 8px;\n line-height: 1;\n vertical-align: 0.15em;\n letter-spacing: 0;\n }\n\n /*\n * Usage-row overrides. Placed after the base rules they refine rather than up beside\n * .settings-usage-row itself, because stylelint's no-descending-specificity rejects a\n * compound selector appearing before the plain one it overrides.\n */\n\n /* Sized by its content; the buckets take the rest, since four cells need the width. */\n .settings-usage-row > .settings-build-stat {\n flex: 0 1 auto;\n }\n\n .settings-usage-row .settings-token-breakdown {\n flex: 1 1 auto;\n\n /* The row owns the spacing now; this centres the column against the taller cost box. */\n justify-content: center;\n margin-bottom: 0;\n }\n\n /*\n * Basis auto, not 0: a zero basis splits the row into four equal columns, so CACHE WRITE is\n * squeezed exactly as hard as INPUT despite being twice the label. Sizing from content gives the\n * long labels the room their text needs and lets the short ones concede it, which is what makes\n * white-space: nowrap above safe — every column starts at least as wide as its own content,\n * then shares the slack. Still grows and shrinks, so the four stay flush edge to edge.\n */\n .settings-usage-row .settings-token-bucket {\n flex: 1 1 auto;\n }\n\n .settings-model-tags {\n display: flex;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-model-tag {\n font-size: 11px;\n font-weight: 600;\n padding: 3px 9px;\n border-radius: 20px;\n font-family: ui-monospace, monospace;\n border: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-model-tag,\n .settings-cost-history-icon {\n --model-tag-color: var(--neutral-foreground-rest);\n\n background: color-mix(\n in srgb,\n var(--model-tag-color) var(--ai-tier-badge-bg-mix),\n var(--neutral-layer-1)\n );\n border-color: color-mix(in srgb, var(--model-tag-color) 20%, var(--neutral-layer-1));\n color: color-mix(in srgb, var(--model-tag-color), white var(--ai-tier-badge-text-lighten));\n }\n\n .settings-cost-history-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--neutral-foreground-hint);\n font-weight: 600;\n margin: calc(var(--design-unit) * 4px) 0 calc(var(--design-unit) * 2px);\n }\n\n /*\n * The heading labels the lifetime card as well as the rows, so the card gives up its own top\n * margin and the gap becomes the heading's alone. Adjacent siblings collapse to the LARGER of\n * the two margins, so leaving both would space the caps 3 units off the block they label while\n * the rows below sit at 2 — reading as though the heading belonged to neither.\n */\n .settings-cost-history-title + .settings-lifetime-usage {\n margin-top: 0;\n }\n\n .settings-cost-history {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n padding-bottom: calc(var(--design-unit) * 2px);\n }\n\n .settings-cost-history-icon {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n border-radius: calc(var(--control-corner-radius) * 1px);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 700;\n border: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-cost-history-row {\n display: flex;\n flex-direction: column;\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.5px);\n padding: calc(var(--design-unit) * 4px);\n }\n\n .settings-cost-history-row-content {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--design-unit) * 2px);\n }\n\n /*\n * All of these carry a ?hidden binding, and all declare display: flex — which silently beat the\n * UA [hidden] { display: none } rule, because author-origin declarations outrank user-agent\n * origin whatever the specificity. Without this, showTokens: false still rendered the full\n * breakdown in the lifetime block, the live block and every history row, and an empty row list\n * still occupied its padding under the lifetime total. Sibling elements that hide correctly do\n * so only by declaring no display at all. Same treatment as .settings-modal-tab-panel[hidden]\n * near the top of this file.\n */\n .settings-usage-row-tokens[hidden],\n .settings-cost-history[hidden],\n .settings-cost-history-row-content[hidden] {\n display: none !important;\n }\n\n /*\n * The buckets inside a history row drop the panel treatment they carry in the summary\n * blocks — the row is already a bordered card, and a second box inside it reads as a\n * nested panel. The adjacent-sibling rule above supplies the separator instead.\n */\n .settings-cost-history-row-breakdown .settings-token-breakdown {\n flex: 1 1 auto;\n margin-bottom: 0;\n padding: 0;\n background: none;\n border: none;\n }\n\n .settings-cost-history-row-content + .settings-cost-history-row-content {\n border-top: 1px solid var(--neutral-stroke-rest);\n margin-top: calc(var(--design-unit) * 2px);\n padding-top: calc(var(--design-unit) * 2px);\n }\n\n .settings-cost-history-row-body {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n gap: calc(var(--design-unit) * 2px);\n justify-content: space-between;\n }\n\n /*\n * Holds the cost and the delete button as one centred pair. The button used to be a sibling\n * of the whole row body, kept in place by a -2px nudge tuned for when the metrics column had\n * two lines (cost plus a merged token total). Dropping that second line left the nudge\n * compensating for nothing. Centring the two against each other needs no magic number and\n * cannot drift again if the metrics content changes.\n *\n * NB this rule previously set align-items with no display:flex, so it had no effect at all.\n */\n .settings-cost-history-row-right {\n display: flex;\n align-items: center;\n gap: calc(var(--design-unit) * 1px);\n flex: 0 0 auto;\n }\n\n .settings-cost-history-row-cost,\n .settings-cost-history-row-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--neutral-foreground-rest);\n min-width: 0;\n }\n\n .settings-cost-history-row-metrics {\n display: flex;\n flex-direction: column;\n flex: 0 0 auto;\n font-size: 13px;\n font-family: ui-monospace, monospace;\n color: var(--neutral-foreground-rest);\n white-space: nowrap;\n }\n\n .settings-cost-history-row-date {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-cost-history-delete {\n flex: 0 0 auto;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-cost-history-delete:hover {\n color: var(--error-color);\n }\n\n .settings-cost-history ::slotted(*) {\n display: block;\n }\n\n .settings-modal-footer {\n width: 100%;\n }\n\n .settings-done-button {\n margin-left: auto;\n }\n\n .settings-modal-footer-actions {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(var(--design-unit) * 2px);\n width: 100%;\n }\n\n .settings-disclaimer {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n max-width: 60%;\n line-height: 1.4;\n }\n\n /*\n * Inline spinner shown in the \"Download agent log\" button while the log builds\n * (GENC-1351 §5.8) — the lifetime-stitch path is an async provider round-trip.\n * Sized to the button label and tinted with currentColor so it tracks the\n * button's text colour in every appearance / theme.\n */\n [part='download-button'] .download-spinner {\n display: inline-block;\n width: 12px;\n height: 12px;\n margin-right: calc(var(--design-unit) * 1px);\n vertical-align: -1px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: download-spin 0.6s linear infinite;\n }\n\n @keyframes download-spin {\n to {\n transform: rotate(360deg);\n }\n }\n`",
10105
+ "default": "css`\n /*\n * Section headings come from the shared fragment the package also exports, so a host titling\n * its own slotted section applies the same rule rather than a copy of it.\n */\n ${assistantSettingsSectionTitleStyles}\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n .settings-modal {\n --ai-tier-badge-bg-mix: 14%;\n --ai-tier-badge-text-lighten: 18%;\n }\n\n .settings-modal::part(dialog) {\n max-width: 620px;\n width: calc(100vw - 48px);\n max-height: calc(100vh - 48px);\n padding: 0;\n }\n\n .settings-modal::part(content) {\n display: flex;\n flex-direction: column;\n min-height: 0;\n padding: 0;\n width: 100%;\n }\n\n .settings-modal::part(top) {\n padding: calc(var(--design-unit) * 4px) calc(var(--design-unit) * 5px);\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal::part(bottom) {\n padding: calc(var(--design-unit) * 4px) calc(var(--design-unit) * 5px);\n border-top: 1px solid var(--neutral-stroke-rest);\n border-bottom-left-radius: var(--card-border-radius, calc(var(--control-corner-radius) * 1px));\n border-bottom-right-radius: var(--card-border-radius, calc(var(--control-corner-radius) * 1px));\n background-color: var(--neutral-layer-2);\n }\n\n .settings-modal-header {\n display: flex;\n align-items: center;\n min-width: 0;\n }\n\n .settings-modal-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: calc(var(--design-unit) * 2px);\n color: var(--neutral-foreground-rest);\n }\n\n .settings-modal-content {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1 1 auto;\n }\n\n .settings-modal-tabs::part(tablist) {\n border-bottom: 0;\n padding: 0 calc(var(--design-unit) * 5px);\n column-gap: calc(var(--design-unit) * 5px);\n }\n\n .settings-modal-tabs::part(activeIndicator) {\n height: 2px;\n }\n\n .settings-modal-tab {\n padding: 0;\n font-weight: 600;\n gap: 6px;\n }\n\n .settings-modal-tab[aria-selected='true'] {\n color: var(--neutral-foreground-rest);\n }\n\n .settings-modal-tabs::part(tabpanel) {\n border-top: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal-tab-panel {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1 1 auto;\n padding: 0;\n border-radius: 0;\n }\n\n .settings-modal-tab-panel[hidden] {\n display: none !important;\n }\n\n .settings-modal-body {\n overflow-y: auto;\n padding: 0 calc(var(--design-unit) * 5px) calc(var(--design-unit) * 2px);\n max-height: min(60vh, 520px);\n }\n\n .settings-modal-section {\n padding: calc(var(--design-unit) * 5px) 0;\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-modal-section:last-child {\n border-bottom: none;\n }\n\n /*\n * Only the spacing. Everything else about this heading comes from the exported fragment\n * above, which the assistant and its hosts share; margin is deliberately not in there,\n * since the gap below a heading depends on the layout around it.\n */\n .ai-settings-section-title {\n margin: 0 0 calc(var(--design-unit) * 3px);\n }\n\n .settings-modal-section-note {\n margin: calc(var(--design-unit) * -2px) 0 calc(var(--design-unit) * 3px);\n font-size: 12px;\n line-height: 1.5;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-modal-section-body {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-toggle-row {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--design-unit) * 3px);\n padding: 0 0 calc(var(--design-unit) * 4px) 0;\n border-bottom: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-toggle-row:last-child {\n border-bottom: none;\n padding-bottom: calc(var(--design-unit) * 1px);\n }\n\n .settings-toggle-row.is-stacked {\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n }\n\n /*\n * For rows whose control is a button rather than a switch. The base row top-aligns, which suits\n * a switch (it lands level with the title), but a button is tall enough that top-aligning leaves\n * it above the title-plus-description block it acts on.\n */\n .settings-toggle-row.is-centered {\n align-items: center;\n }\n\n .settings-animation-list {\n display: flex;\n flex-direction: column;\n width: 100%;\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.2px);\n padding: calc(var(--design-unit) * 2px);\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-animation-list .settings-toggle-row {\n border: 0;\n }\n\n .settings-animation-category {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-animation-category-label {\n font-weight: 600;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-toggle-row + .settings-animation-category-label {\n border-top: 1px solid var(--neutral-stroke-rest);\n padding-top: calc(var(--design-unit) * 2px);\n }\n\n .settings-animation-option {\n padding: 0;\n margin: 0;\n }\n\n .settings-animation-option:last-child {\n border-bottom: none;\n padding-bottom: calc(var(--design-unit) * 2px);\n }\n\n .settings-toggle-text {\n flex: 1;\n min-width: 0;\n }\n\n .settings-toggle-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--neutral-foreground-rest);\n }\n\n .settings-toggle-desc {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n line-height: 1.5;\n max-width: 92%;\n }\n\n .settings-toggle-control {\n flex: 0 0 auto;\n min-width: 0;\n }\n\n .settings-toggle-control-wide {\n width: 100%;\n max-width: 280px;\n }\n\n .settings-toggle-row.is-stacked .settings-toggle-desc {\n max-width: none;\n }\n\n .settings-toggle-row.is-stacked .settings-toggle-control-wide {\n width: 100%;\n max-width: none;\n }\n\n /*\n * Sub-heading over the provider toggle group. Unlike the section headings it sits INSIDE\n * .settings-modal-section-body, so the parent's 4-unit flex gap already separates it from the\n * first toggle — and flex does not collapse margins, so the shared rule's 3-unit margin-bottom\n * would add to that gap rather than replace it, leaving the label adrift from the rows it\n * names. Zero the margin and pull back 2 of the 4 gap units, expressed in the same token so it\n * tracks if that gap changes.\n */\n .settings-app-toggles-title {\n margin: 0 0 calc(var(--design-unit) * -2px);\n }\n\n .settings-app-toggle-block {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 1px);\n }\n\n .settings-toggle-hint {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n line-height: 1.4;\n padding-left: calc(var(--design-unit) * 1px);\n font-style: italic;\n }\n\n /*\n * The budget meter (GENC-1464) is the context indicator's money sibling and\n * shares its row anatomy 1:1, so the budget classes JOIN these rules rather\n * than restating them — one place to restyle the header/label/figures shape,\n * and the two indicators cannot drift apart. Design-system tokens only, and\n * unfallbacked: every token here ships a DS default, so a var(--x, y)\n * fallback would be dead code that hides a broken token name (#2411/#2436\n * review rule, pinned by settings-modal.styles.test.ts).\n */\n .settings-context-header,\n .settings-budget-header {\n display: flex;\n justify-content: space-between;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 2px);\n font-size: 12px;\n }\n\n .settings-context-label,\n .settings-budget-label {\n color: var(--neutral-foreground-hint);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n }\n\n .settings-context-tokens,\n .settings-budget-figures {\n color: var(--neutral-foreground-rest);\n }\n\n /* Freshness footer (GENC-1464): refresh button + label under the vendor\n rows, left-aligned — a footnote to the figures above it. The button's\n inline padding is pinned and cancelled by the container margin so the\n ICON GLYPH (not the button box) sits flush with the budget labels'\n left edge, whatever the density tokens say. */\n .settings-budget-toolbar {\n display: flex;\n justify-content: flex-start;\n align-items: center;\n gap: 4px;\n margin-inline-start: -4px;\n }\n\n .settings-budget-refresh::part(control) {\n padding-inline: 4px;\n }\n\n .settings-budget-updated {\n color: var(--neutral-foreground-hint);\n font-size: 11px;\n }\n\n /*\n * ONE rhythm for every meter row. The outer wrapper holds the context\n * indicator and the budget meter; the inner one holds the budget meter's\n * vendor rows — and both gap by the same 2 units (half the section body's 4)\n * so that visually identical meter rows are evenly spaced whatever mix of\n * them is on screen. Without the wrapper the context row inherits the body's\n * 4-unit gap while the vendor rows keep 2, and three identical-looking rows\n * read as an uneven 4/2/2. The wrapper renders only when at least one meter\n * does — see settingsUsageMetersTemplate for why an always-on one would cost\n * a stray gap.\n */\n .settings-usage-meters,\n .settings-budget-usage {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-current-build,\n .settings-lifetime-usage {\n background: var(--neutral-layer-2);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.5px);\n padding: calc(var(--design-unit) * 4px);\n margin: calc(var(--design-unit) * 3px) 0;\n }\n\n /*\n * Deliberately quieter than the live block: no pulsing badge and no accent, so the eye lands\n * on what is being built now — which is why that block leads the tab and this one follows,\n * under the history heading whose rows it totals.\n */\n .settings-lifetime-usage-top {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n .settings-lifetime-label {\n font-size: 10px;\n line-height: 1;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-current-build-top {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n /*\n * Optically centring these 10px caps against the 14px project name beside them. Flex offers\n * only two alignments and neither lands it, because a box centre and a text baseline are\n * different lines:\n * - align-items: center sits the badge baseline about 1.2px high\n * - align-items: baseline matches baselines, but the caps then read as bottom-aligned\n * against the name's ascenders, so the badge looks low\n * The eye wants the midpoint, so: centre the boxes, then translate down by the ~1px of\n * overshoot. This is a nudge, but it corrects a fixed relationship between two font sizes\n * rather than some neighbouring element's layout, so unlike a margin tuned to a sibling it\n * cannot rot when that sibling changes. In em, so it tracks the badge text.\n *\n * display: block, not inline-flex: a flex container has no text baseline of its own and\n * synthesizes one from its first child, which here is the dot, an empty span. As a block box\n * the badge takes the baseline of its first line, i.e. the text.\n */\n .settings-live-badge {\n display: block;\n font-size: 10px;\n line-height: 1;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--accent-fill-rest);\n white-space: nowrap;\n transform: translateY(0.1em);\n }\n\n .settings-live-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n\n /* Replaces the flex gap the badge no longer has. */\n margin-right: calc(var(--design-unit) * 1px);\n\n /* Centres the dot on the text beside it without disturbing that text's baseline. */\n vertical-align: middle;\n border-radius: 50%;\n background: var(--accent-fill-rest);\n animation: settings-live-pulse 1.6s ease-out infinite;\n }\n\n @keyframes settings-live-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-fill-rest) 55%, transparent);\n }\n\n 70% {\n box-shadow: 0 0 0 7px transparent;\n }\n\n 100% {\n box-shadow: 0 0 0 0 transparent;\n }\n }\n\n .settings-current-build-name {\n font-size: 14px;\n line-height: 1;\n font-weight: 600;\n }\n\n /*\n * Cost and the token buckets side by side: one number in its own box, the four buckets\n * sharing the box next to it. Stretch, so the two are the same height however tall the 22px\n * cost value makes the left one.\n */\n .settings-usage-row {\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n gap: calc(var(--design-unit) * 3px);\n margin-bottom: calc(var(--design-unit) * 3px);\n }\n\n .settings-usage-row-tokens {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .settings-build-stat {\n min-width: 0;\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1px);\n padding: calc(var(--design-unit) * 3px);\n }\n\n .settings-build-stat-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-build-stat-value {\n font-size: 22px;\n font-weight: 700;\n margin-top: calc(var(--design-unit) * 1px);\n color: var(--neutral-foreground-rest);\n }\n\n .settings-build-stat-value .prefix,\n .settings-build-stat-value .suffix {\n color: var(--accent-fill-rest);\n }\n\n /*\n * Per-bucket token split, beneath the headline cost. Small paired label/value cells\n * rather than four more stat cards: they are the detail behind the cost above, so they\n * have to read as subordinate to it rather than compete with it. The four buckets ARE\n * the token figure now — there is no merged total anywhere for them to break down.\n */\n .settings-token-breakdown {\n display: flex;\n flex-direction: column;\n\n /*\n * Tight against the buckets below. One design unit rather than two, because each bucket\n * separates its own label from its value by only 2px — at two units the title sat four\n * times further from the bucket labels than they sit from their own numbers, which read\n * as a gap between unrelated blocks instead of a heading over its content.\n */\n gap: calc(var(--design-unit) * 1px);\n margin-bottom: calc(var(--design-unit) * 3px);\n padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1px);\n }\n\n /*\n * Titles the box, because nothing else in it named the unit. Same styling and position as\n * the cost box's label, so two boxes sitting side by side are titled the same way; the\n * hierarchy against the bucket labels comes from position, not a second type size.\n */\n .settings-token-breakdown-label {\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-token-buckets {\n display: flex;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 4px);\n }\n\n .settings-token-bucket {\n display: flex;\n\n /*\n * min-content (not 0) so a bucket cannot be shrunk narrower than its own unbreakable label.\n * With the label set to nowrap, a zero minimum would overflow it into the next column instead\n * of wrapping — so the container's flex-wrap takes over and moves a bucket to a second row,\n * which is the degradation we want when the modal is dragged narrow.\n */\n min-width: min-content;\n flex-direction: column;\n gap: 2px;\n }\n\n .settings-token-bucket-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--neutral-foreground-hint);\n\n /*\n * One line, always. The rate marker is a separate inline box after collapsible whitespace, so\n * a wrappable label gives it a break opportunity of its own — in a narrow column it orphaned\n * onto line 2, which also pushed that bucket's value a line below the other three and broke\n * the row of figures. nowrap removes every break opportunity in the label, the space before\n * the marker included, so no glue character is needed.\n */\n white-space: nowrap;\n }\n\n .settings-token-bucket-value {\n font-size: 13px;\n font-weight: 600;\n color: var(--neutral-foreground-rest);\n }\n\n /*\n * The price-direction marker on a cache bucket's label — down for reads (a fraction of the\n * uncached input rate), up for writes (a premium over it). Three tiers, so a bucket with no\n * marker is the reference rate rather than an unlabelled fourth case.\n *\n * Carried by the label rather than the value, which is what this used to do: cache reads are\n * usually the largest bucket by a wide margin in an agentic session, so the value was muted to\n * stop the biggest number reading as the dominant cost driver. That priced the treatment to the\n * wrong thing — a dimmed 0 (every Gemini cache write) looked disabled next to a normal 0, and a\n * rate is a property of the bucket, not of this turn's figure.\n *\n * Sized down and nudged up rather than tinted: the label is already the hint colour, so a\n * dimmer marker would vanish and a brighter one would out-shout the word it qualifies.\n */\n .settings-token-bucket-rate {\n font-size: 8px;\n line-height: 1;\n vertical-align: 0.15em;\n letter-spacing: 0;\n }\n\n /*\n * Usage-row overrides. Placed after the base rules they refine rather than up beside\n * .settings-usage-row itself, because stylelint's no-descending-specificity rejects a\n * compound selector appearing before the plain one it overrides.\n */\n\n /* Sized by its content; the buckets take the rest, since four cells need the width. */\n .settings-usage-row > .settings-build-stat {\n flex: 0 1 auto;\n }\n\n .settings-usage-row .settings-token-breakdown {\n flex: 1 1 auto;\n\n /* The row owns the spacing now; this centres the column against the taller cost box. */\n justify-content: center;\n margin-bottom: 0;\n }\n\n /*\n * Basis auto, not 0: a zero basis splits the row into four equal columns, so CACHE WRITE is\n * squeezed exactly as hard as INPUT despite being twice the label. Sizing from content gives the\n * long labels the room their text needs and lets the short ones concede it, which is what makes\n * white-space: nowrap above safe — every column starts at least as wide as its own content,\n * then shares the slack. Still grows and shrinks, so the four stay flush edge to edge.\n */\n .settings-usage-row .settings-token-bucket {\n flex: 1 1 auto;\n }\n\n .settings-model-tags {\n display: flex;\n flex-wrap: wrap;\n gap: calc(var(--design-unit) * 2px);\n }\n\n .settings-model-tag {\n font-size: 11px;\n font-weight: 600;\n padding: 3px 9px;\n border-radius: 20px;\n font-family: ui-monospace, monospace;\n border: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-model-tag,\n .settings-cost-history-icon {\n --model-tag-color: var(--neutral-foreground-rest);\n\n background: color-mix(\n in srgb,\n var(--model-tag-color) var(--ai-tier-badge-bg-mix),\n var(--neutral-layer-1)\n );\n border-color: color-mix(in srgb, var(--model-tag-color) 20%, var(--neutral-layer-1));\n color: color-mix(in srgb, var(--model-tag-color), white var(--ai-tier-badge-text-lighten));\n }\n\n .settings-cost-history-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--neutral-foreground-hint);\n font-weight: 600;\n margin: calc(var(--design-unit) * 4px) 0 calc(var(--design-unit) * 2px);\n }\n\n /*\n * The heading labels the lifetime card as well as the rows, so the card gives up its own top\n * margin and the gap becomes the heading's alone. Adjacent siblings collapse to the LARGER of\n * the two margins, so leaving both would space the caps 3 units off the block they label while\n * the rows below sit at 2 — reading as though the heading belonged to neither.\n */\n .settings-cost-history-title + .settings-lifetime-usage {\n margin-top: 0;\n }\n\n .settings-cost-history {\n display: flex;\n flex-direction: column;\n gap: calc(var(--design-unit) * 2px);\n padding-bottom: calc(var(--design-unit) * 2px);\n }\n\n .settings-cost-history-icon {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n border-radius: calc(var(--control-corner-radius) * 1px);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 700;\n border: 1px solid var(--neutral-stroke-rest);\n }\n\n .settings-cost-history-row {\n display: flex;\n flex-direction: column;\n background: var(--neutral-layer-1);\n border: 1px solid var(--neutral-stroke-rest);\n border-radius: calc(var(--control-corner-radius) * 1.5px);\n padding: calc(var(--design-unit) * 4px);\n }\n\n .settings-cost-history-row-content {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--design-unit) * 2px);\n }\n\n /*\n * All of these carry a ?hidden binding, and all declare display: flex — which silently beat the\n * UA [hidden] { display: none } rule, because author-origin declarations outrank user-agent\n * origin whatever the specificity. Without this, showTokens: false still rendered the full\n * breakdown in the lifetime block, the live block and every history row, and an empty row list\n * still occupied its padding under the lifetime total. Sibling elements that hide correctly do\n * so only by declaring no display at all. Same treatment as .settings-modal-tab-panel[hidden]\n * near the top of this file.\n */\n .settings-usage-row-tokens[hidden],\n .settings-cost-history[hidden],\n .settings-cost-history-row-content[hidden] {\n display: none !important;\n }\n\n /*\n * The buckets inside a history row drop the panel treatment they carry in the summary\n * blocks — the row is already a bordered card, and a second box inside it reads as a\n * nested panel. The adjacent-sibling rule above supplies the separator instead.\n */\n .settings-cost-history-row-breakdown .settings-token-breakdown {\n flex: 1 1 auto;\n margin-bottom: 0;\n padding: 0;\n background: none;\n border: none;\n }\n\n .settings-cost-history-row-content + .settings-cost-history-row-content {\n border-top: 1px solid var(--neutral-stroke-rest);\n margin-top: calc(var(--design-unit) * 2px);\n padding-top: calc(var(--design-unit) * 2px);\n }\n\n .settings-cost-history-row-body {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n gap: calc(var(--design-unit) * 2px);\n justify-content: space-between;\n }\n\n /*\n * Holds the cost and the delete button as one centred pair. The button used to be a sibling\n * of the whole row body, kept in place by a -2px nudge tuned for when the metrics column had\n * two lines (cost plus a merged token total). Dropping that second line left the nudge\n * compensating for nothing. Centring the two against each other needs no magic number and\n * cannot drift again if the metrics content changes.\n *\n * NB this rule previously set align-items with no display:flex, so it had no effect at all.\n */\n .settings-cost-history-row-right {\n display: flex;\n align-items: center;\n gap: calc(var(--design-unit) * 1px);\n flex: 0 0 auto;\n }\n\n .settings-cost-history-row-cost,\n .settings-cost-history-row-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--neutral-foreground-rest);\n min-width: 0;\n }\n\n .settings-cost-history-row-metrics {\n display: flex;\n flex-direction: column;\n flex: 0 0 auto;\n font-size: 13px;\n font-family: ui-monospace, monospace;\n color: var(--neutral-foreground-rest);\n white-space: nowrap;\n }\n\n .settings-cost-history-row-date {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-cost-history-delete {\n flex: 0 0 auto;\n color: var(--neutral-foreground-hint);\n }\n\n .settings-cost-history-delete:hover {\n color: var(--error-color);\n }\n\n .settings-cost-history ::slotted(*) {\n display: block;\n }\n\n .settings-modal-footer {\n width: 100%;\n }\n\n .settings-done-button {\n margin-left: auto;\n }\n\n .settings-modal-footer-actions {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(var(--design-unit) * 2px);\n width: 100%;\n }\n\n .settings-disclaimer {\n font-size: 12px;\n color: var(--neutral-foreground-hint);\n max-width: 60%;\n line-height: 1.4;\n }\n\n /*\n * Inline spinner shown in the \"Download agent log\" button while the log builds\n * (GENC-1351 §5.8) — the lifetime-stitch path is an async provider round-trip.\n * Sized to the button label and tinted with currentColor so it tracks the\n * button's text colour in every appearance / theme.\n */\n [part='download-button'] .download-spinner {\n display: inline-block;\n width: 12px;\n height: 12px;\n margin-right: calc(var(--design-unit) * 1px);\n vertical-align: -1px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: download-spin 0.6s linear infinite;\n }\n\n @keyframes download-spin {\n to {\n transform: rotate(360deg);\n }\n }\n`",
10011
10106
  "description": "Styles for the assistant settings modal (tabs, sections, toggles, cost summary)."
10012
10107
  }
10013
10108
  ],
@@ -10056,67 +10151,6 @@
10056
10151
  }
10057
10152
  ]
10058
10153
  },
10059
- {
10060
- "kind": "javascript-module",
10061
- "path": "src/components/chat-markdown/chat-markdown.ts",
10062
- "declarations": [
10063
- {
10064
- "kind": "class",
10065
- "description": "",
10066
- "name": "AiChatMarkdown",
10067
- "members": [
10068
- {
10069
- "kind": "field",
10070
- "name": "content",
10071
- "type": {
10072
- "text": "string"
10073
- },
10074
- "default": "''",
10075
- "description": "The markdown string to render."
10076
- }
10077
- ],
10078
- "superclass": {
10079
- "name": "GenesisElement",
10080
- "package": "@genesislcap/web-core"
10081
- },
10082
- "tagName": "ai-chat-markdown",
10083
- "customElement": true
10084
- }
10085
- ],
10086
- "exports": [
10087
- {
10088
- "kind": "js",
10089
- "name": "AiChatMarkdown",
10090
- "declaration": {
10091
- "name": "AiChatMarkdown",
10092
- "module": "src/components/chat-markdown/chat-markdown.ts"
10093
- }
10094
- },
10095
- {
10096
- "kind": "custom-element-definition",
10097
- "name": "ai-chat-markdown",
10098
- "declaration": {
10099
- "name": "AiChatMarkdown",
10100
- "module": "src/components/chat-markdown/chat-markdown.ts"
10101
- }
10102
- }
10103
- ]
10104
- },
10105
- {
10106
- "kind": "javascript-module",
10107
- "path": "src/components/chat-markdown/index.ts",
10108
- "declarations": [],
10109
- "exports": [
10110
- {
10111
- "kind": "js",
10112
- "name": "*",
10113
- "declaration": {
10114
- "name": "*",
10115
- "package": "./chat-markdown"
10116
- }
10117
- }
10118
- ]
10119
- },
10120
10154
  {
10121
10155
  "kind": "javascript-module",
10122
10156
  "path": "src/state/persistence/build-timeline-entries.ts",