@aceshooting/lyra-ui 21.0.0 → 21.1.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.
- package/CHANGELOG.md +56 -0
- package/README.md +6 -3
- package/custom-elements.json +1 -1
- package/dist/ai/runtime.js +1 -1
- package/dist/ai/types.d.ts +13 -5
- package/dist/cli/lyra-rename-ledger.mjs +1 -0
- package/dist/cli/migrate-wa.mjs +33 -13
- package/dist/cli/migration-contract.json +1 -1
- package/dist/components/agent-tools/agent-trace/agent-trace.class.d.ts +5 -3
- package/dist/components/agent-tools/agent-trace/agent-trace.class.js +3 -2
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.d.ts +15 -3
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +3 -3
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.d.ts +24 -8
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +3 -3
- package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +4 -0
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.d.ts +9 -5
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +2 -2
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/agent-tools/tool-status.js +2 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +8 -0
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +3 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.styles.js +1 -1
- package/dist/components/agent-tools/trace-tree/span.d.ts +2 -2
- package/dist/components/charts/chart/box-plot.class.d.ts +3 -1
- package/dist/components/charts/chart/box-plot.class.js +9 -9
- package/dist/components/charts/chart/chart-bidi.d.ts +109 -0
- package/dist/components/charts/chart/chart-bidi.js +1 -0
- package/dist/components/charts/chart/chart.class.d.ts +17 -2
- package/dist/components/charts/chart/chart.class.js +10 -8
- package/dist/components/charts/chart/histogram.class.js +1 -1
- package/dist/components/charts/chart/lite-chart.class.d.ts +24 -5
- package/dist/components/charts/chart/lite-chart.class.js +9 -9
- package/dist/components/charts/chart/lite-chart.styles.js +1 -1
- package/dist/components/conversation/code-block/shiki-dark-theme.d.ts +2 -2
- package/dist/components/conversation/code-block/shiki-dark-theme.js +1 -1
- package/dist/components/conversation/conversation-item/conversation-item.class.d.ts +18 -2
- package/dist/components/conversation/conversation-item/conversation-item.class.js +3 -3
- package/dist/components/conversation/conversation-item/conversation-item.styles.js +1 -1
- package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
- package/dist/components/conversation/markdown/markdown-core.class.d.ts +3 -1
- package/dist/components/conversation/markdown/markdown.class.d.ts +3 -1
- package/dist/components/conversation/message-actions/message-actions.class.d.ts +7 -2
- package/dist/components/conversation/message-actions/message-actions.class.js +2 -2
- package/dist/components/conversation/message-parts/message-parts.class.d.ts +6 -2
- package/dist/components/conversation/message-parts/message-parts.class.js +5 -3
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.d.ts +3 -2
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
- package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -5
- package/dist/components/conversation/streaming-text/streaming-text-base.class.js +2 -2
- package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text-core.class.js +3 -4
- package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text.class.js +3 -4
- package/dist/components/conversation/thread-list/thread-list.class.d.ts +13 -2
- package/dist/components/conversation/thread-list/thread-list.class.js +3 -3
- package/dist/components/data/data-grid/data-grid.class.js +1 -1
- package/dist/components/data/document-library/document-library.class.js +2 -0
- package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
- package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
- package/dist/components/data/flow-node/flow-node.class.d.ts +6 -2
- package/dist/components/data/flow-node/flow-node.class.js +6 -2
- package/dist/components/data/flow-node/flow-node.styles.js +1 -1
- package/dist/components/data/gauge/gauge.styles.js +1 -1
- package/dist/components/data/heatmap/heatmap.class.d.ts +3 -0
- package/dist/components/data/heatmap/heatmap.class.js +2 -2
- package/dist/components/data/heatmap/heatmap.styles.js +1 -1
- package/dist/components/data/sparkline/sparkline.class.d.ts +5 -3
- package/dist/components/data/stat/stat.class.d.ts +4 -3
- package/dist/components/data/table/table.class.d.ts +32 -5
- package/dist/components/data/table/table.class.js +3 -2
- package/dist/components/forms/button/button.class.d.ts +7 -1
- package/dist/components/forms/button/button.styles.js +1 -1
- package/dist/components/forms/date-picker/date-input.styles.js +1 -1
- package/dist/components/forms/input/input.styles.js +1 -1
- package/dist/components/forms/rubric-form/rubric-form.class.js +1 -0
- package/dist/components/layout/app-rail/app-rail.class.d.ts +50 -11
- package/dist/components/layout/app-rail/app-rail.class.js +2 -2
- package/dist/components/layout/command-palette/command-palette.class.js +1 -1
- package/dist/components/layout/menu/menu.class.d.ts +6 -5
- package/dist/components/layout/split-panel/split-panel.class.d.ts +2 -1
- package/dist/components/media/flag/language-map.d.ts +8 -0
- package/dist/components/media/flag/language-map.js +1 -1
- package/dist/components/media/map/map-loader.d.ts +7 -1
- package/dist/components/media/map/map.class.d.ts +111 -5
- package/dist/components/media/map/map.class.js +3 -3
- package/dist/components/media/media-card/media-card.class.d.ts +18 -5
- package/dist/components/media/media-card/media-card.class.js +1 -1
- package/dist/components/overlays/overlay/popover.class.d.ts +20 -0
- package/dist/components/overlays/overlay/popover.class.js +2 -2
- package/dist/components/retrieval/graph/graph.class.d.ts +3 -3
- package/dist/components/retrieval/graph/graph.class.js +1 -1
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +5 -3
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +3 -2
- package/dist/components/retrieval/mind-map/mind-map.class.d.ts +3 -3
- package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +5 -1
- package/dist/components/retrieval/source-picker/source-picker.class.d.ts +4 -1
- package/dist/components/retrieval/source-picker/source-picker.class.js +5 -1
- package/dist/components/utility/icon/icon.class.d.ts +9 -3
- package/dist/components/utility/icon/icon.class.js +1 -1
- package/dist/components/utility/mutation-observer/mutation-observer.class.d.ts +14 -8
- package/dist/components/utility/mutation-observer/mutation-observer.class.js +1 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.d.ts +6 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.js +7 -1
- package/dist/components/viewers/document-viewer/registry.d.ts +12 -3
- package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +13 -5
- package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
- package/dist/components/viewers/geojson-view/geojson-viewer.class.d.ts +3 -2
- package/dist/custom-elements-jsx.d.ts +1 -1
- package/dist/events.d.ts +644 -1242
- package/dist/internal/anchored-overlay-runtime.d.ts +2 -2
- package/dist/internal/cell-range.d.ts +0 -6
- package/dist/internal/cell-range.js +1 -1
- package/dist/internal/css-token-length.d.ts +7 -0
- package/dist/internal/css-token-length.js +1 -0
- package/dist/internal/default-strings.generated.d.ts +1 -1
- package/dist/internal/default-strings.generated.js +1 -1
- package/dist/internal/deferred-focus-return.d.ts +7 -4
- package/dist/internal/deferred-focus-return.js +1 -1
- package/dist/internal/dev-mode-attribute-warning.d.ts +35 -0
- package/dist/internal/dev-mode-attribute-warning.js +1 -1
- package/dist/internal/localization-runtime.js +1 -1
- package/dist/internal/localization-types.d.ts +1 -1
- package/dist/internal/localization.js +2 -2
- package/dist/internal/package-metadata.d.ts +1 -1
- package/dist/internal/package-metadata.js +1 -1
- package/dist/internal/papaparse-loader.d.ts +1 -1
- package/dist/internal/papaparse-loader.js +1 -1
- package/dist/internal/positioner.d.ts +10 -0
- package/dist/internal/positioner.js +1 -1
- package/dist/internal/shared-unions.d.ts +2 -1
- package/dist/internal/slotted-overlay-controller.d.ts +20 -0
- package/dist/internal/slotted-overlay-controller.js +1 -0
- package/dist/internal/text-quote.d.ts +1 -1
- package/dist/internal/text-quote.js +1 -1
- package/dist/internal/theme-observation.d.ts +4 -0
- package/dist/internal/theme-observation.js +1 -0
- package/dist/internal/theme-watcher.js +1 -1
- package/dist/localization.d.ts +6 -1
- package/dist/localization.js +1 -1
- package/dist/lyra.d.ts +1 -1
- package/dist/lyra.js +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/testing/lyra-tag-event-map.js +1 -1
- package/dist/translations/ar/agent-tools.js +1 -1
- package/dist/translations/cs/agent-tools.js +1 -1
- package/dist/translations/da/agent-tools.js +1 -1
- package/dist/translations/de/agent-tools.js +1 -1
- package/dist/translations/de-CH/agent-tools.js +1 -1
- package/dist/translations/es/agent-tools.js +1 -1
- package/dist/translations/fa/agent-tools.js +1 -1
- package/dist/translations/fi/agent-tools.js +1 -1
- package/dist/translations/fr/agent-tools.js +1 -1
- package/dist/translations/he/agent-tools.js +1 -1
- package/dist/translations/hi/agent-tools.js +1 -1
- package/dist/translations/hr/agent-tools.js +1 -1
- package/dist/translations/hu/agent-tools.js +1 -1
- package/dist/translations/id/agent-tools.js +1 -1
- package/dist/translations/it/agent-tools.js +1 -1
- package/dist/translations/ja/agent-tools.js +1 -1
- package/dist/translations/kk/agent-tools.js +1 -1
- package/dist/translations/ko/agent-tools.js +1 -1
- package/dist/translations/nb/agent-tools.js +1 -1
- package/dist/translations/nl/agent-tools.js +1 -1
- package/dist/translations/nn/agent-tools.js +1 -1
- package/dist/translations/pl/agent-tools.js +1 -1
- package/dist/translations/pt-BR/agent-tools.js +1 -1
- package/dist/translations/pt-PT/agent-tools.js +1 -1
- package/dist/translations/pt-PT/charts.js +1 -1
- package/dist/translations/pt-PT/conversation.js +1 -1
- package/dist/translations/pt-PT/data.js +1 -1
- package/dist/translations/pt-PT/forms.js +1 -1
- package/dist/translations/pt-PT/layout.js +1 -1
- package/dist/translations/pt-PT/media.js +1 -1
- package/dist/translations/pt-PT/retrieval.js +1 -1
- package/dist/translations/pt-PT/shared.js +1 -1
- package/dist/translations/pt-PT/utility.js +1 -1
- package/dist/translations/pt-PT/viewers.js +2 -2
- package/dist/translations/ro/agent-tools.js +1 -1
- package/dist/translations/ru/agent-tools.js +1 -1
- package/dist/translations/sl/agent-tools.js +1 -1
- package/dist/translations/sv/agent-tools.js +1 -1
- package/dist/translations/tr/agent-tools.js +1 -1
- package/dist/translations/uk/agent-tools.js +1 -1
- package/dist/translations/zh-CN/agent-tools.js +1 -1
- package/dist/translations/zh-TW/agent-tools.js +1 -1
- package/dist/utilities/index.d.ts +1 -1
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/localization.d.ts +28 -10
- package/dist/vue.d.ts +1 -1
- package/llms/components/lr-accordion-item.md +2 -344
- package/llms/components/lr-accordion.md +2 -344
- package/llms/components/lr-app-rail-group.md +2 -721
- package/llms/components/lr-app-rail-item.md +2 -721
- package/llms/components/lr-app-rail.md +29 -4
- package/llms/components/lr-bar-chart.md +2 -87
- package/llms/components/lr-box-plot.md +1 -1
- package/llms/components/lr-breadcrumb-item.md +2 -67
- package/llms/components/lr-bubble-chart.md +2 -87
- package/llms/components/lr-button.md +7 -0
- package/llms/components/lr-chart.md +4 -5
- package/llms/components/lr-chip-group.md +2 -272
- package/llms/components/lr-conversation-item.md +20 -2
- package/llms/components/lr-date-input.md +2 -457
- package/llms/components/lr-date-picker.md +6 -2
- package/llms/components/lr-document-library.md +3 -3
- package/llms/components/lr-document-viewer.md +14 -5
- package/llms/components/lr-doughnut-chart.md +2 -87
- package/llms/components/lr-dropdown-item.md +2 -306
- package/llms/components/lr-dropdown.md +6 -0
- package/llms/components/lr-flag.md +9 -1
- package/llms/components/lr-flow-node.md +4 -1
- package/llms/components/lr-geojson-view.md +3 -90
- package/llms/components/lr-geojson-viewer.md +13 -5
- package/llms/components/lr-icon-button.md +4 -1
- package/llms/components/lr-icon.md +14 -6
- package/llms/components/lr-input.md +9 -2
- package/llms/components/lr-lite-chart.md +17 -9
- package/llms/components/lr-map.md +87 -2
- package/llms/components/lr-markdown-core.md +5 -4
- package/llms/components/lr-markdown.md +4 -2
- package/llms/components/lr-media-card.md +10 -5
- package/llms/components/lr-menu-item.md +2 -306
- package/llms/components/lr-menu.md +9 -6
- package/llms/components/lr-menubar-item.md +2 -125
- package/llms/components/lr-message-actions.md +4 -1
- package/llms/components/lr-message-parts.md +14 -5
- package/llms/components/lr-mutation-observer.md +12 -6
- package/llms/components/lr-navigation-menu-item.md +2 -237
- package/llms/components/lr-number-input.md +4 -0
- package/llms/components/lr-option.md +2 -712
- package/llms/components/lr-pie-chart.md +2 -87
- package/llms/components/lr-polar-area-chart.md +2 -87
- package/llms/components/lr-popover.md +10 -0
- package/llms/components/lr-prompt-input.md +5 -1
- package/llms/components/lr-radar-chart.md +2 -87
- package/llms/components/lr-reorder-item.md +2 -157
- package/llms/components/lr-result-field.md +2 -117
- package/llms/components/lr-retrieval-results.md +3 -1
- package/llms/components/lr-rubric-form.md +3 -2
- package/llms/components/lr-scatter-chart.md +2 -87
- package/llms/components/lr-selection-toolbar.md +4 -2
- package/llms/components/lr-source-card.md +2 -186
- package/llms/components/lr-source-picker.md +3 -1
- package/llms/components/lr-sparkline.md +9 -4
- package/llms/components/lr-split-panel.md +5 -3
- package/llms/components/lr-stat.md +4 -3
- package/llms/components/lr-streaming-text-core.md +7 -5
- package/llms/components/lr-streaming-text.md +9 -6
- package/llms/components/lr-swatch-picker.md +130 -0
- package/llms/components/lr-tag.md +2 -177
- package/llms/components/lr-thread-list.md +27 -4
- package/llms/components/lr-timeline-item.md +2 -120
- package/llms/components/lr-toast-item.md +2 -223
- package/llms/components/lr-tool-call-block.md +12 -3
- package/llms/components/lr-tool-call-chip.md +12 -6
- package/llms/components/lr-tool-param-form.md +3 -2
- package/llms/components/lr-tool-result-dialog.md +7 -3
- package/llms/components/lr-tool-result-view.md +4 -3
- package/llms/components/lr-tool-timeline.md +12 -5
- package/llms/components/lr-tree-item.md +2 -354
- package/llms/index.md +22 -4
- package/llms/migration.md +75 -1
- package/llms/peers.md +1 -1
- package/llms/shared.md +86 -39
- package/llms-full.txt +739 -163
- package/package.json +6 -6
- package/registrations.json +1 -1
- package/vscode-css-data.json +1 -1
- package/vscode-html-data.json +1 -1
- package/web-types.json +1 -1
package/dist/ai/runtime.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createProviderSnapshotBudget,snapshotProviderValue}from"./snapshot.js";const DEFAULT_AGENT_STREAM_LIMITS=Object.freeze({maxMessages:512,maxPartsPerMessage:512,maxTools:256,maxDeltaCharacters:32768,maxTextCharactersPerPart:262144,maxIdentifierCharacters:1024,maxStatusMessageCharacters:8192,maxPatchOperations:256,maxSnapshotDepth:32,maxSnapshotNodes:1e4,maxSnapshotBytes:1048576,maxRetainedBytes:8388608}),usageByState=new WeakMap,FORBIDDEN_PATH_SEGMENTS=new Set(["__proto__","prototype","constructor"]),ARRAY_INDEX=/^(0|[1-9][0-9]*)$/,MESSAGE_ROLES=new Set(["user","assistant","system"]),MESSAGE_STATUSES=new Set(["sending","sent","failed","streaming"]),PART_STATES=new Set(["streaming","complete"]),TOOL_STATUSES=new Set(["pending","running","success","error","denied"]);function positiveInteger(value,fallback){return typeof value=="number"&&Number.isFinite(value)&&Number.isInteger(value)&&value>0?value:fallback}function resolveLimits(limits){const resolved=Object.fromEntries(Object.entries(DEFAULT_AGENT_STREAM_LIMITS).map(([key,fallback])=>[key,positiveInteger(limits[key],fallback)]));return resolved.maxSnapshotBytes>resolved.maxRetainedBytes&&(resolved.maxSnapshotBytes=resolved.maxRetainedBytes),Object.freeze(resolved)}function emptyUsage(){return{messages:new Map,tools:new Map,sharedStateBytes:0,totalBytes:0}}function copyUsage(usage){return{messages:new Map(usage.messages),tools:new Map(usage.tools),sharedStateBytes:usage.sharedStateBytes,totalBytes:usage.totalBytes}}function createAgentStreamState(limits={}){const state={generation:0,cursor:0,limits:resolveLimits(limits),status:{kind:"idle"},messages:[],tools:[],sharedState:null};return usageByState.set(state,emptyUsage()),state}function snapshotLimits(limits,maxBytes=limits.maxSnapshotBytes){return{maxDepth:limits.maxSnapshotDepth,maxNodes:limits.maxSnapshotNodes,maxBytes,maxStringCharacters:Math.max(limits.maxTextCharactersPerPart,limits.maxStatusMessageCharacters)}}function ownValue(value,limits){const result=snapshotProviderValue(value,createProviderSnapshotBudget(snapshotLimits(limits)));return result.ok?{value:result.value,bytes:result.bytes}:null}function isRecord(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function validIdentifier(value,limits){return typeof value=="string"&&value.length>0&&value.length<=limits.maxIdentifierCharacters}function validOptionalString(value,maximum){return value===void 0||typeof value=="string"&&value.length<=maximum}function validStatus(value,limits){return!isRecord(value)||!validIdentifier(value.kind,limits)?!1:validOptionalString(value.message,limits.maxStatusMessageCharacters)}function validDocument(value,limits){return!isRecord(value)||!validIdentifier(value.id,limits)||typeof value.name!="string"?!1:["mimeType","uri","version"].every(key=>validOptionalString(value[key],limits.maxSnapshotBytes))}const MAX_TOOL_REDACTION_PATHS=100,MAX_TOOL_REDACTION_PATH_CHARACTERS=4096;function validOptionalFiniteNumber(value){return value===void 0||typeof value=="number"&&Number.isFinite(value)}function validOptionalRedactionPaths(value){return value===void 0?!0:Array.isArray(value)&&value.length<=MAX_TOOL_REDACTION_PATHS&&value.every(path=>typeof path=="string"&&path.length<=MAX_TOOL_REDACTION_PATH_CHARACTERS)}function validTool(value,limits){return isRecord(value)?validIdentifier(value.id,limits)&&validIdentifier(value.name,limits)&&isRecord(value.args)&&TOOL_STATUSES.has(String(value.status))&&validOptionalString(value.error,limits.maxStatusMessageCharacters)&&validOptionalFiniteNumber(value.startedAt)&&validOptionalFiniteNumber(value.endedAt)&&validOptionalRedactionPaths(value.redactedFields):!1}function validPart(value,limits){if(!isRecord(value)||!validIdentifier(value.id,limits)||typeof value.type!="string"||value.state!==void 0&&!PART_STATES.has(String(value.state))||value.metadata!==void 0&&!isRecord(value.metadata))return!1;switch(value.type){case"text":case"reasoning":return typeof value.text=="string"&&value.text.length<=limits.maxTextCharactersPerPart;case"tool-call":return validTool(value.invocation,limits);case"tool-result":{if(!validIdentifier(value.invocationId,limits)||!validOptionalString(value.name,limits.maxIdentifierCharacters))return!1;const hasResult=Object.hasOwn(value,"result");return Object.hasOwn(value,"error")?typeof value.error=="string"&&value.error.length<=limits.maxStatusMessageCharacters:hasResult}case"citation":return isRecord(value.citation)&&validIdentifier(value.citation.id,limits);case"attachment":return validDocument(value.document,limits);case"data":{const hasData=Object.hasOwn(value,"data"),hasWidget=Object.hasOwn(value,"widget");return hasData!==hasWidget&&validOptionalString(value.name,limits.maxIdentifierCharacters)}case"audio":return["src","transcript","mimeType"].every(key=>validOptionalString(value[key],limits.maxSnapshotBytes));case"error":return typeof value.message=="string"&&value.message.length<=limits.maxStatusMessageCharacters&&validOptionalString(value.code,limits.maxIdentifierCharacters)&&(value.retryable===void 0||typeof value.retryable=="boolean");default:return!1}}function ownedPart(value,limits){const owned=ownValue(value,limits);return owned&&validPart(owned.value,limits)?owned:null}function validMessage(value,limits){return!(!isRecord(value)||!validIdentifier(value.id,limits)||!MESSAGE_ROLES.has(value.role)||value.status!==void 0&&!MESSAGE_STATUSES.has(String(value.status))||value.timestamp!==void 0&&typeof value.timestamp!="string"||!validOptionalString(value.text,limits.maxTextCharactersPerPart)||value.metadata!==void 0&&!isRecord(value.metadata)||value.attachments!==void 0&&(!Array.isArray(value.attachments)||!value.attachments.every(item=>validDocument(item,limits)))||value.parts!==void 0&&(!Array.isArray(value.parts)||value.parts.length>limits.maxPartsPerMessage||!value.parts.every(part=>validPart(part,limits))))}function ownedMessage(value,limits){const owned=ownValue(value,limits);return owned&&validMessage(owned.value,limits)?owned:null}function ownedTool(value,limits){const owned=ownValue(value,limits);return owned&&validTool(owned.value,limits)?owned:null}function usageFor(state){const cached=usageByState.get(state);if(cached)return cached;const usage=emptyUsage();for(const message of state.messages){const bytes=ownValue(message,state.limits)?.bytes??state.limits.maxRetainedBytes;usage.messages.set(message.id,bytes),usage.totalBytes+=bytes}for(const tool of state.tools){const bytes=ownValue(tool,state.limits)?.bytes??state.limits.maxRetainedBytes;usage.tools.set(tool.id,bytes),usage.totalBytes+=bytes}const shared=ownValue(state.sharedState,state.limits);return usage.sharedStateBytes=shared?.bytes??state.limits.maxRetainedBytes,usage.totalBytes+=usage.sharedStateBytes,usageByState.set(state,usage),usage}function replaceUsageEntry(usage,collection,id,bytes){const map=usage[collection];usage.totalBytes-=map.get(id)??0,map.set(id,bytes),usage.totalBytes+=bytes}function commit(state,next,usage,cursor){if(usage.totalBytes>state.limits.maxRetainedBytes)return failEvent(state,cursor,"Retained stream state exceeds the configured limit.","stream_limit_exceeded");const committed={...next,generation:cursor.generation,cursor:cursor.sequence};return usageByState.set(committed,usage),committed}function failEvent(state,cursor,message,code){const failed={...state,...cursor?{generation:cursor.generation,cursor:cursor.sequence}:{},status:{kind:"error",message},error:{message,code}};return usageByState.set(failed,usageFor(state)),failed}function pointerSegments(path){if(path==="")return[];if(!path.startsWith("/"))return null;const rawSegments=path.slice(1).split("/");if(rawSegments.some(segment=>/~(?:[^01]|$)/.test(segment)))return null;const segments=rawSegments.map(segment=>segment.replace(/~1/g,"/").replace(/~0/g,"~"));return segments.some(segment=>FORBIDDEN_PATH_SEGMENTS.has(segment))?null:segments}function validPatchKeys(record,operation){const expected=operation==="remove"?["op","path"]:["op","path","value"],keys=Object.keys(record).sort();return keys.length===expected.length&&keys.every((key,index)=>key===expected.sort()[index])}function parseJsonPatch(value,limits={}){const resolved=resolveLimits(limits),owned=ownValue(value,resolved);if(!owned||!Array.isArray(owned.value)||owned.value.length>resolved.maxPatchOperations)return null;const operations=[];for(const candidate of owned.value){if(!isRecord(candidate))return null;const operation=candidate.op,path=candidate.path;if(operation!=="add"&&operation!=="replace"&&operation!=="remove"||typeof path!="string"||pointerSegments(path)===null||!validPatchKeys(candidate,operation))return null;operations.push(operation==="remove"?{op:operation,path}:{op:operation,path,value:candidate.value})}return operations}function arrayIndex(segment){if(!ARRAY_INDEX.test(segment))return null;const index=Number(segment);return Number.isSafeInteger(index)?index:null}function applyPatchOperation(root,operation){const segments=pointerSegments(operation.path);if(!segments)return root;if(!segments.length)return operation.op==="remove"?null:operation.value;let parent=root;for(const segment of segments.slice(0,-1))if(Array.isArray(parent)){const index=arrayIndex(segment);if(index===null||index>=parent.length)return root;parent=parent[index]}else if(isRecord(parent)){if(!Object.hasOwn(parent,segment))return root;parent=parent[segment]}else return root;const key=segments.at(-1);if(key===void 0)return root;if(Array.isArray(parent)){if(key==="-")return operation.op==="add"&&parent.push(operation.value),root;const index=arrayIndex(key);return index===null||(operation.op==="add"?index<=parent.length&&parent.splice(index,0,operation.value):operation.op==="remove"?index<parent.length&&parent.splice(index,1):index<parent.length&&(parent[index]=operation.value)),root}return isRecord(parent)&&(operation.op==="add"?parent[key]=operation.value:operation.op==="remove"?Object.hasOwn(parent,key)&&delete parent[key]:Object.hasOwn(parent,key)&&(parent[key]=operation.value)),root}function applySharedStatePatch(value,patch){const limits=DEFAULT_AGENT_STREAM_LIMITS,root=ownValue(value,limits),operations=parseJsonPatch(patch,limits);if(!root)return null;if(!operations)return root.value;let next=root.value;for(const operation of operations)next=applyPatchOperation(next,operation);return next}function baseCursor(event){const generation=event.generation,sequence=event.sequence;return typeof generation=="number"&&Number.isSafeInteger(generation)&&generation>=0&&typeof sequence=="number"&&Number.isSafeInteger(sequence)&&sequence>0?{generation,sequence}:null}function shouldIgnore(state,type,cursor){return type==="run-start"?cursor.generation<=state.generation:type==="reset"?cursor.generation<state.generation||cursor.generation===state.generation&&cursor.sequence<=state.cursor:cursor.generation!==state.generation||cursor.sequence<=state.cursor}function reduceAgentStream(state,event){const eventSnapshot=snapshotProviderValue(event,createProviderSnapshotBudget(snapshotLimits(state.limits)));if(!eventSnapshot.ok)return eventSnapshot.failure==="limit"?failEvent(state,void 0,"Stream event exceeds the configured limit.","stream_limit_exceeded"):failEvent(state,void 0,"Stream event is not valid serializable data.","invalid_stream_event");const source=eventSnapshot.value;if(!isRecord(source))return failEvent(state,void 0,"Stream event is not a record.","invalid_stream_event");const cursor=baseCursor(source);if(!cursor||typeof source.type!="string")return failEvent(state,void 0,"Stream event generation, sequence, or type is invalid.","invalid_stream_event");if(shouldIgnore(state,source.type,cursor))return state;const usage=copyUsage(usageFor(state));switch(source.type){case"reset":{const reset=createAgentStreamState(state.limits);return commit(state,reset,emptyUsage(),cursor)}case"run-start":{if(!validIdentifier(source.runId,state.limits))return failEvent(state,cursor,"Run start has an invalid run id.","invalid_stream_event");const started=createAgentStreamState(state.limits);return started.runId=source.runId,started.status={kind:"running"},commit(state,started,emptyUsage(),cursor)}case"run-status":{if(!validStatus(source.status,state.limits)||source.runId!==void 0&&!validIdentifier(source.runId,state.limits))return failEvent(state,cursor,"Run status has an invalid shape.","invalid_stream_event");const status=source.status,error=status.kind==="error"?{message:status.message??state.error?.message??"Agent run failed",...state.error?.code&&(!status.message||status.message===state.error.message)?{code:state.error.code}:{}}:void 0;return commit(state,{...state,runId:source.runId??state.runId,status,error},usage,cursor)}case"messages-snapshot":{if(!Array.isArray(source.messages)||source.messages.length>state.limits.maxMessages)return failEvent(state,cursor,"Message snapshot exceeds the configured limit.","stream_limit_exceeded");const messages=[],messageBytes=new Map;let total=usage.totalBytes-[...usage.messages.values()].reduce((sum,bytes)=>sum+bytes,0);for(const candidate of source.messages){const message=ownedMessage(candidate,state.limits);if(!message||messageBytes.has(message.value.id))return failEvent(state,cursor,"Message snapshot contains an invalid or duplicate message.","invalid_stream_event");messages.push(message.value),messageBytes.set(message.value.id,message.bytes),total+=message.bytes}usage.messages.clear();for(const[id,bytes]of messageBytes)usage.messages.set(id,bytes);return usage.totalBytes=total,commit(state,{...state,messages},usage,cursor)}case"message-start":{const incoming=ownedMessage(source.message,state.limits);if(!incoming)return failEvent(state,cursor,"Message has an invalid shape.","invalid_stream_event");const index=state.messages.findIndex(message2=>message2.id===incoming.value.id);if(index<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const existing=index>=0?state.messages[index]:void 0,merged=existing?{...existing,...incoming.value,parts:incoming.value.parts??existing.parts}:incoming.value,message=ownedMessage(merged,state.limits);if(!message)return failEvent(state,cursor,"Message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return index<0?messages.push(message.value):messages[index]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-part-upsert":{if(!validIdentifier(source.messageId,state.limits)||source.role!==void 0&&!MESSAGE_ROLES.has(source.role))return failEvent(state,cursor,"Message part target is invalid.","invalid_stream_event");const part=ownedPart(source.part,state.limits);if(!part)return failEvent(state,cursor,"Message part has an invalid shape.","invalid_stream_event");const messageIndex=state.messages.findIndex(message2=>message2.id===source.messageId);if(messageIndex<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const current=messageIndex>=0?state.messages[messageIndex]:{id:source.messageId,role:source.role??"assistant",status:"streaming",parts:[]},parts=[...current.parts??[]],partIndex=parts.findIndex(candidate=>candidate.id===part.value.id);if(partIndex<0&&parts.length>=state.limits.maxPartsPerMessage)return failEvent(state,cursor,"Message part count exceeds the configured limit.","stream_limit_exceeded");partIndex<0?parts.push(part.value):parts[partIndex]=part.value;const message=ownedMessage({...current,parts},state.limits);if(!message)return failEvent(state,cursor,"Updated message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messageIndex<0?messages.push(message.value):messages[messageIndex]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-part-delta":{if(!validIdentifier(source.messageId,state.limits)||!validIdentifier(source.partId,state.limits)||source.partType!=="text"&&source.partType!=="reasoning"||typeof source.delta!="string"||source.role!==void 0&&!MESSAGE_ROLES.has(source.role))return failEvent(state,cursor,"Message delta has an invalid shape.","invalid_stream_event");if(source.delta.length>state.limits.maxDeltaCharacters)return failEvent(state,cursor,"Message delta exceeds the configured limit.","stream_limit_exceeded");const messageIndex=state.messages.findIndex(message2=>message2.id===source.messageId);if(messageIndex<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const current=messageIndex>=0?state.messages[messageIndex]:{id:source.messageId,role:source.role??"assistant",status:"streaming",parts:[]},parts=[...current.parts??[]],partIndex=parts.findIndex(part2=>part2.id===source.partId),previous=partIndex>=0?parts[partIndex]:void 0;if(previous&&previous.type!=="text"&&previous.type!=="reasoning")return failEvent(state,cursor,"Message delta targets a non-text part.","invalid_stream_event");if(partIndex<0&&parts.length>=state.limits.maxPartsPerMessage)return failEvent(state,cursor,"Message part count exceeds the configured limit.","stream_limit_exceeded");const text=(previous?.text??"")+source.delta;if(text.length>state.limits.maxTextCharactersPerPart)return failEvent(state,cursor,"Message text exceeds the configured limit.","stream_limit_exceeded");const part=source.partType==="reasoning"?{id:source.partId,type:"reasoning",text,state:"streaming"}:{id:source.partId,type:"text",text,state:"streaming"};partIndex<0?parts.push(part):parts[partIndex]=part;const message=ownedMessage({...current,parts},state.limits);if(!message)return failEvent(state,cursor,"Updated message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messageIndex<0?messages.push(message.value):messages[messageIndex]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-complete":{if(!validIdentifier(source.messageId,state.limits))return failEvent(state,cursor,"Message completion target is invalid.","invalid_stream_event");const index=state.messages.findIndex(message2=>message2.id===source.messageId);if(index<0)return commit(state,state,usage,cursor);const current=state.messages[index],message=ownedMessage({...current,status:"sent",parts:current.parts?.map(part=>({...part,state:"complete"}))},state.limits);if(!message)return failEvent(state,cursor,"Completed message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messages[index]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"tool-upsert":{const incoming=ownedTool(source.invocation,state.limits);if(!incoming)return failEvent(state,cursor,"Tool invocation has an invalid shape.","invalid_stream_event");const index=state.tools.findIndex(tool2=>tool2.id===incoming.value.id);if(index<0&&state.tools.length>=state.limits.maxTools)return failEvent(state,cursor,"Tool count exceeds the configured limit.","stream_limit_exceeded");const tool=ownedTool(index<0?incoming.value:{...state.tools[index],...incoming.value},state.limits);if(!tool)return failEvent(state,cursor,"Tool invocation has an invalid shape.","invalid_stream_event");const tools=[...state.tools];return index<0?tools.push(tool.value):tools[index]=tool.value,replaceUsageEntry(usage,"tools",tool.value.id,tool.bytes),commit(state,{...state,tools},usage,cursor)}case"state-snapshot":{const snapshot=ownValue(source.snapshot,state.limits);return snapshot?(usage.totalBytes+=snapshot.bytes-usage.sharedStateBytes,usage.sharedStateBytes=snapshot.bytes,commit(state,{...state,sharedState:snapshot.value},usage,cursor)):failEvent(state,cursor,"Shared state snapshot is invalid.","invalid_stream_event")}case"state-delta":{const patch=parseJsonPatch(source.patch,state.limits);if(!patch)return failEvent(state,cursor,"Shared state patch is invalid.","invalid_stream_event");const current=ownValue(state.sharedState,state.limits);if(!current)return failEvent(state,cursor,"Retained shared state is invalid.","invalid_stream_event");let patched=current.value;for(const operation of patch)patched=applyPatchOperation(patched,operation);const snapshot=ownValue(patched,state.limits);return snapshot?(usage.totalBytes+=snapshot.bytes-usage.sharedStateBytes,usage.sharedStateBytes=snapshot.bytes,commit(state,{...state,sharedState:snapshot.value},usage,cursor)):failEvent(state,cursor,"Shared state exceeds the configured limit.","stream_limit_exceeded")}case"error":{if(typeof source.message!="string"||source.message.length>state.limits.maxStatusMessageCharacters||!validOptionalString(source.code,state.limits.maxIdentifierCharacters))return failEvent(state,cursor,"Error event has an invalid shape.","invalid_stream_event");const error={message:source.message,...source.code?{code:source.code}:{}};return commit(state,{...state,status:{kind:"error",message:source.message},error},usage,cursor)}default:return failEvent(state,cursor,"Stream event type is not supported.","invalid_stream_event")}}function reduceAgentStreamEvents(state,events){return events.reduce(reduceAgentStream,state)}export{DEFAULT_AGENT_STREAM_LIMITS,applySharedStatePatch,createAgentStreamState,parseJsonPatch,reduceAgentStream,reduceAgentStreamEvents};
|
|
1
|
+
import{createProviderSnapshotBudget,snapshotProviderValue}from"./snapshot.js";const DEFAULT_AGENT_STREAM_LIMITS=Object.freeze({maxMessages:512,maxPartsPerMessage:512,maxTools:256,maxDeltaCharacters:32768,maxTextCharactersPerPart:262144,maxIdentifierCharacters:1024,maxStatusMessageCharacters:8192,maxPatchOperations:256,maxSnapshotDepth:32,maxSnapshotNodes:1e4,maxSnapshotBytes:1048576,maxRetainedBytes:8388608}),usageByState=new WeakMap,FORBIDDEN_PATH_SEGMENTS=new Set(["__proto__","prototype","constructor"]),ARRAY_INDEX=/^(0|[1-9][0-9]*)$/,MESSAGE_ROLES=new Set(["user","assistant","system"]),MESSAGE_STATUSES=new Set(["sending","sent","failed","streaming"]),PART_STATES=new Set(["streaming","complete"]),TOOL_STATUSES=new Set(["pending","running","success","error","denied","incomplete"]);function positiveInteger(value,fallback){return typeof value=="number"&&Number.isFinite(value)&&Number.isInteger(value)&&value>0?value:fallback}function resolveLimits(limits){const resolved=Object.fromEntries(Object.entries(DEFAULT_AGENT_STREAM_LIMITS).map(([key,fallback])=>[key,positiveInteger(limits[key],fallback)]));return resolved.maxSnapshotBytes>resolved.maxRetainedBytes&&(resolved.maxSnapshotBytes=resolved.maxRetainedBytes),Object.freeze(resolved)}function emptyUsage(){return{messages:new Map,tools:new Map,sharedStateBytes:0,totalBytes:0}}function copyUsage(usage){return{messages:new Map(usage.messages),tools:new Map(usage.tools),sharedStateBytes:usage.sharedStateBytes,totalBytes:usage.totalBytes}}function createAgentStreamState(limits={}){const state={generation:0,cursor:0,limits:resolveLimits(limits),status:{kind:"idle"},messages:[],tools:[],sharedState:null};return usageByState.set(state,emptyUsage()),state}function snapshotLimits(limits,maxBytes=limits.maxSnapshotBytes){return{maxDepth:limits.maxSnapshotDepth,maxNodes:limits.maxSnapshotNodes,maxBytes,maxStringCharacters:Math.max(limits.maxTextCharactersPerPart,limits.maxStatusMessageCharacters)}}function ownValue(value,limits){const result=snapshotProviderValue(value,createProviderSnapshotBudget(snapshotLimits(limits)));return result.ok?{value:result.value,bytes:result.bytes}:null}function isRecord(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function validIdentifier(value,limits){return typeof value=="string"&&value.length>0&&value.length<=limits.maxIdentifierCharacters}function validOptionalString(value,maximum){return value===void 0||typeof value=="string"&&value.length<=maximum}function validStatus(value,limits){return!isRecord(value)||!validIdentifier(value.kind,limits)?!1:validOptionalString(value.message,limits.maxStatusMessageCharacters)}function validDocument(value,limits){return!isRecord(value)||!validIdentifier(value.id,limits)||typeof value.name!="string"?!1:["mimeType","uri","version"].every(key=>validOptionalString(value[key],limits.maxSnapshotBytes))}const MAX_TOOL_REDACTION_PATHS=100,MAX_TOOL_REDACTION_PATH_CHARACTERS=4096;function validOptionalFiniteNumber(value){return value===void 0||typeof value=="number"&&Number.isFinite(value)}function validOptionalRedactionPaths(value){return value===void 0?!0:Array.isArray(value)&&value.length<=MAX_TOOL_REDACTION_PATHS&&value.every(path=>typeof path=="string"&&path.length<=MAX_TOOL_REDACTION_PATH_CHARACTERS)}function validTool(value,limits){return isRecord(value)?validIdentifier(value.id,limits)&&validIdentifier(value.name,limits)&&validOptionalString(value.displayName,limits.maxIdentifierCharacters)&&isRecord(value.args)&&TOOL_STATUSES.has(String(value.status))&&validOptionalString(value.error,limits.maxStatusMessageCharacters)&&validOptionalFiniteNumber(value.startedAt)&&validOptionalFiniteNumber(value.endedAt)&&validOptionalRedactionPaths(value.redactedFields):!1}function validPart(value,limits){if(!isRecord(value)||!validIdentifier(value.id,limits)||typeof value.type!="string"||value.state!==void 0&&!PART_STATES.has(String(value.state))||value.metadata!==void 0&&!isRecord(value.metadata))return!1;switch(value.type){case"text":case"reasoning":return typeof value.text=="string"&&value.text.length<=limits.maxTextCharactersPerPart;case"tool-call":return validTool(value.invocation,limits);case"tool-result":{if(!validIdentifier(value.invocationId,limits)||!validOptionalString(value.name,limits.maxIdentifierCharacters))return!1;const hasResult=Object.hasOwn(value,"result");return Object.hasOwn(value,"error")?typeof value.error=="string"&&value.error.length<=limits.maxStatusMessageCharacters:hasResult}case"citation":return isRecord(value.citation)&&validIdentifier(value.citation.id,limits);case"attachment":return validDocument(value.document,limits);case"data":{const hasData=Object.hasOwn(value,"data"),hasWidget=Object.hasOwn(value,"widget");return hasData!==hasWidget&&validOptionalString(value.name,limits.maxIdentifierCharacters)}case"audio":return["src","transcript","mimeType"].every(key=>validOptionalString(value[key],limits.maxSnapshotBytes));case"error":return typeof value.message=="string"&&value.message.length<=limits.maxStatusMessageCharacters&&validOptionalString(value.code,limits.maxIdentifierCharacters)&&(value.retryable===void 0||typeof value.retryable=="boolean");default:return!1}}function ownedPart(value,limits){const owned=ownValue(value,limits);return owned&&validPart(owned.value,limits)?owned:null}function validMessage(value,limits){return!(!isRecord(value)||!validIdentifier(value.id,limits)||!MESSAGE_ROLES.has(value.role)||value.status!==void 0&&!MESSAGE_STATUSES.has(String(value.status))||value.timestamp!==void 0&&typeof value.timestamp!="string"||!validOptionalString(value.text,limits.maxTextCharactersPerPart)||value.metadata!==void 0&&!isRecord(value.metadata)||value.attachments!==void 0&&(!Array.isArray(value.attachments)||!value.attachments.every(item=>validDocument(item,limits)))||value.parts!==void 0&&(!Array.isArray(value.parts)||value.parts.length>limits.maxPartsPerMessage||!value.parts.every(part=>validPart(part,limits))))}function ownedMessage(value,limits){const owned=ownValue(value,limits);return owned&&validMessage(owned.value,limits)?owned:null}function ownedTool(value,limits){const owned=ownValue(value,limits);return owned&&validTool(owned.value,limits)?owned:null}function usageFor(state){const cached=usageByState.get(state);if(cached)return cached;const usage=emptyUsage();for(const message of state.messages){const bytes=ownValue(message,state.limits)?.bytes??state.limits.maxRetainedBytes;usage.messages.set(message.id,bytes),usage.totalBytes+=bytes}for(const tool of state.tools){const bytes=ownValue(tool,state.limits)?.bytes??state.limits.maxRetainedBytes;usage.tools.set(tool.id,bytes),usage.totalBytes+=bytes}const shared=ownValue(state.sharedState,state.limits);return usage.sharedStateBytes=shared?.bytes??state.limits.maxRetainedBytes,usage.totalBytes+=usage.sharedStateBytes,usageByState.set(state,usage),usage}function replaceUsageEntry(usage,collection,id,bytes){const map=usage[collection];usage.totalBytes-=map.get(id)??0,map.set(id,bytes),usage.totalBytes+=bytes}function commit(state,next,usage,cursor){if(usage.totalBytes>state.limits.maxRetainedBytes)return failEvent(state,cursor,"Retained stream state exceeds the configured limit.","stream_limit_exceeded");const committed={...next,generation:cursor.generation,cursor:cursor.sequence};return usageByState.set(committed,usage),committed}function failEvent(state,cursor,message,code){const failed={...state,...cursor?{generation:cursor.generation,cursor:cursor.sequence}:{},status:{kind:"error",message},error:{message,code}};return usageByState.set(failed,usageFor(state)),failed}function pointerSegments(path){if(path==="")return[];if(!path.startsWith("/"))return null;const rawSegments=path.slice(1).split("/");if(rawSegments.some(segment=>/~(?:[^01]|$)/.test(segment)))return null;const segments=rawSegments.map(segment=>segment.replace(/~1/g,"/").replace(/~0/g,"~"));return segments.some(segment=>FORBIDDEN_PATH_SEGMENTS.has(segment))?null:segments}function validPatchKeys(record,operation){const expected=operation==="remove"?["op","path"]:["op","path","value"],keys=Object.keys(record).sort();return keys.length===expected.length&&keys.every((key,index)=>key===expected.sort()[index])}function parseJsonPatch(value,limits={}){const resolved=resolveLimits(limits),owned=ownValue(value,resolved);if(!owned||!Array.isArray(owned.value)||owned.value.length>resolved.maxPatchOperations)return null;const operations=[];for(const candidate of owned.value){if(!isRecord(candidate))return null;const operation=candidate.op,path=candidate.path;if(operation!=="add"&&operation!=="replace"&&operation!=="remove"||typeof path!="string"||pointerSegments(path)===null||!validPatchKeys(candidate,operation))return null;operations.push(operation==="remove"?{op:operation,path}:{op:operation,path,value:candidate.value})}return operations}function arrayIndex(segment){if(!ARRAY_INDEX.test(segment))return null;const index=Number(segment);return Number.isSafeInteger(index)?index:null}function applyPatchOperation(root,operation){const segments=pointerSegments(operation.path);if(!segments)return root;if(!segments.length)return operation.op==="remove"?null:operation.value;let parent=root;for(const segment of segments.slice(0,-1))if(Array.isArray(parent)){const index=arrayIndex(segment);if(index===null||index>=parent.length)return root;parent=parent[index]}else if(isRecord(parent)){if(!Object.hasOwn(parent,segment))return root;parent=parent[segment]}else return root;const key=segments.at(-1);if(key===void 0)return root;if(Array.isArray(parent)){if(key==="-")return operation.op==="add"&&parent.push(operation.value),root;const index=arrayIndex(key);return index===null||(operation.op==="add"?index<=parent.length&&parent.splice(index,0,operation.value):operation.op==="remove"?index<parent.length&&parent.splice(index,1):index<parent.length&&(parent[index]=operation.value)),root}return isRecord(parent)&&(operation.op==="add"?parent[key]=operation.value:operation.op==="remove"?Object.hasOwn(parent,key)&&delete parent[key]:Object.hasOwn(parent,key)&&(parent[key]=operation.value)),root}function applySharedStatePatch(value,patch){const limits=DEFAULT_AGENT_STREAM_LIMITS,root=ownValue(value,limits),operations=parseJsonPatch(patch,limits);if(!root)return null;if(!operations)return root.value;let next=root.value;for(const operation of operations)next=applyPatchOperation(next,operation);return next}function baseCursor(event){const generation=event.generation,sequence=event.sequence;return typeof generation=="number"&&Number.isSafeInteger(generation)&&generation>=0&&typeof sequence=="number"&&Number.isSafeInteger(sequence)&&sequence>0?{generation,sequence}:null}function shouldIgnore(state,type,cursor){return type==="run-start"?cursor.generation<=state.generation:type==="reset"?cursor.generation<state.generation||cursor.generation===state.generation&&cursor.sequence<=state.cursor:cursor.generation!==state.generation||cursor.sequence<=state.cursor}function reduceAgentStream(state,event){const eventSnapshot=snapshotProviderValue(event,createProviderSnapshotBudget(snapshotLimits(state.limits)));if(!eventSnapshot.ok)return eventSnapshot.failure==="limit"?failEvent(state,void 0,"Stream event exceeds the configured limit.","stream_limit_exceeded"):failEvent(state,void 0,"Stream event is not valid serializable data.","invalid_stream_event");const source=eventSnapshot.value;if(!isRecord(source))return failEvent(state,void 0,"Stream event is not a record.","invalid_stream_event");const cursor=baseCursor(source);if(!cursor||typeof source.type!="string")return failEvent(state,void 0,"Stream event generation, sequence, or type is invalid.","invalid_stream_event");if(shouldIgnore(state,source.type,cursor))return state;const usage=copyUsage(usageFor(state));switch(source.type){case"reset":{const reset=createAgentStreamState(state.limits);return commit(state,reset,emptyUsage(),cursor)}case"run-start":{if(!validIdentifier(source.runId,state.limits))return failEvent(state,cursor,"Run start has an invalid run id.","invalid_stream_event");const started=createAgentStreamState(state.limits);return started.runId=source.runId,started.status={kind:"running"},commit(state,started,emptyUsage(),cursor)}case"run-status":{if(!validStatus(source.status,state.limits)||source.runId!==void 0&&!validIdentifier(source.runId,state.limits))return failEvent(state,cursor,"Run status has an invalid shape.","invalid_stream_event");const status=source.status,error=status.kind==="error"?{message:status.message??state.error?.message??"Agent run failed",...state.error?.code&&(!status.message||status.message===state.error.message)?{code:state.error.code}:{}}:void 0;return commit(state,{...state,runId:source.runId??state.runId,status,error},usage,cursor)}case"messages-snapshot":{if(!Array.isArray(source.messages)||source.messages.length>state.limits.maxMessages)return failEvent(state,cursor,"Message snapshot exceeds the configured limit.","stream_limit_exceeded");const messages=[],messageBytes=new Map;let total=usage.totalBytes-[...usage.messages.values()].reduce((sum,bytes)=>sum+bytes,0);for(const candidate of source.messages){const message=ownedMessage(candidate,state.limits);if(!message||messageBytes.has(message.value.id))return failEvent(state,cursor,"Message snapshot contains an invalid or duplicate message.","invalid_stream_event");messages.push(message.value),messageBytes.set(message.value.id,message.bytes),total+=message.bytes}usage.messages.clear();for(const[id,bytes]of messageBytes)usage.messages.set(id,bytes);return usage.totalBytes=total,commit(state,{...state,messages},usage,cursor)}case"message-start":{const incoming=ownedMessage(source.message,state.limits);if(!incoming)return failEvent(state,cursor,"Message has an invalid shape.","invalid_stream_event");const index=state.messages.findIndex(message2=>message2.id===incoming.value.id);if(index<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const existing=index>=0?state.messages[index]:void 0,merged=existing?{...existing,...incoming.value,parts:incoming.value.parts??existing.parts}:incoming.value,message=ownedMessage(merged,state.limits);if(!message)return failEvent(state,cursor,"Message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return index<0?messages.push(message.value):messages[index]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-part-upsert":{if(!validIdentifier(source.messageId,state.limits)||source.role!==void 0&&!MESSAGE_ROLES.has(source.role))return failEvent(state,cursor,"Message part target is invalid.","invalid_stream_event");const part=ownedPart(source.part,state.limits);if(!part)return failEvent(state,cursor,"Message part has an invalid shape.","invalid_stream_event");const messageIndex=state.messages.findIndex(message2=>message2.id===source.messageId);if(messageIndex<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const current=messageIndex>=0?state.messages[messageIndex]:{id:source.messageId,role:source.role??"assistant",status:"streaming",parts:[]},parts=[...current.parts??[]],partIndex=parts.findIndex(candidate=>candidate.id===part.value.id);if(partIndex<0&&parts.length>=state.limits.maxPartsPerMessage)return failEvent(state,cursor,"Message part count exceeds the configured limit.","stream_limit_exceeded");partIndex<0?parts.push(part.value):parts[partIndex]=part.value;const message=ownedMessage({...current,parts},state.limits);if(!message)return failEvent(state,cursor,"Updated message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messageIndex<0?messages.push(message.value):messages[messageIndex]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-part-delta":{if(!validIdentifier(source.messageId,state.limits)||!validIdentifier(source.partId,state.limits)||source.partType!=="text"&&source.partType!=="reasoning"||typeof source.delta!="string"||source.role!==void 0&&!MESSAGE_ROLES.has(source.role))return failEvent(state,cursor,"Message delta has an invalid shape.","invalid_stream_event");if(source.delta.length>state.limits.maxDeltaCharacters)return failEvent(state,cursor,"Message delta exceeds the configured limit.","stream_limit_exceeded");const messageIndex=state.messages.findIndex(message2=>message2.id===source.messageId);if(messageIndex<0&&state.messages.length>=state.limits.maxMessages)return failEvent(state,cursor,"Message count exceeds the configured limit.","stream_limit_exceeded");const current=messageIndex>=0?state.messages[messageIndex]:{id:source.messageId,role:source.role??"assistant",status:"streaming",parts:[]},parts=[...current.parts??[]],partIndex=parts.findIndex(part2=>part2.id===source.partId),previous=partIndex>=0?parts[partIndex]:void 0;if(previous&&previous.type!=="text"&&previous.type!=="reasoning")return failEvent(state,cursor,"Message delta targets a non-text part.","invalid_stream_event");if(partIndex<0&&parts.length>=state.limits.maxPartsPerMessage)return failEvent(state,cursor,"Message part count exceeds the configured limit.","stream_limit_exceeded");const text=(previous?.text??"")+source.delta;if(text.length>state.limits.maxTextCharactersPerPart)return failEvent(state,cursor,"Message text exceeds the configured limit.","stream_limit_exceeded");const part=source.partType==="reasoning"?{id:source.partId,type:"reasoning",text,state:"streaming"}:{id:source.partId,type:"text",text,state:"streaming"};partIndex<0?parts.push(part):parts[partIndex]=part;const message=ownedMessage({...current,parts},state.limits);if(!message)return failEvent(state,cursor,"Updated message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messageIndex<0?messages.push(message.value):messages[messageIndex]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"message-complete":{if(!validIdentifier(source.messageId,state.limits))return failEvent(state,cursor,"Message completion target is invalid.","invalid_stream_event");const index=state.messages.findIndex(message2=>message2.id===source.messageId);if(index<0)return commit(state,state,usage,cursor);const current=state.messages[index],message=ownedMessage({...current,status:"sent",parts:current.parts?.map(part=>({...part,state:"complete"}))},state.limits);if(!message)return failEvent(state,cursor,"Completed message has an invalid shape.","invalid_stream_event");const messages=[...state.messages];return messages[index]=message.value,replaceUsageEntry(usage,"messages",message.value.id,message.bytes),commit(state,{...state,messages},usage,cursor)}case"tool-upsert":{const incoming=ownedTool(source.invocation,state.limits);if(!incoming)return failEvent(state,cursor,"Tool invocation has an invalid shape.","invalid_stream_event");const index=state.tools.findIndex(tool2=>tool2.id===incoming.value.id);if(index<0&&state.tools.length>=state.limits.maxTools)return failEvent(state,cursor,"Tool count exceeds the configured limit.","stream_limit_exceeded");const tool=ownedTool(index<0?incoming.value:{...state.tools[index],...incoming.value},state.limits);if(!tool)return failEvent(state,cursor,"Tool invocation has an invalid shape.","invalid_stream_event");const tools=[...state.tools];return index<0?tools.push(tool.value):tools[index]=tool.value,replaceUsageEntry(usage,"tools",tool.value.id,tool.bytes),commit(state,{...state,tools},usage,cursor)}case"state-snapshot":{const snapshot=ownValue(source.snapshot,state.limits);return snapshot?(usage.totalBytes+=snapshot.bytes-usage.sharedStateBytes,usage.sharedStateBytes=snapshot.bytes,commit(state,{...state,sharedState:snapshot.value},usage,cursor)):failEvent(state,cursor,"Shared state snapshot is invalid.","invalid_stream_event")}case"state-delta":{const patch=parseJsonPatch(source.patch,state.limits);if(!patch)return failEvent(state,cursor,"Shared state patch is invalid.","invalid_stream_event");const current=ownValue(state.sharedState,state.limits);if(!current)return failEvent(state,cursor,"Retained shared state is invalid.","invalid_stream_event");let patched=current.value;for(const operation of patch)patched=applyPatchOperation(patched,operation);const snapshot=ownValue(patched,state.limits);return snapshot?(usage.totalBytes+=snapshot.bytes-usage.sharedStateBytes,usage.sharedStateBytes=snapshot.bytes,commit(state,{...state,sharedState:snapshot.value},usage,cursor)):failEvent(state,cursor,"Shared state exceeds the configured limit.","stream_limit_exceeded")}case"error":{if(typeof source.message!="string"||source.message.length>state.limits.maxStatusMessageCharacters||!validOptionalString(source.code,state.limits.maxIdentifierCharacters))return failEvent(state,cursor,"Error event has an invalid shape.","invalid_stream_event");const error={message:source.message,...source.code?{code:source.code}:{}};return commit(state,{...state,status:{kind:"error",message:source.message},error},usage,cursor)}default:return failEvent(state,cursor,"Stream event type is not supported.","invalid_stream_event")}}function reduceAgentStreamEvents(state,events){return events.reduce(reduceAgentStream,state)}export{DEFAULT_AGENT_STREAM_LIMITS,applySharedStatePatch,createAgentStreamState,parseJsonPatch,reduceAgentStream,reduceAgentStreamEvents};
|
package/dist/ai/types.d.ts
CHANGED
|
@@ -23,10 +23,10 @@ import type{ChatMessageRole,ChatMessageStatus}from'../components/conversation/ch
|
|
|
23
23
|
/**
|
|
24
24
|
* Coarse lifecycle state for a whole agent run or a single step within one. Broader than any
|
|
25
25
|
* single existing component's own status vocabulary (e.g. `ToolCallStatus`'s `'pending' |
|
|
26
|
-
* 'running' | 'success' | 'error' | 'denied'`, reused as-is by `ToolInvocation`
|
|
27
|
-
* run/step can also be waiting on the user (`'waiting-input'`), waiting on an
|
|
28
|
-
* (`'waiting-approval'`), or user-cancelled (`'cancelled'`) --
|
|
29
|
-
*
|
|
26
|
+
* 'running' | 'success' | 'error' | 'denied' | 'incomplete'`, reused as-is by `ToolInvocation`
|
|
27
|
+
* below) because a run/step can also be waiting on the user (`'waiting-input'`), waiting on an
|
|
28
|
+
* approval gate (`'waiting-approval'`), or user-cancelled (`'cancelled'`) -- a single tool call
|
|
29
|
+
* has no waiting states, and folds an interrupted or cancelled call into its own `'incomplete'`.
|
|
30
30
|
*/
|
|
31
31
|
export type AgentStatusKind='idle'|'running'|'queued'|'collecting'|'waiting-input'|'waiting-approval'|'done'|'error'|'cancelled'|(string&{});
|
|
32
32
|
/** A status value paired with an optional human-readable detail, e.g. `{ kind: 'error', message: 'Rate limited' }`. */
|
|
@@ -73,7 +73,15 @@ parts?:readonly MessagePart[];metadata?:Record<string,unknown>;}
|
|
|
73
73
|
* `args: unknown` / `result: unknown` properties, so a `ToolInvocation`'s fields assign directly
|
|
74
74
|
* onto that component too.
|
|
75
75
|
*/
|
|
76
|
-
export interface ToolInvocation{id:string;name:string;
|
|
76
|
+
export interface ToolInvocation{id:string;name:string;
|
|
77
|
+
/** The application's own, already-translated label for the tool (e.g. `Lecture d’un fichier`).
|
|
78
|
+
* Shown in place of `name` by `<lr-message-parts>` (either `tool-display`) and
|
|
79
|
+
* `<lr-tool-timeline>`; `name` still selects the result renderer and is still the id reported in
|
|
80
|
+
* events. Omitted or empty shows `name`. */
|
|
81
|
+
displayName?:string;args:Record<string,unknown>;
|
|
82
|
+
/** `'incomplete'` marks a call that ended without a result (an interrupted stream, a cancelled
|
|
83
|
+
* run). */
|
|
84
|
+
status:ToolCallStatus;result?:unknown;error?:string;
|
|
77
85
|
/** Epoch milliseconds the call started. */
|
|
78
86
|
startedAt?:number;
|
|
79
87
|
/** Epoch milliseconds the call reached a terminal state. With `startedAt`, derives the duration
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const LYRA_RENAME_LEDGER_SCHEMA_VERSION=1,UNRELEASED_VERSION="unreleased",LYRA_RENAME_PROFILES=Object.freeze([Object.freeze({origin:"lyra-v21",fromMajor:21,toMajor:22,aliasRemovalMajor:23})]),LYRA_RENAME_ORIGINS=Object.freeze(LYRA_RENAME_PROFILES.map(profile=>profile.origin)),RENAME_KINDS=Object.freeze(["attribute","property","event","part","css-property","slot"]),REVIEW_KINDS=Object.freeze(["component","attribute","property","event","slot","part","css-property","css-state","method"]),EXPOSURE_KINDS=Object.freeze(["event","part","css-property"]),SURFACE_SECTIONS=Object.freeze({attribute:"attributes",property:"properties",event:"events",part:"parts","css-property":"cssProperties",slot:"slots","css-state":"cssStates",method:"methods"}),TAG_PATTERN=/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,ELEMENT_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,JS_IDENTIFIER_PATTERN=/^[A-Za-z_$][\w$]*$/,VERSION_PATTERN=/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/,NAME_PATTERNS=Object.freeze({component:TAG_PATTERN,attribute:/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,property:JS_IDENTIFIER_PATTERN,method:JS_IDENTIFIER_PATTERN,event:/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,part:/^[A-Za-z0-9_-]+$/,slot:/^(?:[A-Za-z0-9_-]+)?$/,"css-property":/^--[A-Za-z0-9_-]+$/,"css-state":/^[a-z][a-z0-9-]*$/}),PROFILE_LIST_KEYS=["renames","defaults","detailChanges","reviews","slotContent"],AUTHORED_PROFILE_KEYS=["origin","fromMajor","toMajor","aliasRemovalMajor",...PROFILE_LIST_KEYS],PROJECTED_PROFILE_KEYS=[...AUTHORED_PROFILE_KEYS,"exposure"];function isPlainObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function unknownKeys(value,allowed){return Object.keys(value).filter(key=>!allowed.includes(key))}function compareText(left,right){return left<right?-1:left>right?1:0}function parseVersion(version){const match=VERSION_PATTERN.exec(String(version??""));return match?match.slice(1,4).map(Number):null}function compareVersions(left,right){const a=parseVersion(left),b=parseVersion(right);if(!a||!b)return null;for(let index=0;index<3;index+=1)if(a[index]!==b[index])return a[index]-b[index];return 0}function majorOf(version){return parseVersion(version)?.[0]??null}function emptyRenameLedger(){return{schemaVersion:1,profiles:LYRA_RENAME_PROFILES.map(header=>({...header,...Object.fromEntries(PROFILE_LIST_KEYS.map(list=>[list,[]]))}))}}function emptyRenameProjection(){return{schemaVersion:1,profiles:emptyRenameLedger().profiles.map(profile=>({...profile,exposure:Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]))}))}}function entryLabel(origin,list,entry){return list==="renames"?`${origin}: rename ${entry?.tag} ${entry?.kind} ${entry?.from}`:list==="defaults"?`${origin}: default ${entry?.tag} ${entry?.attribute}`:list==="detailChanges"?`${origin}: detail change ${entry?.tag} ${entry?.event}`:list==="slotContent"?`${origin}: slot content ${entry?.tag} ${JSON.stringify(entry?.slot??null)}`:`${origin}: review ${entry?.tag} ${entry?.kind} ${JSON.stringify(entry?.name??null)}`}function sortKey(list,entry){return list==="renames"?[entry.tag,entry.kind,entry.from].join("\0"):list==="defaults"?[entry.tag,entry.attribute].join("\0"):list==="detailChanges"?[entry.tag,entry.event].join("\0"):list==="slotContent"?[entry.tag,entry.slot].join("\0"):[entry.tag,entry.kind,entry.name].join("\0")}function validateName(findings,label,kind,field,value){return typeof value!="string"||!NAME_PATTERNS[kind]?.test(value)?(findings.push(`${label}: ${field} must be a valid ${kind} name, got ${JSON.stringify(value)}`),!1):!0}function validateProjectedSince(findings,label,entry,projected){projected&&entry.since!==UNRELEASED_VERSION&&!parseVersion(entry.since)&&findings.push(`${label}: since must be a version or ${JSON.stringify(UNRELEASED_VERSION)}`)}function validateRenameEntry(findings,label,entry,projected){const allowed=["tag","kind","from","to","polarity",...projected?["since","reflects"]:[]],unknown=unknownKeys(entry,allowed);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!RENAME_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${RENAME_KINDS.join(", ")}`);return}const validFrom=validateName(findings,label,entry.kind,"from",entry.from),validTo=validateName(findings,label,entry.kind,"to",entry.to);validFrom&&validTo&&entry.from===entry.to&&findings.push(`${label}: from and to must differ`),entry.kind==="slot"&&(entry.from===""||entry.to==="")&&findings.push(`${label}: the default slot can be reviewed but not renamed`),Object.hasOwn(entry,"polarity")&&(entry.polarity!=="inverted"&&findings.push(`${label}: polarity may only be "inverted"`),entry.kind!=="attribute"&&entry.kind!=="property"&&findings.push(`${label}: only attribute and property renames can invert polarity`)),validateProjectedSince(findings,label,entry,projected),projected&&Object.hasOwn(entry,"reflects")!==(entry.kind==="attribute")&&findings.push(`${label}: reflects is recorded for attribute renames only`),projected&&entry.kind==="attribute"&&typeof entry.reflects!="boolean"&&findings.push(`${label}: reflects must be a boolean`)}function validateDefaultEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","attribute","value",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"attribute","attribute",entry.attribute),Object.hasOwn(entry,"value")?typeof entry.value!="string"&&entry.value!==!0&&!(typeof entry.value=="number"&&Number.isFinite(entry.value))&&findings.push(`${label}: value must be a string, a finite number, or true (presence)`):findings.push(`${label}: needs a value`),validateProjectedSince(findings,label,entry,projected)}function validateSummary(findings,label,summary){(typeof summary!="string"||summary.trim().length<16||/\n/.test(summary))&&findings.push(`${label}: summary must be a single-line description of the change`)}function validateDetailEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateSlotContentEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","slot","report","allow","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"slot","slot",entry.slot);const lists=["report","allow"].filter(key=>Object.hasOwn(entry,key));if(lists.length!==1)findings.push(`${label}: needs exactly one of report (elements to report) or allow (the only elements kept)`);else{const elements=entry[lists[0]];(!Array.isArray(elements)||!elements.length||!elements.every(element=>typeof element=="string"&&ELEMENT_PATTERN.test(element))||JSON.stringify(elements)!==JSON.stringify([...new Set(elements)].sort(compareText)))&&findings.push(`${label}: ${lists[0]} must be a sorted, unique list of lowercase element names`)}validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateReviewEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,projected?["tag","kind","name","replacement","removalNotBefore","since"]:["tag","kind","name"]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!REVIEW_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${REVIEW_KINDS.join(", ")}`);return}validateName(findings,label,entry.kind,"name",entry.name)&&entry.kind==="component"&&entry.name!==entry.tag&&findings.push(`${label}: a component review must name its own tag`),projected&&((typeof entry.replacement!="string"||!entry.replacement)&&findings.push(`${label}: replacement text missing`),majorOf(entry.removalNotBefore)===null&&findings.push(`${label}: removalNotBefore must be a version`)),validateProjectedSince(findings,label,entry,projected)}function requiredExposure(profile){const required=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map])),need=(kind,name,tag)=>{if(!required.has(kind))return;const tags=required.get(kind).get(name)??new Set;tag&&tags.add(tag),required.get(kind).set(name,tags)};for(const entry of profile.renames??[])need(entry.kind,entry.from,entry.tag),need(entry.kind,entry.to,entry.tag);for(const entry of profile.reviews??[])need(entry.kind,entry.name,entry.tag);for(const entry of profile.detailChanges??[])need("event",entry.event,entry.tag);return required}function validateExposure(findings,profile){const origin=profile.origin;if(!isPlainObject(profile.exposure)){findings.push(`${origin}: exposure must be an object`);return}const unknown=unknownKeys(profile.exposure,EXPOSURE_KINDS);unknown.length&&findings.push(`${origin}: exposure has unknown kind(s) ${unknown.join(", ")}`);const required=requiredExposure(profile);for(const kind of EXPOSURE_KINDS){const recorded=profile.exposure[kind];if(!isPlainObject(recorded)){findings.push(`${origin}: exposure.${kind} must be an object`);continue}for(const[name,tags]of Object.entries(recorded))required.get(kind).has(name)||findings.push(`${origin}: exposure.${kind} records unreferenced name ${name}`),(!Array.isArray(tags)||!tags.every(tag=>typeof tag=="string"&&TAG_PATTERN.test(tag))||JSON.stringify(tags)!==JSON.stringify([...new Set(tags)].sort(compareText)))&&findings.push(`${origin}: exposure.${kind}.${name} must be a sorted, unique tag list`);for(const[name,tags]of required.get(kind)){if(!Object.hasOwn(recorded,name)){findings.push(`${origin}: exposure.${kind}.${name} is missing`);continue}const recordedTags=new Set(recorded[name]??[]);for(const tag of tags)recordedTags.has(tag)||findings.push(`${origin}: exposure.${kind}.${name} must include ${tag}`)}}}function validateRenameLedgerShape(ledger,{projected=!1}={}){const findings=[];if(!isPlainObject(ledger))return["rename ledger must be an object"];const unknownTop=unknownKeys(ledger,projected?["schemaVersion","profiles"]:["$comment","schemaVersion","profiles"]);if(unknownTop.length&&findings.push(`rename ledger has unknown key(s) ${unknownTop.join(", ")}`),ledger.schemaVersion!==1&&findings.push("rename ledger schemaVersion must be 1"),!Array.isArray(ledger.profiles))return[...findings,"rename ledger profiles must be an array"];const origins=ledger.profiles.map(profile=>profile?.origin);JSON.stringify(origins)!==JSON.stringify(LYRA_RENAME_ORIGINS)&&findings.push(`rename ledger profiles must be exactly ${LYRA_RENAME_ORIGINS.join(", ")} in that order`);for(const profile of ledger.profiles){if(!isPlainObject(profile)){findings.push("rename ledger profiles must be objects");continue}const origin=String(profile.origin),unknown=unknownKeys(profile,projected?PROJECTED_PROFILE_KEYS:AUTHORED_PROFILE_KEYS);unknown.length&&findings.push(`${origin}: unknown key(s) ${unknown.join(", ")}`);const header=LYRA_RENAME_PROFILES.find(candidate=>candidate.origin===profile.origin);if(header)for(const field of["fromMajor","toMajor","aliasRemovalMajor"])profile[field]!==header[field]&&findings.push(`${origin}: ${field} must be ${header[field]}`);for(const list of PROFILE_LIST_KEYS){if(!Array.isArray(profile[list])){findings.push(`${origin}: ${list} must be an array`);continue}const seen=new Set,keys=[];for(const entry of profile[list]){const label=entryLabel(origin,list,entry);if(!isPlainObject(entry)){findings.push(`${origin}: ${list} entries must be objects`);continue}(typeof entry.tag!="string"||!TAG_PATTERN.test(entry.tag))&&findings.push(`${label}: tag must be an lr-* element name`),list==="renames"?validateRenameEntry(findings,label,entry,projected):list==="defaults"?validateDefaultEntry(findings,label,entry,projected):list==="detailChanges"?validateDetailEntry(findings,label,entry,projected):list==="slotContent"?validateSlotContentEntry(findings,label,entry,projected):validateReviewEntry(findings,label,entry,projected);const key=sortKey(list,entry);seen.has(key)&&findings.push(`${label}: duplicate entry`),seen.add(key),keys.push(key)}JSON.stringify(keys)!==JSON.stringify([...keys].sort(compareText))&&findings.push(`${origin}: ${list} must be sorted by tag and name`)}if(!Array.isArray(profile.renames)||!Array.isArray(profile.reviews))continue;const sources=new Map;for(const entry of profile.renames){const names=sources.get(entry.kind)??new Set;names.add(entry.from),sources.set(entry.kind,names)}for(const entry of profile.renames)sources.get(entry.kind)?.has(entry.to)&&findings.push(`${entryLabel(origin,"renames",entry)}: target ${entry.to} is itself renamed; rename directly to the final name`);const renamed=new Set(profile.renames.map(entry=>`${entry.tag}\0${entry.kind}\0${entry.from}`));for(const entry of profile.reviews)renamed.has(`${entry.tag}\0${entry.kind}\0${entry.name}`)&&findings.push(`${entryLabel(origin,"reviews",entry)}: a member is either renamed or reviewed, not both`);projected&&validateExposure(findings,profile)}return findings}function componentMap(inventory){return new Map((inventory?.components??[]).map(component=>[component.tag,component]))}function surfaceEntry(component,kind,name){return(component?.surface?.[SURFACE_SECTIONS[kind]]??[]).find(entry=>entry.name===name)??null}function deprecationRecords(component){const records=[...component?.maturity?.deprecations??[]],componentRecord=component?.maturity?.deprecated;return isPlainObject(componentRecord)&&!records.some(record=>record.kind==="component")&&records.push(componentRecord),records}function deprecationRecordFor(component,kind,name){const records=deprecationRecords(component);if(kind==="component")return records.find(record=>record.kind==="component")??null;const direct=records.find(record=>record.kind===kind&&record.name===name);return direct||kind!=="attribute"?direct??null:records.find(record=>record.kind==="property"&&record.attribute===name)??null}function replacementMatches(component,kind,record,to){const replacement=record?.replacement;return isPlainObject(replacement)?replacement.kind===kind&&replacement.name===to?!0:kind==="attribute"&&replacement.kind==="property"?surfaceEntry(component,"property",replacement.name)?.attribute===to:!1:!1}function isBooleanMember(entry){return String(entry?.type??"").split("|").map(part=>part.trim()).includes("boolean")}function exposingTags(inventory,kind,name){return(inventory?.components??[]).filter(component=>surfaceEntry(component,kind,name)).map(component=>component.tag).sort(compareText)}const UNMIRRORED_CLASSIFICATIONS=new Set(["conceptual-only","unsupported"]);function mirroredMembers(inventory){const upstreamComponents=new Map;for(const ecosystem of Object.values(inventory?.upstreams??{}))for(const component of ecosystem?.components??[])upstreamComponents.set(component.tag,component);const mirrored=new Map;for(const mapping of inventory?.mappings??[]){if(!mapping?.targetTag||UNMIRRORED_CLASSIFICATIONS.has(mapping.classification))continue;const upstream=upstreamComponents.get(mapping.upstreamTag);for(const[kind,section]of Object.entries(SURFACE_SECTIONS)){const rewrites=mapping.rewrites?.[section]??[],renamedAway=new Set(rewrites.map(rule=>rule?.from)),names=[...rewrites.map(rule=>rule?.to).filter(name=>typeof name=="string"),...(upstream?.surface?.[section]??[]).map(entry=>entry.name).filter(name=>!renamedAway.has(name))];for(const name of names){const key=`${mapping.targetTag}\0${kind}\0${name}`;mirrored.has(key)||mirrored.set(key,mapping.upstreamTag)}}}return mirrored}function validateRenameLedger(ledger,{inventory,requireCoverage=!1,sharedTokens=null}){const findings=validateRenameLedgerShape(ledger);if(findings.length)return findings;const components=componentMap(inventory),mirrored=mirroredMembers(inventory),mirrorOf=(tag,kind,name)=>mirrored.get(`${tag}\0${kind}\0${name}`)??null;for(const profile of ledger.profiles){const origin=profile.origin,covered=new Set,release=`${profile.toMajor}.0.0`,removalMajorMatches=record=>majorOf(record?.removalNotBefore)===profile.aliasRemovalMajor,checkRecordWindow=(label,record)=>{if(removalMajorMatches(record)||findings.push(`${label}: its deprecation record must set removalNotBefore to ${profile.aliasRemovalMajor}.0.0`),record?.since===UNRELEASED_VERSION)return;const since=compareVersions(record?.since,release);(since===null||since>0)&&findings.push(`${label}: its deprecation record must start no later than ${release} (since ${JSON.stringify(record?.since)})`)},checkLyraOnly=(label,tag,kind,name)=>{const upstream=mirrorOf(tag,kind,name);upstream&&findings.push(`${label}: ${name} mirrors ${upstream}; mirrored names never change`)},checkSharedToken=(label,kind,name)=>{kind==="css-property"&&sharedTokens?.has(name)&&findings.push(`${label}: ${name} is a canonical design token, not a component-owned custom property`)},presenceDefaults=new Set(profile.defaults.filter(entry=>entry.value===!0).map(entry=>`${entry.tag}\0${entry.attribute}`)),inversionCompanions=new Set;for(const entry of profile.renames){const label=entryLabel(origin,"renames",entry),component=components.get(entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.from}`),checkLyraOnly(label,entry.tag,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.to);const from=surfaceEntry(component,entry.kind,entry.from),to=surfaceEntry(component,entry.kind,entry.to);from?from.deprecated||findings.push(`${label}: the alias ${entry.from} is not marked deprecated in the manifest`):findings.push(`${label}: the deprecated alias ${entry.from} is not on the public surface`),to?to.deprecated&&findings.push(`${label}: the canonical name ${entry.to} is itself deprecated`):findings.push(`${label}: the canonical name ${entry.to} is not on the public surface`);const record=deprecationRecordFor(component,entry.kind,entry.from);if(record?(checkRecordWindow(label,record),replacementMatches(component,entry.kind,record,entry.to)||findings.push(`${label}: its deprecation record names a different replacement than ${entry.to}`)):findings.push(`${label}: no deprecation record retires ${entry.from}`),(entry.kind==="attribute"||entry.kind==="property")&&from&&to&&isBooleanMember(from)&&isBooleanMember(to)){const toAttribute=entry.kind==="attribute"?entry.to:to.attribute,preservesPresence=!!toAttribute&&presenceDefaults.has(`${entry.tag}\0${toAttribute}`);entry.polarity==="inverted"?(to.default===!0&&findings.push(`${label}: an inverted rename must target a boolean that defaults to false`),from.default!==!0&&toAttribute&&inversionCompanions.add(`${entry.tag}\0${toAttribute}`),from.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to false, so the inverted rename needs a defaults entry inserting ${toAttribute??entry.to} where both are absent`)):from.default===!0&&to.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to true and ${entry.to} to false; declare "polarity": "inverted" or preserve the default with a defaults entry`)}else entry.polarity==="inverted"&&from&&to&&findings.push(`${label}: an inverted rename must retire a boolean for a boolean`)}for(const entry of profile.reviews){const label=entryLabel(origin,"reviews",entry),component=components.get(entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.name}`),entry.kind!=="component"&&checkLyraOnly(label,entry.tag,entry.kind,entry.name),checkSharedToken(label,entry.kind,entry.name);const record=deprecationRecordFor(component,entry.kind,entry.name);record?checkRecordWindow(label,record):findings.push(`${label}: no deprecation record retires ${JSON.stringify(entry.name)}`)}for(const entry of profile.detailChanges){const label=entryLabel(origin,"detailChanges",entry),component=components.get(entry.tag);component?surfaceEntry(component,"event",entry.event)?checkLyraOnly(label,entry.tag,"event",entry.event):findings.push(`${label}: the event is not dispatched by ${entry.tag}`):findings.push(`${label}: component is not in the inventory`)}for(const entry of profile.slotContent){const label=entryLabel(origin,"slotContent",entry),component=components.get(entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}surfaceEntry(component,"slot",entry.slot)||findings.push(`${label}: the slot is not on the public surface`);const record=deprecationRecordFor(component,"slot-content",entry.slot);record&&(covered.add(`${entry.tag}\0slot-content\0${entry.slot}`),Array.isArray(record.permittedContent)&&JSON.stringify(entry.allow??null)!==JSON.stringify(record.permittedContent)&&findings.push(`${label}: its deprecation record permits ${record.permittedContent.join(", ")}; allow must list exactly those elements`))}for(const entry of profile.defaults){const label=entryLabel(origin,"defaults",entry),component=components.get(entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}const attribute=surfaceEntry(component,"attribute",entry.attribute);if(!attribute){findings.push(`${label}: the attribute is not on the public surface`);continue}inversionCompanions.has(`${entry.tag}\0${entry.attribute}`)||checkLyraOnly(label,entry.tag,"attribute",entry.attribute),entry.value===!0&&!isBooleanMember(attribute)&&findings.push(`${label}: presence insertion requires a boolean attribute`),attribute.hasDefault&&String(attribute.default)===String(entry.value)&&findings.push(`${label}: the value equals the current default, so there is nothing to preserve`)}if(requireCoverage)for(const component of inventory?.components??[])for(const record of deprecationRecords(component)){if(!removalMajorMatches(record))continue;const keys=[[record.kind,record.kind==="component"?component.tag:record.name]];record.kind==="property"&&record.attribute&&keys.push(["attribute",record.attribute]);for(const[kind,name]of keys)kind!=="component"&&mirrorOf(component.tag,kind,name)||covered.has(`${component.tag}\0${kind}\0${name}`)||findings.push(`${origin}: ${component.tag} ${kind} ${JSON.stringify(name)} is removed in ${profile.aliasRemovalMajor}.0.0 but has no ${kind==="slot-content"?"slotContent":"rename or review"} entry`)}}return[...new Set(findings)]}function projectRenameLedger(ledger,inventory){const findings=validateRenameLedger(ledger,{inventory});if(findings.length)throw new Error(`Invalid Lyra rename ledger: ${findings.join("; ")}`);const components=componentMap(inventory),projection={schemaVersion:1,profiles:ledger.profiles.map(profile=>{const release=`${profile.toMajor}.0.0`,exposure=Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]));for(const[kind,names]of requiredExposure(profile))for(const name of[...names.keys()].sort(compareText))exposure[kind][name]=exposingTags(inventory,kind,name);const recordFor=(entry,kind,name)=>deprecationRecordFor(components.get(entry.tag),kind,name);return{origin:profile.origin,fromMajor:profile.fromMajor,toMajor:profile.toMajor,aliasRemovalMajor:profile.aliasRemovalMajor,renames:profile.renames.map(entry=>{const target=surfaceEntry(components.get(entry.tag),entry.kind,entry.to),attribute=entry.kind==="attribute"?entry.to:target?.attribute,preservesDefault=entry.polarity==="inverted"&&profile.defaults.some(rule=>rule.tag===entry.tag&&rule.attribute===attribute),since=recordFor(entry,entry.kind,entry.from).since,beforeRelease=since===UNRELEASED_VERSION||compareVersions(since,release)<0;return{...structuredClone(entry),since:preservesDefault&&beforeRelease?release:since,...entry.kind==="attribute"?{reflects:!!target?.reflects}:{}}}),defaults:profile.defaults.map(entry=>({...structuredClone(entry),since:release})),detailChanges:profile.detailChanges.map(entry=>({...structuredClone(entry),since:release})),reviews:profile.reviews.map(entry=>{const record=recordFor(entry,entry.kind,entry.name);return{...structuredClone(entry),replacement:String(record.replacement?.usage||record.replacement?.name),removalNotBefore:record.removalNotBefore,since:record.since}}),slotContent:profile.slotContent.map(entry=>({...structuredClone(entry),since:release})),exposure}})},shapeFindings=validateRenameLedgerShape(projection,{projected:!0});if(shapeFindings.length)throw new Error(`Invalid Lyra rename projection: ${shapeFindings.join("; ")}`);return projection}function indexBy(entries,keyOf){const index=new Map;for(const entry of entries){const key=keyOf(entry),list=index.get(key)??[];list.push(entry),index.set(key,list)}return index}const ownerKey=(tag,kind,name)=>`${tag}\0${kind}\0${name}`,nameKey=(kind,name)=>`${kind}\0${name}`;function createRenameProfiles(projection,{lyraVersion=null}={}){const findings=validateRenameLedgerShape(projection,{projected:!0});if(findings.length)throw new Error(`Invalid Lyra rename projection: ${findings.join("; ")}`);if(lyraVersion!==null&&!parseVersion(lyraVersion))throw new Error(`Invalid Lyra version: ${lyraVersion}`);const profiles=new Map;for(const full of projection.profiles){const skipped=[],available=list=>full[list].filter(entry=>lyraVersion===null||entry.since!==UNRELEASED_VERSION&&compareVersions(entry.since,lyraVersion)<=0?!0:(skipped.push({list,...entry}),!1)),data={...full,...Object.fromEntries(PROFILE_LIST_KEYS.map(list=>[list,available(list)]))},renamesByOwner=new Map(data.renames.map(entry=>[ownerKey(entry.tag,entry.kind,entry.from),entry])),renamesByName=indexBy(data.renames,entry=>nameKey(entry.kind,entry.from)),renamesByTarget=indexBy(data.renames,entry=>nameKey(entry.kind,entry.to)),reviewsByOwner=new Map(data.reviews.map(entry=>[ownerKey(entry.tag,entry.kind,entry.name),entry])),reviewsByName=indexBy(data.reviews,entry=>nameKey(entry.kind,entry.name)),detailsByOwner=new Map(data.detailChanges.map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),detailsByName=indexBy(data.detailChanges,entry=>entry.event),exposure=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map(Object.entries(data.exposure[kind]).map(([name,tags2])=>[name,new Set(tags2)]))])),defaultsByTag=indexBy(data.defaults,entry=>entry.tag),slotContentByTag=indexBy(data.slotContent,entry=>entry.tag),tags=new Set([...data.renames,...data.reviews,...data.detailChanges,...data.defaults,...data.slotContent].map(entry=>entry.tag)),renamesNamed=(kind,name)=>renamesByName.get(nameKey(kind,name))??[],exposersOf=(kind,name)=>exposure.get(kind)?.get(name)??new Set,movers=(kind,from,to)=>new Set(renamesNamed(kind,from).filter(entry=>entry.to===to&&!entry.polarity).map(entry=>entry.tag)),subset=(left,right)=>[...left].every(tag=>right.has(tag));profiles.set(data.origin,{origin:data.origin,fromMajor:data.fromMajor,toMajor:data.toMajor,aliasRemovalMajor:data.aliasRemovalMajor,data,skipped,tags,isEmpty:tags.size===0,renameFor:(tag,kind,name)=>renamesByOwner.get(ownerKey(tag,kind,name))??null,renamesNamed,renamesOnto:(kind,name)=>renamesByTarget.get(nameKey(kind,name))??[],reviewFor:(tag,kind,name)=>reviewsByOwner.get(ownerKey(tag,kind,name))??null,reviewsNamed:(kind,name)=>reviewsByName.get(nameKey(kind,name))??[],detailFor:(tag,event)=>detailsByOwner.get(ownerKey(tag,"event",event))??null,detailsNamed:event=>detailsByName.get(event)??[],defaultsFor:tag=>defaultsByTag.get(tag)??[],slotContentFor:tag=>slotContentByTag.get(tag)??[],exposes:(kind,name,tag)=>!!tag&&exposersOf(kind,name).has(tag),exposers:(kind,name)=>[...exposersOf(kind,name)],targetKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,to)].filter(tag=>!moving.has(tag))},sourceKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,from)].filter(tag=>!moving.has(tag))},isGlobal(kind,from){const entries=renamesNamed(kind,from);if(!entries.length||entries.some(entry=>entry.polarity))return!1;const targets=new Set(entries.map(entry=>entry.to));if(targets.size!==1)return!1;const[to]=targets,moving=movers(kind,from,to);return exposersOf(kind,from).size>0&&subset(exposersOf(kind,from),moving)&&subset(exposersOf(kind,to),moving)},gainedOwners(kind,name){const onto=(renamesByTarget.get(nameKey(kind,name))??[]).filter(entry=>!entry.polarity);if(!onto.length)return[];const gained=new Set(onto.map(entry=>entry.tag));return[...exposersOf(kind,name)].filter(tag=>!gained.has(tag)).length?[...gained].sort(compareText):[]}})}return profiles}export{LYRA_RENAME_ORIGINS,compareVersions,createRenameProfiles,deprecationRecordFor,emptyRenameLedger,emptyRenameProjection,mirroredMembers,projectRenameLedger,validateRenameLedger,validateRenameLedgerShape};
|