@alvin0/ai-agent-sdk-core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/agent/define/session/model-config.js +1 -1
  2. package/dist/agent/define/session/model-config.js.map +1 -1
  3. package/dist/agent/define/session/types.d.ts +21 -0
  4. package/dist/agent/define/session/types.d.ts.map +1 -1
  5. package/dist/agent/define/session.d.ts +2 -0
  6. package/dist/agent/define/session.d.ts.map +1 -1
  7. package/dist/agent/define/session.js +1 -1
  8. package/dist/agent/define/session.js.map +1 -1
  9. package/dist/agent/history/validation.js +1 -1
  10. package/dist/agent/history/validation.js.map +1 -1
  11. package/dist/agent/loop/events.d.ts +5 -0
  12. package/dist/agent/loop/events.d.ts.map +1 -1
  13. package/dist/agent/loop/schedule.js +1 -1
  14. package/dist/agent/loop/schedule.js.map +1 -1
  15. package/dist/agent/loop/turn/model-round.js +1 -1
  16. package/dist/agent/loop/turn/model-round.js.map +1 -1
  17. package/dist/agent/loop/turn/types.d.ts +2 -0
  18. package/dist/agent/loop/turn/types.d.ts.map +1 -1
  19. package/dist/agent/loop/turn/validation.js +1 -1
  20. package/dist/agent/loop/turn/validation.js.map +1 -1
  21. package/dist/agent/memory/compaction.d.ts +10 -1
  22. package/dist/agent/memory/compaction.d.ts.map +1 -1
  23. package/dist/agent/memory/compaction.js +2 -2
  24. package/dist/agent/memory/compaction.js.map +1 -1
  25. package/dist/agent/memory/token-estimator.js +1 -1
  26. package/dist/agent/memory/token-estimator.js.map +1 -1
  27. package/dist/agent/mode/run-agent.js +1 -1
  28. package/dist/agent/mode/run-agent.js.map +1 -1
  29. package/dist/composition/agent/options.js +1 -1
  30. package/dist/composition/agent/options.js.map +1 -1
  31. package/dist/composition/agent/session.js +1 -1
  32. package/dist/composition/agent/session.js.map +1 -1
  33. package/dist/composition/agent/types.d.ts +43 -1
  34. package/dist/composition/agent/types.d.ts.map +1 -1
  35. package/dist/composition/common/errors.d.ts.map +1 -1
  36. package/dist/composition/common/errors.js +1 -1
  37. package/dist/composition/common/errors.js.map +1 -1
  38. package/dist/composition/embedding/activation.js +2 -0
  39. package/dist/composition/embedding/activation.js.map +1 -0
  40. package/dist/composition/embedding/cache.js +2 -0
  41. package/dist/composition/embedding/cache.js.map +1 -0
  42. package/dist/composition/embedding/definition.d.ts +7 -0
  43. package/dist/composition/embedding/definition.d.ts.map +1 -0
  44. package/dist/composition/embedding/definition.js +2 -0
  45. package/dist/composition/embedding/definition.js.map +1 -0
  46. package/dist/composition/embedding/handle.js +2 -0
  47. package/dist/composition/embedding/handle.js.map +1 -0
  48. package/dist/composition/embedding/limiter.js +2 -0
  49. package/dist/composition/embedding/limiter.js.map +1 -0
  50. package/dist/composition/embedding/manager.js +2 -0
  51. package/dist/composition/embedding/manager.js.map +1 -0
  52. package/dist/composition/embedding/observation.js +2 -0
  53. package/dist/composition/embedding/observation.js.map +1 -0
  54. package/dist/composition/embedding/planner.js +2 -0
  55. package/dist/composition/embedding/planner.js.map +1 -0
  56. package/dist/composition/embedding/plugin-types.d.ts +48 -0
  57. package/dist/composition/embedding/plugin-types.d.ts.map +1 -0
  58. package/dist/composition/embedding/plugin-types.js +2 -0
  59. package/dist/composition/embedding/plugin-types.js.map +1 -0
  60. package/dist/composition/embedding/preflight.js +2 -0
  61. package/dist/composition/embedding/preflight.js.map +1 -0
  62. package/dist/composition/embedding/registry.js +2 -0
  63. package/dist/composition/embedding/registry.js.map +1 -0
  64. package/dist/composition/embedding/retry.js +2 -0
  65. package/dist/composition/embedding/retry.js.map +1 -0
  66. package/dist/composition/embedding/usage.js +2 -0
  67. package/dist/composition/embedding/usage.js.map +1 -0
  68. package/dist/composition/lifecycle/types.d.ts +5 -1
  69. package/dist/composition/lifecycle/types.d.ts.map +1 -1
  70. package/dist/composition/lifecycle/types.js +1 -1
  71. package/dist/composition/lifecycle/types.js.map +1 -1
  72. package/dist/composition/preflight.js +1 -1
  73. package/dist/composition/preflight.js.map +1 -1
  74. package/dist/composition/provider/activation.js +1 -1
  75. package/dist/composition/provider/activation.js.map +1 -1
  76. package/dist/composition/provider/model-selection.js +1 -1
  77. package/dist/composition/provider/model-selection.js.map +1 -1
  78. package/dist/composition/provider/preflight.js +1 -1
  79. package/dist/composition/provider/preflight.js.map +1 -1
  80. package/dist/composition/runtime/owner.js +1 -1
  81. package/dist/composition/runtime/owner.js.map +1 -1
  82. package/dist/composition/runtime/public.js +1 -1
  83. package/dist/composition/runtime/public.js.map +1 -1
  84. package/dist/composition/runtime/types.d.ts +25 -2
  85. package/dist/composition/runtime/types.d.ts.map +1 -1
  86. package/dist/composition/startup.js +1 -1
  87. package/dist/composition/startup.js.map +1 -1
  88. package/dist/composition/team/options.js +1 -1
  89. package/dist/composition/team/options.js.map +1 -1
  90. package/dist/composition/team/runtime.js +1 -1
  91. package/dist/composition/team/runtime.js.map +1 -1
  92. package/dist/composition/team/types.d.ts +3 -0
  93. package/dist/composition/team/types.d.ts.map +1 -1
  94. package/dist/contract/generate-options.d.ts +2 -0
  95. package/dist/contract/generate-options.d.ts.map +1 -1
  96. package/dist/contract/model-info.d.ts +12 -2
  97. package/dist/contract/model-info.d.ts.map +1 -1
  98. package/dist/embedding/adapter.d.ts +104 -0
  99. package/dist/embedding/adapter.d.ts.map +1 -0
  100. package/dist/embedding/adapter.js +2 -0
  101. package/dist/embedding/adapter.js.map +1 -0
  102. package/dist/embedding/catalog.d.ts +82 -0
  103. package/dist/embedding/catalog.d.ts.map +1 -0
  104. package/dist/embedding/catalog.js +2 -0
  105. package/dist/embedding/catalog.js.map +1 -0
  106. package/dist/embedding/errors.d.ts +98 -0
  107. package/dist/embedding/errors.d.ts.map +1 -0
  108. package/dist/embedding/errors.js +2 -0
  109. package/dist/embedding/errors.js.map +1 -0
  110. package/dist/embedding/handle.d.ts +76 -0
  111. package/dist/embedding/handle.d.ts.map +1 -0
  112. package/dist/embedding/limits.d.ts +47 -0
  113. package/dist/embedding/limits.d.ts.map +1 -0
  114. package/dist/embedding/limits.js +2 -0
  115. package/dist/embedding/limits.js.map +1 -0
  116. package/dist/embedding/profile.d.ts +89 -0
  117. package/dist/embedding/profile.d.ts.map +1 -0
  118. package/dist/embedding/profile.js +2 -0
  119. package/dist/embedding/profile.js.map +1 -0
  120. package/dist/embedding/purpose.d.ts +37 -0
  121. package/dist/embedding/purpose.d.ts.map +1 -0
  122. package/dist/embedding/request.d.ts +54 -0
  123. package/dist/embedding/request.d.ts.map +1 -0
  124. package/dist/embedding/request.js +2 -0
  125. package/dist/embedding/request.js.map +1 -0
  126. package/dist/embedding/result.d.ts +50 -0
  127. package/dist/embedding/result.d.ts.map +1 -0
  128. package/dist/embedding/usage.d.ts +64 -0
  129. package/dist/embedding/usage.d.ts.map +1 -0
  130. package/dist/embedding/usage.js +2 -0
  131. package/dist/embedding/usage.js.map +1 -0
  132. package/dist/embedding/validation.d.ts +68 -0
  133. package/dist/embedding/validation.d.ts.map +1 -0
  134. package/dist/embedding/validation.js +2 -0
  135. package/dist/embedding/validation.js.map +1 -0
  136. package/dist/embedding.d.ts +12 -0
  137. package/dist/embedding.js +1 -0
  138. package/dist/index.d.ts +6 -3
  139. package/dist/index.js +1 -1
  140. package/dist/message/content.d.ts +71 -1
  141. package/dist/message/content.d.ts.map +1 -1
  142. package/dist/message/projection.d.ts +28 -2
  143. package/dist/message/projection.d.ts.map +1 -1
  144. package/dist/message/projection.js +1 -1
  145. package/dist/message/projection.js.map +1 -1
  146. package/dist/observability/bus.js +1 -1
  147. package/dist/observation/event.d.ts +1 -1
  148. package/dist/observation/event.d.ts.map +1 -1
  149. package/dist/observation/event.js.map +1 -1
  150. package/dist/observation/port.d.ts +1 -1
  151. package/dist/observation/port.d.ts.map +1 -1
  152. package/dist/observation/port.js.map +1 -1
  153. package/dist/observation/privacy.js +1 -1
  154. package/dist/observation/privacy.js.map +1 -1
  155. package/dist/observation/report.d.ts +2 -0
  156. package/dist/observation/report.d.ts.map +1 -1
  157. package/dist/observation/report.js.map +1 -1
  158. package/dist/primitives/version.d.ts +1 -1
  159. package/dist/primitives/version.js +1 -1
  160. package/dist/primitives/version.js.map +1 -1
  161. package/dist/provider.d.ts +3 -1
  162. package/dist/provider.js +1 -1
  163. package/dist/runtime/model-call-handle.js +1 -1
  164. package/dist/runtime/model-call-handle.js.map +1 -1
  165. package/dist/runtime/model-metadata.js +1 -1
  166. package/dist/runtime/model-metadata.js.map +1 -1
  167. package/dist/runtime/model-stream.js +1 -1
  168. package/dist/runtime/model-stream.js.map +1 -1
  169. package/dist/runtime/registry.js +1 -1
  170. package/dist/runtime/with-retry.d.ts.map +1 -1
  171. package/dist/runtime/with-retry.js +1 -1
  172. package/dist/runtime/with-retry.js.map +1 -1
  173. package/dist/stream/assembler.d.ts.map +1 -1
  174. package/dist/stream/assembler.js +1 -1
  175. package/dist/stream/assembler.js.map +1 -1
  176. package/dist/stream/chunk.d.ts +6 -0
  177. package/dist/stream/chunk.d.ts.map +1 -1
  178. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"model-metadata.js","names":[],"sources":["../../src/runtime/model-metadata.ts"],"sourcesContent":["import type { ModelAdapter } from '../contract/adapter.ts'\nimport type { CallConfig } from '../contract/call-config.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport type {\n ModelCatalogSnapshot,\n ModelContext,\n ModelInfo,\n ResolvedModelInfo,\n} from '../contract/model-info.ts'\nimport { ModelError, REGISTRY_ERROR_CODES } from '../errors/model-error.ts'\nimport { freezeMessage, type Message } from '../message/message.ts'\nimport { deepFreeze } from '../primitives/freeze.ts'\n\nexport function validateCatalogModels(\n provider: string,\n models: readonly ModelInfo[],\n maxModels: number,\n maxBytes: number,\n): ModelInfo[] {\n if (!Array.isArray(models)) {\n throw new ModelError(\n `route \"${provider}\" returned a catalog that is not an array`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n if (models.length > maxModels\n || serializedBytes(models, REGISTRY_ERROR_CODES.INVALID_CATALOG) > maxBytes) {\n throw new ModelError(\n `route \"${provider}\" catalog exceeds the configured registry limit`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n const seen = new Set<string>()\n return models.map((model) => {\n if (model.provider !== provider || typeof model.id !== 'string' || model.id.length === 0\n || typeof model.name !== 'string' || model.name.length === 0) {\n throw new ModelError(\n `route \"${provider}\" advertised a model entry with invalid identity`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n if (seen.has(model.id)) {\n throw new ModelError(\n `route \"${provider}\" advertised model \"${model.id}\" more than once`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n seen.add(model.id)\n return structuredClone(model)\n })\n}\n\nexport function validateModelCatalogSnapshot(\n provider: string,\n snapshot: ModelCatalogSnapshot,\n maxModels: number,\n maxBytes: number,\n): ModelCatalogSnapshot {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.provider?.id !== provider || typeof snapshot.provider.name !== 'string'\n || snapshot.provider.name.length === 0\n || !['static', 'fresh', 'empty', 'stale', 'unavailable'].includes(snapshot.state)\n || typeof snapshot.revision !== 'string' || snapshot.revision.length === 0\n || typeof snapshot.observedAt !== 'string' || !Number.isFinite(Date.parse(snapshot.observedAt))) {\n throw new ModelError(\n `route \"${provider}\" returned an invalid catalog snapshot`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n const models = validateCatalogModels(provider, snapshot.models, maxModels, maxBytes)\n return deepFreeze({ ...structuredClone(snapshot), provider: { ...snapshot.provider }, models })\n}\n\nexport function normalizeResolvedModelInfo(\n provider: string,\n model: string,\n info: ResolvedModelInfo,\n maxBytes: number,\n): ResolvedModelInfo {\n if (info.provider !== provider || info.id !== model\n || typeof info.name !== 'string' || info.name.length === 0) {\n throw invalidModel(provider, model, 'mismatched identity')\n }\n if (info.context !== undefined\n && (!Number.isSafeInteger(info.context.contextWindow) || info.context.contextWindow <= 0)) {\n throw invalidModel(provider, model, 'a non-positive context window')\n }\n if (info.defaultMaxTokens !== undefined\n && (!Number.isSafeInteger(info.defaultMaxTokens) || info.defaultMaxTokens <= 0)) {\n throw invalidModel(provider, model, 'a non-positive defaultMaxTokens')\n }\n if (info.maxOutputTokens !== undefined\n && (!Number.isSafeInteger(info.maxOutputTokens) || info.maxOutputTokens <= 0)) {\n throw invalidModel(provider, model, 'a non-positive maxOutputTokens')\n }\n if (info.defaultMaxTokens !== undefined && info.maxOutputTokens !== undefined\n && info.defaultMaxTokens > info.maxOutputTokens) {\n throw invalidModel(provider, model, 'defaultMaxTokens above maxOutputTokens')\n }\n if (info.context !== undefined && info.defaultMaxTokens !== undefined\n && info.defaultMaxTokens >= info.context.contextWindow) {\n throw invalidModel(provider, model, 'no input headroom')\n }\n if (info.context !== undefined && info.maxOutputTokens !== undefined\n && info.maxOutputTokens >= info.context.contextWindow) {\n throw invalidModel(provider, model, 'no input headroom')\n }\n validateReasoning(provider, model, info)\n validateCapabilities(provider, model, info)\n const normalized = deepFreeze(structuredClone({\n ...info,\n ...(info.inputModalities === undefined ? {} : { inputModalities: [...info.inputModalities] }),\n ...(info.outputModalities === undefined ? {} : { outputModalities: [...info.outputModalities] }),\n ...(info.nativeTools === undefined ? {} : { nativeTools: [...info.nativeTools] }),\n }))\n if (serializedBytes(normalized, REGISTRY_ERROR_CODES.INVALID_MODEL_INFO) > maxBytes) {\n throw new ModelError(\n `route \"${provider}\" model metadata exceeds the configured registry limit`,\n REGISTRY_ERROR_CODES.INVALID_MODEL_INFO,\n )\n }\n return normalized\n}\n\nfunction validateReasoning(provider: string, model: string, info: ResolvedModelInfo): void {\n if (info.reasoning === undefined) return\n const ids = info.reasoning.efforts.map(effort => effort.id)\n if (ids.length === 0 || new Set(ids).size !== ids.length\n || info.reasoning.efforts.some(effort =>\n typeof effort.id !== 'string' || effort.id.length === 0\n || typeof effort.name !== 'string' || effort.name.length === 0)) {\n throw invalidModel(provider, model, 'empty or duplicated reasoning efforts')\n }\n if (info.reasoning.defaultEffort !== undefined\n && !ids.includes(info.reasoning.defaultEffort)) {\n throw invalidModel(provider, model, 'a default reasoning effort it does not offer')\n }\n}\n\nfunction validateCapabilities(provider: string, model: string, info: ResolvedModelInfo): void {\n for (const [label, values] of [\n ['input modalities', info.inputModalities],\n ['output modalities', info.outputModalities],\n ['native tools', info.nativeTools],\n ] as const) {\n if (values !== undefined\n && (new Set(values).size !== values.length\n || values.some(value => typeof value !== 'string' || value.length === 0))) {\n throw invalidModel(provider, model, `invalid ${label}`)\n }\n }\n}\n\nfunction invalidModel(provider: string, model: string, reason: string): ModelError {\n return new ModelError(\n `route \"${provider}\" resolved model \"${model}\" with ${reason}`,\n REGISTRY_ERROR_CODES.INVALID_MODEL_INFO,\n )\n}\n\nexport function resolveCallWithModelInfo(\n config: CallConfig,\n info: ResolvedModelInfo,\n): { readonly config: CallConfig; readonly context: ModelContext | undefined } {\n const reasoningEffort = config.reasoningEffort ?? info.reasoning?.defaultEffort\n if (config.reasoningEffort !== undefined\n && info.reasoning?.efforts.some(effort => effort.id === config.reasoningEffort) !== true) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" does not offer reasoning effort `\n + `\"${config.reasoningEffort}\"`,\n REGISTRY_ERROR_CODES.UNSUPPORTED_REASONING_EFFORT,\n )\n }\n const maxTokens = config.maxTokens ?? info.defaultMaxTokens\n if (maxTokens !== undefined && info.maxOutputTokens !== undefined\n && maxTokens > info.maxOutputTokens) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" supports at most `\n + `${info.maxOutputTokens} output tokens, received ${maxTokens}`,\n REGISTRY_ERROR_CODES.OUTPUT_TOKEN_LIMIT_EXCEEDED,\n )\n }\n if (maxTokens !== undefined && info.context !== undefined\n && maxTokens >= info.context.contextWindow) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" cannot reserve ${maxTokens} output tokens `\n + `inside its ${info.context.contextWindow}-token combined context window`,\n REGISTRY_ERROR_CODES.OUTPUT_TOKEN_LIMIT_EXCEEDED,\n )\n }\n return {\n config: {\n provider: config.provider,\n model: config.model,\n ...(reasoningEffort === undefined ? {} : { reasoningEffort }),\n ...(config.temperature === undefined ? {} : { temperature: config.temperature }),\n ...(config.topP === undefined ? {} : { topP: config.topP }),\n ...(maxTokens === undefined ? {} : { maxTokens }),\n ...(config.stop === undefined ? {} : { stop: [...config.stop] }),\n },\n context: info.context,\n }\n}\n\nexport function projectReplayForAdapter(\n options: GenerateOptions,\n adapter: ModelAdapter,\n registeredAdapter: (provider: string) => ModelAdapter | undefined,\n): GenerateOptions {\n const messages: Message[] = options.messages.map((message) => {\n const source = message.source\n if (message.role !== 'assistant' || source.kind !== 'model'\n || source.replayState === undefined || registeredAdapter(source.provider) === adapter) {\n return message\n }\n return freezeMessage({\n ...message,\n source: { kind: 'model', provider: source.provider, model: source.model },\n })\n })\n if (messages.every((message, index) => message === options.messages[index])) return options\n return { ...options, messages }\n}\n\nfunction serializedBytes(value: unknown, code: string): number {\n let encoded: string | undefined\n try { encoded = JSON.stringify(value) }\n catch (error) {\n throw new ModelError('adapter model metadata must be JSON-serializable', code, { cause: error })\n }\n if (encoded === undefined) {\n throw new ModelError('adapter model metadata must be JSON-serializable', code)\n }\n return new TextEncoder().encode(encoded).byteLength\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model-metadata.js","names":[],"sources":["../../src/runtime/model-metadata.ts"],"sourcesContent":["import type { ModelAdapter } from '../contract/adapter.ts'\nimport type { CallConfig } from '../contract/call-config.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport type {\n ModelCatalogSnapshot,\n ModelContext,\n ModelInfo,\n ResolvedModelInfo,\n} from '../contract/model-info.ts'\nimport { ModelError, REGISTRY_ERROR_CODES } from '../errors/model-error.ts'\nimport { freezeMessage, type Message } from '../message/message.ts'\nimport { deepFreeze } from '../primitives/freeze.ts'\n\nexport function validateCatalogModels(\n provider: string,\n models: readonly ModelInfo[],\n maxModels: number,\n maxBytes: number,\n): ModelInfo[] {\n if (!Array.isArray(models)) {\n throw new ModelError(\n `route \"${provider}\" returned a catalog that is not an array`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n if (models.length > maxModels\n || serializedBytes(models, REGISTRY_ERROR_CODES.INVALID_CATALOG) > maxBytes) {\n throw new ModelError(\n `route \"${provider}\" catalog exceeds the configured registry limit`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n const seen = new Set<string>()\n return models.map((model) => {\n if (model.provider !== provider || typeof model.id !== 'string' || model.id.length === 0\n || typeof model.name !== 'string' || model.name.length === 0) {\n throw new ModelError(\n `route \"${provider}\" advertised a model entry with invalid identity`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n if (seen.has(model.id)) {\n throw new ModelError(\n `route \"${provider}\" advertised model \"${model.id}\" more than once`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n seen.add(model.id)\n return structuredClone(model)\n })\n}\n\nexport function validateModelCatalogSnapshot(\n provider: string,\n snapshot: ModelCatalogSnapshot,\n maxModels: number,\n maxBytes: number,\n): ModelCatalogSnapshot {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.provider?.id !== provider || typeof snapshot.provider.name !== 'string'\n || snapshot.provider.name.length === 0\n || !['static', 'fresh', 'empty', 'stale', 'unavailable'].includes(snapshot.state)\n || typeof snapshot.revision !== 'string' || snapshot.revision.length === 0\n || typeof snapshot.observedAt !== 'string' || !Number.isFinite(Date.parse(snapshot.observedAt))) {\n throw new ModelError(\n `route \"${provider}\" returned an invalid catalog snapshot`,\n REGISTRY_ERROR_CODES.INVALID_CATALOG,\n )\n }\n const models = validateCatalogModels(provider, snapshot.models, maxModels, maxBytes)\n return deepFreeze({ ...structuredClone(snapshot), provider: { ...snapshot.provider }, models })\n}\n\nexport function normalizeResolvedModelInfo(\n provider: string,\n model: string,\n info: ResolvedModelInfo,\n maxBytes: number,\n): ResolvedModelInfo {\n if (info.provider !== provider || info.id !== model\n || typeof info.name !== 'string' || info.name.length === 0) {\n throw invalidModel(provider, model, 'mismatched identity')\n }\n if (info.context !== undefined\n && (!Number.isSafeInteger(info.context.contextWindow) || info.context.contextWindow <= 0)) {\n throw invalidModel(provider, model, 'a non-positive context window')\n }\n if (info.defaultMaxTokens !== undefined\n && (!Number.isSafeInteger(info.defaultMaxTokens) || info.defaultMaxTokens <= 0)) {\n throw invalidModel(provider, model, 'a non-positive defaultMaxTokens')\n }\n for (const value of [info.context?.maxContextWindow, info.context?.defaultContextWindow, info.context?.standardPriceInputTokens]) {\n if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {\n throw invalidModel(provider, model, 'invalid context policy')\n }\n }\n if (info.context?.maxContextWindow !== undefined && (\n info.context.contextWindow > info.context.maxContextWindow\n || (info.context.defaultContextWindow ?? 0) > info.context.maxContextWindow\n )) throw invalidModel(provider, model, 'contextWindow above maxContextWindow')\n if (info.maxOutputTokens !== undefined\n && (!Number.isSafeInteger(info.maxOutputTokens) || info.maxOutputTokens <= 0)) {\n throw invalidModel(provider, model, 'a non-positive maxOutputTokens')\n }\n if (info.defaultMaxTokens !== undefined && info.maxOutputTokens !== undefined\n && info.defaultMaxTokens > info.maxOutputTokens) {\n throw invalidModel(provider, model, 'defaultMaxTokens above maxOutputTokens')\n }\n if (info.context !== undefined && info.defaultMaxTokens !== undefined\n && info.defaultMaxTokens >= info.context.contextWindow) {\n throw invalidModel(provider, model, 'no input headroom')\n }\n if (info.context !== undefined && info.maxOutputTokens !== undefined\n && info.maxOutputTokens >= (info.context.maxContextWindow ?? info.context.contextWindow)) {\n throw invalidModel(provider, model, 'no input headroom')\n }\n validateReasoning(provider, model, info)\n validateCapabilities(provider, model, info)\n const normalized = deepFreeze(structuredClone({\n ...info,\n ...(info.inputModalities === undefined ? {} : { inputModalities: [...info.inputModalities] }),\n ...(info.outputModalities === undefined ? {} : { outputModalities: [...info.outputModalities] }),\n ...(info.nativeTools === undefined ? {} : { nativeTools: [...info.nativeTools] }),\n }))\n if (serializedBytes(normalized, REGISTRY_ERROR_CODES.INVALID_MODEL_INFO) > maxBytes) {\n throw new ModelError(\n `route \"${provider}\" model metadata exceeds the configured registry limit`,\n REGISTRY_ERROR_CODES.INVALID_MODEL_INFO,\n )\n }\n return normalized\n}\n\nfunction validateReasoning(provider: string, model: string, info: ResolvedModelInfo): void {\n if (info.reasoning === undefined) return\n const ids = info.reasoning.efforts.map(effort => effort.id)\n if (ids.length === 0 || new Set(ids).size !== ids.length\n || info.reasoning.efforts.some(effort =>\n typeof effort.id !== 'string' || effort.id.length === 0\n || typeof effort.name !== 'string' || effort.name.length === 0)) {\n throw invalidModel(provider, model, 'empty or duplicated reasoning efforts')\n }\n if (info.reasoning.defaultEffort !== undefined\n && !ids.includes(info.reasoning.defaultEffort)) {\n throw invalidModel(provider, model, 'a default reasoning effort it does not offer')\n }\n}\n\nfunction validateCapabilities(provider: string, model: string, info: ResolvedModelInfo): void {\n for (const [label, values] of [\n ['input modalities', info.inputModalities],\n ['output modalities', info.outputModalities],\n ['native tools', info.nativeTools],\n ] as const) {\n if (values !== undefined\n && (new Set(values).size !== values.length\n || values.some(value => typeof value !== 'string' || value.length === 0))) {\n throw invalidModel(provider, model, `invalid ${label}`)\n }\n }\n}\n\nfunction invalidModel(provider: string, model: string, reason: string): ModelError {\n return new ModelError(\n `route \"${provider}\" resolved model \"${model}\" with ${reason}`,\n REGISTRY_ERROR_CODES.INVALID_MODEL_INFO,\n )\n}\n\nexport function resolveCallWithModelInfo(\n config: CallConfig,\n info: ResolvedModelInfo,\n): { readonly config: CallConfig; readonly context: ModelContext | undefined } {\n const reasoningEffort = config.reasoningEffort ?? info.reasoning?.defaultEffort\n if (config.reasoningEffort !== undefined\n && info.reasoning?.efforts.some(effort => effort.id === config.reasoningEffort) !== true) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" does not offer reasoning effort `\n + `\"${config.reasoningEffort}\"`,\n REGISTRY_ERROR_CODES.UNSUPPORTED_REASONING_EFFORT,\n )\n }\n const maxTokens = config.maxTokens ?? info.defaultMaxTokens\n if (maxTokens !== undefined && info.maxOutputTokens !== undefined\n && maxTokens > info.maxOutputTokens) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" supports at most `\n + `${info.maxOutputTokens} output tokens, received ${maxTokens}`,\n REGISTRY_ERROR_CODES.OUTPUT_TOKEN_LIMIT_EXCEEDED,\n )\n }\n if (maxTokens !== undefined && info.context !== undefined\n && maxTokens >= info.context.contextWindow) {\n throw new ModelError(\n `model \"${info.id}\" on route \"${info.provider}\" cannot reserve ${maxTokens} output tokens `\n + `inside its ${info.context.contextWindow}-token combined context window`,\n REGISTRY_ERROR_CODES.OUTPUT_TOKEN_LIMIT_EXCEEDED,\n )\n }\n return {\n config: {\n provider: config.provider,\n model: config.model,\n ...(reasoningEffort === undefined ? {} : { reasoningEffort }),\n ...(config.temperature === undefined ? {} : { temperature: config.temperature }),\n ...(config.topP === undefined ? {} : { topP: config.topP }),\n ...(maxTokens === undefined ? {} : { maxTokens }),\n ...(config.stop === undefined ? {} : { stop: [...config.stop] }),\n },\n context: info.context,\n }\n}\n\nexport function projectReplayForAdapter(\n options: GenerateOptions,\n adapter: ModelAdapter,\n registeredAdapter: (provider: string) => ModelAdapter | undefined,\n): GenerateOptions {\n const messages: Message[] = options.messages.map((message) => {\n const source = message.source\n if (message.role !== 'assistant' || source.kind !== 'model'\n || source.replayState === undefined || registeredAdapter(source.provider) === adapter) {\n return message\n }\n return freezeMessage({\n ...message,\n source: { kind: 'model', provider: source.provider, model: source.model },\n })\n })\n if (messages.every((message, index) => message === options.messages[index])) return options\n return { ...options, messages }\n}\n\nfunction serializedBytes(value: unknown, code: string): number {\n let encoded: string | undefined\n try { encoded = JSON.stringify(value) }\n catch (error) {\n throw new ModelError('adapter model metadata must be JSON-serializable', code, { cause: error })\n }\n if (encoded === undefined) {\n throw new ModelError('adapter model metadata must be JSON-serializable', code)\n }\n return new TextEncoder().encode(encoded).byteLength\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{waitForSettlement as e}from"../async/settlement.js";import{MODEL_ERROR_CODES as t,ModelError as n,REGISTRY_ERROR_CODES as r}from"../errors/model-error.js";import{normalizeModelFailure as i}from"../errors/failure.js";import{contentHasImage as a,projectImagesForTextModel as o}from"../message/projection.js";import{callConfigEquals as s}from"../contract/call-config.js";import{isNativeToolSchema as c}from"../contract/tool.js";import{normalizeResolvedModelInfo as l,projectReplayForAdapter as u,resolveCallWithModelInfo as d}from"./model-metadata.js";async function*f(e){let{options:t,context:r}=e,i;try{let c=e.prepared?.registration??e.registration(t.provider),l=c.adapter,d=await p(e,c,l),f=s(t,d.config)?t:{...t,...d.config},h=d.modelInfo.inputModalities!==void 0&&!d.modelInfo.inputModalities.includes(`image`)&&f.messages.some(e=>a(e.content));if(f.imagePolicy!==void 0&&f.imagePolicy!==`strict`&&f.imagePolicy!==`project`)throw new n(`invalid image policy`,`INVALID_IMAGE_POLICY`);if(h&&f.imagePolicy===`strict`)throw new n(`model ${d.modelInfo.id} does not support required image input`,`UNSUPPORTED_IMAGE_INPUT`);let g=h?{...f,messages:o(f.messages)}:f;m(g,d.modelInfo),e.onDispatch(),i=d.dispatch(u(g,l,e.registeredAdapter),r)[Symbol.asyncIterator]()}catch(e){yield _(e,t.signal);return}yield*h(i,t.signal)}async function p(e,t,i){let{options:a,context:o,prepared:c}=e;if(c!==void 0){if(!s(a,c.config))throw new n(`prepared call config changed before adapter dispatch`,r.INVALID_PREPARED_CALL);return{modelInfo:c.modelInfo,config:c.config,dispatch:c.dispatch}}let u=await i.prepareCall(a.provider,a.model,a.signal,o),f=l(t.provider.id,a.model,u.model,e.maxCatalogBytes);return{modelInfo:f,config:d(a,f).config,dispatch:(e,t)=>u.stream(e,t)}}function m(e,t){if(t.nativeTools!==void 0){for(let i of e.tools??[])if(c(i)&&!t.nativeTools.includes(i.name))throw new n(`model "${t.id}" on route "${t.provider}" does not support native tool "${i.name}"`,r.UNSUPPORTED_NATIVE_TOOL)}}async function*h(e,t){let n=!1;try{for(;;){let r;try{let t=await e.next();r=t.done===!0?{done:!0}:{done:!1,value:t.value}}catch(e){n=!0,yield _(e,t);return}if(r.done){n=!0;return}yield r.value}}finally{n||await g(e)}}async function g(r){let i=r.return?.bind(r);if(i===void 0)return;let a=Promise.resolve().then(async()=>{await i()});if(!await e(a,3e4))throw new n(`model adapter ignored cancellation for more than 30000ms`,t.TEARDOWN_TIMEOUT)}function _(e,t){let n=i(e);return{type:`finish`,reason:t?.aborted===!0||n.code===`ABORTED`?{kind:`aborted`,failure:n}:{kind:`error`,failure:n}}}export{f as streamAdapter};
1
+ import{waitForSettlement as e}from"../async/settlement.js";import{MODEL_ERROR_CODES as t,ModelError as n,REGISTRY_ERROR_CODES as r}from"../errors/model-error.js";import{normalizeModelFailure as i}from"../errors/failure.js";import{contentHasDocument as a,contentHasImage as o,projectDocumentsForTextModel as s,projectImagesForTextModel as c}from"../message/projection.js";import{callConfigEquals as l}from"../contract/call-config.js";import{isNativeToolSchema as u}from"../contract/tool.js";import{normalizeResolvedModelInfo as d,projectReplayForAdapter as f,resolveCallWithModelInfo as p}from"./model-metadata.js";async function*m(e){let{options:t,context:r}=e,i;try{let u=e.prepared?.registration??e.registration(t.provider),d=u.adapter,p=await h(e,u,d),m=l(t,p.config)?t:{...t,...p.config},_=p.modelInfo.inputModalities!==void 0&&!p.modelInfo.inputModalities.includes(`image`)&&m.messages.some(e=>o(e.content)),v=p.modelInfo.inputModalities!==void 0&&!p.modelInfo.inputModalities.includes(`document`)&&m.messages.some(e=>a(e.content));if(m.imagePolicy!==void 0&&m.imagePolicy!==`strict`&&m.imagePolicy!==`project`)throw new n(`invalid image policy`,`INVALID_IMAGE_POLICY`);if(m.documentPolicy!==void 0&&m.documentPolicy!==`strict`&&m.documentPolicy!==`project`)throw new n(`invalid document policy`,`INVALID_DOCUMENT_POLICY`);if(_&&m.imagePolicy===`strict`)throw new n(`model ${p.modelInfo.id} does not support required image input`,`UNSUPPORTED_IMAGE_INPUT`);if(v&&m.documentPolicy===`strict`)throw new n(`model ${p.modelInfo.id} does not support required document input`,`UNSUPPORTED_DOCUMENT_INPUT`);let y=_?{...m,messages:c(m.messages)}:m,b=v?{...y,messages:s(y.messages)}:y;g(b,p.modelInfo),e.onDispatch(),i=p.dispatch(f(b,d,e.registeredAdapter),r)[Symbol.asyncIterator]()}catch(e){yield y(e,t.signal);return}yield*_(i,t.signal)}async function h(e,t,i){let{options:a,context:o,prepared:s}=e;if(s!==void 0){if(!l(a,s.config))throw new n(`prepared call config changed before adapter dispatch`,r.INVALID_PREPARED_CALL);return{modelInfo:s.modelInfo,config:s.config,dispatch:s.dispatch}}let c=await i.prepareCall(a.provider,a.model,a.signal,o),u=d(t.provider.id,a.model,c.model,e.maxCatalogBytes);return{modelInfo:u,config:p(a,u).config,dispatch:(e,t)=>c.stream(e,t)}}function g(e,t){if(t.nativeTools!==void 0){for(let i of e.tools??[])if(u(i)&&!t.nativeTools.includes(i.name))throw new n(`model "${t.id}" on route "${t.provider}" does not support native tool "${i.name}"`,r.UNSUPPORTED_NATIVE_TOOL)}}async function*_(e,t){let n=!1;try{for(;;){let r;try{let t=await e.next();r=t.done===!0?{done:!0}:{done:!1,value:t.value}}catch(e){n=!0,yield y(e,t);return}if(r.done){n=!0;return}yield r.value}}finally{n||await v(e)}}async function v(r){let i=r.return?.bind(r);if(i===void 0)return;let a=Promise.resolve().then(async()=>{await i()});if(!await e(a,3e4))throw new n(`model adapter ignored cancellation for more than 30000ms`,t.TEARDOWN_TIMEOUT)}function y(e,t){let n=i(e);return{type:`finish`,reason:t?.aborted===!0||n.code===`ABORTED`?{kind:`aborted`,failure:n}:{kind:`error`,failure:n}}}export{m as streamAdapter};
2
2
  //# sourceMappingURL=model-stream.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-stream.js","names":[],"sources":["../../src/runtime/model-stream.ts"],"sourcesContent":["import type { ModelAdapter, PreparedAdapterCall } from '../contract/adapter.ts'\nimport { callConfigEquals, type CallConfig } from '../contract/call-config.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport { isNativeToolSchema } from '../contract/tool.ts'\nimport type { ProviderInfo, ResolvedModelInfo } from '../contract/model-info.ts'\nimport type { ResolvedRetryPolicy } from '../contract/retry-policy.ts'\nimport { normalizeModelFailure } from '../errors/failure.ts'\nimport { MODEL_ERROR_CODES, ModelError, REGISTRY_ERROR_CODES } from '../errors/model-error.ts'\nimport { contentHasImage, projectImagesForTextModel } from '../message/projection.ts'\nimport type { ModelInvocationContext } from '../observation/report.ts'\nimport type { StreamChunk } from '../stream/chunk.ts'\nimport { waitForSettlement } from '../async/settlement.ts'\nimport {\n normalizeResolvedModelInfo,\n projectReplayForAdapter,\n resolveCallWithModelInfo,\n} from './model-metadata.ts'\n\nexport interface RuntimeAdapterRegistration {\n readonly adapter: ModelAdapter\n readonly provider: ProviderInfo\n readonly retryPolicy: ResolvedRetryPolicy\n readonly pluginId?: string\n readonly family?: string\n}\n\nexport interface PreparedDispatch {\n readonly registration: RuntimeAdapterRegistration\n readonly config: CallConfig\n readonly modelInfo: ResolvedModelInfo\n readonly dispatch: (options: GenerateOptions, context: ModelInvocationContext) => AsyncIterable<StreamChunk>\n}\n\nexport interface AdapterStreamInput {\n readonly options: GenerateOptions\n readonly context: ModelInvocationContext\n readonly onDispatch: () => void\n readonly prepared?: PreparedDispatch\n readonly maxCatalogBytes: number\n readonly registration: (provider: string) => RuntimeAdapterRegistration\n readonly registeredAdapter: (provider: string) => ModelAdapter | undefined\n}\n\n/** Run one adapter generation through the registry's single failure/cancellation funnel. */\nexport async function* streamAdapter(input: AdapterStreamInput): AsyncGenerator<StreamChunk> {\n const { options, context } = input\n let iterator: AsyncIterator<StreamChunk>\n try {\n const registration = input.prepared?.registration ?? input.registration(options.provider)\n const adapter = registration.adapter\n const prepared = await prepareDispatch(input, registration, adapter)\n const withConfig = callConfigEquals(options, prepared.config)\n ? options\n : { ...options, ...prepared.config }\n const hasUnsupportedImages = prepared.modelInfo.inputModalities !== undefined\n && !prepared.modelInfo.inputModalities.includes('image')\n && withConfig.messages.some(message => contentHasImage(message.content))\n if (withConfig.imagePolicy !== undefined && withConfig.imagePolicy !== 'strict' && withConfig.imagePolicy !== 'project') throw new ModelError('invalid image policy', 'INVALID_IMAGE_POLICY')\n if (hasUnsupportedImages && withConfig.imagePolicy === 'strict') throw new ModelError(\n `model ${prepared.modelInfo.id} does not support required image input`, 'UNSUPPORTED_IMAGE_INPUT',\n )\n const projected = hasUnsupportedImages\n ? { ...withConfig, messages: projectImagesForTextModel(withConfig.messages) }\n : withConfig\n validateNativeTools(projected, prepared.modelInfo)\n input.onDispatch()\n iterator = prepared.dispatch(projectReplayForAdapter(\n projected, adapter, input.registeredAdapter,\n ), context)[Symbol.asyncIterator]()\n } catch (error: unknown) {\n yield adapterFailureChunk(error, options.signal)\n return\n }\n yield* consumeIterator(iterator, options.signal)\n}\n\nasync function prepareDispatch(\n input: AdapterStreamInput,\n registration: RuntimeAdapterRegistration,\n adapter: ModelAdapter,\n): Promise<{ readonly modelInfo: ResolvedModelInfo; readonly config: CallConfig;\n readonly dispatch: PreparedDispatch['dispatch'] }> {\n const { options, context, prepared } = input\n if (prepared !== undefined) {\n if (!callConfigEquals(options, prepared.config)) {\n throw new ModelError(\n 'prepared call config changed before adapter dispatch',\n REGISTRY_ERROR_CODES.INVALID_PREPARED_CALL,\n )\n }\n return { modelInfo: prepared.modelInfo, config: prepared.config, dispatch: prepared.dispatch }\n }\n const adapterCall: PreparedAdapterCall = await adapter.prepareCall(\n options.provider, options.model, options.signal, context,\n )\n const modelInfo = normalizeResolvedModelInfo(\n registration.provider.id, options.model, adapterCall.model, input.maxCatalogBytes,\n )\n return {\n modelInfo,\n config: resolveCallWithModelInfo(options, modelInfo).config,\n dispatch: (request, activeContext) => adapterCall.stream(request, activeContext),\n }\n}\n\nfunction validateNativeTools(options: GenerateOptions, model: ResolvedModelInfo): void {\n if (model.nativeTools === undefined) return\n for (const tool of options.tools ?? []) {\n if (isNativeToolSchema(tool) && !model.nativeTools.includes(tool.name)) {\n throw new ModelError(\n `model \"${model.id}\" on route \"${model.provider}\" does not support native tool \"${tool.name}\"`,\n REGISTRY_ERROR_CODES.UNSUPPORTED_NATIVE_TOOL,\n )\n }\n }\n}\n\nasync function* consumeIterator(\n iterator: AsyncIterator<StreamChunk>,\n signal?: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n let completed = false\n try {\n while (true) {\n let item: { done: true } | { done: false; value: StreamChunk }\n try {\n const next = await iterator.next()\n item = next.done === true ? { done: true } : { done: false, value: next.value }\n } catch (error: unknown) {\n completed = true\n yield adapterFailureChunk(error, signal)\n return\n }\n if (item.done) { completed = true; return }\n yield item.value\n }\n } finally {\n if (!completed) await closeIterator(iterator)\n }\n}\n\nasync function closeIterator(iterator: AsyncIterator<StreamChunk>): Promise<void> {\n const close = iterator.return?.bind(iterator)\n if (close === undefined) return\n const closing = Promise.resolve().then(async () => { await close() })\n if (!await waitForSettlement(closing, 30_000)) {\n throw new ModelError(\n 'model adapter ignored cancellation for more than 30000ms',\n MODEL_ERROR_CODES.TEARDOWN_TIMEOUT,\n )\n }\n}\n\nfunction adapterFailureChunk(error: unknown, signal?: AbortSignal): StreamChunk {\n const failure = normalizeModelFailure(error)\n return {\n type: 'finish',\n reason: signal?.aborted === true || failure.code === 'ABORTED'\n ? { kind: 'aborted', failure }\n : { kind: 'error', failure },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model-stream.js","names":[],"sources":["../../src/runtime/model-stream.ts"],"sourcesContent":["import type { ModelAdapter, PreparedAdapterCall } from '../contract/adapter.ts'\nimport { callConfigEquals, type CallConfig } from '../contract/call-config.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport { isNativeToolSchema } from '../contract/tool.ts'\nimport type { ProviderInfo, ResolvedModelInfo } from '../contract/model-info.ts'\nimport type { ResolvedRetryPolicy } from '../contract/retry-policy.ts'\nimport { normalizeModelFailure } from '../errors/failure.ts'\nimport { MODEL_ERROR_CODES, ModelError, REGISTRY_ERROR_CODES } from '../errors/model-error.ts'\nimport {\n contentHasDocument, contentHasImage, projectDocumentsForTextModel, projectImagesForTextModel,\n} from '../message/projection.ts'\nimport type { ModelInvocationContext } from '../observation/report.ts'\nimport type { StreamChunk } from '../stream/chunk.ts'\nimport { waitForSettlement } from '../async/settlement.ts'\nimport {\n normalizeResolvedModelInfo,\n projectReplayForAdapter,\n resolveCallWithModelInfo,\n} from './model-metadata.ts'\n\nexport interface RuntimeAdapterRegistration {\n readonly adapter: ModelAdapter\n readonly provider: ProviderInfo\n readonly retryPolicy: ResolvedRetryPolicy\n readonly pluginId?: string\n readonly family?: string\n}\n\nexport interface PreparedDispatch {\n readonly registration: RuntimeAdapterRegistration\n readonly config: CallConfig\n readonly modelInfo: ResolvedModelInfo\n readonly dispatch: (options: GenerateOptions, context: ModelInvocationContext) => AsyncIterable<StreamChunk>\n}\n\nexport interface AdapterStreamInput {\n readonly options: GenerateOptions\n readonly context: ModelInvocationContext\n readonly onDispatch: () => void\n readonly prepared?: PreparedDispatch\n readonly maxCatalogBytes: number\n readonly registration: (provider: string) => RuntimeAdapterRegistration\n readonly registeredAdapter: (provider: string) => ModelAdapter | undefined\n}\n\n/** Run one adapter generation through the registry's single failure/cancellation funnel. */\nexport async function* streamAdapter(input: AdapterStreamInput): AsyncGenerator<StreamChunk> {\n const { options, context } = input\n let iterator: AsyncIterator<StreamChunk>\n try {\n const registration = input.prepared?.registration ?? input.registration(options.provider)\n const adapter = registration.adapter\n const prepared = await prepareDispatch(input, registration, adapter)\n const withConfig = callConfigEquals(options, prepared.config)\n ? options\n : { ...options, ...prepared.config }\n const hasUnsupportedImages = prepared.modelInfo.inputModalities !== undefined\n && !prepared.modelInfo.inputModalities.includes('image')\n && withConfig.messages.some(message => contentHasImage(message.content))\n const hasUnsupportedDocuments = prepared.modelInfo.inputModalities !== undefined\n && !prepared.modelInfo.inputModalities.includes('document')\n && withConfig.messages.some(message => contentHasDocument(message.content))\n if (withConfig.imagePolicy !== undefined && withConfig.imagePolicy !== 'strict' && withConfig.imagePolicy !== 'project') throw new ModelError('invalid image policy', 'INVALID_IMAGE_POLICY')\n if (withConfig.documentPolicy !== undefined && withConfig.documentPolicy !== 'strict' && withConfig.documentPolicy !== 'project') throw new ModelError('invalid document policy', 'INVALID_DOCUMENT_POLICY')\n if (hasUnsupportedImages && withConfig.imagePolicy === 'strict') throw new ModelError(\n `model ${prepared.modelInfo.id} does not support required image input`, 'UNSUPPORTED_IMAGE_INPUT',\n )\n if (hasUnsupportedDocuments && withConfig.documentPolicy === 'strict') throw new ModelError(\n `model ${prepared.modelInfo.id} does not support required document input`, 'UNSUPPORTED_DOCUMENT_INPUT',\n )\n const withImages = hasUnsupportedImages\n ? { ...withConfig, messages: projectImagesForTextModel(withConfig.messages) }\n : withConfig\n const projected = hasUnsupportedDocuments\n ? { ...withImages, messages: projectDocumentsForTextModel(withImages.messages) }\n : withImages\n validateNativeTools(projected, prepared.modelInfo)\n input.onDispatch()\n iterator = prepared.dispatch(projectReplayForAdapter(\n projected, adapter, input.registeredAdapter,\n ), context)[Symbol.asyncIterator]()\n } catch (error: unknown) {\n yield adapterFailureChunk(error, options.signal)\n return\n }\n yield* consumeIterator(iterator, options.signal)\n}\n\nasync function prepareDispatch(\n input: AdapterStreamInput,\n registration: RuntimeAdapterRegistration,\n adapter: ModelAdapter,\n): Promise<{ readonly modelInfo: ResolvedModelInfo; readonly config: CallConfig;\n readonly dispatch: PreparedDispatch['dispatch'] }> {\n const { options, context, prepared } = input\n if (prepared !== undefined) {\n if (!callConfigEquals(options, prepared.config)) {\n throw new ModelError(\n 'prepared call config changed before adapter dispatch',\n REGISTRY_ERROR_CODES.INVALID_PREPARED_CALL,\n )\n }\n return { modelInfo: prepared.modelInfo, config: prepared.config, dispatch: prepared.dispatch }\n }\n const adapterCall: PreparedAdapterCall = await adapter.prepareCall(\n options.provider, options.model, options.signal, context,\n )\n const modelInfo = normalizeResolvedModelInfo(\n registration.provider.id, options.model, adapterCall.model, input.maxCatalogBytes,\n )\n return {\n modelInfo,\n config: resolveCallWithModelInfo(options, modelInfo).config,\n dispatch: (request, activeContext) => adapterCall.stream(request, activeContext),\n }\n}\n\nfunction validateNativeTools(options: GenerateOptions, model: ResolvedModelInfo): void {\n if (model.nativeTools === undefined) return\n for (const tool of options.tools ?? []) {\n if (isNativeToolSchema(tool) && !model.nativeTools.includes(tool.name)) {\n throw new ModelError(\n `model \"${model.id}\" on route \"${model.provider}\" does not support native tool \"${tool.name}\"`,\n REGISTRY_ERROR_CODES.UNSUPPORTED_NATIVE_TOOL,\n )\n }\n }\n}\n\nasync function* consumeIterator(\n iterator: AsyncIterator<StreamChunk>,\n signal?: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n let completed = false\n try {\n while (true) {\n let item: { done: true } | { done: false; value: StreamChunk }\n try {\n const next = await iterator.next()\n item = next.done === true ? { done: true } : { done: false, value: next.value }\n } catch (error: unknown) {\n completed = true\n yield adapterFailureChunk(error, signal)\n return\n }\n if (item.done) { completed = true; return }\n yield item.value\n }\n } finally {\n if (!completed) await closeIterator(iterator)\n }\n}\n\nasync function closeIterator(iterator: AsyncIterator<StreamChunk>): Promise<void> {\n const close = iterator.return?.bind(iterator)\n if (close === undefined) return\n const closing = Promise.resolve().then(async () => { await close() })\n if (!await waitForSettlement(closing, 30_000)) {\n throw new ModelError(\n 'model adapter ignored cancellation for more than 30000ms',\n MODEL_ERROR_CODES.TEARDOWN_TIMEOUT,\n )\n }\n}\n\nfunction adapterFailureChunk(error: unknown, signal?: AbortSignal): StreamChunk {\n const failure = normalizeModelFailure(error)\n return {\n type: 'finish',\n reason: signal?.aborted === true || failure.code === 'ABORTED'\n ? { kind: 'aborted', failure }\n : { kind: 'error', failure },\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{PLUGIN_ERROR_CODES as t,PluginError as n}from"../plugin/provider-plugin.js";import{ModelError as r,REGISTRY_ERROR_CODES as i}from"../errors/model-error.js";import{callConfigEquals as a}from"../contract/call-config.js";import{resolveRetryPolicy as o}from"../contract/retry-policy.js";import{createModelCallHandle as s}from"./model-call-handle.js";import{normalizeResolvedModelInfo as c,resolveCallWithModelInfo as l,validateCatalogModels as u,validateModelCatalogSnapshot as d}from"./model-metadata.js";import{streamAdapter as f}from"./model-stream.js";function p(e){if((typeof e!=`object`||!e)&&typeof e!=`function`)return!1;let t;try{t=Reflect.get(e,`then`)}catch{return!0}if(typeof t!=`function`)return!1;try{Promise.resolve(e).catch(()=>{})}catch{}return!0}function m(e){try{return p(e())?TypeError(`plugin cleanup must be synchronous`):void 0}catch(e){return e}}var h=class{adapters=new Map;middleware=[];listeners=new Set;plugins=new Map;maxCatalogModels;maxCatalogBytes;observation;observationResource;constructor(t={}){this.maxCatalogModels=g(t.maxCatalogModels??2048,`maxCatalogModels`),this.maxCatalogBytes=g(t.maxCatalogBytes??4194304,`maxCatalogBytes`),this.observation=t.observation,this.observationResource=e(t.observationResource??{sdkName:`ai-agent-sdk`,sdkVersion:`0.1.0`,runtime:`unknown`})}install(e){let a=typeof e?.id==`string`?e.id:``,o=(e,r,i=[])=>{let o=[...r===void 0?[]:[r],...i],s=o.length<=1?o[0]:AggregateError(o,`plugin ${a} install cleanup failed`);throw new n(e,t.INSTALL_FAILED,a,s===void 0?void 0:{cause:s})};(a.trim().length===0||a!==a.trim())&&o(`plugin id must be a non-empty trimmed string`),this.plugins.has(a)&&o(`plugin "${a}" is already installed`),(typeof e.displayName!=`string`||e.displayName.trim().length===0)&&o(`plugin "${a}" displayName must be non-empty`),typeof e.setup!=`function`&&o(`plugin "${a}" setup must be a function`);let s=e.family??a;(typeof s!=`string`||s.trim().length===0||s!==s.trim())&&o(`plugin "${a}" family must be a non-empty trimmed string`);let c=Object.freeze({pluginId:a,family:s}),l=[],u=[],d=!0,f={registerAdapter:(e,t)=>{if(d||o(`plugin "${a}" staging registrar is closed`),e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n={active:!0,routes:[...e],adapter:t},s=new Set(l.filter(e=>e.active).flatMap(e=>e.routes));for(let t of e)if(s.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),l.push(n);let u=(()=>{d&&(n.active=!1)});return u.replace=e=>{if(!d||!n.active)throw new r(`a disposed staged registration cannot replace routes`,i.REGISTRATION_DISPOSED);let a=new Set(l.filter(e=>e.active&&e!==n).flatMap(e=>e.routes));for(let t of e)if(a.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),n.routes=[...e]},u},use:e=>{if(d||o(`plugin "${a}" staging registrar is closed`),typeof e!=`function`)throw TypeError(`plugin middleware must be a function`);let t={active:!0,middleware:e};return u.push(t),()=>{d&&(t.active=!1)}}},h;try{let t=e.setup(f);p(t)&&o(`plugin "${a}" setup must be synchronous`),t!==void 0&&typeof t!=`function`&&o(`plugin "${a}" setup must be synchronous and return void or cleanup`),h=typeof t==`function`?t:void 0}catch(e){if(e instanceof n&&e.code===t.INSTALL_FAILED)throw e;o(`plugin "${a}" setup failed`,e)}finally{d=!1}let g=[],_=new Set;try{for(let e of l)if(e.active){for(let t of e.routes){if(_.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);_.add(t)}g.push(...this.prepareRoutes(e.routes,e.adapter,new Set,c))}}catch(e){let t=[];if(h){let e=m(h);e!==void 0&&t.push(e)}o(`plugin "${a}" commit validation failed`,e,t)}let v=u.filter(e=>e.active).map(e=>e.middleware);for(let e of g)this.adapters.set(e.provider.id,e);this.middleware.push(...v);let y={routes:_,registrations:g,middleware:v,...h===void 0?{}:{cleanup:h}};this.plugins.set(a,y),this.emitAdaptersUpdated();let b=!1,x=(()=>{if(b)return;b=!0,this.plugins.delete(a);for(let e of y.registrations)this.adapters.get(e.provider.id)===e&&this.adapters.delete(e.provider.id);for(let e=y.middleware.length-1;e>=0;e--){let t=y.middleware[e],n=t===void 0?-1:this.middleware.lastIndexOf(t);n>=0&&this.middleware.splice(n,1)}this.emitAdaptersUpdated();let e=[];if(y.cleanup){let t=m(y.cleanup);t!==void 0&&e.push(t)}if(e.length>0)throw new n(`plugin "${a}" cleanup failed`,t.CLEANUP_FAILED,a,{cause:AggregateError(e,`plugin "${a}" cleanup failed`)})});return Object.defineProperty(x,"pluginId",{value:a,enumerable:!0}),x}registerAdapter(e,t){if(e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n=new Set,a=!1;this.commitRoutes(n,this.prepareRoutes(e,t,n));let o=(()=>{if(!a){a=!0;for(let e of n)this.adapters.delete(e);n.clear(),this.emitAdaptersUpdated()}});return o.replace=e=>{if(a)throw new r(`a disposed adapter registration cannot replace its routes`,i.REGISTRATION_DISPOSED);this.commitRoutes(n,this.prepareRoutes(e,t,n))},o}prepareRoutes(e,t,n,a){let s=new Set,c=[];for(let l of e){if(typeof l!=`string`||l.length===0)throw new r(`adapter provider route names must be non-empty strings`,i.INVALID_ADAPTER);if(s.has(l)||this.adapters.has(l)&&!n.has(l))throw new r(`an adapter for provider route "${l}" is already registered`,i.DUPLICATE_ADAPTER);let e=t.providerInfo(l);if(typeof e.id!=`string`||e.id!==l||typeof e.name!=`string`||e.name.length===0)throw new r(`adapter metadata for route "${l}" must preserve its id and carry a non-empty name`,i.INVALID_ADAPTER);s.add(l),c.push({adapter:t,provider:{id:e.id,name:e.name},...a===void 0?{}:a,retryPolicy:t.providerRetryPolicy(l)??o(void 0,`provider "${l}" retryPolicy`)})}return c}commitRoutes(e,t){for(let t of e)this.adapters.delete(t);e.clear();for(let n of t)this.adapters.set(n.provider.id,n),e.add(n.provider.id);this.emitAdaptersUpdated()}use(e){return this.middleware.push(e),()=>{let t=this.middleware.indexOf(e);t>=0&&this.middleware.splice(t,1)}}onAdaptersUpdated(e){return this.listeners.add(e),()=>void this.listeners.delete(e)}emitAdaptersUpdated(){for(let e of[...this.listeners])try{e()}catch{}}listProviders(){return[...this.adapters.values()].map(({provider:e})=>({...e}))}async listModels(e,t){let n=await this.registration(e).adapter.listModels(e,t);return u(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async modelCatalog(e,t={}){let n=await this.registration(e).adapter.modelCatalog(e,t);return d(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async resolveModelInfo(e,t,n){let r=this.registration(e),i=await r.adapter.resolveModel(e,t,n);return c(r.provider.id,t,i,this.maxCatalogBytes)}retryPolicy(e){return this.registration(e).retryPolicy}async prepareCall(t,n,o){let s=this.registration(t.provider),u=await s.adapter.prepareCall(t.provider,t.model,n,o),d=c(s.provider.id,t.model,u.model,this.maxCatalogBytes),f=l(t,d),p=e(structuredClone(f.config)),m=f.context===void 0?void 0:e(structuredClone(f.context)),h=e({...t.reasoningEffort===void 0&&p.reasoningEffort!==void 0?{reasoningEffort:!0}:{},...t.maxTokens===void 0&&p.maxTokens!==void 0?{maxTokens:!0}:{}}),g=!1,_=e(structuredClone(d));return Object.freeze({config:p,model:_,retryPolicy:s.retryPolicy,adapterDefaults:h,...m===void 0?{}:{context:m},...d.inputModalities===void 0?{}:{inputModalities:Object.freeze([...d.inputModalities])},stream:(e,t=o)=>{if(g)throw new r(`a prepared call can only be dispatched once`,i.INVALID_PREPARED_CALL);if(!a(e,p))throw new r(`prepared call config changed before adapter dispatch`,i.INVALID_PREPARED_CALL);return g=!0,this.dispatch(e,t,{registration:s,config:p,modelInfo:d,dispatch:(e,t)=>u.stream(e,t)})}})}stream(e,t){return this.dispatch(e,t)}dispatch(e,t,n){let r=`not-sent`,i=n?.registration??this.adapters.get(e.provider);return s({options:e,...i?.family===void 0?{}:{providerFamily:i.family},...i?.pluginId===void 0?{}:{providerPluginId:i.pluginId},...i===void 0?{}:{isRetryable:e=>i.retryPolicy.mode===`always`||i.retryPolicy.retryableCodes.includes(e)},...t===void 0?{}:{context:t},...this.observation===void 0?{}:{defaultObservation:this.observation},resource:this.observationResource,routePresent:n!==void 0||this.adapters.has(e.provider),dispatchState:()=>r,stream:t=>this.dispatchRaw(e,t,()=>{r=`unknown`},n)})}dispatchRaw(e,t,n,r){let i=[...this.middleware],a=()=>{let a=()=>f({options:e,context:t,onDispatch:n,maxCatalogBytes:this.maxCatalogBytes,registration:e=>this.registration(e),registeredAdapter:e=>this.adapters.get(e)?.adapter,...r===void 0?{}:{prepared:r}});for(let n=i.length-1;n>=0;n--){let r=i[n];if(r===void 0)continue;let o=a;a=()=>r(e,o,t)}return a()};return{async*[Symbol.asyncIterator](){yield*a()}}}registration(e){let t=this.adapters.get(e);if(t===void 0)throw new r(`no adapter registered for provider route "${e}"`,i.NO_ADAPTER);return t}};function g(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`ModelRegistry ${t} must be a positive safe integer`);return e}export{h as ModelRegistry};
1
+ import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{PLUGIN_ERROR_CODES as t,PluginError as n}from"../plugin/provider-plugin.js";import{ModelError as r,REGISTRY_ERROR_CODES as i}from"../errors/model-error.js";import{callConfigEquals as a}from"../contract/call-config.js";import{resolveRetryPolicy as o}from"../contract/retry-policy.js";import{createModelCallHandle as s}from"./model-call-handle.js";import{normalizeResolvedModelInfo as c,resolveCallWithModelInfo as l,validateCatalogModels as u,validateModelCatalogSnapshot as d}from"./model-metadata.js";import{streamAdapter as f}from"./model-stream.js";function p(e){if((typeof e!=`object`||!e)&&typeof e!=`function`)return!1;let t;try{t=Reflect.get(e,`then`)}catch{return!0}if(typeof t!=`function`)return!1;try{Promise.resolve(e).catch(()=>{})}catch{}return!0}function m(e){try{return p(e())?TypeError(`plugin cleanup must be synchronous`):void 0}catch(e){return e}}var h=class{adapters=new Map;middleware=[];listeners=new Set;plugins=new Map;maxCatalogModels;maxCatalogBytes;observation;observationResource;constructor(t={}){this.maxCatalogModels=g(t.maxCatalogModels??2048,`maxCatalogModels`),this.maxCatalogBytes=g(t.maxCatalogBytes??4194304,`maxCatalogBytes`),this.observation=t.observation,this.observationResource=e(t.observationResource??{sdkName:`ai-agent-sdk`,sdkVersion:`0.1.2`,runtime:`unknown`})}install(e){let a=typeof e?.id==`string`?e.id:``,o=(e,r,i=[])=>{let o=[...r===void 0?[]:[r],...i],s=o.length<=1?o[0]:AggregateError(o,`plugin ${a} install cleanup failed`);throw new n(e,t.INSTALL_FAILED,a,s===void 0?void 0:{cause:s})};(a.trim().length===0||a!==a.trim())&&o(`plugin id must be a non-empty trimmed string`),this.plugins.has(a)&&o(`plugin "${a}" is already installed`),(typeof e.displayName!=`string`||e.displayName.trim().length===0)&&o(`plugin "${a}" displayName must be non-empty`),typeof e.setup!=`function`&&o(`plugin "${a}" setup must be a function`);let s=e.family??a;(typeof s!=`string`||s.trim().length===0||s!==s.trim())&&o(`plugin "${a}" family must be a non-empty trimmed string`);let c=Object.freeze({pluginId:a,family:s}),l=[],u=[],d=!0,f={registerAdapter:(e,t)=>{if(d||o(`plugin "${a}" staging registrar is closed`),e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n={active:!0,routes:[...e],adapter:t},s=new Set(l.filter(e=>e.active).flatMap(e=>e.routes));for(let t of e)if(s.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),l.push(n);let u=(()=>{d&&(n.active=!1)});return u.replace=e=>{if(!d||!n.active)throw new r(`a disposed staged registration cannot replace routes`,i.REGISTRATION_DISPOSED);let a=new Set(l.filter(e=>e.active&&e!==n).flatMap(e=>e.routes));for(let t of e)if(a.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),n.routes=[...e]},u},use:e=>{if(d||o(`plugin "${a}" staging registrar is closed`),typeof e!=`function`)throw TypeError(`plugin middleware must be a function`);let t={active:!0,middleware:e};return u.push(t),()=>{d&&(t.active=!1)}}},h;try{let t=e.setup(f);p(t)&&o(`plugin "${a}" setup must be synchronous`),t!==void 0&&typeof t!=`function`&&o(`plugin "${a}" setup must be synchronous and return void or cleanup`),h=typeof t==`function`?t:void 0}catch(e){if(e instanceof n&&e.code===t.INSTALL_FAILED)throw e;o(`plugin "${a}" setup failed`,e)}finally{d=!1}let g=[],_=new Set;try{for(let e of l)if(e.active){for(let t of e.routes){if(_.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);_.add(t)}g.push(...this.prepareRoutes(e.routes,e.adapter,new Set,c))}}catch(e){let t=[];if(h){let e=m(h);e!==void 0&&t.push(e)}o(`plugin "${a}" commit validation failed`,e,t)}let v=u.filter(e=>e.active).map(e=>e.middleware);for(let e of g)this.adapters.set(e.provider.id,e);this.middleware.push(...v);let y={routes:_,registrations:g,middleware:v,...h===void 0?{}:{cleanup:h}};this.plugins.set(a,y),this.emitAdaptersUpdated();let b=!1,x=(()=>{if(b)return;b=!0,this.plugins.delete(a);for(let e of y.registrations)this.adapters.get(e.provider.id)===e&&this.adapters.delete(e.provider.id);for(let e=y.middleware.length-1;e>=0;e--){let t=y.middleware[e],n=t===void 0?-1:this.middleware.lastIndexOf(t);n>=0&&this.middleware.splice(n,1)}this.emitAdaptersUpdated();let e=[];if(y.cleanup){let t=m(y.cleanup);t!==void 0&&e.push(t)}if(e.length>0)throw new n(`plugin "${a}" cleanup failed`,t.CLEANUP_FAILED,a,{cause:AggregateError(e,`plugin "${a}" cleanup failed`)})});return Object.defineProperty(x,"pluginId",{value:a,enumerable:!0}),x}registerAdapter(e,t){if(e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n=new Set,a=!1;this.commitRoutes(n,this.prepareRoutes(e,t,n));let o=(()=>{if(!a){a=!0;for(let e of n)this.adapters.delete(e);n.clear(),this.emitAdaptersUpdated()}});return o.replace=e=>{if(a)throw new r(`a disposed adapter registration cannot replace its routes`,i.REGISTRATION_DISPOSED);this.commitRoutes(n,this.prepareRoutes(e,t,n))},o}prepareRoutes(e,t,n,a){let s=new Set,c=[];for(let l of e){if(typeof l!=`string`||l.length===0)throw new r(`adapter provider route names must be non-empty strings`,i.INVALID_ADAPTER);if(s.has(l)||this.adapters.has(l)&&!n.has(l))throw new r(`an adapter for provider route "${l}" is already registered`,i.DUPLICATE_ADAPTER);let e=t.providerInfo(l);if(typeof e.id!=`string`||e.id!==l||typeof e.name!=`string`||e.name.length===0)throw new r(`adapter metadata for route "${l}" must preserve its id and carry a non-empty name`,i.INVALID_ADAPTER);s.add(l),c.push({adapter:t,provider:{id:e.id,name:e.name},...a===void 0?{}:a,retryPolicy:t.providerRetryPolicy(l)??o(void 0,`provider "${l}" retryPolicy`)})}return c}commitRoutes(e,t){for(let t of e)this.adapters.delete(t);e.clear();for(let n of t)this.adapters.set(n.provider.id,n),e.add(n.provider.id);this.emitAdaptersUpdated()}use(e){return this.middleware.push(e),()=>{let t=this.middleware.indexOf(e);t>=0&&this.middleware.splice(t,1)}}onAdaptersUpdated(e){return this.listeners.add(e),()=>void this.listeners.delete(e)}emitAdaptersUpdated(){for(let e of[...this.listeners])try{e()}catch{}}listProviders(){return[...this.adapters.values()].map(({provider:e})=>({...e}))}async listModels(e,t){let n=await this.registration(e).adapter.listModels(e,t);return u(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async modelCatalog(e,t={}){let n=await this.registration(e).adapter.modelCatalog(e,t);return d(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async resolveModelInfo(e,t,n){let r=this.registration(e),i=await r.adapter.resolveModel(e,t,n);return c(r.provider.id,t,i,this.maxCatalogBytes)}retryPolicy(e){return this.registration(e).retryPolicy}async prepareCall(t,n,o){let s=this.registration(t.provider),u=await s.adapter.prepareCall(t.provider,t.model,n,o),d=c(s.provider.id,t.model,u.model,this.maxCatalogBytes),f=l(t,d),p=e(structuredClone(f.config)),m=f.context===void 0?void 0:e(structuredClone(f.context)),h=e({...t.reasoningEffort===void 0&&p.reasoningEffort!==void 0?{reasoningEffort:!0}:{},...t.maxTokens===void 0&&p.maxTokens!==void 0?{maxTokens:!0}:{}}),g=!1,_=e(structuredClone(d));return Object.freeze({config:p,model:_,retryPolicy:s.retryPolicy,adapterDefaults:h,...m===void 0?{}:{context:m},...d.inputModalities===void 0?{}:{inputModalities:Object.freeze([...d.inputModalities])},stream:(e,t=o)=>{if(g)throw new r(`a prepared call can only be dispatched once`,i.INVALID_PREPARED_CALL);if(!a(e,p))throw new r(`prepared call config changed before adapter dispatch`,i.INVALID_PREPARED_CALL);return g=!0,this.dispatch(e,t,{registration:s,config:p,modelInfo:d,dispatch:(e,t)=>u.stream(e,t)})}})}stream(e,t){return this.dispatch(e,t)}dispatch(e,t,n){let r=`not-sent`,i=n?.registration??this.adapters.get(e.provider);return s({options:e,...i?.family===void 0?{}:{providerFamily:i.family},...i?.pluginId===void 0?{}:{providerPluginId:i.pluginId},...i===void 0?{}:{isRetryable:e=>i.retryPolicy.mode===`always`||i.retryPolicy.retryableCodes.includes(e)},...t===void 0?{}:{context:t},...this.observation===void 0?{}:{defaultObservation:this.observation},resource:this.observationResource,routePresent:n!==void 0||this.adapters.has(e.provider),dispatchState:()=>r,stream:t=>this.dispatchRaw(e,t,()=>{r=`unknown`},n)})}dispatchRaw(e,t,n,r){let i=[...this.middleware],a=()=>{let a=()=>f({options:e,context:t,onDispatch:n,maxCatalogBytes:this.maxCatalogBytes,registration:e=>this.registration(e),registeredAdapter:e=>this.adapters.get(e)?.adapter,...r===void 0?{}:{prepared:r}});for(let n=i.length-1;n>=0;n--){let r=i[n];if(r===void 0)continue;let o=a;a=()=>r(e,o,t)}return a()};return{async*[Symbol.asyncIterator](){yield*a()}}}registration(e){let t=this.adapters.get(e);if(t===void 0)throw new r(`no adapter registered for provider route "${e}"`,i.NO_ADAPTER);return t}};function g(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`ModelRegistry ${t} must be a positive safe integer`);return e}export{h as ModelRegistry};
2
2
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"with-retry.d.ts","names":[],"sources":["../../src/runtime/with-retry.ts"],"mappings":";;;;;UAkCiB;;WAEN;;WAEA;;WAEA;;WAEA,SAAS;;WAET;;;UAIM;;;;;EAKf,SAAS;;EAET;;EAEA,WAAW,SAAS;;EAEpB;;;;;;;;;;;;iBAySc,UAAU,SAAS,cAAc,UAAS,mBAAwB"}
1
+ {"version":3,"file":"with-retry.d.ts","names":[],"sources":["../../src/runtime/with-retry.ts"],"mappings":";;;;;UAkCiB;;WAEN;;WAEA;;WAEA;;WAEA,SAAS;;WAET;;;UAIM;;;;;EAKf,SAAS;;EAET;;EAEA,WAAW,SAAS;;EAEpB;;;;;;;;;;;;iBAqTc,UAAU,SAAS,cAAc,UAAS,mBAAwB"}
@@ -1,2 +1,2 @@
1
- import{waitForSettlement as e}from"../async/settlement.js";import{MODEL_ERROR_CODES as t,ModelError as n}from"../errors/model-error.js";import{normalizeModelFailure as r}from"../errors/failure.js";import{ModelAdapter as i}from"../contract/adapter.js";import{backoffDelayMs as a,resolveRetryPolicy as o}from"../contract/retry-policy.js";function s(e,t,n,r){let i=t.providerRetryAfterMs;return i!==void 0&&Number.isFinite(i)&&i>0?i<=e.maxDelayMs?i:e.mode===`always`?a(e,n,r):`give-up`:a(e,n,r)}function c(e,t){return t?.aborted===!0?Promise.resolve(!1):new Promise(n=>{let r=()=>{clearTimeout(i),n(!1)},i=setTimeout(()=>{t?.removeEventListener(`abort`,r),n(!0)},e);t?.addEventListener(`abort`,r,{once:!0})})}var l=class extends i{inner;options;constructor(e,t){super(),this.inner=e,this.options=t}providerInfo(e){return this.inner.providerInfo(e)}providerRetryPolicy(e){return this.inner.providerRetryPolicy(e)}listModels(e){return this.inner.listModels(e)}resolveModel(e,t,n){return this.inner.resolveModel(e,t,n)}async prepareCall(e,t,n,r){let i=await this.inner.prepareCall(e,t,n,r),a=this.policyFor(e);return{model:i.model,stream:(e,t=r)=>this.retryStream(e,e=>i.stream(e,t),a,t)}}stream(e,t){return this.retryStream(e,e=>this.inner.stream(e,t),this.policyFor(e.provider),t)}policyFor(e){return this.options.policy===void 0?this.inner.providerRetryPolicy(e)??o(void 0,`withRetry("${e}").policy`):o(this.options.policy,`withRetry("${e}").policy`)}async*retryStream(e,n,r,i){if(r.mode===`always`&&e.signal===void 0){yield{type:`finish`,reason:{kind:`error`,failure:{message:`an always retry policy requires an AbortSignal or deadline`,code:t.INVALID_REQUEST}}};return}let a=this.options.random??Math.random,o=0;for(;;){let l=await this.runAttempt(e,n);if(l.kind===`forward`){yield*l.chunks;return}let u=l.failure;if(e.signal?.aborted===!0||u.code===t.ABORTED){yield{type:`finish`,reason:{kind:`aborted`,failure:u}};return}if(!(r.mode===`always`||r.retryableCodes.includes(u.code)&&o<r.maxRetries)){yield{type:`finish`,reason:{kind:`error`,failure:u}};return}let d=s(r,u,o+1,a);if(d===`give-up`){yield{type:`finish`,reason:{kind:`error`,failure:u}};return}o+=1,i?.recordProviderRetry?.({nextAttemptNumber:o+1,delayMs:d,failureCode:u.code});try{this.options.onRetry?.({provider:e.provider,attempt:o,maxRetries:r.mode===`normal`?r.maxRetries:void 0,failure:u,delayMs:d})}catch{}if(!await c(d,e.signal)){yield{type:`finish`,reason:{kind:`aborted`,failure:{message:`model call aborted while waiting to retry`,code:t.ABORTED}}};return}}}async runAttempt(e,n){let i;try{i=n(e)[Symbol.asyncIterator]()}catch(e){return{kind:`retryable`,failure:r(e)}}let a;try{a=await i.next()}catch(e){return await f(i,this.options.teardownTimeoutMs??3e4),{kind:`retryable`,failure:r(e)}}if(a.done===!0)return{kind:`retryable`,failure:{message:`the adapter produced no chunks`,code:t.UNKNOWN}};let o=a.value;return o.type===`finish`&&o.reason.kind===`error`?(await f(i,this.options.teardownTimeoutMs??3e4),{kind:`retryable`,failure:o.reason.failure}):{kind:`forward`,chunks:d(o,i,this.options.teardownTimeoutMs??3e4)}}};function u(e,t){if(!Number.isFinite(e)||e<=0)throw RangeError(`${t} must be a positive finite number`);return e}async function*d(e,t,n){let r=!1;try{for(yield e;;){let e=await t.next();if(e.done===!0){r=!0;return}yield e.value}}finally{r||await f(t,n)}}async function f(r,i){let a=r.return?.();if(a!==void 0&&!await e(Promise.resolve(a),u(i,`withRetry teardownTimeoutMs`)))throw new n(`retry attempt teardown exceeded ${i}ms`,t.TEARDOWN_TIMEOUT)}function p(e,t={}){return new l(e,t)}export{p as withRetry};
1
+ import{waitForSettlement as e}from"../async/settlement.js";import{MODEL_ERROR_CODES as t,ModelError as n}from"../errors/model-error.js";import{normalizeModelFailure as r}from"../errors/failure.js";import{ModelAdapter as i}from"../contract/adapter.js";import{backoffDelayMs as a,resolveRetryPolicy as o}from"../contract/retry-policy.js";function s(e,t,n,r){let i=t.providerRetryAfterMs;return i!==void 0&&Number.isFinite(i)&&i>0?i<=e.maxDelayMs?i:e.mode===`always`?a(e,n,r):`give-up`:a(e,n,r)}function c(e,t){return t?.aborted===!0?Promise.resolve(!1):new Promise(n=>{let r=()=>{clearTimeout(i),n(!1)},i=setTimeout(()=>{t?.removeEventListener(`abort`,r),n(!0)},e);t?.addEventListener(`abort`,r,{once:!0})})}var l=class extends i{inner;options;constructor(e,t){super(),this.inner=e,this.options=t}providerInfo(e){return this.inner.providerInfo(e)}providerRetryPolicy(e){return this.inner.providerRetryPolicy(e)}listModels(e){return this.inner.listModels(e)}resolveModel(e,t,n){return this.inner.resolveModel(e,t,n)}async prepareCall(e,t,n,r){let i=await this.inner.prepareCall(e,t,n,r),a=this.policyFor(e);return{model:i.model,stream:(e,t=r)=>this.retryStream(e,e=>i.stream(e,t),a,t)}}stream(e,t){return this.retryStream(e,e=>this.inner.stream(e,t),this.policyFor(e.provider),t)}policyFor(e){return this.options.policy===void 0?this.inner.providerRetryPolicy(e)??o(void 0,`withRetry("${e}").policy`):o(this.options.policy,`withRetry("${e}").policy`)}async*retryStream(e,n,r,i){if(r.mode===`always`&&e.signal===void 0){yield{type:`finish`,reason:{kind:`error`,failure:{message:`an always retry policy requires an AbortSignal or deadline`,code:t.INVALID_REQUEST}}};return}let a=this.options.random??Math.random,o=0;for(;;){let l=yield*this.runAttempt(e,n);if(l.kind===`forward`){yield*l.chunks;return}let u=l.failure;if(e.signal?.aborted===!0||u.code===t.ABORTED){yield{type:`finish`,reason:{kind:`aborted`,failure:u}};return}if(!(r.mode===`always`||r.retryableCodes.includes(u.code)&&o<r.maxRetries)){yield{type:`finish`,reason:{kind:`error`,failure:u}};return}let d=s(r,u,o+1,a);if(d===`give-up`){yield{type:`finish`,reason:{kind:`error`,failure:u}};return}o+=1,i?.recordProviderRetry?.({nextAttemptNumber:o+1,delayMs:d,failureCode:u.code});try{this.options.onRetry?.({provider:e.provider,attempt:o,maxRetries:r.mode===`normal`?r.maxRetries:void 0,failure:u,delayMs:d})}catch{}if(!await c(d,e.signal)){yield{type:`finish`,reason:{kind:`aborted`,failure:{message:`model call aborted while waiting to retry`,code:t.ABORTED}}};return}}}async*runAttempt(e,n){let i;try{i=n(e)[Symbol.asyncIterator]()}catch(e){return{kind:`retryable`,failure:r(e)}}let a;try{for(a=await i.next();!a.done&&a.value.type===`usage-progress`;){let e=!1;try{yield a.value,e=!0}finally{e||await f(i,this.options.teardownTimeoutMs??3e4)}a=await i.next()}}catch(e){return await f(i,this.options.teardownTimeoutMs??3e4),{kind:`retryable`,failure:r(e)}}if(a.done===!0)return{kind:`retryable`,failure:{message:`the adapter produced no chunks`,code:t.UNKNOWN}};let o=a.value;return o.type===`finish`&&o.reason.kind===`error`?(await f(i,this.options.teardownTimeoutMs??3e4),{kind:`retryable`,failure:o.reason.failure}):{kind:`forward`,chunks:d(o,i,this.options.teardownTimeoutMs??3e4)}}};function u(e,t){if(!Number.isFinite(e)||e<=0)throw RangeError(`${t} must be a positive finite number`);return e}async function*d(e,t,n){let r=!1;try{for(yield e;;){let e=await t.next();if(e.done===!0){r=!0;return}yield e.value}}finally{r||await f(t,n)}}async function f(r,i){let a=r.return?.();if(a!==void 0&&!await e(Promise.resolve(a),u(i,`withRetry teardownTimeoutMs`)))throw new n(`retry attempt teardown exceeded ${i}ms`,t.TEARDOWN_TIMEOUT)}function p(e,t={}){return new l(e,t)}export{p as withRetry};
2
2
  //# sourceMappingURL=with-retry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"with-retry.js","names":[],"sources":["../../src/runtime/with-retry.ts"],"sourcesContent":["/**\n * Retry as an adapter DECORATOR.\n *\n * In deepseek-harness this was a plugin on the agent loop's failed-step hook,\n * which let it restart a whole step and recover its attempt count from a durable\n * session log. A standalone SDK has neither, so retry moves down to the adapter\n * and holds its attempt count in the call's own closure.\n *\n * That relocation brings one hard constraint, and it is the thing to understand\n * about this module: a retry may only happen while NOTHING has been yielded\n * downstream yet. Once a text delta has reached the consumer, re-running the\n * request would replay those tokens and the consumer would render them twice.\n * So a failure that arrives mid-stream is forwarded, not retried  Erecovering\n * from it requires re-running the whole turn, which only the caller can decide.\n *\n * @module ai-agent-sdk/core/runtime/with-retry\n */\n\nimport { ModelAdapter, type PreparedAdapterCall } from '../contract/adapter.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport type { ModelInfo, ResolvedModelInfo } from '../contract/model-info.ts'\nimport {\n backoffDelayMs,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n type RetryPolicyConfig,\n} from '../contract/retry-policy.ts'\nimport { normalizeModelFailure, type ModelFailure } from '../errors/failure.ts'\nimport { MODEL_ERROR_CODES, ModelError } from '../errors/model-error.ts'\nimport type { StreamChunk } from '../stream/chunk.ts'\nimport type { ModelInvocationContext } from '../observation/report.ts'\nimport { waitForSettlement } from '../async/settlement.ts'\n\n/** One retry that is about to be waited out. */\nexport interface RetryAttempt {\n /** The route whose call failed. */\n readonly provider: string\n /** 1-based retry number. */\n readonly attempt: number\n /** Retry ceiling, or `undefined` under an `always` policy. */\n readonly maxRetries: number | undefined\n /** The failure being recovered from. */\n readonly failure: ModelFailure\n /** How long the decorator will wait before re-dispatching. */\n readonly delayMs: number\n}\n\n/** Options for {@link withRetry}. */\nexport interface WithRetryOptions {\n /**\n * Policy to apply. Omission uses the wrapped adapter's own per-route policy,\n * falling back to the shared defaults.\n */\n policy?: RetryPolicyConfig\n /** Sample in `[0, 1)` for jitter; injectable so tests can be deterministic. */\n random?: () => number\n /** Observe each scheduled retry  Ethe hook for logging and metrics. */\n onRetry?: (attempt: RetryAttempt) => void\n /** Maximum wait while closing an unsuccessful attempt. Defaults to 30s. */\n teardownTimeoutMs?: number\n}\n\n/** Resolve a delay that honours a provider-requested `retry-after` when sane. */\nfunction delayFor(\n policy: ResolvedRetryPolicy,\n failure: ModelFailure,\n attempt: number,\n random: () => number,\n): number | 'give-up' {\n const requested = failure.providerRetryAfterMs\n if (requested !== undefined && Number.isFinite(requested) && requested > 0) {\n if (requested <= policy.maxDelayMs) return requested\n // The provider asked for longer than this policy is willing to wait. Under a\n // bounded policy that is a refusal: sleeping less than asked would just earn\n // another rate-limit response. An `always` policy has nowhere to give up to,\n // so it falls back to local backoff and keeps trying.\n return policy.mode === 'always' ? backoffDelayMs(policy, attempt, random) : 'give-up'\n }\n return backoffDelayMs(policy, attempt, random)\n}\n\n/** Sleep, resolving early and reporting false if the signal aborts first. */\nfunction cancellableDelay(delayMs: number, signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted === true) return Promise.resolve(false)\n return new Promise((resolve) => {\n const onAbort = (): void => {\n clearTimeout(timer)\n resolve(false)\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve(true)\n }, delayMs)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\nclass RetryingAdapter extends ModelAdapter {\n private readonly inner: ModelAdapter\n private readonly options: WithRetryOptions\n\n // Explicit fields rather than constructor parameter properties: Node's\n // strip-only TypeScript mode rejects those, and this package is meant to run\n // under `node file.ts` without a build step.\n constructor(inner: ModelAdapter, options: WithRetryOptions) {\n super()\n this.inner = inner\n this.options = options\n }\n\n override providerInfo(provider: string): ReturnType<ModelAdapter['providerInfo']> {\n return this.inner.providerInfo(provider)\n }\n\n override providerRetryPolicy(provider: string): ResolvedRetryPolicy | undefined {\n return this.inner.providerRetryPolicy(provider)\n }\n\n override listModels(provider: string): Promise<readonly ModelInfo[]> {\n return this.inner.listModels(provider)\n }\n\n override resolveModel(\n provider: string,\n model: string,\n signal?: AbortSignal,\n ): Promise<ResolvedModelInfo> {\n return this.inner.resolveModel(provider, model, signal)\n }\n\n override async prepareCall(\n provider: string,\n model: string,\n signal?: AbortSignal,\n context?: ModelInvocationContext,\n ): Promise<PreparedAdapterCall> {\n const prepared = await this.inner.prepareCall(provider, model, signal, context)\n const policy = this.policyFor(provider)\n return {\n model: prepared.model,\n // Retries reuse the SAME prepared generation. Re-preparing mid-retry could\n // pair a fresh endpoint with capabilities resolved against the old one,\n // which is exactly what the prepare/dispatch binding exists to prevent.\n stream: (options, invocation = context) => this.retryStream(options, request => prepared.stream(request, invocation), policy, invocation),\n }\n }\n\n stream(options: GenerateOptions, context?: ModelInvocationContext): AsyncIterable<StreamChunk> {\n return this.retryStream(\n options,\n request => this.inner.stream(request, context),\n this.policyFor(options.provider),\n context,\n )\n }\n\n private policyFor(provider: string): ResolvedRetryPolicy {\n if (this.options.policy !== undefined) {\n return resolveRetryPolicy(this.options.policy, `withRetry(\"${provider}\").policy`)\n }\n return this.inner.providerRetryPolicy(provider)\n ?? resolveRetryPolicy(undefined, `withRetry(\"${provider}\").policy`)\n }\n\n private async * retryStream(\n options: GenerateOptions,\n dispatch: (request: GenerateOptions) => AsyncIterable<StreamChunk>,\n policy: ResolvedRetryPolicy,\n context?: ModelInvocationContext,\n ): AsyncGenerator<StreamChunk> {\n if (policy.mode === 'always' && options.signal === undefined) {\n yield {\n type: 'finish',\n reason: {\n kind: 'error',\n failure: {\n message: 'an always retry policy requires an AbortSignal or deadline',\n code: MODEL_ERROR_CODES.INVALID_REQUEST,\n },\n },\n }\n return\n }\n const random = this.options.random ?? Math.random\n let retries = 0\n\n while (true) {\n const attempt = await this.runAttempt(options, dispatch)\n if (attempt.kind === 'forward') {\n yield* attempt.chunks\n return\n }\n\n const failure = attempt.failure\n // An abort is the caller's decision, never a transient fault.\n if (options.signal?.aborted === true || failure.code === MODEL_ERROR_CODES.ABORTED) {\n yield { type: 'finish', reason: { kind: 'aborted', failure } }\n return\n }\n\n const eligible = policy.mode === 'always'\n || (policy.retryableCodes.includes(failure.code) && retries < policy.maxRetries)\n if (!eligible) {\n yield { type: 'finish', reason: { kind: 'error', failure } }\n return\n }\n\n const delayMs = delayFor(policy, failure, retries + 1, random)\n if (delayMs === 'give-up') {\n yield { type: 'finish', reason: { kind: 'error', failure } }\n return\n }\n\n retries += 1\n context?.recordProviderRetry?.({\n nextAttemptNumber: retries + 1,\n delayMs,\n failureCode: failure.code,\n })\n try {\n this.options.onRetry?.({\n provider: options.provider,\n attempt: retries,\n maxRetries: policy.mode === 'normal' ? policy.maxRetries : undefined,\n failure,\n delayMs,\n })\n } catch {\n // Metrics/logging observers do not own request availability.\n }\n if (!await cancellableDelay(delayMs, options.signal)) {\n yield {\n type: 'finish',\n reason: {\n kind: 'aborted',\n failure: {\n message: 'model call aborted while waiting to retry',\n code: MODEL_ERROR_CODES.ABORTED,\n },\n },\n }\n return\n }\n }\n }\n\n /**\n * Run one attempt WITHOUT yielding anything downstream.\n *\n * Buffering is what makes retry safe: until the attempt either produces its\n * first chunk or fails, nothing has been committed to the consumer. Once a\n * chunk exists the attempt is no longer retryable, so it switches to\n * `forward` and streams the rest through untouched.\n */\n private async runAttempt(\n options: GenerateOptions,\n dispatch: (request: GenerateOptions) => AsyncIterable<StreamChunk>,\n ): Promise<\n | { kind: 'forward'; chunks: AsyncIterable<StreamChunk> }\n | { kind: 'retryable'; failure: ModelFailure }\n > {\n let iterator: AsyncIterator<StreamChunk>\n try {\n iterator = dispatch(options)[Symbol.asyncIterator]()\n } catch (error: unknown) {\n return { kind: 'retryable', failure: normalizeModelFailure(error) }\n }\n\n let first: IteratorResult<StreamChunk>\n try {\n first = await iterator.next()\n } catch (error: unknown) {\n await closeIterator(iterator, this.options.teardownTimeoutMs ?? 30_000)\n return { kind: 'retryable', failure: normalizeModelFailure(error) }\n }\n\n // A stream that ends with no chunks at all told us nothing; treat it as the\n // degenerate empty response rather than a silently successful turn.\n if (first.done === true) {\n return {\n kind: 'retryable',\n failure: {\n message: 'the adapter produced no chunks',\n code: MODEL_ERROR_CODES.UNKNOWN,\n },\n }\n }\n\n // An adapter behind the registry's funnel reports failure as a terminal\n // error finish rather than a throw. As the FIRST chunk, that is still a\n // clean nothing-emitted failure and remains retryable.\n const chunk = first.value\n if (chunk.type === 'finish' && chunk.reason.kind === 'error') {\n await closeIterator(iterator, this.options.teardownTimeoutMs ?? 30_000)\n return { kind: 'retryable', failure: chunk.reason.failure }\n }\n\n return {\n kind: 'forward',\n chunks: resume(chunk, iterator, this.options.teardownTimeoutMs ?? 30_000),\n }\n }\n}\n\nfunction positiveFinite(value: number, name: string): number {\n if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be a positive finite number`)\n return value\n}\n\n/** Re-attach an already-read first chunk to the front of its iterator. */\nasync function* resume(\n first: StreamChunk,\n iterator: AsyncIterator<StreamChunk>,\n teardownTimeoutMs: number,\n): AsyncGenerator<StreamChunk> {\n let exhausted = false\n try {\n yield first\n while (true) {\n const next = await iterator.next()\n if (next.done === true) {\n exhausted = true\n return\n }\n yield next.value\n }\n } finally {\n if (!exhausted) await closeIterator(iterator, teardownTimeoutMs)\n }\n}\n\nasync function closeIterator(iterator: AsyncIterator<StreamChunk>, timeoutMs: number): Promise<void> {\n const closing = iterator.return?.()\n if (closing === undefined) return\n const settled = await waitForSettlement(\n Promise.resolve(closing),\n positiveFinite(timeoutMs, 'withRetry teardownTimeoutMs'),\n )\n if (!settled) {\n throw new ModelError(\n `retry attempt teardown exceeded ${timeoutMs}ms`,\n MODEL_ERROR_CODES.TEARDOWN_TIMEOUT,\n )\n }\n}\n\n/**\n * Wrap an adapter so eligible transient failures are retried with bounded\n * exponential backoff and jitter.\n *\n * Only failures that occur BEFORE the first chunk reaches the consumer are\n * retried; see the module note for why.\n * @param adapter - the adapter to wrap; its metadata methods are delegated unchanged.\n * @param options - policy, jitter source, and retry observer.\n * @returns a new adapter with retry behaviour; the original is unmodified.\n */\nexport function withRetry(adapter: ModelAdapter, options: WithRetryOptions = {}): ModelAdapter {\n return new RetryingAdapter(adapter, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"with-retry.js","names":[],"sources":["../../src/runtime/with-retry.ts"],"sourcesContent":["/**\n * Retry as an adapter DECORATOR.\n *\n * In deepseek-harness this was a plugin on the agent loop's failed-step hook,\n * which let it restart a whole step and recover its attempt count from a durable\n * session log. A standalone SDK has neither, so retry moves down to the adapter\n * and holds its attempt count in the call's own closure.\n *\n * That relocation brings one hard constraint, and it is the thing to understand\n * about this module: a retry may only happen before response content is yielded.\n * Provisional usage snapshots are the only exception. Once a text delta has reached the consumer, re-running the\n * request would replay those tokens and the consumer would render them twice.\n * So a failure that arrives mid-stream is forwarded, not retried  Erecovering\n * from it requires re-running the whole turn, which only the caller can decide.\n *\n * @module ai-agent-sdk/core/runtime/with-retry\n */\n\nimport { ModelAdapter, type PreparedAdapterCall } from '../contract/adapter.ts'\nimport type { GenerateOptions } from '../contract/generate-options.ts'\nimport type { ModelInfo, ResolvedModelInfo } from '../contract/model-info.ts'\nimport {\n backoffDelayMs,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n type RetryPolicyConfig,\n} from '../contract/retry-policy.ts'\nimport { normalizeModelFailure, type ModelFailure } from '../errors/failure.ts'\nimport { MODEL_ERROR_CODES, ModelError } from '../errors/model-error.ts'\nimport type { StreamChunk } from '../stream/chunk.ts'\nimport type { ModelInvocationContext } from '../observation/report.ts'\nimport { waitForSettlement } from '../async/settlement.ts'\n\n/** One retry that is about to be waited out. */\nexport interface RetryAttempt {\n /** The route whose call failed. */\n readonly provider: string\n /** 1-based retry number. */\n readonly attempt: number\n /** Retry ceiling, or `undefined` under an `always` policy. */\n readonly maxRetries: number | undefined\n /** The failure being recovered from. */\n readonly failure: ModelFailure\n /** How long the decorator will wait before re-dispatching. */\n readonly delayMs: number\n}\n\n/** Options for {@link withRetry}. */\nexport interface WithRetryOptions {\n /**\n * Policy to apply. Omission uses the wrapped adapter's own per-route policy,\n * falling back to the shared defaults.\n */\n policy?: RetryPolicyConfig\n /** Sample in `[0, 1)` for jitter; injectable so tests can be deterministic. */\n random?: () => number\n /** Observe each scheduled retry  Ethe hook for logging and metrics. */\n onRetry?: (attempt: RetryAttempt) => void\n /** Maximum wait while closing an unsuccessful attempt. Defaults to 30s. */\n teardownTimeoutMs?: number\n}\n\n/** Resolve a delay that honours a provider-requested `retry-after` when sane. */\nfunction delayFor(\n policy: ResolvedRetryPolicy,\n failure: ModelFailure,\n attempt: number,\n random: () => number,\n): number | 'give-up' {\n const requested = failure.providerRetryAfterMs\n if (requested !== undefined && Number.isFinite(requested) && requested > 0) {\n if (requested <= policy.maxDelayMs) return requested\n // The provider asked for longer than this policy is willing to wait. Under a\n // bounded policy that is a refusal: sleeping less than asked would just earn\n // another rate-limit response. An `always` policy has nowhere to give up to,\n // so it falls back to local backoff and keeps trying.\n return policy.mode === 'always' ? backoffDelayMs(policy, attempt, random) : 'give-up'\n }\n return backoffDelayMs(policy, attempt, random)\n}\n\n/** Sleep, resolving early and reporting false if the signal aborts first. */\nfunction cancellableDelay(delayMs: number, signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted === true) return Promise.resolve(false)\n return new Promise((resolve) => {\n const onAbort = (): void => {\n clearTimeout(timer)\n resolve(false)\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve(true)\n }, delayMs)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\nclass RetryingAdapter extends ModelAdapter {\n private readonly inner: ModelAdapter\n private readonly options: WithRetryOptions\n\n // Explicit fields rather than constructor parameter properties: Node's\n // strip-only TypeScript mode rejects those, and this package is meant to run\n // under `node file.ts` without a build step.\n constructor(inner: ModelAdapter, options: WithRetryOptions) {\n super()\n this.inner = inner\n this.options = options\n }\n\n override providerInfo(provider: string): ReturnType<ModelAdapter['providerInfo']> {\n return this.inner.providerInfo(provider)\n }\n\n override providerRetryPolicy(provider: string): ResolvedRetryPolicy | undefined {\n return this.inner.providerRetryPolicy(provider)\n }\n\n override listModels(provider: string): Promise<readonly ModelInfo[]> {\n return this.inner.listModels(provider)\n }\n\n override resolveModel(\n provider: string,\n model: string,\n signal?: AbortSignal,\n ): Promise<ResolvedModelInfo> {\n return this.inner.resolveModel(provider, model, signal)\n }\n\n override async prepareCall(\n provider: string,\n model: string,\n signal?: AbortSignal,\n context?: ModelInvocationContext,\n ): Promise<PreparedAdapterCall> {\n const prepared = await this.inner.prepareCall(provider, model, signal, context)\n const policy = this.policyFor(provider)\n return {\n model: prepared.model,\n // Retries reuse the SAME prepared generation. Re-preparing mid-retry could\n // pair a fresh endpoint with capabilities resolved against the old one,\n // which is exactly what the prepare/dispatch binding exists to prevent.\n stream: (options, invocation = context) => this.retryStream(options, request => prepared.stream(request, invocation), policy, invocation),\n }\n }\n\n stream(options: GenerateOptions, context?: ModelInvocationContext): AsyncIterable<StreamChunk> {\n return this.retryStream(\n options,\n request => this.inner.stream(request, context),\n this.policyFor(options.provider),\n context,\n )\n }\n\n private policyFor(provider: string): ResolvedRetryPolicy {\n if (this.options.policy !== undefined) {\n return resolveRetryPolicy(this.options.policy, `withRetry(\"${provider}\").policy`)\n }\n return this.inner.providerRetryPolicy(provider)\n ?? resolveRetryPolicy(undefined, `withRetry(\"${provider}\").policy`)\n }\n\n private async * retryStream(\n options: GenerateOptions,\n dispatch: (request: GenerateOptions) => AsyncIterable<StreamChunk>,\n policy: ResolvedRetryPolicy,\n context?: ModelInvocationContext,\n ): AsyncGenerator<StreamChunk> {\n if (policy.mode === 'always' && options.signal === undefined) {\n yield {\n type: 'finish',\n reason: {\n kind: 'error',\n failure: {\n message: 'an always retry policy requires an AbortSignal or deadline',\n code: MODEL_ERROR_CODES.INVALID_REQUEST,\n },\n },\n }\n return\n }\n const random = this.options.random ?? Math.random\n let retries = 0\n\n while (true) {\n const attempt = yield* this.runAttempt(options, dispatch)\n if (attempt.kind === 'forward') {\n yield* attempt.chunks\n return\n }\n\n const failure = attempt.failure\n // An abort is the caller's decision, never a transient fault.\n if (options.signal?.aborted === true || failure.code === MODEL_ERROR_CODES.ABORTED) {\n yield { type: 'finish', reason: { kind: 'aborted', failure } }\n return\n }\n\n const eligible = policy.mode === 'always'\n || (policy.retryableCodes.includes(failure.code) && retries < policy.maxRetries)\n if (!eligible) {\n yield { type: 'finish', reason: { kind: 'error', failure } }\n return\n }\n\n const delayMs = delayFor(policy, failure, retries + 1, random)\n if (delayMs === 'give-up') {\n yield { type: 'finish', reason: { kind: 'error', failure } }\n return\n }\n\n retries += 1\n context?.recordProviderRetry?.({\n nextAttemptNumber: retries + 1,\n delayMs,\n failureCode: failure.code,\n })\n try {\n this.options.onRetry?.({\n provider: options.provider,\n attempt: retries,\n maxRetries: policy.mode === 'normal' ? policy.maxRetries : undefined,\n failure,\n delayMs,\n })\n } catch {\n // Metrics/logging observers do not own request availability.\n }\n if (!await cancellableDelay(delayMs, options.signal)) {\n yield {\n type: 'finish',\n reason: {\n kind: 'aborted',\n failure: {\n message: 'model call aborted while waiting to retry',\n code: MODEL_ERROR_CODES.ABORTED,\n },\n },\n }\n return\n }\n }\n }\n\n /**\n * Run one attempt, forwarding only provisional usage before committing content.\n *\n * Buffering is what makes retry safe: until the attempt either produces its\n * first non-progress chunk or fails, no content has been committed. Once that\n * chunk exists the attempt is no longer retryable, so it switches to\n * `forward` and streams the rest through untouched.\n */\n private async *runAttempt(\n options: GenerateOptions,\n dispatch: (request: GenerateOptions) => AsyncIterable<StreamChunk>,\n ): AsyncGenerator<StreamChunk,\n | { kind: 'forward'; chunks: AsyncIterable<StreamChunk> }\n | { kind: 'retryable'; failure: ModelFailure }\n > {\n let iterator: AsyncIterator<StreamChunk>\n try {\n iterator = dispatch(options)[Symbol.asyncIterator]()\n } catch (error: unknown) {\n return { kind: 'retryable', failure: normalizeModelFailure(error) }\n }\n\n let first: IteratorResult<StreamChunk>\n try {\n first = await iterator.next()\n // Provisional accounting is not response content: preserve pre-content\n // retries while allowing the caller to observe usage as it arrives.\n while (!first.done && first.value.type === 'usage-progress') {\n let resumed = false\n try {\n yield first.value\n resumed = true\n } finally {\n if (!resumed) await closeIterator(iterator, this.options.teardownTimeoutMs ?? 30_000)\n }\n first = await iterator.next()\n }\n } catch (error: unknown) {\n await closeIterator(iterator, this.options.teardownTimeoutMs ?? 30_000)\n return { kind: 'retryable', failure: normalizeModelFailure(error) }\n }\n\n // A stream that ends with no chunks at all told us nothing; treat it as the\n // degenerate empty response rather than a silently successful turn.\n if (first.done === true) {\n return {\n kind: 'retryable',\n failure: {\n message: 'the adapter produced no chunks',\n code: MODEL_ERROR_CODES.UNKNOWN,\n },\n }\n }\n\n // An adapter behind the registry's funnel reports failure as a terminal\n // error finish rather than a throw. As the FIRST chunk, that is still a\n // clean nothing-emitted failure and remains retryable.\n const chunk = first.value\n if (chunk.type === 'finish' && chunk.reason.kind === 'error') {\n await closeIterator(iterator, this.options.teardownTimeoutMs ?? 30_000)\n return { kind: 'retryable', failure: chunk.reason.failure }\n }\n\n return {\n kind: 'forward',\n chunks: resume(chunk, iterator, this.options.teardownTimeoutMs ?? 30_000),\n }\n }\n}\n\nfunction positiveFinite(value: number, name: string): number {\n if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be a positive finite number`)\n return value\n}\n\n/** Re-attach an already-read first chunk to the front of its iterator. */\nasync function* resume(\n first: StreamChunk,\n iterator: AsyncIterator<StreamChunk>,\n teardownTimeoutMs: number,\n): AsyncGenerator<StreamChunk> {\n let exhausted = false\n try {\n yield first\n while (true) {\n const next = await iterator.next()\n if (next.done === true) {\n exhausted = true\n return\n }\n yield next.value\n }\n } finally {\n if (!exhausted) await closeIterator(iterator, teardownTimeoutMs)\n }\n}\n\nasync function closeIterator(iterator: AsyncIterator<StreamChunk>, timeoutMs: number): Promise<void> {\n const closing = iterator.return?.()\n if (closing === undefined) return\n const settled = await waitForSettlement(\n Promise.resolve(closing),\n positiveFinite(timeoutMs, 'withRetry teardownTimeoutMs'),\n )\n if (!settled) {\n throw new ModelError(\n `retry attempt teardown exceeded ${timeoutMs}ms`,\n MODEL_ERROR_CODES.TEARDOWN_TIMEOUT,\n )\n }\n}\n\n/**\n * Wrap an adapter so eligible transient failures are retried with bounded\n * exponential backoff and jitter.\n *\n * Only failures that occur BEFORE the first chunk reaches the consumer are\n * retried; see the module note for why.\n * @param adapter - the adapter to wrap; its metadata methods are delegated unchanged.\n * @param options - policy, jitter source, and retry observer.\n * @returns a new adapter with retry behaviour; the original is unmodified.\n */\nexport function withRetry(adapter: ModelAdapter, options: WithRetryOptions = {}): ModelAdapter {\n return new RetryingAdapter(adapter, options)\n}\n"],"mappings":"gVA+DA,SAAS,EACP,EACA,EACA,EACA,EACoB,CACpB,IAAM,EAAY,EAAQ,qBAS1B,OARI,IAAc,IAAA,IAAa,OAAO,SAAS,CAAS,GAAK,EAAY,EACnE,GAAa,EAAO,WAAmB,EAKpC,EAAO,OAAS,SAAW,EAAe,EAAQ,EAAS,CAAM,EAAI,UAEvE,EAAe,EAAQ,EAAS,CAAM,CAC/C,CAGA,SAAS,EAAiB,EAAiB,EAAwC,CAEjF,OADI,GAAQ,UAAY,GAAa,QAAQ,QAAQ,EAAK,EACnD,IAAI,QAAS,GAAY,CAC9B,IAAM,MAAsB,CAC1B,aAAa,CAAK,EAClB,EAAQ,EAAK,CACf,EACM,EAAQ,eAAiB,CAC7B,GAAQ,oBAAoB,QAAS,CAAO,EAC5C,EAAQ,EAAI,CACd,EAAG,CAAO,EACV,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,CAC3D,CAAC,CACH,CAEA,IAAM,EAAN,cAA8B,CAAa,CACzC,MACA,QAKA,YAAY,EAAqB,EAA2B,CAC1D,MAAM,EACN,KAAK,MAAQ,EACb,KAAK,QAAU,CACjB,CAEA,aAAsB,EAA4D,CAChF,OAAO,KAAK,MAAM,aAAa,CAAQ,CACzC,CAEA,oBAA6B,EAAmD,CAC9E,OAAO,KAAK,MAAM,oBAAoB,CAAQ,CAChD,CAEA,WAAoB,EAAiD,CACnE,OAAO,KAAK,MAAM,WAAW,CAAQ,CACvC,CAEA,aACE,EACA,EACA,EAC4B,CAC5B,OAAO,KAAK,MAAM,aAAa,EAAU,EAAO,CAAM,CACxD,CAEA,MAAe,YACb,EACA,EACA,EACA,EAC8B,CAC9B,IAAM,EAAW,MAAM,KAAK,MAAM,YAAY,EAAU,EAAO,EAAQ,CAAO,EACxE,EAAS,KAAK,UAAU,CAAQ,EACtC,MAAO,CACL,MAAO,EAAS,MAIhB,QAAS,EAAS,EAAa,IAAY,KAAK,YAAY,EAAS,GAAW,EAAS,OAAO,EAAS,CAAU,EAAG,EAAQ,CAAU,CAC1I,CACF,CAEA,OAAO,EAA0B,EAA8D,CAC7F,OAAO,KAAK,YACV,EACA,GAAW,KAAK,MAAM,OAAO,EAAS,CAAO,EAC7C,KAAK,UAAU,EAAQ,QAAQ,EAC/B,CACF,CACF,CAEA,UAAkB,EAAuC,CAIvD,OAHI,KAAK,QAAQ,SAAW,IAAA,GAGrB,KAAK,MAAM,oBAAoB,CAAQ,GACzC,EAAmB,IAAA,GAAW,cAAc,EAAS,UAAU,EAH3D,EAAmB,KAAK,QAAQ,OAAQ,cAAc,EAAS,UAAU,CAIpF,CAEA,MAAgB,YACd,EACA,EACA,EACA,EAC6B,CAC7B,GAAI,EAAO,OAAS,UAAY,EAAQ,SAAW,IAAA,GAAW,CAC5D,KAAM,CACJ,KAAM,SACN,OAAQ,CACN,KAAM,QACN,QAAS,CACP,QAAS,6DACT,KAAM,EAAkB,eAC1B,CACF,CACF,EACA,MACF,CACA,IAAM,EAAS,KAAK,QAAQ,QAAU,KAAK,OACvC,EAAU,EAEd,OAAa,CACX,IAAM,EAAU,MAAO,KAAK,WAAW,EAAS,CAAQ,EACxD,GAAI,EAAQ,OAAS,UAAW,CAC9B,MAAO,EAAQ,OACf,MACF,CAEA,IAAM,EAAU,EAAQ,QAExB,GAAI,EAAQ,QAAQ,UAAY,IAAQ,EAAQ,OAAS,EAAkB,QAAS,CAClF,KAAM,CAAE,KAAM,SAAU,OAAQ,CAAE,KAAM,UAAW,SAAQ,CAAE,EAC7D,MACF,CAIA,GAAI,EAFa,EAAO,OAAS,UAC3B,EAAO,eAAe,SAAS,EAAQ,IAAI,GAAK,EAAU,EAAO,YACxD,CACb,KAAM,CAAE,KAAM,SAAU,OAAQ,CAAE,KAAM,QAAS,SAAQ,CAAE,EAC3D,MACF,CAEA,IAAM,EAAU,EAAS,EAAQ,EAAS,EAAU,EAAG,CAAM,EAC7D,GAAI,IAAY,UAAW,CACzB,KAAM,CAAE,KAAM,SAAU,OAAQ,CAAE,KAAM,QAAS,SAAQ,CAAE,EAC3D,MACF,CAEA,GAAW,EACX,GAAS,sBAAsB,CAC7B,kBAAmB,EAAU,EAC7B,UACA,YAAa,EAAQ,IACvB,CAAC,EACD,GAAI,CACF,KAAK,QAAQ,UAAU,CACrB,SAAU,EAAQ,SAClB,QAAS,EACT,WAAY,EAAO,OAAS,SAAW,EAAO,WAAa,IAAA,GAC3D,UACA,SACF,CAAC,CACH,MAAQ,CAER,CACA,GAAI,CAAC,MAAM,EAAiB,EAAS,EAAQ,MAAM,EAAG,CACpD,KAAM,CACJ,KAAM,SACN,OAAQ,CACN,KAAM,UACN,QAAS,CACP,QAAS,4CACT,KAAM,EAAkB,OAC1B,CACF,CACF,EACA,MACF,CACF,CACF,CAUA,MAAe,WACb,EACA,EAIA,CACA,IAAI,EACJ,GAAI,CACF,EAAW,EAAS,CAAO,CAAC,CAAC,OAAO,cAAc,CAAC,CACrD,OAAS,EAAgB,CACvB,MAAO,CAAE,KAAM,YAAa,QAAS,EAAsB,CAAK,CAAE,CACpE,CAEA,IAAI,EACJ,GAAI,CAIF,IAHA,EAAQ,MAAM,EAAS,KAAK,EAGrB,CAAC,EAAM,MAAQ,EAAM,MAAM,OAAS,kBAAkB,CAC3D,IAAI,EAAU,GACd,GAAI,CACF,MAAM,EAAM,MACZ,EAAU,EACZ,QAAU,CACH,GAAS,MAAM,EAAc,EAAU,KAAK,QAAQ,mBAAqB,GAAM,CACtF,CACA,EAAQ,MAAM,EAAS,KAAK,CAC9B,CACF,OAAS,EAAgB,CAEvB,OADA,MAAM,EAAc,EAAU,KAAK,QAAQ,mBAAqB,GAAM,EAC/D,CAAE,KAAM,YAAa,QAAS,EAAsB,CAAK,CAAE,CACpE,CAIA,GAAI,EAAM,OAAS,GACjB,MAAO,CACL,KAAM,YACN,QAAS,CACP,QAAS,iCACT,KAAM,EAAkB,OAC1B,CACF,EAMF,IAAM,EAAQ,EAAM,MAMpB,OALI,EAAM,OAAS,UAAY,EAAM,OAAO,OAAS,SACnD,MAAM,EAAc,EAAU,KAAK,QAAQ,mBAAqB,GAAM,EAC/D,CAAE,KAAM,YAAa,QAAS,EAAM,OAAO,OAAQ,GAGrD,CACL,KAAM,UACN,OAAQ,EAAO,EAAO,EAAU,KAAK,QAAQ,mBAAqB,GAAM,CAC1E,CACF,CACF,EAEA,SAAS,EAAe,EAAe,EAAsB,CAC3D,GAAI,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,EAAG,MAAU,WAAW,GAAG,EAAK,kCAAkC,EAC1G,OAAO,CACT,CAGA,eAAgB,EACd,EACA,EACA,EAC6B,CAC7B,IAAI,EAAY,GAChB,GAAI,CAEF,IADA,MAAM,IACO,CACX,IAAM,EAAO,MAAM,EAAS,KAAK,EACjC,GAAI,EAAK,OAAS,GAAM,CACtB,EAAY,GACZ,MACF,CACA,MAAM,EAAK,KACb,CACF,QAAU,CACH,GAAW,MAAM,EAAc,EAAU,CAAiB,CACjE,CACF,CAEA,eAAe,EAAc,EAAsC,EAAkC,CACnG,IAAM,EAAU,EAAS,SAAS,EAC9B,OAAY,IAAA,IAKZ,CAAC,MAJiB,EACpB,QAAQ,QAAQ,CAAO,EACvB,EAAe,EAAW,6BAA6B,CACzD,EAEE,MAAM,IAAI,EACR,mCAAmC,EAAU,IAC7C,EAAkB,gBACpB,CAEJ,CAYA,SAAgB,EAAU,EAAuB,EAA4B,CAAC,EAAiB,CAC7F,OAAO,IAAI,EAAgB,EAAS,CAAO,CAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"assembler.d.ts","names":[],"sources":["../../src/stream/assembler.ts"],"mappings":";;;;;;;;;;;;;;cAuCa;UACH;;UAEA;UACA;UACA;UACA;;;;;EAMR,KAAK,OAAO;UAuDJ;UAUA;;UAyBA;;;;;;;;;UAgBA;;;;;;EA2BR,UAAU;;;MAMN;;;;EAUJ;IAAgB;IAAe,OAAO,QAAQ;MAAgB;;IAAiB;;;;;;;;;;EAqB/E,qBAAqB;;MAajB,SAAS;;MAKT,UAAU;;;;;MAQV,eAAe;;;;;;EASnB,QAAQ,QAAQ,gBAAgB"}
1
+ {"version":3,"file":"assembler.d.ts","names":[],"sources":["../../src/stream/assembler.ts"],"mappings":";;;;;;;;;;;;;;cAuCa;UACH;;UAEA;UACA;UACA;UACA;;;;;EAMR,KAAK,OAAO;UAyDJ;UAUA;;UAyBA;;;;;;;;;UAgBA;;;;;;EA2BR,UAAU;;;MAMN;;;;EAUJ;IAAgB;IAAe,OAAO,QAAQ;MAAgB;;IAAiB;;;;;;;;;;EAqB/E,qBAAqB;;MAajB,SAAS;;MAKT,UAAU;;;;;MAQV,eAAe;;;;;;EASnB,QAAQ,QAAQ,gBAAgB"}
@@ -1,2 +1,2 @@
1
- import{ToolCallId as e}from"../primitives/brand.js";import{assertNever as t}from"../primitives/never.js";import{createMessage as n}from"../message/message.js";var r=class{partials=new Map;order=[];_usage;_finish;_replayState;push(e){switch(e.type){case`block-start`:this.partials.has(e.index)||(this.order.push(e.index),this.partials.set(e.index,{blockType:e.blockType,text:``,toolCallArguments:``}));return;case`text-delta`:case`reasoning-delta`:{let t=this.ensure(e.index,e.type===`text-delta`?`text`:`reasoning`);if(t.block!==void 0)return;t.text+=e.text,e.type===`text-delta`&&e.phase!==void 0&&(t.textPhase=e.phase);return}case`tool-call-delta`:{let t=this.ensure(e.index,`tool-call`);if(t.block!==void 0)return;t.toolCallId=e.id,e.name!==void 0&&e.name.length>0&&(t.toolCallName=e.name),t.toolCallArguments+=e.argumentsDelta;return}case`image-delta`:return;case`block-end`:{let t=this.ensure(e.index,e.block.type);if(t.block!==void 0)return;t.block=e.block;return}case`usage`:this._usage=e.usage;return;case`finish`:this._finish=e.reason,this._replayState=e.replayState;return;default:return t(e,`BlockAssembler.push`)}}ensure(e,t){let n=this.partials.get(e);return n===void 0&&(n={blockType:t,text:``,toolCallArguments:``},this.partials.set(e,n),this.order.push(e)),n}assemble(t,n){if(t.block!==void 0)return t.block;switch(t.blockType){case`text`:return{type:`text`,text:t.text,...t.textPhase===void 0?{}:{phase:t.textPhase}};case`reasoning`:return{type:`reasoning`,text:t.text};case`tool-call`:return{type:`tool-call`,id:t.toolCallId??e(`call-${n}`),name:t.toolCallName??``,arguments:t.toolCallArguments};default:throw Error(`cannot assemble incomplete block of type "${t.blockType}"`)}}mustGet(e){let t=this.partials.get(e);if(t===void 0)throw Error(`BlockAssembler invariant violated: no partial for index ${e}`);return t}assembled(){let e=this.order.map(e=>this.assemble(this.mustGet(e),e)),t=this.finish.kind===`max-tokens`?e.map(e=>e.type!==`tool-call`):void 0,n=t===void 0?e:e.filter((e,n)=>t[n]===!0),r=this._replayState;return r?.blocks===void 0?{blocks:n,replay:r}:r.blocks.length===e.length?{blocks:n,replay:t===void 0||n.length===e.length?r:{response:r.response,blocks:r.blocks.filter((e,n)=>t[n]===!0)}}:{blocks:n,replay:void 0}}blocks(){return this.assembled().blocks}get hasToolCalls(){return this.order.some(e=>{let t=this.mustGet(e);return(t.block?.type??t.blockType)===`tool-call`})}textBlocks(){let e=this.order.findLastIndex(e=>{let t=this.mustGet(e);return(t.block?.type??t.blockType)===`native-tool-call`});return this.order.flatMap((t,n)=>{let r=this.mustGet(t);if((r.block?.type??r.blockType)!==`text`)return[];let i=this.assemble(r,t);return i.type===`text`?[{index:t,block:i,beforeNativeCall:n<e}]:[]})}interruptedBlocks(){return this.order.map(e=>{let t=this.mustGet(e),n=t.block?.type??t.blockType;if(n===`text`||n===`reasoning`)return this.assemble(t,e)}).filter(e=>(e?.type===`text`||e?.type===`reasoning`)&&e.text.trim()!==``)}get usage(){return this._usage}get finish(){return this._finish??{kind:`stop`}}get replayState(){return this.assembled().replay}message(e){return n({role:`assistant`,content:this.blocks(),source:e})}};export{r as BlockAssembler};
1
+ import{ToolCallId as e}from"../primitives/brand.js";import{assertNever as t}from"../primitives/never.js";import{createMessage as n}from"../message/message.js";var r=class{partials=new Map;order=[];_usage;_finish;_replayState;push(e){switch(e.type){case`block-start`:this.partials.has(e.index)||(this.order.push(e.index),this.partials.set(e.index,{blockType:e.blockType,text:``,toolCallArguments:``}));return;case`text-delta`:case`reasoning-delta`:{let t=this.ensure(e.index,e.type===`text-delta`?`text`:`reasoning`);if(t.block!==void 0)return;t.text+=e.text,e.type===`text-delta`&&e.phase!==void 0&&(t.textPhase=e.phase);return}case`tool-call-delta`:{let t=this.ensure(e.index,`tool-call`);if(t.block!==void 0)return;t.toolCallId=e.id,e.name!==void 0&&e.name.length>0&&(t.toolCallName=e.name),t.toolCallArguments+=e.argumentsDelta;return}case`image-delta`:return;case`block-end`:{let t=this.ensure(e.index,e.block.type);if(t.block!==void 0)return;t.block=e.block;return}case`usage`:this._usage=e.usage;return;case`usage-progress`:return;case`finish`:this._finish=e.reason,this._replayState=e.replayState;return;default:return t(e,`BlockAssembler.push`)}}ensure(e,t){let n=this.partials.get(e);return n===void 0&&(n={blockType:t,text:``,toolCallArguments:``},this.partials.set(e,n),this.order.push(e)),n}assemble(t,n){if(t.block!==void 0)return t.block;switch(t.blockType){case`text`:return{type:`text`,text:t.text,...t.textPhase===void 0?{}:{phase:t.textPhase}};case`reasoning`:return{type:`reasoning`,text:t.text};case`tool-call`:return{type:`tool-call`,id:t.toolCallId??e(`call-${n}`),name:t.toolCallName??``,arguments:t.toolCallArguments};default:throw Error(`cannot assemble incomplete block of type "${t.blockType}"`)}}mustGet(e){let t=this.partials.get(e);if(t===void 0)throw Error(`BlockAssembler invariant violated: no partial for index ${e}`);return t}assembled(){let e=this.order.map(e=>this.assemble(this.mustGet(e),e)),t=this.finish.kind===`max-tokens`?e.map(e=>e.type!==`tool-call`):void 0,n=t===void 0?e:e.filter((e,n)=>t[n]===!0),r=this._replayState;return r?.blocks===void 0?{blocks:n,replay:r}:r.blocks.length===e.length?{blocks:n,replay:t===void 0||n.length===e.length?r:{response:r.response,blocks:r.blocks.filter((e,n)=>t[n]===!0)}}:{blocks:n,replay:void 0}}blocks(){return this.assembled().blocks}get hasToolCalls(){return this.order.some(e=>{let t=this.mustGet(e);return(t.block?.type??t.blockType)===`tool-call`})}textBlocks(){let e=this.order.findLastIndex(e=>{let t=this.mustGet(e);return(t.block?.type??t.blockType)===`native-tool-call`});return this.order.flatMap((t,n)=>{let r=this.mustGet(t);if((r.block?.type??r.blockType)!==`text`)return[];let i=this.assemble(r,t);return i.type===`text`?[{index:t,block:i,beforeNativeCall:n<e}]:[]})}interruptedBlocks(){return this.order.map(e=>{let t=this.mustGet(e),n=t.block?.type??t.blockType;if(n===`text`||n===`reasoning`)return this.assemble(t,e)}).filter(e=>(e?.type===`text`||e?.type===`reasoning`)&&e.text.trim()!==``)}get usage(){return this._usage}get finish(){return this._finish??{kind:`stop`}}get replayState(){return this.assembled().replay}message(e){return n({role:`assistant`,content:this.blocks(),source:e})}};export{r as BlockAssembler};
2
2
  //# sourceMappingURL=assembler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assembler.js","names":[],"sources":["../../src/stream/assembler.ts"],"sourcesContent":["/**\n * The single canonical chunk-to-message assembly algorithm.\n *\n * Every consumer that needs a finished message from a stream uses this, so that\n * \"what the user saw streaming\" and \"what got stored in history\" cannot drift\n * apart. Feed it every chunk, then read {@link BlockAssembler.blocks},\n * {@link BlockAssembler.message}, `usage`, and `finish` once the stream ends  Eor\n * {@link BlockAssembler.interruptedBlocks} when cancellation cut it short.\n *\n * @module ai-agent-sdk/core/stream/assembler\n */\n\nimport type { AssistantTextPhase, ContentBlock } from '../message/content.ts'\nimport { createMessage, type Message, type MessageSource } from '../message/message.ts'\nimport { ToolCallId } from '../primitives/brand.ts'\nimport { assertNever } from '../primitives/never.ts'\nimport type { FinishReason, ReplayEnvelope, StreamChunk, TokenUsage } from './chunk.ts'\n\ninterface PartialBlock {\n blockType: string\n text: string\n textPhase?: AssistantTextPhase\n toolCallId?: ToolCallId\n toolCallName?: string\n toolCallArguments: string\n /** Set by `block-end`  Eauthoritative, and freezes this partial against further deltas. */\n block?: ContentBlock\n}\n\n/**\n * Incrementally assembles {@link StreamChunk}s into {@link ContentBlock}s and a\n * final assistant {@link Message}.\n *\n * Tolerant by design: it accepts delta-only protocols that never send\n * `block-start`, and it IGNORES deltas that arrive for an index already closed by\n * `block-end`. That last rule is a containment boundary  Ea misbehaving adapter\n * cannot grow memory without bound or corrupt a block a consumer has already\n * been told is final.\n */\nexport class BlockAssembler {\n private partials = new Map<number, PartialBlock>()\n /** Block indexes in FIRST-SEEN order; the stream's own ordering, not sorted. */\n private order: number[] = []\n private _usage: TokenUsage | undefined\n private _finish: FinishReason | undefined\n private _replayState: ReplayEnvelope | undefined\n\n /**\n * Feed one chunk into the assembly state.\n * @param chunk - the next chunk, in stream order.\n */\n push(chunk: StreamChunk): void {\n switch (chunk.type) {\n case 'block-start': {\n if (!this.partials.has(chunk.index)) {\n this.order.push(chunk.index)\n this.partials.set(chunk.index, {\n blockType: chunk.blockType,\n text: '',\n toolCallArguments: '',\n })\n }\n return\n }\n case 'text-delta':\n case 'reasoning-delta': {\n const partial = this.ensure(chunk.index, chunk.type === 'text-delta' ? 'text' : 'reasoning')\n if (partial.block !== undefined) return // closed by block-end; ignore stragglers\n partial.text += chunk.text\n if (chunk.type === 'text-delta' && chunk.phase !== undefined) partial.textPhase = chunk.phase\n return\n }\n case 'tool-call-delta': {\n const partial = this.ensure(chunk.index, 'tool-call')\n if (partial.block !== undefined) return // closed by block-end; ignore stragglers\n partial.toolCallId = chunk.id\n if (chunk.name !== undefined && chunk.name.length > 0) partial.toolCallName = chunk.name\n partial.toolCallArguments += chunk.argumentsDelta\n return\n }\n case 'image-delta':\n // Progressive images are presentation-only. The authoritative final\n // image arrives inside the native-tool-call `block-end`.\n return\n case 'block-end': {\n const partial = this.ensure(chunk.index, chunk.block.type)\n // First close wins. Ignoring re-close stragglers keeps the streamed\n // output and the final assembled block in agreement.\n if (partial.block !== undefined) return\n partial.block = chunk.block\n return\n }\n case 'usage': {\n this._usage = chunk.usage\n return\n }\n case 'finish': {\n this._finish = chunk.reason\n this._replayState = chunk.replayState\n return\n }\n default:\n return assertNever(chunk, 'BlockAssembler.push')\n }\n }\n\n private ensure(index: number, blockType: string): PartialBlock {\n let partial = this.partials.get(index)\n if (partial === undefined) {\n partial = { blockType, text: '', toolCallArguments: '' }\n this.partials.set(index, partial)\n this.order.push(index)\n }\n return partial\n }\n\n private assemble(partial: PartialBlock, index: number): ContentBlock {\n if (partial.block !== undefined) return partial.block\n switch (partial.blockType) {\n case 'text': return {\n type: 'text',\n text: partial.text,\n ...partial.textPhase === undefined ? {} : { phase: partial.textPhase },\n }\n case 'reasoning': return { type: 'reasoning', text: partial.text }\n case 'tool-call': return {\n type: 'tool-call',\n // A synthesized id keeps the result correlatable even from a provider\n // that only sent the id on a chunk we never received.\n id: partial.toolCallId ?? ToolCallId(`call-${index}`),\n name: partial.toolCallName ?? '',\n arguments: partial.toolCallArguments,\n }\n default:\n // An extension block type that never received its authoritative\n // `block-end`: only the adapter that invented it knows how to build one.\n throw new Error(`cannot assemble incomplete block of type \"${partial.blockType}\"`)\n }\n }\n\n /** Invariant accessor: every index in `order` has a partial. */\n private mustGet(index: number): PartialBlock {\n const partial = this.partials.get(index)\n if (partial === undefined) {\n throw new Error(`BlockAssembler invariant violated: no partial for index ${index}`)\n }\n return partial\n }\n\n /**\n * The ONE keep/drop decision over all seen blocks, so emitted blocks and\n * replay metadata cannot disagree about what survived.\n *\n * Max-token truncation drops tool calls: a call cut off mid-arguments has\n * incomplete JSON, and executing it would act on arguments the model never\n * finished choosing.\n */\n private assembled(): { blocks: ContentBlock[]; replay: ReplayEnvelope | undefined } {\n const all = this.order.map(index => this.assemble(this.mustGet(index), index))\n const kept = this.finish.kind === 'max-tokens'\n ? all.map(block => block.type !== 'tool-call')\n : undefined\n const blocks = kept === undefined ? all : all.filter((_, position) => kept[position] === true)\n const envelope = this._replayState\n if (envelope?.blocks === undefined) return { blocks, replay: envelope }\n // A per-block envelope that does not line up with the blocks it describes is\n // discarded whole: a misaligned mapping is worse than no mapping.\n if (envelope.blocks.length !== all.length) return { blocks, replay: undefined }\n return {\n blocks,\n replay: kept === undefined || blocks.length === all.length\n ? envelope\n : {\n response: envelope.response,\n blocks: envelope.blocks.filter((_, position) => kept[position] === true),\n },\n }\n }\n\n /**\n * Assemble every block seen so far, in stream order.\n * @returns one block per seen index, minus tool calls dropped by max-token\n * truncation. An open block assembles from its accumulated deltas.\n */\n blocks(): ContentBlock[] {\n return this.assembled().blocks\n }\n\n /** Whether the canonical stream contains host tool calls, including calls\n * dropped from blocks() on truncation. No incomplete arguments are parsed. */\n get hasToolCalls(): boolean {\n return this.order.some(index => {\n const partial = this.mustGet(index)\n return (partial.block?.type ?? partial.blockType) === 'tool-call'\n })\n }\n\n /** Canonical text blocks with their original stream indexes, in first-seen\n * order. Shares first-close-wins semantics with blocks(), including ignored\n * straggler deltas and authoritative block-end replacements. */\n textBlocks(): { index: number; block: Extract<ContentBlock, { type: 'text' }>; beforeNativeCall: boolean }[] {\n const lastNative = this.order.findLastIndex(index => {\n const partial = this.mustGet(index)\n return (partial.block?.type ?? partial.blockType) === 'native-tool-call'\n })\n return this.order.flatMap((index, position) => {\n const partial = this.mustGet(index)\n if ((partial.block?.type ?? partial.blockType) !== 'text') return []\n const block = this.assemble(partial, index)\n return block.type === 'text' ? [{ index, block, beforeNativeCall: position < lastNative }] : []\n })\n }\n\n /**\n * Assemble the prefix an INTERRUPTED stream can safely finalize: closed and\n * open text/reasoning blocks carrying non-whitespace content.\n *\n * Tool calls are omitted because interruption precedes dispatch, so keeping one\n * would oblige the caller to fabricate a result for a call that never ran.\n * @returns the kept blocks; empty when nothing usable streamed first.\n */\n interruptedBlocks(): ContentBlock[] {\n return this.order\n .map((index) => {\n const partial = this.mustGet(index)\n const type = partial.block?.type ?? partial.blockType\n if (type !== 'text' && type !== 'reasoning') return undefined\n return this.assemble(partial, index)\n })\n .filter((block): block is ContentBlock =>\n (block?.type === 'text' || block?.type === 'reasoning') && block.text.trim() !== '')\n }\n\n /** Usage from the `usage` chunk; undefined until one arrives. */\n get usage(): TokenUsage | undefined {\n return this._usage\n }\n\n /** Finish reason; `{ kind: 'stop' }` when the stream ended without one. */\n get finish(): FinishReason {\n return this._finish ?? { kind: 'stop' }\n }\n\n /**\n * Replay metadata, with per-block entries pruned in step with {@link blocks}.\n * Undefined when the envelope did not align with the emitted blocks.\n */\n get replayState(): ReplayEnvelope | undefined {\n return this.assembled().replay\n }\n\n /**\n * The assembled assistant message.\n * @param source - producer attribution for the message.\n * @returns a frozen assistant-role message over {@link blocks}.\n */\n message(source: MessageSource): Message {\n return createMessage({ role: 'assistant', content: this.blocks(), source })\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"assembler.js","names":[],"sources":["../../src/stream/assembler.ts"],"sourcesContent":["/**\n * The single canonical chunk-to-message assembly algorithm.\n *\n * Every consumer that needs a finished message from a stream uses this, so that\n * \"what the user saw streaming\" and \"what got stored in history\" cannot drift\n * apart. Feed it every chunk, then read {@link BlockAssembler.blocks},\n * {@link BlockAssembler.message}, `usage`, and `finish` once the stream ends  Eor\n * {@link BlockAssembler.interruptedBlocks} when cancellation cut it short.\n *\n * @module ai-agent-sdk/core/stream/assembler\n */\n\nimport type { AssistantTextPhase, ContentBlock } from '../message/content.ts'\nimport { createMessage, type Message, type MessageSource } from '../message/message.ts'\nimport { ToolCallId } from '../primitives/brand.ts'\nimport { assertNever } from '../primitives/never.ts'\nimport type { FinishReason, ReplayEnvelope, StreamChunk, TokenUsage } from './chunk.ts'\n\ninterface PartialBlock {\n blockType: string\n text: string\n textPhase?: AssistantTextPhase\n toolCallId?: ToolCallId\n toolCallName?: string\n toolCallArguments: string\n /** Set by `block-end`  Eauthoritative, and freezes this partial against further deltas. */\n block?: ContentBlock\n}\n\n/**\n * Incrementally assembles {@link StreamChunk}s into {@link ContentBlock}s and a\n * final assistant {@link Message}.\n *\n * Tolerant by design: it accepts delta-only protocols that never send\n * `block-start`, and it IGNORES deltas that arrive for an index already closed by\n * `block-end`. That last rule is a containment boundary  Ea misbehaving adapter\n * cannot grow memory without bound or corrupt a block a consumer has already\n * been told is final.\n */\nexport class BlockAssembler {\n private partials = new Map<number, PartialBlock>()\n /** Block indexes in FIRST-SEEN order; the stream's own ordering, not sorted. */\n private order: number[] = []\n private _usage: TokenUsage | undefined\n private _finish: FinishReason | undefined\n private _replayState: ReplayEnvelope | undefined\n\n /**\n * Feed one chunk into the assembly state.\n * @param chunk - the next chunk, in stream order.\n */\n push(chunk: StreamChunk): void {\n switch (chunk.type) {\n case 'block-start': {\n if (!this.partials.has(chunk.index)) {\n this.order.push(chunk.index)\n this.partials.set(chunk.index, {\n blockType: chunk.blockType,\n text: '',\n toolCallArguments: '',\n })\n }\n return\n }\n case 'text-delta':\n case 'reasoning-delta': {\n const partial = this.ensure(chunk.index, chunk.type === 'text-delta' ? 'text' : 'reasoning')\n if (partial.block !== undefined) return // closed by block-end; ignore stragglers\n partial.text += chunk.text\n if (chunk.type === 'text-delta' && chunk.phase !== undefined) partial.textPhase = chunk.phase\n return\n }\n case 'tool-call-delta': {\n const partial = this.ensure(chunk.index, 'tool-call')\n if (partial.block !== undefined) return // closed by block-end; ignore stragglers\n partial.toolCallId = chunk.id\n if (chunk.name !== undefined && chunk.name.length > 0) partial.toolCallName = chunk.name\n partial.toolCallArguments += chunk.argumentsDelta\n return\n }\n case 'image-delta':\n // Progressive images are presentation-only. The authoritative final\n // image arrives inside the native-tool-call `block-end`.\n return\n case 'block-end': {\n const partial = this.ensure(chunk.index, chunk.block.type)\n // First close wins. Ignoring re-close stragglers keeps the streamed\n // output and the final assembled block in agreement.\n if (partial.block !== undefined) return\n partial.block = chunk.block\n return\n }\n case 'usage': {\n this._usage = chunk.usage\n return\n }\n case 'usage-progress':\n return\n case 'finish': {\n this._finish = chunk.reason\n this._replayState = chunk.replayState\n return\n }\n default:\n return assertNever(chunk, 'BlockAssembler.push')\n }\n }\n\n private ensure(index: number, blockType: string): PartialBlock {\n let partial = this.partials.get(index)\n if (partial === undefined) {\n partial = { blockType, text: '', toolCallArguments: '' }\n this.partials.set(index, partial)\n this.order.push(index)\n }\n return partial\n }\n\n private assemble(partial: PartialBlock, index: number): ContentBlock {\n if (partial.block !== undefined) return partial.block\n switch (partial.blockType) {\n case 'text': return {\n type: 'text',\n text: partial.text,\n ...partial.textPhase === undefined ? {} : { phase: partial.textPhase },\n }\n case 'reasoning': return { type: 'reasoning', text: partial.text }\n case 'tool-call': return {\n type: 'tool-call',\n // A synthesized id keeps the result correlatable even from a provider\n // that only sent the id on a chunk we never received.\n id: partial.toolCallId ?? ToolCallId(`call-${index}`),\n name: partial.toolCallName ?? '',\n arguments: partial.toolCallArguments,\n }\n default:\n // An extension block type that never received its authoritative\n // `block-end`: only the adapter that invented it knows how to build one.\n throw new Error(`cannot assemble incomplete block of type \"${partial.blockType}\"`)\n }\n }\n\n /** Invariant accessor: every index in `order` has a partial. */\n private mustGet(index: number): PartialBlock {\n const partial = this.partials.get(index)\n if (partial === undefined) {\n throw new Error(`BlockAssembler invariant violated: no partial for index ${index}`)\n }\n return partial\n }\n\n /**\n * The ONE keep/drop decision over all seen blocks, so emitted blocks and\n * replay metadata cannot disagree about what survived.\n *\n * Max-token truncation drops tool calls: a call cut off mid-arguments has\n * incomplete JSON, and executing it would act on arguments the model never\n * finished choosing.\n */\n private assembled(): { blocks: ContentBlock[]; replay: ReplayEnvelope | undefined } {\n const all = this.order.map(index => this.assemble(this.mustGet(index), index))\n const kept = this.finish.kind === 'max-tokens'\n ? all.map(block => block.type !== 'tool-call')\n : undefined\n const blocks = kept === undefined ? all : all.filter((_, position) => kept[position] === true)\n const envelope = this._replayState\n if (envelope?.blocks === undefined) return { blocks, replay: envelope }\n // A per-block envelope that does not line up with the blocks it describes is\n // discarded whole: a misaligned mapping is worse than no mapping.\n if (envelope.blocks.length !== all.length) return { blocks, replay: undefined }\n return {\n blocks,\n replay: kept === undefined || blocks.length === all.length\n ? envelope\n : {\n response: envelope.response,\n blocks: envelope.blocks.filter((_, position) => kept[position] === true),\n },\n }\n }\n\n /**\n * Assemble every block seen so far, in stream order.\n * @returns one block per seen index, minus tool calls dropped by max-token\n * truncation. An open block assembles from its accumulated deltas.\n */\n blocks(): ContentBlock[] {\n return this.assembled().blocks\n }\n\n /** Whether the canonical stream contains host tool calls, including calls\n * dropped from blocks() on truncation. No incomplete arguments are parsed. */\n get hasToolCalls(): boolean {\n return this.order.some(index => {\n const partial = this.mustGet(index)\n return (partial.block?.type ?? partial.blockType) === 'tool-call'\n })\n }\n\n /** Canonical text blocks with their original stream indexes, in first-seen\n * order. Shares first-close-wins semantics with blocks(), including ignored\n * straggler deltas and authoritative block-end replacements. */\n textBlocks(): { index: number; block: Extract<ContentBlock, { type: 'text' }>; beforeNativeCall: boolean }[] {\n const lastNative = this.order.findLastIndex(index => {\n const partial = this.mustGet(index)\n return (partial.block?.type ?? partial.blockType) === 'native-tool-call'\n })\n return this.order.flatMap((index, position) => {\n const partial = this.mustGet(index)\n if ((partial.block?.type ?? partial.blockType) !== 'text') return []\n const block = this.assemble(partial, index)\n return block.type === 'text' ? [{ index, block, beforeNativeCall: position < lastNative }] : []\n })\n }\n\n /**\n * Assemble the prefix an INTERRUPTED stream can safely finalize: closed and\n * open text/reasoning blocks carrying non-whitespace content.\n *\n * Tool calls are omitted because interruption precedes dispatch, so keeping one\n * would oblige the caller to fabricate a result for a call that never ran.\n * @returns the kept blocks; empty when nothing usable streamed first.\n */\n interruptedBlocks(): ContentBlock[] {\n return this.order\n .map((index) => {\n const partial = this.mustGet(index)\n const type = partial.block?.type ?? partial.blockType\n if (type !== 'text' && type !== 'reasoning') return undefined\n return this.assemble(partial, index)\n })\n .filter((block): block is ContentBlock =>\n (block?.type === 'text' || block?.type === 'reasoning') && block.text.trim() !== '')\n }\n\n /** Usage from the `usage` chunk; undefined until one arrives. */\n get usage(): TokenUsage | undefined {\n return this._usage\n }\n\n /** Finish reason; `{ kind: 'stop' }` when the stream ended without one. */\n get finish(): FinishReason {\n return this._finish ?? { kind: 'stop' }\n }\n\n /**\n * Replay metadata, with per-block entries pruned in step with {@link blocks}.\n * Undefined when the envelope did not align with the emitted blocks.\n */\n get replayState(): ReplayEnvelope | undefined {\n return this.assembled().replay\n }\n\n /**\n * The assembled assistant message.\n * @param source - producer attribution for the message.\n * @returns a frozen assistant-role message over {@link blocks}.\n */\n message(source: MessageSource): Message {\n return createMessage({ role: 'assistant', content: this.blocks(), source })\n }\n}\n"],"mappings":"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"}
@@ -124,6 +124,12 @@ type StreamChunk = {
124
124
  } | {
125
125
  type: 'usage';
126
126
  usage: TokenUsage;
127
+ } |
128
+ /** Cumulative snapshot for this attempt, never an additive or final report. */
129
+ {
130
+ type: 'usage-progress';
131
+ usage: Partial<TokenUsage>;
132
+ attemptId?: string;
127
133
  } | {
128
134
  type: 'finish';
129
135
  reason: FinishReason;
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.d.ts","names":[],"sources":["../../src/stream/chunk.ts"],"mappings":";;;;;;;;;;;;UAkBiB;EACf;IAAU;;EACV;IAAgB;;EAChB;IAAgB;;EAChB;IAAa;IAAiB,SAAS;;EACvC;IAAW;IAAe,SAAS;;;;KAIzB,eAAe,sBAAsB;;;;;;;;;;UAWhC;;EAEf;EACA;;;;;;;EAOA;EACA;EACA;EACA;;;;;;;;;;UAWe;;EAEf;;;;;;;;;;EAUA;;;;;;;;;;;;;;;;KAiBU;EACN;EAAqB;EAAe,WAAW;;EAC/C;EAAoB;EAAe;EAAc,QAAQ;;EACzD;EAAyB;EAAe;;EAE1C;EACA;EACA;EACA;EACA,WAAW;EACX;;EAEE;EAAyB;EAAe,IAAI;EAAY;EAAe;;EACvE;EAAmB;EAAe,OAAO;;EACzC;EAAe,OAAO;;EAExB;EACA,QAAQ;;EAER,cAAc"}
1
+ {"version":3,"file":"chunk.d.ts","names":[],"sources":["../../src/stream/chunk.ts"],"mappings":";;;;;;;;;;;;UAkBiB;EACf;IAAU;;EACV;IAAgB;;EAChB;IAAgB;;EAChB;IAAa;IAAiB,SAAS;;EACvC;IAAW;IAAe,SAAS;;;;KAIzB,eAAe,sBAAsB;;;;;;;;;;UAWhC;;EAEf;EACA;;;;;;;EAOA;EACA;EACA;EACA;;;;;;;;;;UAWe;;EAEf;;;;;;;;;;EAUA;;;;;;;;;;;;;;;;KAiBU;EACN;EAAqB;EAAe,WAAW;;EAC/C;EAAoB;EAAe;EAAc,QAAQ;;EACzD;EAAyB;EAAe;;EAE1C;EACA;EACA;EACA;EACA,WAAW;EACX;;EAEE;EAAyB;EAAe,IAAI;EAAY;EAAe;;EACvE;EAAmB;EAAe,OAAO;;EACzC;EAAe,OAAO;;;;EAEtB;EAAwB,OAAO,QAAQ;EAAa;;EAEtD;EACA,QAAQ;;EAER,cAAc"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "alvin0 - chaulamdinhai",
5
5
  "email": "chaulamdinhai@gmail.com"
6
6
  },
7
- "version": "0.1.0",
7
+ "version": "0.1.2",
8
8
  "description": "Universal contracts and runtime primitives for ai-agent-sdk",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -56,6 +56,11 @@
56
56
  "import": "./dist/tools.js",
57
57
  "default": "./dist/tools.js"
58
58
  },
59
+ "./embedding": {
60
+ "types": "./dist/embedding.d.ts",
61
+ "import": "./dist/embedding.js",
62
+ "default": "./dist/embedding.js"
63
+ },
59
64
  "./observability": {
60
65
  "types": "./dist/observability.d.ts",
61
66
  "import": "./dist/observability.js",