@open-mercato/ai-assistant 0.6.2-develop.3406.1.2b403f40da → 0.6.2-develop.3446.1.bd060c6017

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 (54) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +8 -1
  3. package/build.mjs +1 -0
  4. package/dist/frontend/components/AiChatButton.js +1 -1
  5. package/dist/frontend/components/AiChatButton.js.map +2 -2
  6. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js +16 -5
  7. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js.map +2 -2
  8. package/dist/modules/ai_assistant/ai-tools/meta-pack.js +58 -1
  9. package/dist/modules/ai_assistant/ai-tools/meta-pack.js.map +2 -2
  10. package/dist/modules/ai_assistant/api/ai/agents/route.js +2 -1
  11. package/dist/modules/ai_assistant/api/ai/agents/route.js.map +2 -2
  12. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
  13. package/dist/modules/ai_assistant/i18n/de.json +7 -1
  14. package/dist/modules/ai_assistant/i18n/en.json +7 -1
  15. package/dist/modules/ai_assistant/i18n/es.json +7 -1
  16. package/dist/modules/ai_assistant/i18n/pl.json +7 -1
  17. package/dist/modules/ai_assistant/lib/agent-registry.js +26 -6
  18. package/dist/modules/ai_assistant/lib/agent-registry.js.map +2 -2
  19. package/dist/modules/ai_assistant/lib/agent-runtime.js +21 -8
  20. package/dist/modules/ai_assistant/lib/agent-runtime.js.map +3 -3
  21. package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
  22. package/dist/modules/ai_assistant/lib/pending-action-types.js.map +2 -2
  23. package/dist/modules/ai_assistant/lib/prepare-mutation.js +16 -6
  24. package/dist/modules/ai_assistant/lib/prepare-mutation.js.map +2 -2
  25. package/dist/modules/ai_assistant/lib/task-plan-labels.js +95 -0
  26. package/dist/modules/ai_assistant/lib/task-plan-labels.js.map +7 -0
  27. package/dist/modules/ai_assistant/lib/task-plan-stream.js +349 -0
  28. package/dist/modules/ai_assistant/lib/task-plan-stream.js.map +7 -0
  29. package/dist/modules/ai_assistant/lib/tool-test-fixtures.js +3 -0
  30. package/dist/modules/ai_assistant/lib/tool-test-fixtures.js.map +2 -2
  31. package/package.json +6 -6
  32. package/src/frontend/components/AiChatButton.tsx +1 -1
  33. package/src/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.ts +20 -8
  34. package/src/modules/ai_assistant/ai-tools/__tests__/meta-pack.test.ts +60 -4
  35. package/src/modules/ai_assistant/ai-tools/meta-pack.ts +79 -2
  36. package/src/modules/ai_assistant/api/ai/agents/route.ts +2 -1
  37. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +1 -0
  38. package/src/modules/ai_assistant/i18n/de.json +7 -1
  39. package/src/modules/ai_assistant/i18n/en.json +7 -1
  40. package/src/modules/ai_assistant/i18n/es.json +7 -1
  41. package/src/modules/ai_assistant/i18n/pl.json +7 -1
  42. package/src/modules/ai_assistant/lib/__tests__/agent-registry.test.ts +60 -0
  43. package/src/modules/ai_assistant/lib/__tests__/ai-agent-definition.test.ts +4 -0
  44. package/src/modules/ai_assistant/lib/__tests__/prepare-mutation.test.ts +43 -0
  45. package/src/modules/ai_assistant/lib/__tests__/task-plan-stream.test.ts +375 -0
  46. package/src/modules/ai_assistant/lib/agent-registry.ts +36 -5
  47. package/src/modules/ai_assistant/lib/agent-runtime.ts +26 -8
  48. package/src/modules/ai_assistant/lib/ai-agent-definition.ts +14 -0
  49. package/src/modules/ai_assistant/lib/pending-action-types.ts +4 -1
  50. package/src/modules/ai_assistant/lib/prepare-mutation.ts +17 -5
  51. package/src/modules/ai_assistant/lib/task-plan-labels.ts +112 -0
  52. package/src/modules/ai_assistant/lib/task-plan-stream.ts +463 -0
  53. package/src/modules/ai_assistant/lib/tool-test-fixtures.ts +3 -0
  54. package/src/modules/ai_assistant/lib/types.ts +16 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/agent-runtime.ts"],
4
- "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport { createContainer } from 'awilix'\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type {\n GenerateObjectResult,\n GenerateTextResult,\n LanguageModel,\n PrepareStepFunction,\n PrepareStepResult,\n StreamObjectResult,\n StreamTextResult,\n ToolSet,\n UIMessage,\n ToolLoopAgentSettings,\n} from 'ai'\nimport {\n convertToModelMessages,\n generateObject,\n hasToolCall,\n stepCountIs,\n streamObject,\n streamText,\n Experimental_Agent as ToolLoopAgent,\n} from 'ai'\nimport type { StopCondition } from 'ai'\nimport type { ZodTypeAny } from 'zod'\nimport { createModelFactory, resolveAllowRuntimeOverride } from './model-factory'\nimport type {\n AiAgentDefinition,\n AiAgentLoopConfig,\n AiAgentPageContextInput,\n AiAgentStructuredOutput,\n LoopStepRecord,\n LoopTrace,\n} from './ai-agent-definition'\nimport type {\n AiChatRequestContext,\n AiResolvedAttachmentPart,\n} from './attachment-bridge-types'\nimport {\n resolveAiAgentTools,\n AgentPolicyError,\n desanitizeToolNameForDisplay,\n sanitizeToolNameForModel,\n} from './agent-tools'\nimport { resolveEffectiveMutationPolicy } from './agent-policy'\nimport { toolRegistry } from './tool-registry'\nimport {\n attachmentPartsToUiFileParts,\n resolveAttachmentPartsForAgent,\n summarizeAttachmentPartsForPrompt,\n} from './attachment-parts'\nimport { AiAgentPromptOverrideRepository } from '../data/repositories/AiAgentPromptOverrideRepository'\nimport { AiAgentMutationPolicyOverrideRepository } from '../data/repositories/AiAgentMutationPolicyOverrideRepository'\nimport { AiAgentRuntimeOverrideRepository } from '../data/repositories/AiAgentRuntimeOverrideRepository'\nimport { AiTenantModelAllowlistRepository } from '../data/repositories/AiTenantModelAllowlistRepository'\nimport type { TenantAllowlistSnapshot } from './model-allowlist'\nimport { composeSystemPromptWithOverride } from './prompt-override-merge'\nimport { isKnownMutationPolicy } from './agent-policy'\nimport type { AiAgentMutationPolicy } from './ai-agent-definition'\nimport { recordTokenUsage } from './token-usage-recorder'\n\n// Ensure built-in LLM providers are registered. Side-effect import; identical to\n// what `./ai-sdk.ts` consumers already rely on.\nimport './llm-bootstrap'\n\nexport interface AgentRequestPageContext {\n pageId?: string | null\n entityType?: string | null\n recordId?: string | null\n [key: string]: unknown\n}\n\nexport interface RunAiAgentTextInput {\n agentId: string\n messages: UIMessage[]\n attachmentIds?: string[]\n pageContext?: AgentRequestPageContext\n debug?: boolean\n /**\n * Phase 1 exposes the caller-supplied auth context directly on the helper\n * input. Phase 4 may wrap this behind a thinner public API once a global\n * request-context resolver exists. Helpers running inside the HTTP\n * dispatcher receive the same `AiChatRequestContext` used by `checkAgentPolicy`.\n */\n authContext: AiChatRequestContext\n /**\n * Optional per-call model id override that wins over `agent.defaultModel`.\n * The production model-factory extraction lives in Step 5.1; this Step\n * accepts a literal model id string so the Phase 1 runtime already honors\n * `agent.defaultModel` without inventing a new indirection layer.\n */\n modelOverride?: string\n /**\n * Optional request-time provider override. When non-empty, wins for the\n * provider axis at the same priority as `modelOverride` for the model axis.\n * A value that does not match any registered provider id is silently ignored.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n providerOverride?: string\n /**\n * Optional per-call base URL override. Wins over every other source in the\n * baseURL resolution chain. Intended for programmatic callers only \u2014 the\n * HTTP query-param baseUrl and the AI_RUNTIME_BASEURL_ALLOWLIST arrive in\n * Phase 4a and MUST NOT be exposed here.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n baseUrlOverride?: string\n /**\n * Per-request HTTP dispatcher override (query params `?provider=`, `?model=`,\n * `?baseUrl=`). Validated by the dispatcher route before being forwarded\n * here. Wins over tenantOverride and all lower-priority sources when\n * `agent.allowRuntimeOverride !== false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n requestOverride?: {\n providerId?: string | null\n modelId?: string | null\n baseURL?: string | null\n }\n /**\n * Optional DI container used by `resolvePageContext` callbacks. When omitted\n * and the agent declares a `resolvePageContext`, hydration is skipped with a\n * warning (callbacks that need database/DI cannot run safely without one).\n */\n container?: AwilixContainer\n /**\n * Stable per-conversation id that ties every turn of a chat together for\n * token-usage correlation and pending-action idempotency (Phase 6.2).\n *\n * When omitted the server generates one and echoes it on the SSE `done`\n * event so the client can persist it for subsequent turns. Callers that\n * supply a value from a previous turn will have their usage rows grouped\n * under the same `session_id` in the token-usage tables.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId?: string | null\n /**\n * @deprecated Use `sessionId` instead. This alias was the original name of\n * the same field; both names are accepted for one minor release to let\n * callers migrate without a hard cut. `sessionId` takes precedence when both\n * are provided.\n */\n conversationId?: string | null\n /**\n * Optional per-call loop config override. Fields set here win over the\n * agent's `loop` declaration and the tenant DB override. The override is\n * gated by `agent.loop?.allowRuntimeOverride ?? true` \u2014 agents that pin\n * a loop policy for correctness reasons can set `allowRuntimeOverride: false`\n * to reject any per-call override with `AgentPolicyError` code\n * `loop_runtime_override_disabled`.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: Partial<AiAgentLoopConfig>\n /**\n * Optional escape-hatch callback that receives the fully prepared AI SDK\n * options bag and must return the SDK result (either from `streamText` or\n * `generateText`). When supplied, the wrapper still enforces every policy\n * guardrail (features, tool allowlist, mutation approval, model factory,\n * prompt composition, attachment bridging) and then hands control to this\n * callback instead of calling `streamText` directly.\n *\n * The callback MUST pass `stopWhen` and `prepareStep` through to the AI SDK\n * call \u2014 dropping either one disables the agent's loop policy or mutation\n * approval guards respectively. See `agents.mdx` \u00A7\"Option B\" for the full\n * contract and what you lose when fields are omitted.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n generateText?: (\n options: PreparedAiSdkOptions,\n ) => Promise<GenerateTextResult<ToolSet, never> | StreamTextResult<ToolSet, never>>\n /**\n * When `true`, the runtime appends a `loop-finish` SSE event to the\n * response stream after the AI SDK stream closes. The event payload is the\n * serialized `LoopTrace` for the turn (agent id, turn id, per-step records,\n * stop reason, total duration, total usage).\n *\n * Consumed by `useAiChat` to populate `lastLoopTrace` and by the playground\n * debug panel to render the per-turn trace via `LoopTracePanel`.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n emitLoopTrace?: boolean\n}\n\n/**\n * The wrapper default loop config used when neither the caller, tenant, agent,\n * nor legacy maxSteps supplies any config. Chat mode defaults to `{ maxSteps: 10 }`\n * to ensure tool-using agents can loop; object mode defaults to an empty config\n * (single structured-output call, no explicit step cap).\n */\nconst WRAPPER_DEFAULT_LOOP_CHAT: AiAgentLoopConfig = { maxSteps: 10 }\nconst WRAPPER_DEFAULT_LOOP_OBJECT: AiAgentLoopConfig = {}\n\n/**\n * Named loop-budget preset values for `?loopBudget=<preset>` query param.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentLoopBudgetPreset = 'tight' | 'default' | 'loose'\n\n/**\n * Maps a `loopBudget` preset name to the corresponding `AiAgentLoopBudget`\n * triple. `'default'` returns `undefined` (no override \u2014 agent default applies).\n * Values are pinned per spec \u00A7\"loopBudget preset values (Phase 4)\".\n */\nexport function resolveLoopBudgetPreset(\n preset: AiAgentLoopBudgetPreset,\n): Partial<AiAgentLoopConfig> | undefined {\n switch (preset) {\n case 'tight':\n return { maxSteps: 3, budget: { maxToolCalls: 3, maxWallClockMs: 10_000, maxTokens: 50_000 } }\n case 'loose':\n return { maxSteps: 20, budget: { maxToolCalls: 20, maxWallClockMs: 120_000, maxTokens: 500_000 } }\n case 'default':\n return undefined\n }\n}\n\nconst SSE_ENCODER = new TextEncoder()\n\n/**\n * Wraps a streaming `Response` to append a typed `loop-finish` SSE event\n * after the AI SDK stream closes. The event carries the serialized `LoopTrace`\n * for the turn so the `useAiChat` hook can render it in the debug panel.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nfunction appendLoopFinishToStream(\n baseResponse: Response,\n finalizeLoopTrace: () => LoopTrace,\n): Response {\n const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>()\n const writer = writable.getWriter()\n\n async function pump(): Promise<void> {\n if (!baseResponse.body) {\n await writer.close()\n return\n }\n const reader = baseResponse.body.getReader()\n try {\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n await writer.write(value)\n }\n const trace = finalizeLoopTrace()\n const eventLine = `data: ${JSON.stringify({ type: 'loop-finish', trace })}\\n\\n`\n await writer.write(SSE_ENCODER.encode(eventLine))\n } catch {\n // Pass through \u2014 the reader abort is surfaced by the upstream consumer.\n } finally {\n reader.releaseLock()\n await writer.close().catch(() => undefined)\n }\n }\n\n void pump()\n return new Response(readable, {\n status: baseResponse.status,\n headers: baseResponse.headers,\n })\n}\n\n/**\n * The fully prepared options bag handed to the `runAiAgentText({ generateText })`\n * escape-hatch callback. Callers receive a complete set of wrapper-composed\n * loop primitives so they can forward them to `streamText` / `generateText`.\n *\n * SECURITY CONTRACT: callers MUST forward `prepareStep` to the AI SDK call.\n * Dropping it removes the per-step tool-allowlist re-check and the mutation-\n * approval wrapping. Dropping `stopWhen` removes the agent's loop policy and\n * the R3 step-count fallback.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface PreparedAiSdkOptions {\n model: LanguageModel\n tools: ToolSet\n system: string\n messages: Awaited<ReturnType<typeof convertToModelMessages>>\n /** Alias kept for SDK compat \u2014 equals `stopWhen` array's effective maxSteps. */\n maxSteps: number\n /**\n * Wrapper-composed stop conditions (R3 mitigated: always ends with\n * `stepCountIs(maxSteps)`). MUST be forwarded to the SDK call.\n */\n stopWhen: StopCondition<ToolSet>[]\n /**\n * Wrapper-owned `PrepareStepFunction` that re-asserts the tool allowlist and\n * mutation-approval wrapping per step. SECURITY-CRITICAL: callers MUST\n * forward this to the SDK call or they lose mutation-approval guarantees.\n */\n prepareStep: PrepareStepFunction<ToolSet>\n /** Wrapper trace aggregator chained with the agent's `onStepFinish` hook. */\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n onStepStart: AiAgentLoopConfig['onStepStart']\n onToolCallStart: AiAgentLoopConfig['onToolCallStart']\n onToolCallFinish: AiAgentLoopConfig['onToolCallFinish']\n experimental_repairToolCall: AiAgentLoopConfig['repairToolCall']\n activeTools: AiAgentLoopConfig['activeTools']\n toolChoice: AiAgentLoopConfig['toolChoice']\n /**\n * Pre-wired to the per-turn `AbortController` used by budget enforcement\n * (Phase 3). Forward to the SDK call so budget limits can abort in-flight\n * requests. May be `undefined` when budget enforcement is not yet active\n * (Phases 0\u20132); the SDK treats `undefined` the same as no signal.\n */\n abortSignal: AbortSignal | undefined\n /**\n * Finalizes the per-turn `LoopTrace` and returns it. Callers that use the\n * `generateText` escape-hatch SHOULD call this after the SDK call resolves so\n * the trace is available for logging or SSE emission.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n finalizeLoopTrace: () => LoopTrace\n /**\n * Present only when `agent.executionEngine === 'tool-loop-agent'`. Callers\n * can invoke `agent.generate(...)` / `agent.stream(...)` directly with their\n * own `providerOptions` as an escape-hatch \u2014 the `ToolLoopAgent` instance is\n * already wired with the wrapper-owned `prepareStep`, `stopWhen`, and\n * `onStepFinish` at construction.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n toolLoopAgent?: ToolLoopAgent<never, ToolSet>\n}\n\n/**\n * The fully prepared options bag handed to the `runAiAgentObject({ generateObject })`\n * escape-hatch callback. Object-mode subset \u2014 chat-only fields are absent.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface PreparedAiSdkObjectOptions {\n model: LanguageModel\n system: string\n messages: Awaited<ReturnType<typeof convertToModelMessages>>\n schemaName: string\n schema: unknown\n maxSteps: number | undefined\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n onStepStart: AiAgentLoopConfig['onStepStart']\n abortSignal: AbortSignal | undefined\n}\n\n/**\n * Guards the per-call loop override against agents that have opted out of\n * runtime overrides by setting `loop.allowRuntimeOverride: false`.\n *\n * Throws `AgentPolicyError` with code `loop_runtime_override_disabled` when\n * the agent has opted out and a caller override was supplied.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nfunction assertLoopRuntimeOverrideAllowed(\n agent: AiAgentDefinition,\n callerLoop: Partial<AiAgentLoopConfig> | undefined,\n): void {\n if (!callerLoop) return\n const allowed = agent.loop?.allowRuntimeOverride ?? true\n if (!allowed) {\n throw new AgentPolicyError(\n 'loop_runtime_override_disabled',\n `Agent \"${agent.id}\" has disabled per-call loop overrides (loop.allowRuntimeOverride: false). Remove the loop override to proceed.`,\n )\n }\n}\n\n/**\n * Reads `<MODULE>_AI_LOOP_*` env shorthands for the given module id.\n * Returns a partial `AiAgentLoopConfig` containing only the axes that are\n * explicitly set in the environment. Missing or malformed values are silently\n * ignored (fail-open \u2014 env vars are a best-effort static deployment mechanism).\n *\n * Supported variables (Phase 3 of spec\n * `2026-04-28-ai-agents-agentic-loop-controls`):\n *\n * - `<MODULE>_AI_LOOP_MAX_STEPS` \u2014 maps to `loop.maxSteps`\n * - `<MODULE>_AI_LOOP_MAX_WALL_CLOCK_MS` \u2014 maps to `loop.budget.maxWallClockMs`\n * - `<MODULE>_AI_LOOP_MAX_TOKENS` \u2014 maps to `loop.budget.maxTokens`\n */\nfunction readModuleLoopEnv(moduleId: string): Partial<AiAgentLoopConfig> {\n const prefix = moduleId.toUpperCase()\n const partial: Partial<AiAgentLoopConfig> = {}\n\n const maxStepsRaw = process.env[`${prefix}_AI_LOOP_MAX_STEPS`]\n if (maxStepsRaw) {\n const parsed = parseInt(maxStepsRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) partial.maxSteps = parsed\n }\n\n const maxWallClockRaw = process.env[`${prefix}_AI_LOOP_MAX_WALL_CLOCK_MS`]\n const maxTokensRaw = process.env[`${prefix}_AI_LOOP_MAX_TOKENS`]\n\n if (maxWallClockRaw || maxTokensRaw) {\n const budgetPartial: AiAgentLoopConfig['budget'] = {}\n if (maxWallClockRaw) {\n const parsed = parseInt(maxWallClockRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) budgetPartial.maxWallClockMs = parsed\n }\n if (maxTokensRaw) {\n const parsed = parseInt(maxTokensRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) budgetPartial.maxTokens = parsed\n }\n if (Object.keys(budgetPartial).length > 0) partial.budget = budgetPartial\n }\n\n return partial\n}\n\n/**\n * Resolves the effective loop config for a turn by walking the precedence\n * chain (highest first):\n *\n * 1. `callerLoop` \u2014 per-call `runAiAgentText({ loop })` override (Phase 1).\n * 2. Tenant override row \u2014 NOT yet implemented in DB; always `undefined` here.\n * // TODO(Phase 1782-3): hydrate loop columns from ai_agent_runtime_overrides\n * 3. `<MODULE>_AI_LOOP_*` env shorthands (Phase 3) \u2014 only MAX_STEPS,\n * MAX_WALL_CLOCK_MS, MAX_TOKENS. Lower precedence than DB override but higher\n * than the agent's code-declared defaults.\n * 4. `agent.loop` \u2014 agent's declarative loop config.\n * 5. `agent.maxSteps` (deprecated alias) \u2014 mapped to `{ maxSteps: agent.maxSteps }`.\n * 6. `wrapperDefault` \u2014 the wrapper's hardcoded fallback.\n *\n * Each source contributes only the fields it sets explicitly; fields absent at\n * a higher-priority source fall through to a lower-priority one. The merge is\n * performed left-to-right with higher-priority sources winning field-by-field.\n *\n * Throws `AgentPolicyError` code `loop_runtime_override_disabled` when the\n * agent opts out of per-call overrides and a caller loop was supplied.\n *\n * Phase 0 + Phase 1 + Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function resolveEffectiveLoopConfig(\n agent: AiAgentDefinition,\n callerLoop?: Partial<AiAgentLoopConfig> | undefined,\n wrapperDefault?: AiAgentLoopConfig,\n): AiAgentLoopConfig {\n assertLoopRuntimeOverrideAllowed(agent, callerLoop)\n\n const effectiveDefault = wrapperDefault ?? WRAPPER_DEFAULT_LOOP_CHAT\n\n // Build base from lowest-priority: wrapper default \u2192 legacy maxSteps \u2192 agent.loop\n const legacyMaxSteps: AiAgentLoopConfig | undefined =\n typeof agent.maxSteps === 'number' && agent.maxSteps > 0 && !agent.loop\n ? { maxSteps: agent.maxSteps }\n : undefined\n\n const base: AiAgentLoopConfig = {\n ...effectiveDefault,\n ...(legacyMaxSteps ?? {}),\n ...(agent.loop ?? {}),\n }\n\n // Phase 3 \u2014 env shorthands at priority 3 (above agent.loop, below DB override).\n // TODO(Phase 1782-3): hydrate loop columns from ai_agent_runtime_overrides\n // and merge tenantOverride here at priority #2 (above envOverride).\n const envOverride = readModuleLoopEnv(agent.moduleId)\n const withEnv: AiAgentLoopConfig = {\n ...base,\n ...envOverride,\n ...(envOverride.budget != null\n ? { budget: { ...(base.budget ?? {}), ...envOverride.budget } }\n : {}),\n }\n\n const withCaller: AiAgentLoopConfig = callerLoop\n ? { ...withEnv, ...callerLoop }\n : withEnv\n\n // Phase 3 \u2014 kill switch: when disabled is set to true, force maxSteps: 1 so the\n // agent executes as a single model call with no tool looping. All other loop config\n // is preserved (budget, etc.) but the step cap wins.\n if (withCaller.disabled === true) {\n return { ...withCaller, maxSteps: 1 }\n }\n\n return withCaller\n}\n\n/**\n * The reason a budget limit was hit, exposed on `LoopAbortReason` (Phase 3).\n */\nexport type LoopBudgetAbortReason =\n | 'budget-tool-calls'\n | 'budget-wall-clock'\n | 'budget-tokens'\n\n/**\n * Tracks per-turn budget usage and aborts the run when any limit is exceeded.\n *\n * Usage:\n * 1. Construct with the loop budget and the turn's `AbortController`.\n * 2. Call `wire(onStepFinish)` to get a composed `onStepFinish` that feeds\n * usage data into the enforcer on every completed step.\n * 3. The enforcer calls `abortController.abort()` with a typed\n * `LoopBudgetAbortReason` when a limit is hit.\n *\n * Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport class BudgetEnforcer {\n private toolCallsUsed = 0\n private tokensUsed = 0\n readonly turnStartMs: number\n abortReason: LoopBudgetAbortReason | null = null\n\n constructor(\n private readonly budget: AiAgentLoopConfig['budget'],\n private readonly abortController: AbortController,\n ) {\n this.turnStartMs = Date.now()\n }\n\n get hasActiveBudget(): boolean {\n const b = this.budget\n return (\n b !== undefined &&\n (b.maxToolCalls !== undefined || b.maxWallClockMs !== undefined || b.maxTokens !== undefined)\n )\n }\n\n recordStep(usage: { inputTokens?: number; outputTokens?: number; toolCalls?: number }): void {\n if (!this.budget) return\n this.toolCallsUsed += usage.toolCalls ?? 0\n this.tokensUsed += (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)\n this.checkLimits()\n }\n\n private checkLimits(): void {\n const b = this.budget\n if (!b) return\n\n if (b.maxToolCalls !== undefined && this.toolCallsUsed >= b.maxToolCalls) {\n this.abort('budget-tool-calls')\n return\n }\n\n const elapsedMs = Date.now() - this.turnStartMs\n if (b.maxWallClockMs !== undefined && elapsedMs >= b.maxWallClockMs) {\n this.abort('budget-wall-clock')\n return\n }\n\n if (b.maxTokens !== undefined && this.tokensUsed >= b.maxTokens) {\n this.abort('budget-tokens')\n }\n }\n\n private abort(reason: LoopBudgetAbortReason): void {\n if (this.abortReason !== null) return\n this.abortReason = reason\n console.info(\n `[AI Agents] Budget exceeded \u2014 aborting turn. Reason: ${reason}. ` +\n `toolCalls=${this.toolCallsUsed}, tokens=${this.tokensUsed}, ` +\n `elapsedMs=${Date.now() - this.turnStartMs}.`,\n )\n this.abortController.abort(reason)\n }\n\n wire(\n userOnStepFinish: AiAgentLoopConfig['onStepFinish'],\n ): AiAgentLoopConfig['onStepFinish'] {\n if (!this.hasActiveBudget) return userOnStepFinish\n return async (event) => {\n this.recordStep({\n inputTokens: event.usage?.inputTokens,\n outputTokens: event.usage?.outputTokens,\n toolCalls: event.toolCalls?.length,\n })\n if (userOnStepFinish) {\n try {\n await userOnStepFinish(event)\n } catch (err) {\n console.error('[AI Agents] User onStepFinish threw; ignoring:', err)\n }\n }\n }\n }\n}\n\n/**\n * Builds a wrapper-owned `onStepFinish` collector that aggregates per-step\n * usage and tool-call data into a `LoopTrace` object. The collector chains\n * the user's `onStepFinish` after it aggregates (exceptions from the user's\n * hook are caught and logged but do not abort the turn).\n *\n * Returns both the wired `onStepFinish` hook and a `finalize()` function that\n * resolves the `LoopTrace` once the turn is complete. The `budgetEnforcer`\n * is already wired into `onStepFinish` at a lower layer \u2014 this collector sits\n * above it.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function buildLoopTraceCollector(\n agentId: string,\n sessionId: string,\n turnId: string,\n userOnStepFinish: AiAgentLoopConfig['onStepFinish'],\n): {\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n finalize: (abortReason: LoopBudgetAbortReason | null) => LoopTrace\n} {\n const turnStartMs = Date.now()\n const steps: LoopStepRecord[] = []\n\n const onStepFinish: AiAgentLoopConfig['onStepFinish'] = async (event) => {\n const stepIndex = steps.length\n const toolCalls = (event.toolCalls ?? []).map((tc) => {\n const raw = tc as unknown as {\n toolName?: string\n args?: unknown\n result?: unknown\n experimental_toToolResultError?: { code?: string; message?: string }\n repairAttempted?: boolean\n startTime?: number\n endTime?: number\n }\n return {\n toolName: raw.toolName\n ? desanitizeToolNameForDisplay(raw.toolName)\n : 'unknown',\n args: raw.args ?? {},\n result: raw.result,\n error: raw.experimental_toToolResultError\n ? {\n code: String(raw.experimental_toToolResultError?.code ?? 'unknown'),\n message: String(raw.experimental_toToolResultError?.message ?? ''),\n }\n : undefined,\n repairAttempted: raw.repairAttempted === true,\n durationMs:\n typeof raw.startTime === 'number' && typeof raw.endTime === 'number'\n ? raw.endTime - raw.startTime\n : 0,\n }\n })\n\n const textDelta =\n (event as unknown as { text?: string }).text ?? ''\n\n const finishReason = (\n (event as unknown as { finishReason?: string }).finishReason ?? 'stop'\n ) as LoopStepRecord['finishReason']\n\n const modelId =\n (event as unknown as { response?: { modelId?: string } }).response?.modelId ?? 'unknown'\n\n steps.push({\n stepIndex,\n modelId,\n toolCalls,\n textDelta,\n usage: {\n inputTokens: event.usage?.inputTokens ?? 0,\n outputTokens: event.usage?.outputTokens ?? 0,\n },\n finishReason,\n })\n\n if (userOnStepFinish) {\n try {\n await userOnStepFinish(event)\n } catch (err) {\n console.error('[AI Agents] User onStepFinish in LoopTrace collector threw; ignoring:', err)\n }\n }\n }\n\n function finalize(abortReason: LoopBudgetAbortReason | null): LoopTrace {\n const totalDurationMs = Date.now() - turnStartMs\n const totalUsage = steps.reduce(\n (acc, step) => ({\n inputTokens: acc.inputTokens + step.usage.inputTokens,\n outputTokens: acc.outputTokens + step.usage.outputTokens,\n }),\n { inputTokens: 0, outputTokens: 0 },\n )\n\n let stopReason: LoopTrace['stopReason'] = 'finish-reason'\n if (abortReason === 'budget-tool-calls') stopReason = 'budget-tool-calls'\n else if (abortReason === 'budget-wall-clock') stopReason = 'budget-wall-clock'\n else if (abortReason === 'budget-tokens') stopReason = 'budget-tokens'\n\n return {\n agentId,\n sessionId,\n turnId,\n steps,\n stopReason,\n totalDurationMs,\n totalUsage,\n }\n }\n\n return { onStepFinish, finalize }\n}\n\n/**\n * Translates serializable `AiAgentLoopStopCondition` items into the Vercel AI\n * SDK `StopCondition` array ready to pass to `streamText` / `generateText`.\n *\n * The wrapper ALWAYS appends `stepCountIs(maxSteps ?? 10)` as the final item\n * in the returned array (R3 mitigation). This guarantees that a misconfigured\n * `hasToolCall` for a non-existent tool can never cause an infinite loop\n * because the SDK treats `stopWhen` arrays with OR semantics \u2014 the step-count\n * fallback will always trip eventually.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function translateStopConditions(\n loopConfig: AiAgentLoopConfig,\n mapToolName: (toolName: string) => string = (toolName) => toolName,\n): StopCondition<ToolSet>[] {\n const effectiveMaxSteps = loopConfig.maxSteps ?? 10\n const userConditions: StopCondition<ToolSet>[] = []\n\n const rawStopWhen = loopConfig.stopWhen\n if (rawStopWhen) {\n const items = Array.isArray(rawStopWhen) ? rawStopWhen : [rawStopWhen]\n for (const item of items) {\n if (item.kind === 'stepCount') {\n userConditions.push(stepCountIs(item.count))\n } else if (item.kind === 'hasToolCall') {\n userConditions.push(hasToolCall(mapToolName(item.toolName)))\n } else if (item.kind === 'custom') {\n userConditions.push(item.stop as StopCondition<ToolSet>)\n }\n }\n }\n\n // Always append the hard step-count fallback (R3 mitigation).\n return [...userConditions, stepCountIs(effectiveMaxSteps)]\n}\n\n/**\n * Security-critical merge of the wrapper-owned step override with the user's\n * `prepareStep` return value.\n *\n * Guarantees (R1 mitigation \u2014 preserving the mutation-approval contract):\n * 1. Any `tools` map returned by the user is intersected with `toolRegistry`\n * (the policy-gated, mutation-approval-wrapped map). If the user returned\n * a raw mutation handler, the merged map points at the wrapped one.\n * 2. Any `activeTools` returned by the user is intersected with\n * `agent.allowedTools`. Out-of-set names are dropped with a single\n * `loop:active_tools_filtered` warning.\n * 3. A user-returned `tools` map that contains a mutation tool pointing at the\n * raw handler (not the wrapped one) is rejected with\n * `AgentPolicyError` code `loop_violates_mutation_policy`.\n * 4. Non-policy fields (`model`, `toolChoice`, `system`, `messages`) from the\n * user override are honored as-is.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n *\n * ai-sdk 6.0.177 dropped the `tools` field from `PrepareStepResult`, so the\n * map is no longer consumed by `streamText` / `generateText`. We still inspect\n * any `tools` map that a caller passes via a type-asserted result \u2014 even\n * though the SDK ignores it \u2014 to keep the defense-in-depth guard and the\n * existing test contract (R1 mitigation #3) intact.\n */\ntype StepOverrideWithTools = NonNullable<PrepareStepResult<ToolSet>> & {\n tools?: Record<string, unknown>\n}\n\nfunction normalizeAllowedToolNameForAgent(\n toolName: string,\n agent: AiAgentDefinition,\n): string | null {\n if (agent.allowedTools.includes(toolName)) return toolName\n const dottedName = desanitizeToolNameForDisplay(toolName)\n return agent.allowedTools.includes(dottedName) ? dottedName : null\n}\n\nexport function mergeStepOverrides(\n wrapperOverride: PrepareStepResult<ToolSet>,\n userOverride: PrepareStepResult<ToolSet> | undefined | null,\n agent: AiAgentDefinition,\n wrappedToolRegistry: Record<string, unknown>,\n): PrepareStepResult<ToolSet> {\n if (!userOverride) return wrapperOverride\n\n const merged: StepOverrideWithTools = { ...(wrapperOverride as StepOverrideWithTools) }\n const userWithTools = userOverride as StepOverrideWithTools\n\n if (userOverride.model !== undefined) {\n merged.model = userOverride.model\n }\n if (userOverride.toolChoice !== undefined) {\n merged.toolChoice = userOverride.toolChoice\n }\n\n if (userOverride.activeTools !== undefined) {\n const filtered = userOverride.activeTools.flatMap((name) => {\n const normalized = normalizeAllowedToolNameForAgent(name, agent)\n const allowed = normalized !== null\n if (!allowed) {\n console.warn(\n `[AI Agents] loop:active_tools_filtered \u2014 tool \"${name}\" is not in agent \"${agent.id}\" allowedTools; dropping from activeTools.`,\n )\n }\n return normalized ? [normalized] : []\n })\n merged.activeTools = filtered\n }\n\n if (userWithTools.tools !== undefined) {\n const userTools = userWithTools.tools\n const mergedTools: Record<string, unknown> = {}\n\n for (const [toolKey, userHandler] of Object.entries(userTools)) {\n const wrappedHandler = wrappedToolRegistry[toolKey]\n if (!wrappedHandler) {\n console.warn(\n `[AI Agents] mergeStepOverrides \u2014 tool \"${toolKey}\" from user prepareStep is not in the wrapper tool registry; dropping.`,\n )\n continue\n }\n if (userHandler !== wrappedHandler) {\n const toolDef = toolRegistry.getTool(\n toolKey.replace(/__/g, '.'),\n ) as { isMutation?: boolean } | undefined\n if (toolDef?.isMutation === true) {\n throw new AgentPolicyError(\n 'loop_violates_mutation_policy',\n `User prepareStep returned a tools map with raw (unwrapped) mutation handler for \"${toolKey}\". This bypasses the mutation-approval gate and is rejected.`,\n )\n }\n }\n mergedTools[toolKey] = wrappedHandler\n }\n merged.tools = mergedTools\n }\n\n return merged\n}\n\nfunction mapPrepareStepResultForModel(\n result: PrepareStepResult<ToolSet>,\n wrappedTools: Record<string, unknown>,\n): PrepareStepResult<ToolSet> {\n if (!result?.activeTools) return result\n const activeTools = result.activeTools\n .map((toolName) => sanitizeToolNameForModel(toolName))\n .filter((toolName) => wrappedTools[toolName] !== undefined)\n return { ...result, activeTools }\n}\n\nfunction mapToolChoiceForModel(\n toolChoice: AiAgentLoopConfig['toolChoice'],\n): AiAgentLoopConfig['toolChoice'] {\n if (!toolChoice || typeof toolChoice !== 'object' || toolChoice.type !== 'tool') {\n return toolChoice\n }\n return {\n ...toolChoice,\n toolName: sanitizeToolNameForModel(toolChoice.toolName),\n } as AiAgentLoopConfig['toolChoice']\n}\n\nfunction mapActiveToolsForModel(\n activeTools: string[] | undefined,\n wrappedTools: Record<string, unknown>,\n): string[] | undefined {\n if (!activeTools) return undefined\n return activeTools\n .map((toolName) => sanitizeToolNameForModel(toolName))\n .filter((toolName) => wrappedTools[toolName] !== undefined)\n}\n\n/**\n * Builds the wrapper-owned `PrepareStepFunction` that enforces the tool\n * allowlist and mutation-approval contract on every step, then composes\n * the user's `prepareStep` on top via `mergeStepOverrides`.\n *\n * This is the SECURITY-CRITICAL function for Phase 0. The wrapper `prepareStep`\n * ensures:\n * - Tool active-set is always a subset of `effectiveLoop.activeTools ?? agent.allowedTools`.\n * - Mutation tools always point at the prepareMutation-wrapped handlers.\n * - User's `prepareStep` return value cannot smuggle raw mutation handlers.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function buildWrapperPrepareStep(\n agent: AiAgentDefinition,\n effectiveLoop: AiAgentLoopConfig,\n wrappedTools: Record<string, unknown>,\n): PrepareStepFunction<ToolSet> {\n return async (state) => {\n const wrapperOverride: PrepareStepResult<ToolSet> = {}\n\n if (effectiveLoop.activeTools && effectiveLoop.activeTools.length > 0) {\n wrapperOverride.activeTools = effectiveLoop.activeTools.flatMap((name) => {\n const normalized = normalizeAllowedToolNameForAgent(name, agent)\n const allowed = normalized !== null\n if (!allowed) {\n console.warn(\n `[AI Agents] loop:active_tools_filtered \u2014 tool \"${name}\" is not in agent \"${agent.id}\" allowedTools; dropping from activeTools.`,\n )\n }\n return normalized ? [normalized] : []\n })\n }\n\n if (effectiveLoop.prepareStep) {\n let userOverride: PrepareStepResult<ToolSet> | undefined | null\n try {\n userOverride = await effectiveLoop.prepareStep(state)\n } catch (error) {\n console.error(\n `[AI Agents] User prepareStep threw for agent \"${agent.id}\"; ignoring user override:`,\n error,\n )\n return mapPrepareStepResultForModel(wrapperOverride, wrappedTools)\n }\n return mapPrepareStepResultForModel(\n mergeStepOverrides(wrapperOverride, userOverride, agent, wrappedTools),\n wrappedTools,\n )\n }\n\n return mapPrepareStepResultForModel(wrapperOverride, wrappedTools)\n }\n}\n\n/**\n * Validates that a loop config does not set any primitives that are\n * unsupported by the object-mode SDK path (`generateObject` / `streamObject`).\n *\n * Object mode accepts ONLY: `maxSteps`, `budget`, `onStepFinish`,\n * `onStepStart`, `allowRuntimeOverride`. The remaining fields\n * (`prepareStep`, `repairToolCall`, `stopWhen`, `activeTools`,\n * `toolChoice`) are chat-only and will never reach `generateObject`.\n *\n * Throws `AgentPolicyError` code `loop_unsupported_in_object_mode` if any\n * unsupported field is set. This provides an explicit, actionable error\n * rather than a silent no-op.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function assertLoopObjectModeCompatible(loopConfig: Partial<AiAgentLoopConfig>): void {\n const unsupportedFields: string[] = []\n\n if (loopConfig.prepareStep !== undefined) unsupportedFields.push('prepareStep')\n if (loopConfig.repairToolCall !== undefined) unsupportedFields.push('repairToolCall')\n if (loopConfig.stopWhen !== undefined) unsupportedFields.push('stopWhen')\n if (loopConfig.activeTools !== undefined) unsupportedFields.push('activeTools')\n if (loopConfig.toolChoice !== undefined) unsupportedFields.push('toolChoice')\n\n if (unsupportedFields.length > 0) {\n throw new AgentPolicyError(\n 'loop_unsupported_in_object_mode',\n `Object-mode agents do not support these loop primitives: ${unsupportedFields.join(', ')}. Use runAiAgentText for agents that require these loop controls.`,\n )\n }\n}\n\ninterface ResolvedAgentModel {\n model: LanguageModel\n modelId: string\n providerId: string\n}\n\nfunction resolveAgentModel(\n agent: AiAgentDefinition,\n modelOverride: string | undefined,\n providerOverride: string | undefined,\n container: AwilixContainer | undefined,\n baseUrlOverride?: string,\n tenantOverride?: { providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null,\n requestOverride?: { providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null,\n tenantAllowlist?: TenantAllowlistSnapshot | null,\n): ResolvedAgentModel {\n const effectiveContainer = container ?? createContainer()\n const resolution = createModelFactory(effectiveContainer).resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n callerOverride: modelOverride,\n providerOverride,\n baseUrlOverride,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n tenantOverride: tenantOverride ?? undefined,\n requestOverride: requestOverride ?? undefined,\n tenantAllowlist: tenantAllowlist ?? null,\n })\n return {\n model: resolution.model as LanguageModel,\n modelId: resolution.modelId,\n providerId: resolution.providerId,\n }\n}\n\nasync function resolveTenantAllowlistSnapshot(\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<TenantAllowlistSnapshot | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiTenantModelAllowlistRepository(em)\n return await repo.getSnapshot({\n tenantId,\n organizationId: organizationId ?? null,\n })\n } catch (error) {\n console.warn(\n '[AI Agents] Tenant allowlist lookup failed; falling back to env-only enforcement.',\n error,\n )\n return null\n }\n}\n\n/**\n * Composes the effective system prompt for a run. When the agent declares a\n * `resolvePageContext` callback AND the incoming request carries both\n * `entityType` and `recordId`, the callback is invoked and its return value\n * is appended to `agent.systemPrompt`. Throwing callbacks are caught and\n * logged without failing the request \u2014 the spec allows hydration to be\n * best-effort until Step 5.2 wires a stricter contract.\n */\nexport async function composeSystemPrompt(\n agent: AiAgentDefinition,\n pageContext: AgentRequestPageContext | undefined,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<string> {\n const baseFromOverride = await resolveBaseSystemPromptWithOverride(\n agent,\n container,\n tenantId,\n organizationId,\n )\n const resolve = agent.resolvePageContext\n if (!resolve) return baseFromOverride\n const entityType = pageContext?.entityType\n const recordId = pageContext?.recordId\n if (typeof entityType !== 'string' || entityType.length === 0) return baseFromOverride\n if (typeof recordId !== 'string' || recordId.length === 0) return baseFromOverride\n if (!container) {\n console.warn(\n `[AI Agents] Agent \"${agent.id}\" declares resolvePageContext but no container was passed to runAiAgentText; skipping hydration.`,\n )\n return baseFromOverride\n }\n const hydrationInput: AiAgentPageContextInput = {\n entityType,\n recordId,\n container,\n tenantId,\n organizationId,\n }\n try {\n const hydrated = await resolve(hydrationInput)\n if (typeof hydrated === 'string' && hydrated.trim().length > 0) {\n return `${baseFromOverride}\\n\\n${hydrated}`\n }\n } catch (error) {\n console.error(\n `[AI Agents] resolvePageContext for agent \"${agent.id}\" failed; continuing without hydration:`,\n error,\n )\n }\n return baseFromOverride\n}\n\n/**\n * Fetches the latest tenant-scoped prompt override for `agent` (if any) and\n * layers it onto the built-in `systemPrompt` via the additive merge helper.\n *\n * BC + fail-open: every failure mode \u2014 missing container, missing `em`\n * registration, repository throw, missing migration \u2014 is logged at `warn`\n * and falls back to `agent.systemPrompt`. A chat turn MUST never fail on\n * override lookup (per Step 5.3 spec: \"If the repo call throws, log and\n * fall back to the built-in prompt \u2014 never fail the chat request\").\n */\nasync function resolveBaseSystemPromptWithOverride(\n agent: AiAgentDefinition,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<string> {\n const base = agent.systemPrompt\n if (!tenantId || !container) return base\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return base\n try {\n const repo = new AiAgentPromptOverrideRepository(em)\n const latest = await repo.getLatest(agent.id, {\n tenantId,\n organizationId: organizationId ?? null,\n })\n if (!latest || !latest.sections || Object.keys(latest.sections).length === 0) {\n return base\n }\n return composeSystemPromptWithOverride(base, { sections: latest.sections })\n } catch (error) {\n console.warn(\n `[AI Agents] Prompt-override lookup failed for agent \"${agent.id}\"; falling back to built-in prompt.`,\n error,\n )\n return base\n }\n}\n\n/**\n * Looks up the tenant-scoped `mutationPolicy` override for `agentId` (Step\n * 5.4). Fails SAFE: any repo error, missing container, missing `em`\n * registration, or corrupt enum value returns `null`, which causes the\n * runtime to fall back to the agent's code-declared policy. A chat turn\n * MUST never fail on override lookup.\n */\nasync function resolveMutationPolicyOverride(\n agentId: string,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<AiAgentMutationPolicy | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiAgentMutationPolicyOverrideRepository(em)\n const row = await repo.get(agentId, { tenantId, organizationId: organizationId ?? null })\n if (!row) return null\n const raw = row.mutationPolicy\n if (!isKnownMutationPolicy(raw)) {\n console.warn(\n `[AI Agents] Ignoring corrupt mutationPolicy override row for agent \"${agentId}\": \"${raw}\". Falling back to code-declared policy.`,\n )\n return null\n }\n return raw\n } catch (error) {\n console.warn(\n `[AI Agents] mutationPolicy override lookup failed for agent \"${agentId}\"; falling back to code-declared policy.`,\n error,\n )\n return null\n }\n}\n\n/**\n * Looks up the per-tenant AI runtime override (provider / model / baseURL) for\n * the given agent (Phase 4a of spec\n * `2026-04-27-ai-agents-provider-model-baseurl-overrides`).\n *\n * Mirrors the fail-open contract of {@link resolveMutationPolicyOverride}: any\n * error \u2014 missing container, missing `em`, repository throw, missing migration\n * \u2014 is logged at `warn` level and returns null so the model factory falls\n * through to lower-priority sources. A chat turn MUST never fail on override\n * lookup.\n */\nasync function resolveRuntimeModelOverride(\n agentId: string,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<{ providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const row = await repo.getDefault({\n tenantId,\n organizationId: organizationId ?? null,\n agentId,\n })\n if (!row) return null\n return {\n providerId: row.providerId ?? null,\n modelId: row.modelId ?? null,\n baseURL: row.baseUrl ?? null,\n }\n } catch (error) {\n console.warn(\n `[AI Agents] Runtime model override lookup failed for agent \"${agentId}\"; falling back to lower-priority sources.`,\n error,\n )\n return null\n }\n}\n\n/**\n * Normalizes simple `{ role, content }` chat messages into the AI SDK\n * `UIMessage` shape that `convertToModelMessages` requires. When the\n * incoming message already carries a `parts` array it is left untouched;\n * otherwise a single `TextUIPart` is synthesized from `content`.\n */\nfunction ensureUiMessageShape(messages: UIMessage[]): UIMessage[] {\n return messages.map((message, index) => {\n const raw = message as unknown as { id?: string; role?: string; content?: string; parts?: unknown[] }\n if (Array.isArray(raw.parts) && raw.parts.length > 0) {\n // Already has parts \u2014 only ensure `id` is present\n return { ...message, id: raw.id ?? `msg-${index}` } as UIMessage\n }\n const textContent = typeof raw.content === 'string' ? raw.content : ''\n return {\n id: raw.id ?? `msg-${index}`,\n role: raw.role ?? 'user',\n parts: [{ type: 'text', text: textContent }],\n } as unknown as UIMessage\n })\n}\n\n/**\n * Appends AI SDK v6 `FileUIPart` entries to the last user message in the\n * request so resolved attachment bytes / signed URLs reach the model. Pure\n * helper so chat-mode and object-mode share identical behavior \u2014 any\n * divergence here breaks the Step 3.6 parity contract.\n */\nfunction attachAttachmentsToMessages(\n messages: UIMessage[],\n parts: readonly AiResolvedAttachmentPart[],\n): UIMessage[] {\n if (parts.length === 0) return messages\n const fileParts = attachmentPartsToUiFileParts(parts)\n if (fileParts.length === 0) return messages\n const next = messages.slice()\n let lastUserIndex = -1\n for (let index = next.length - 1; index >= 0; index -= 1) {\n const candidate = next[index] as unknown as { role?: string }\n if (candidate?.role === 'user') {\n lastUserIndex = index\n break\n }\n }\n if (lastUserIndex === -1) {\n next.push({\n id: 'ai-runtime-attachments',\n role: 'user',\n parts: fileParts as unknown as UIMessage['parts'],\n } as unknown as UIMessage)\n return next\n }\n const source = next[lastUserIndex] as unknown as { parts?: unknown[] }\n const existingParts = Array.isArray(source.parts) ? source.parts : []\n next[lastUserIndex] = {\n ...(next[lastUserIndex] as object),\n parts: [...existingParts, ...fileParts],\n } as UIMessage\n return next\n}\n\nfunction appendAttachmentSummary(\n systemPrompt: string,\n parts: readonly AiResolvedAttachmentPart[],\n): string {\n const summary = summarizeAttachmentPartsForPrompt(parts)\n if (!summary) return systemPrompt\n return `${systemPrompt}\\n\\n${summary}`\n}\n\n/**\n * Builds a runtime \"MUTATION POLICY (RUNTIME)\" block describing the\n * EFFECTIVE policy for this turn \u2014 what the model should expect when it\n * calls each whitelisted mutation tool. Generated dynamically because:\n *\n * - the agent's static prompt cannot know which per-tenant override is\n * in force (`destructive-confirm-required` flips most writes to\n * run-direct) and would otherwise mislead the operator with stale\n * \"this requires approval\" copy;\n * - the per-tool `isDestructive` flag determines whether each\n * whitelisted write goes through the approval card or runs inline.\n *\n * Without this block, the model parrots its hardcoded \"always route\n * through the approval card\" prompt language and tells the user \"your\n * change is awaiting approval\" when in fact the dispatcher already\n * applied the change directly. The injected block flips the model to\n * report results accurately (\"applied\", \"pending your approval\", or\n * \"blocked because read-only\") tool-by-tool.\n */\nfunction buildRuntimeMutationPolicySection(\n agent: { id: string; mutationPolicy?: string | null; allowedTools: string[] },\n mutationPolicyOverride: string | null,\n): string | null {\n const effective = resolveEffectiveMutationPolicy(\n (agent.mutationPolicy ?? null) as never,\n (mutationPolicyOverride ?? null) as never,\n agent.id,\n )\n const lines: string[] = []\n lines.push('MUTATION POLICY (RUNTIME)')\n lines.push(`Declared agent policy: ${agent.mutationPolicy ?? 'read-only'}.`)\n if (mutationPolicyOverride && mutationPolicyOverride !== agent.mutationPolicy) {\n lines.push(`Tenant override active: ${mutationPolicyOverride}.`)\n }\n lines.push(`Effective policy: ${effective}.`)\n\n // Bucket the agent's allowlisted tools into \"gated\" / \"direct\" / \"conditional\"\n // / \"blocked\" so the model can phrase outcomes correctly per tool.\n // `conditional` covers tools whose `isDestructive` is a predicate function:\n // their gate-vs-direct decision depends on the per-call input (e.g.\n // `customers.manage_deal_comment` gates only its delete branch under\n // `destructive-confirm-required`).\n const direct: string[] = []\n const gated: string[] = []\n const conditional: string[] = []\n const blocked: string[] = []\n for (const toolName of agent.allowedTools) {\n const tool = toolRegistry.getTool(toolName) as\n | { isMutation?: boolean; isDestructive?: boolean | ((input: unknown) => boolean) }\n | undefined\n if (!tool || tool.isMutation !== true) continue\n if (effective === 'read-only') {\n blocked.push(toolName)\n continue\n }\n if (effective === 'confirm-required') {\n gated.push(toolName)\n continue\n }\n // destructive-confirm-required\n if (typeof tool.isDestructive === 'function') {\n conditional.push(toolName)\n } else if (tool.isDestructive === true) {\n gated.push(toolName)\n } else {\n direct.push(toolName)\n }\n }\n\n if (\n direct.length === 0 &&\n gated.length === 0 &&\n conditional.length === 0 &&\n blocked.length === 0\n ) {\n // Read-only agent with no mutation tools \u2014 no runtime policy block needed.\n return null\n }\n if (direct.length > 0) {\n lines.push('')\n lines.push(\n `Tools that WILL RUN DIRECTLY (no approval card, no pending action) under the effective policy: ${direct.join(', ')}.`,\n )\n lines.push(\n 'When you call any of these and the call returns successfully, the change has ALREADY BEEN APPLIED. Report it in the past tense (\"Updated \u2026\", \"Added \u2026\", \"Created \u2026\"). Do NOT tell the operator the action is \"pending your approval\" or \"awaiting confirmation\" \u2014 that would be a false statement under the current policy.',\n )\n }\n if (gated.length > 0) {\n lines.push('')\n lines.push(\n `Tools that REQUIRE APPROVAL under the effective policy: ${gated.join(', ')}.`,\n )\n lines.push(\n 'When you call any of these, the dispatcher returns an \"awaiting confirmation\" envelope and renders an inline approval card. Tell the operator the change is pending their confirmation; do NOT claim it has been applied.',\n )\n }\n if (conditional.length > 0) {\n lines.push('')\n lines.push(\n `Tools whose approval requirement DEPENDS ON THE INPUT under the effective policy: ${conditional.join(', ')}.`,\n )\n lines.push(\n 'These multi-operation tools gate ONLY the destructive branches (typically `operation: \"delete\"` or similar). Read the tool result envelope: if it carries `status: \"pending-confirmation\"` then the change is pending \u2014 tell the operator it needs their approval. If it carries direct success data, the change has ALREADY BEEN APPLIED \u2014 report it in the past tense. Never assume one branch behaves like another.',\n )\n }\n if (blocked.length > 0) {\n lines.push('')\n lines.push(\n `Tools that are BLOCKED under the effective policy (read-only): ${blocked.join(', ')}.`,\n )\n lines.push(\n 'Calls to these tools are refused before the handler runs. Do not attempt them; instead direct the operator to the matching backoffice page or to switch the tenant policy if they have permission.',\n )\n }\n lines.push('')\n lines.push(\n 'This RUNTIME policy block always wins over any conflicting \"approval card\" language earlier in the prompt \u2014 the static prompt is written for the most restrictive case but real behavior depends on the per-call policy described here.',\n )\n return lines.join('\\n')\n}\n\nfunction appendRuntimeMutationPolicy(\n systemPrompt: string,\n agent: { id: string; mutationPolicy?: string | null; allowedTools: string[] },\n mutationPolicyOverride: string | null,\n): string {\n const block = buildRuntimeMutationPolicySection(agent, mutationPolicyOverride)\n if (!block) return systemPrompt\n return `${systemPrompt}\\n\\n${block}`\n}\n\n/**\n * Server-side helper that runs an Open Mercato agent in chat mode via the\n * Vercel AI SDK and returns a streaming `Response` ready to be emitted from a\n * route handler. Shares the same policy gate and tool resolution path as the\n * HTTP dispatcher \u2014 a caller using this helper can never bypass the agent's\n * `requiredFeatures`, `allowedTools`, `executionMode`, or `mutationPolicy`.\n *\n * Attachment-to-model conversion (Step 3.7): resolved\n * {@link AiResolvedAttachmentPart}s are materialized inline as AI SDK v6\n * `FileUIPart` entries on the last user message (images/PDFs) and as a\n * structured `[ATTACHMENTS]` block appended to the system prompt (text\n * extracts + metadata-only summaries). The existing `attachmentIds`\n * pass-through into `resolveAiAgentTools` is preserved \u2014 Step 3.6 parity\n * invariant #7 still holds.\n */\nexport async function runAiAgentText(input: RunAiAgentTextInput): Promise<Response> {\n const [mutationPolicyOverride, tenantRuntimeOverride, tenantAllowlistSnapshot] = await Promise.all([\n resolveMutationPolicyOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveRuntimeModelOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveTenantAllowlistSnapshot(\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n ])\n // Phase 6.2 \u2014 resolve the effective session id. `sessionId` takes precedence\n // over the deprecated `conversationId` alias. When neither is supplied, a\n // fresh UUID is generated server-side so token-usage rows and pending-action\n // idempotency hashes are always correlated within the same session.\n const effectiveSessionId = (input.sessionId ?? input.conversationId) || randomUUID()\n\n const { agent, tools } = await resolveAiAgentTools({\n agentId: input.agentId,\n authContext: input.authContext,\n pageContext: input.pageContext,\n attachmentIds: input.attachmentIds,\n mutationPolicyOverride,\n container: input.container,\n conversationId: effectiveSessionId,\n })\n\n const resolvedAttachments = await resolveAttachmentPartsForAgent({\n agent,\n attachmentIds: input.attachmentIds,\n authContext: input.authContext,\n container: input.container,\n })\n\n const baseSystemPrompt = await composeSystemPrompt(\n agent,\n input.pageContext,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n )\n const systemPrompt = appendRuntimeMutationPolicy(\n appendAttachmentSummary(baseSystemPrompt, resolvedAttachments),\n agent,\n mutationPolicyOverride,\n )\n\n const resolvedModel = resolveAgentModel(\n agent,\n input.modelOverride,\n input.providerOverride,\n input.container,\n input.baseUrlOverride,\n tenantRuntimeOverride,\n input.requestOverride,\n tenantAllowlistSnapshot,\n )\n const { model } = resolvedModel\n const normalizedMessages = ensureUiMessageShape(input.messages)\n const hydratedMessages = attachAttachmentsToMessages(normalizedMessages, resolvedAttachments)\n const modelMessages = await convertToModelMessages(hydratedMessages)\n\n const effectiveLoop = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_CHAT)\n const stopConditions = translateStopConditions(effectiveLoop, sanitizeToolNameForModel)\n const wrapperPrepareStep = buildWrapperPrepareStep(agent, effectiveLoop, tools)\n const sdkActiveTools = mapActiveToolsForModel(effectiveLoop.activeTools, tools)\n const sdkToolChoice = mapToolChoiceForModel(effectiveLoop.toolChoice)\n\n // Phase 3 + Phase 4 \u2014 budget enforcement + LoopTrace collection.\n // Layer order (outer \u2192 inner):\n // budgetEnforcer.wire(traceOnStepFinish) \u2192 traceOnStepFinish calls userOnStepFinish\n // The trace collector builds the per-turn LoopTrace; the budget enforcer\n // aborts via AbortController when any limit is exceeded.\n // Phase 6.2 \u2014 generate a per-call turnId as a UUID.\n const turnId = randomUUID()\n const loopTraceCollector = buildLoopTraceCollector(agent.id, effectiveSessionId, turnId, effectiveLoop.onStepFinish)\n const abortController = new AbortController()\n const budgetEnforcer = new BudgetEnforcer(effectiveLoop.budget, abortController)\n const tracedOnStepFinish = budgetEnforcer.wire(loopTraceCollector.onStepFinish)\n\n // Phase 6.3 \u2014 wire the token-usage recorder into the wrapper-owned onStepFinish.\n // The recorder fires AFTER the trace collector so modelId is already in the trace.\n // It is invoked as void (detached) and MUST NEVER throw per R12.\n // resolvedModel is already computed above (model, modelId, providerId).\n\n let currentStepIndex = 0\n const wiredOnStepFinish: AiAgentLoopConfig['onStepFinish'] = async (event) => {\n const capturedStepIndex = currentStepIndex\n currentStepIndex += 1\n\n if (tracedOnStepFinish) {\n await tracedOnStepFinish(event)\n }\n if (input.container) {\n const rawEvent = event as unknown as {\n usage?: { inputTokens?: number; outputTokens?: number; cachedInputTokens?: number; reasoningTokens?: number }\n finishReason?: string\n }\n void recordTokenUsage(\n {\n authContext: input.authContext,\n agentId: agent.id,\n moduleId: agent.moduleId,\n sessionId: effectiveSessionId,\n turnId,\n stepIndex: capturedStepIndex,\n providerId: resolvedModel.providerId,\n modelId: resolvedModel.modelId,\n usage: {\n inputTokens: rawEvent.usage?.inputTokens,\n outputTokens: rawEvent.usage?.outputTokens,\n cachedInputTokens: rawEvent.usage?.cachedInputTokens,\n reasoningTokens: rawEvent.usage?.reasoningTokens,\n },\n finishReason: rawEvent.finishReason,\n loopAbortReason: budgetEnforcer.abortReason ?? undefined,\n },\n input.container,\n )\n }\n }\n\n let wallClockTimer: ReturnType<typeof setTimeout> | undefined\n if (effectiveLoop.budget?.maxWallClockMs) {\n wallClockTimer = setTimeout(() => {\n budgetEnforcer.recordStep({ toolCalls: 0 })\n }, effectiveLoop.budget.maxWallClockMs)\n }\n\n // Phase 5 \u2014 construct ToolLoopAgent when executionEngine === 'tool-loop-agent'.\n // The agent is built ONCE per turn (not pooled) with:\n // - model + tools from the wrapper-resolved registry\n // - stopWhen wired at construction (ToolLoopAgentSettings field)\n // - prepareStep wired at construction (NOT via prepareCall \u2014 it is not in\n // prepareCall's Pick list per spec \u00A7Phase 5 correction)\n // - onStepFinish wired at construction (budget + trace collector)\n // - prepareCall used only for per-turn narrowing of model/tools/stopWhen/\n // activeTools/providerOptions (per spec \u00A7Phase 5 correction)\n let builtToolLoopAgent: ToolLoopAgent<never, ToolSet> | undefined\n if (agent.executionEngine === 'tool-loop-agent') {\n const agentSettings: ToolLoopAgentSettings<never, ToolSet> = {\n model,\n tools: tools as ToolSet,\n stopWhen: stopConditions,\n prepareStep: wrapperPrepareStep,\n onStepFinish: wiredOnStepFinish,\n ...(effectiveLoop.repairToolCall !== undefined\n ? { experimental_repairToolCall: effectiveLoop.repairToolCall }\n : {}),\n ...(sdkActiveTools !== undefined ? { activeTools: sdkActiveTools } : {}),\n ...(sdkToolChoice !== undefined ? { toolChoice: sdkToolChoice } : {}),\n }\n builtToolLoopAgent = new ToolLoopAgent(agentSettings)\n }\n\n const preparedOptions: PreparedAiSdkOptions = {\n model,\n tools,\n system: systemPrompt,\n messages: modelMessages,\n maxSteps: effectiveLoop.maxSteps ?? 10,\n stopWhen: stopConditions,\n prepareStep: wrapperPrepareStep,\n onStepFinish: wiredOnStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n onToolCallStart: effectiveLoop.onToolCallStart,\n onToolCallFinish: effectiveLoop.onToolCallFinish,\n experimental_repairToolCall: effectiveLoop.repairToolCall,\n activeTools: sdkActiveTools,\n toolChoice: sdkToolChoice,\n abortSignal: abortController.signal,\n finalizeLoopTrace: () => loopTraceCollector.finalize(budgetEnforcer.abortReason),\n ...(builtToolLoopAgent !== undefined ? { toolLoopAgent: builtToolLoopAgent } : {}),\n }\n\n if (input.generateText) {\n try {\n const callbackResult = await input.generateText(preparedOptions)\n const baseResponse = (callbackResult as StreamTextResult<ToolSet, never>).toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(baseResponse, preparedOptions.finalizeLoopTrace)\n }\n return baseResponse\n } finally {\n if (wallClockTimer !== undefined) clearTimeout(wallClockTimer)\n }\n }\n\n // Phase 5 \u2014 engine dispatch: tool-loop-agent path vs default stream-text path.\n if (builtToolLoopAgent !== undefined) {\n // `ToolLoopAgent.stream` dispatches via the agent's own prepareCall/prepareStep\n // pipeline. prepareStep is already wired at construction (security-critical).\n const agentStreamResult = await builtToolLoopAgent.stream({\n messages: modelMessages,\n abortSignal: abortController.signal,\n onStepFinish: wiredOnStepFinish,\n })\n if (wallClockTimer !== undefined) {\n const clearTimer = () => clearTimeout(wallClockTimer!)\n Promise.resolve(agentStreamResult.consumeStream()).then(clearTimer, clearTimer)\n }\n const baseResponse = agentStreamResult.toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(baseResponse, preparedOptions.finalizeLoopTrace)\n }\n return baseResponse\n }\n\n // Default stream-text path (executionEngine === 'stream-text' or unset).\n const result = streamText({\n model,\n system: systemPrompt,\n messages: modelMessages,\n tools,\n stopWhen: stopConditions as never,\n prepareStep: wrapperPrepareStep as never,\n onStepFinish: wiredOnStepFinish as never,\n experimental_onStepStart: effectiveLoop.onStepStart as never,\n experimental_onToolCallStart: effectiveLoop.onToolCallStart as never,\n experimental_onToolCallFinish: effectiveLoop.onToolCallFinish as never,\n experimental_repairToolCall: effectiveLoop.repairToolCall as never,\n ...(sdkActiveTools !== undefined ? { activeTools: sdkActiveTools } : {}),\n ...(sdkToolChoice !== undefined ? { toolChoice: sdkToolChoice } : {}),\n abortSignal: abortController.signal,\n })\n if (wallClockTimer !== undefined) {\n const clearTimer = () => clearTimeout(wallClockTimer!)\n Promise.resolve(result.consumeStream()).then(clearTimer, clearTimer)\n }\n const baseResponse = result.toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(baseResponse, preparedOptions.finalizeLoopTrace)\n }\n return baseResponse\n}\n\n/**\n * Runtime override for the structured-output schema used by {@link runAiAgentObject}.\n * When the agent itself declares no `output` block, the caller MUST supply this;\n * otherwise the helper rejects with {@link AgentPolicyError} code\n * `execution_mode_not_supported`.\n */\nexport interface RunAiAgentObjectOutputOverride<TSchema = ZodTypeAny> {\n schemaName: string\n schema: TSchema\n /**\n * `'generate'` (default) calls AI SDK `generateObject` and resolves to the\n * parsed object. `'stream'` calls `streamObject` and returns the SDK's\n * streaming handle so callers can consume partial objects / text deltas.\n */\n mode?: 'generate' | 'stream'\n}\n\nexport interface RunAiAgentObjectInput<TSchema = ZodTypeAny> {\n agentId: string\n /**\n * Accepts either a bare user prompt (wrapped as `[{ role: 'user', content }]`)\n * or a prebuilt `UIMessage[]` array \u2014 matches the source spec's\n * `RunAiAgentObjectInput` contract (\u00A71149\u20131160).\n */\n input: string | UIMessage[]\n attachmentIds?: string[]\n pageContext?: AgentRequestPageContext\n /**\n * Same Phase-1 shim as {@link RunAiAgentTextInput.authContext}. Required until\n * a global request-context resolver lands (Phase 4).\n */\n authContext: AiChatRequestContext\n modelOverride?: string\n /**\n * Optional request-time provider override. When non-empty, wins for the\n * provider axis at the same priority as `modelOverride` for the model axis.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n providerOverride?: string\n /**\n * Optional per-call base URL override. Wins over every other source in the\n * baseURL resolution chain. Intended for programmatic callers only \u2014 the\n * HTTP query-param baseUrl and the AI_RUNTIME_BASEURL_ALLOWLIST arrive in\n * Phase 4a and MUST NOT be exposed here.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n baseUrlOverride?: string\n /**\n * Per-request HTTP dispatcher override (query params `?provider=`, `?model=`,\n * `?baseUrl=`). Validated by the dispatcher route before being forwarded\n * here. Wins over tenantOverride and all lower-priority sources when\n * `agent.allowRuntimeOverride !== false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n requestOverride?: {\n providerId?: string | null\n modelId?: string | null\n baseURL?: string | null\n }\n output?: RunAiAgentObjectOutputOverride<TSchema>\n debug?: boolean\n container?: AwilixContainer\n /**\n * Optional stable per-run session id for token-usage correlation.\n * Object-mode runs are single-turn by definition but the session id lets\n * callers group multiple object runs together for reporting.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId?: string | null\n /**\n * Optional per-call loop config override for object mode. Only the\n * object-safe subset is accepted: `maxSteps`, `budget`, `onStepFinish`,\n * `onStepStart`, and `allowRuntimeOverride`. Providing any chat-only\n * field (`prepareStep`, `repairToolCall`, `stopWhen`, `activeTools`,\n * `toolChoice`) throws `AgentPolicyError` code\n * `loop_unsupported_in_object_mode`.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: Pick<AiAgentLoopConfig, 'maxSteps' | 'budget' | 'onStepFinish' | 'onStepStart' | 'allowRuntimeOverride'>\n /**\n * Optional escape-hatch callback receiving the fully prepared object-mode\n * options bag. When supplied the wrapper still enforces all policy guardrails\n * and then delegates the actual SDK call to this function. The callback MUST\n * return a value compatible with `GenerateObjectResult` or `StreamObjectResult`.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n generateObject?: (\n options: PreparedAiSdkObjectOptions,\n ) => Promise<GenerateObjectResult<unknown> | StreamObjectResult<unknown, unknown, unknown>>\n}\n\nexport type RunAiAgentObjectGenerateResult<TSchema> = {\n mode: 'generate'\n object: TSchema\n finishReason?: string\n usage?: { inputTokens?: number; outputTokens?: number }\n}\n\nexport type RunAiAgentObjectStreamResult<TSchema> = {\n mode: 'stream'\n /** Full parsed object once the stream completes. */\n object: Promise<TSchema>\n /** Async iterator of partial (progressively hydrated) objects. */\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n /** Async iterator of the raw text deltas the model emitted. */\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n}\n\nexport type RunAiAgentObjectResult<TSchema> =\n | RunAiAgentObjectGenerateResult<TSchema>\n | RunAiAgentObjectStreamResult<TSchema>\n\nfunction normalizeObjectMessages(input: string | UIMessage[]): UIMessage[] {\n if (typeof input === 'string') {\n return [\n {\n id: 'user-input',\n role: 'user',\n parts: [{ type: 'text', text: input }],\n } as unknown as UIMessage,\n ]\n }\n return input\n}\n\nfunction resolveStructuredOutput<TSchema>(\n agent: AiAgentDefinition,\n override: RunAiAgentObjectOutputOverride<TSchema> | undefined,\n): { schemaName: string; schema: unknown; mode: 'generate' | 'stream' } {\n if (override) {\n return {\n schemaName: override.schemaName,\n schema: override.schema as unknown,\n mode: override.mode ?? 'generate',\n }\n }\n const declared = agent.output as AiAgentStructuredOutput | undefined\n if (!declared) {\n throw new AgentPolicyError(\n 'execution_mode_not_supported',\n `Agent \"${agent.id}\" does not declare a structured-output schema; pass runAiAgentObject({ output }) or declare agent.output.`,\n )\n }\n return {\n schemaName: declared.schemaName,\n schema: declared.schema as unknown,\n mode: declared.mode ?? 'generate',\n }\n}\n\n/**\n * Server-side helper that runs an Open Mercato agent in structured-output mode\n * via the Vercel AI SDK. Shares the same policy gate, tool resolution path,\n * system-prompt composition, and model resolution as {@link runAiAgentText} \u2014\n * object-mode and chat-mode CANNOT diverge.\n *\n * Attachment-to-model conversion (Step 3.7): resolved\n * {@link AiResolvedAttachmentPart}s are materialized inline as AI SDK v6\n * `FileUIPart` entries on the last user message (images/PDFs) and as a\n * structured `[ATTACHMENTS]` block appended to the system prompt (text\n * extracts + metadata-only summaries). Matches {@link runAiAgentText} byte-\n * for-byte so the Step 3.6 parity contract is preserved.\n */\nexport async function runAiAgentObject<TSchema = unknown>(\n input: RunAiAgentObjectInput<TSchema>,\n): Promise<RunAiAgentObjectResult<TSchema>> {\n const [mutationPolicyOverride, tenantRuntimeOverride, tenantAllowlistSnapshot] = await Promise.all([\n resolveMutationPolicyOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveRuntimeModelOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveTenantAllowlistSnapshot(\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n ])\n const { agent, tools } = await resolveAiAgentTools({\n agentId: input.agentId,\n authContext: input.authContext,\n pageContext: input.pageContext,\n attachmentIds: input.attachmentIds,\n requestedExecutionMode: 'object',\n mutationPolicyOverride,\n container: input.container,\n })\n\n const resolvedOutput = resolveStructuredOutput(agent, input.output)\n\n const resolvedAttachments = await resolveAttachmentPartsForAgent({\n agent,\n attachmentIds: input.attachmentIds,\n authContext: input.authContext,\n container: input.container,\n })\n\n const baseSystemPrompt = await composeSystemPrompt(\n agent,\n input.pageContext,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n )\n const systemPrompt = appendRuntimeMutationPolicy(\n appendAttachmentSummary(baseSystemPrompt, resolvedAttachments),\n agent,\n mutationPolicyOverride,\n )\n\n const { model } = resolveAgentModel(\n agent,\n input.modelOverride,\n input.providerOverride,\n input.container,\n input.baseUrlOverride,\n tenantRuntimeOverride,\n input.requestOverride,\n tenantAllowlistSnapshot,\n )\n const normalizedMessages = ensureUiMessageShape(normalizeObjectMessages(input.input))\n const hydratedMessages = attachAttachmentsToMessages(\n normalizedMessages,\n resolvedAttachments,\n )\n const modelMessages = await convertToModelMessages(hydratedMessages)\n void tools\n\n // Phase 6.2 \u2014 resolve session id and generate per-call turn id for\n // token-usage correlation in object mode.\n const effectiveObjectSessionId = input.sessionId ?? randomUUID()\n const objectTurnId = randomUUID()\n // Expose for token recorder via closure (used by token-usage-recorder in Phase 6.3).\n void effectiveObjectSessionId\n void objectTurnId\n\n if (input.loop) {\n assertLoopObjectModeCompatible(input.loop)\n }\n const effectiveLoop = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_OBJECT)\n\n const abortController = new AbortController()\n\n const preparedObjectOptions: PreparedAiSdkObjectOptions = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schemaName: resolvedOutput.schemaName,\n schema: resolvedOutput.schema,\n maxSteps: effectiveLoop.maxSteps,\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n }\n\n if (input.generateObject) {\n const callbackResult = await input.generateObject(preparedObjectOptions)\n const typedResult = callbackResult as unknown as Record<string, unknown>\n if ('partialObjectStream' in typedResult) {\n const streamResult = typedResult as {\n object: Promise<TSchema>\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n }\n return {\n mode: 'stream',\n object: streamResult.object,\n partialObjectStream: streamResult.partialObjectStream,\n textStream: streamResult.textStream,\n finishReason: streamResult.finishReason,\n usage: streamResult.usage,\n }\n }\n const genResult = typedResult as { object: unknown; finishReason?: string; usage?: { inputTokens?: number; outputTokens?: number } }\n return {\n mode: 'generate',\n object: genResult.object as TSchema,\n finishReason: genResult.finishReason,\n usage: genResult.usage,\n }\n }\n\n if (resolvedOutput.mode === 'stream') {\n const streamArgs = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schema: resolvedOutput.schema as never,\n schemaName: resolvedOutput.schemaName,\n ...(effectiveLoop.maxSteps !== undefined ? { maxSteps: effectiveLoop.maxSteps } : {}),\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n } as Parameters<typeof streamObject>[0]\n const result = streamObject(streamArgs) as unknown as {\n object: Promise<TSchema>\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n }\n return {\n mode: 'stream',\n object: result.object,\n partialObjectStream: result.partialObjectStream,\n textStream: result.textStream,\n finishReason: result.finishReason,\n usage: result.usage,\n }\n }\n\n const generateArgs = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schema: resolvedOutput.schema as never,\n schemaName: resolvedOutput.schemaName,\n ...(effectiveLoop.maxSteps !== undefined ? { maxSteps: effectiveLoop.maxSteps } : {}),\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n } as Parameters<typeof generateObject>[0]\n\n const result = await generateObject(generateArgs)\n return {\n mode: 'generate',\n object: (result as { object: unknown }).object as TSchema,\n finishReason: (result as { finishReason?: string }).finishReason,\n usage: (result as { usage?: { inputTokens?: number; outputTokens?: number } }).usage,\n }\n}\n\nexport { AgentPolicyError }\n"],
5
- "mappings": "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",
6
- "names": ["baseResponse", "result"]
4
+ "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport { createContainer } from 'awilix'\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type {\n GenerateObjectResult,\n GenerateTextResult,\n LanguageModel,\n PrepareStepFunction,\n PrepareStepResult,\n StreamObjectResult,\n StreamTextResult,\n ToolSet,\n UIMessage,\n ToolLoopAgentSettings,\n} from 'ai'\nimport {\n convertToModelMessages,\n generateObject,\n hasToolCall,\n stepCountIs,\n streamObject,\n streamText,\n Experimental_Agent as ToolLoopAgent,\n} from 'ai'\nimport type { StopCondition } from 'ai'\nimport type { ZodTypeAny } from 'zod'\nimport { createModelFactory, resolveAllowRuntimeOverride } from './model-factory'\nimport type {\n AiAgentDefinition,\n AiAgentLoopConfig,\n AiAgentPageContextInput,\n AiAgentStructuredOutput,\n LoopStepRecord,\n LoopTrace,\n} from './ai-agent-definition'\nimport type {\n AiChatRequestContext,\n AiResolvedAttachmentPart,\n} from './attachment-bridge-types'\nimport {\n resolveAiAgentTools,\n AgentPolicyError,\n desanitizeToolNameForDisplay,\n sanitizeToolNameForModel,\n} from './agent-tools'\nimport { resolveEffectiveMutationPolicy } from './agent-policy'\nimport { toolRegistry } from './tool-registry'\nimport {\n attachmentPartsToUiFileParts,\n resolveAttachmentPartsForAgent,\n summarizeAttachmentPartsForPrompt,\n} from './attachment-parts'\nimport { AiAgentPromptOverrideRepository } from '../data/repositories/AiAgentPromptOverrideRepository'\nimport { AiAgentMutationPolicyOverrideRepository } from '../data/repositories/AiAgentMutationPolicyOverrideRepository'\nimport { AiAgentRuntimeOverrideRepository } from '../data/repositories/AiAgentRuntimeOverrideRepository'\nimport { AiTenantModelAllowlistRepository } from '../data/repositories/AiTenantModelAllowlistRepository'\nimport type { TenantAllowlistSnapshot } from './model-allowlist'\nimport { composeSystemPromptWithOverride } from './prompt-override-merge'\nimport { isAgentTaskPlanEnabled } from './agent-registry'\nimport { isKnownMutationPolicy } from './agent-policy'\nimport type { AiAgentMutationPolicy } from './ai-agent-definition'\nimport { recordTokenUsage } from './token-usage-recorder'\nimport { injectTaskPlanIntoStream } from './task-plan-stream'\nimport { TASK_PLAN_RUNTIME_PROMPT_SECTION } from './task-plan-labels'\n\n// Ensure built-in LLM providers are registered. Side-effect import; identical to\n// what `./ai-sdk.ts` consumers already rely on.\nimport './llm-bootstrap'\n\nexport interface AgentRequestPageContext {\n pageId?: string | null\n entityType?: string | null\n recordId?: string | null\n [key: string]: unknown\n}\n\nexport interface RunAiAgentTextInput {\n agentId: string\n messages: UIMessage[]\n attachmentIds?: string[]\n pageContext?: AgentRequestPageContext\n debug?: boolean\n /**\n * Phase 1 exposes the caller-supplied auth context directly on the helper\n * input. Phase 4 may wrap this behind a thinner public API once a global\n * request-context resolver exists. Helpers running inside the HTTP\n * dispatcher receive the same `AiChatRequestContext` used by `checkAgentPolicy`.\n */\n authContext: AiChatRequestContext\n /**\n * Optional per-call model id override that wins over `agent.defaultModel`.\n * The production model-factory extraction lives in Step 5.1; this Step\n * accepts a literal model id string so the Phase 1 runtime already honors\n * `agent.defaultModel` without inventing a new indirection layer.\n */\n modelOverride?: string\n /**\n * Optional request-time provider override. When non-empty, wins for the\n * provider axis at the same priority as `modelOverride` for the model axis.\n * A value that does not match any registered provider id is silently ignored.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n providerOverride?: string\n /**\n * Optional per-call base URL override. Wins over every other source in the\n * baseURL resolution chain. Intended for programmatic callers only \u2014 the\n * HTTP query-param baseUrl and the AI_RUNTIME_BASEURL_ALLOWLIST arrive in\n * Phase 4a and MUST NOT be exposed here.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n baseUrlOverride?: string\n /**\n * Per-request HTTP dispatcher override (query params `?provider=`, `?model=`,\n * `?baseUrl=`). Validated by the dispatcher route before being forwarded\n * here. Wins over tenantOverride and all lower-priority sources when\n * `agent.allowRuntimeOverride !== false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n requestOverride?: {\n providerId?: string | null\n modelId?: string | null\n baseURL?: string | null\n }\n /**\n * Optional DI container used by `resolvePageContext` callbacks. When omitted\n * and the agent declares a `resolvePageContext`, hydration is skipped with a\n * warning (callbacks that need database/DI cannot run safely without one).\n */\n container?: AwilixContainer\n /**\n * Stable per-conversation id that ties every turn of a chat together for\n * token-usage correlation and pending-action idempotency (Phase 6.2).\n *\n * When omitted the server generates one and echoes it on the SSE `done`\n * event so the client can persist it for subsequent turns. Callers that\n * supply a value from a previous turn will have their usage rows grouped\n * under the same `session_id` in the token-usage tables.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId?: string | null\n /**\n * @deprecated Use `sessionId` instead. This alias was the original name of\n * the same field; both names are accepted for one minor release to let\n * callers migrate without a hard cut. `sessionId` takes precedence when both\n * are provided.\n */\n conversationId?: string | null\n /**\n * Optional per-call loop config override. Fields set here win over the\n * agent's `loop` declaration and the tenant DB override. The override is\n * gated by `agent.loop?.allowRuntimeOverride ?? true` \u2014 agents that pin\n * a loop policy for correctness reasons can set `allowRuntimeOverride: false`\n * to reject any per-call override with `AgentPolicyError` code\n * `loop_runtime_override_disabled`.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: Partial<AiAgentLoopConfig>\n /**\n * Optional escape-hatch callback that receives the fully prepared AI SDK\n * options bag and must return the SDK result (either from `streamText` or\n * `generateText`). When supplied, the wrapper still enforces every policy\n * guardrail (features, tool allowlist, mutation approval, model factory,\n * prompt composition, attachment bridging) and then hands control to this\n * callback instead of calling `streamText` directly.\n *\n * The callback MUST pass `stopWhen` and `prepareStep` through to the AI SDK\n * call \u2014 dropping either one disables the agent's loop policy or mutation\n * approval guards respectively. See `agents.mdx` \u00A7\"Option B\" for the full\n * contract and what you lose when fields are omitted.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n generateText?: (\n options: PreparedAiSdkOptions,\n ) => Promise<GenerateTextResult<ToolSet, never> | StreamTextResult<ToolSet, never>>\n /**\n * When `true`, the runtime appends a `loop-finish` SSE event to the\n * response stream after the AI SDK stream closes. The event payload is the\n * serialized `LoopTrace` for the turn (agent id, turn id, per-step records,\n * stop reason, total duration, total usage).\n *\n * Consumed by `useAiChat` to populate `lastLoopTrace` and by the playground\n * debug panel to render the per-turn trace via `LoopTracePanel`.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n emitLoopTrace?: boolean\n}\n\n/**\n * The wrapper default loop config used when neither the caller, tenant, agent,\n * nor legacy maxSteps supplies any config. Chat mode defaults to `{ maxSteps: 10 }`\n * to ensure tool-using agents can loop; object mode defaults to an empty config\n * (single structured-output call, no explicit step cap).\n */\nconst WRAPPER_DEFAULT_LOOP_CHAT: AiAgentLoopConfig = { maxSteps: 10 }\nconst WRAPPER_DEFAULT_LOOP_OBJECT: AiAgentLoopConfig = {}\n\n/**\n * Named loop-budget preset values for `?loopBudget=<preset>` query param.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentLoopBudgetPreset = 'tight' | 'default' | 'loose'\n\n/**\n * Maps a `loopBudget` preset name to the corresponding `AiAgentLoopBudget`\n * triple. `'default'` returns `undefined` (no override \u2014 agent default applies).\n * Values are pinned per spec \u00A7\"loopBudget preset values (Phase 4)\".\n */\nexport function resolveLoopBudgetPreset(\n preset: AiAgentLoopBudgetPreset,\n): Partial<AiAgentLoopConfig> | undefined {\n switch (preset) {\n case 'tight':\n return { maxSteps: 3, budget: { maxToolCalls: 3, maxWallClockMs: 10_000, maxTokens: 50_000 } }\n case 'loose':\n return { maxSteps: 20, budget: { maxToolCalls: 20, maxWallClockMs: 120_000, maxTokens: 500_000 } }\n case 'default':\n return undefined\n }\n}\n\nconst SSE_ENCODER = new TextEncoder()\n\n/**\n * Wraps a streaming `Response` to append a typed `loop-finish` SSE event\n * after the AI SDK stream closes. The event carries the serialized `LoopTrace`\n * for the turn so the `useAiChat` hook can render it in the debug panel.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nfunction appendLoopFinishToStream(\n baseResponse: Response,\n finalizeLoopTrace: () => LoopTrace,\n): Response {\n const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>()\n const writer = writable.getWriter()\n\n async function pump(): Promise<void> {\n if (!baseResponse.body) {\n await writer.close()\n return\n }\n const reader = baseResponse.body.getReader()\n try {\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n await writer.write(value)\n }\n const trace = finalizeLoopTrace()\n const eventLine = `data: ${JSON.stringify({ type: 'loop-finish', trace })}\\n\\n`\n await writer.write(SSE_ENCODER.encode(eventLine))\n } catch {\n // Pass through \u2014 the reader abort is surfaced by the upstream consumer.\n } finally {\n reader.releaseLock()\n await writer.close().catch(() => undefined)\n }\n }\n\n void pump()\n return new Response(readable, {\n status: baseResponse.status,\n headers: baseResponse.headers,\n })\n}\n\n/**\n * The fully prepared options bag handed to the `runAiAgentText({ generateText })`\n * escape-hatch callback. Callers receive a complete set of wrapper-composed\n * loop primitives so they can forward them to `streamText` / `generateText`.\n *\n * SECURITY CONTRACT: callers MUST forward `prepareStep` to the AI SDK call.\n * Dropping it removes the per-step tool-allowlist re-check and the mutation-\n * approval wrapping. Dropping `stopWhen` removes the agent's loop policy and\n * the R3 step-count fallback.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface PreparedAiSdkOptions {\n model: LanguageModel\n tools: ToolSet\n system: string\n messages: Awaited<ReturnType<typeof convertToModelMessages>>\n /** Alias kept for SDK compat \u2014 equals `stopWhen` array's effective maxSteps. */\n maxSteps: number\n /**\n * Wrapper-composed stop conditions (R3 mitigated: always ends with\n * `stepCountIs(maxSteps)`). MUST be forwarded to the SDK call.\n */\n stopWhen: StopCondition<ToolSet>[]\n /**\n * Wrapper-owned `PrepareStepFunction` that re-asserts the tool allowlist and\n * mutation-approval wrapping per step. SECURITY-CRITICAL: callers MUST\n * forward this to the SDK call or they lose mutation-approval guarantees.\n */\n prepareStep: PrepareStepFunction<ToolSet>\n /** Wrapper trace aggregator chained with the agent's `onStepFinish` hook. */\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n onStepStart: AiAgentLoopConfig['onStepStart']\n onToolCallStart: AiAgentLoopConfig['onToolCallStart']\n onToolCallFinish: AiAgentLoopConfig['onToolCallFinish']\n experimental_repairToolCall: AiAgentLoopConfig['repairToolCall']\n activeTools: AiAgentLoopConfig['activeTools']\n toolChoice: AiAgentLoopConfig['toolChoice']\n /**\n * Pre-wired to the per-turn `AbortController` used by budget enforcement\n * (Phase 3). Forward to the SDK call so budget limits can abort in-flight\n * requests. May be `undefined` when budget enforcement is not yet active\n * (Phases 0\u20132); the SDK treats `undefined` the same as no signal.\n */\n abortSignal: AbortSignal | undefined\n /**\n * Finalizes the per-turn `LoopTrace` and returns it. Callers that use the\n * `generateText` escape-hatch SHOULD call this after the SDK call resolves so\n * the trace is available for logging or SSE emission.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n finalizeLoopTrace: () => LoopTrace\n /**\n * Present only when `agent.executionEngine === 'tool-loop-agent'`. Callers\n * can invoke `agent.generate(...)` / `agent.stream(...)` directly with their\n * own `providerOptions` as an escape-hatch \u2014 the `ToolLoopAgent` instance is\n * already wired with the wrapper-owned `prepareStep`, `stopWhen`, and\n * `onStepFinish` at construction.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n toolLoopAgent?: ToolLoopAgent<never, ToolSet>\n}\n\n/**\n * The fully prepared options bag handed to the `runAiAgentObject({ generateObject })`\n * escape-hatch callback. Object-mode subset \u2014 chat-only fields are absent.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface PreparedAiSdkObjectOptions {\n model: LanguageModel\n system: string\n messages: Awaited<ReturnType<typeof convertToModelMessages>>\n schemaName: string\n schema: unknown\n maxSteps: number | undefined\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n onStepStart: AiAgentLoopConfig['onStepStart']\n abortSignal: AbortSignal | undefined\n}\n\n/**\n * Guards the per-call loop override against agents that have opted out of\n * runtime overrides by setting `loop.allowRuntimeOverride: false`.\n *\n * Throws `AgentPolicyError` with code `loop_runtime_override_disabled` when\n * the agent has opted out and a caller override was supplied.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nfunction assertLoopRuntimeOverrideAllowed(\n agent: AiAgentDefinition,\n callerLoop: Partial<AiAgentLoopConfig> | undefined,\n): void {\n if (!callerLoop) return\n const allowed = agent.loop?.allowRuntimeOverride ?? true\n if (!allowed) {\n throw new AgentPolicyError(\n 'loop_runtime_override_disabled',\n `Agent \"${agent.id}\" has disabled per-call loop overrides (loop.allowRuntimeOverride: false). Remove the loop override to proceed.`,\n )\n }\n}\n\n/**\n * Reads `<MODULE>_AI_LOOP_*` env shorthands for the given module id.\n * Returns a partial `AiAgentLoopConfig` containing only the axes that are\n * explicitly set in the environment. Missing or malformed values are silently\n * ignored (fail-open \u2014 env vars are a best-effort static deployment mechanism).\n *\n * Supported variables (Phase 3 of spec\n * `2026-04-28-ai-agents-agentic-loop-controls`):\n *\n * - `<MODULE>_AI_LOOP_MAX_STEPS` \u2014 maps to `loop.maxSteps`\n * - `<MODULE>_AI_LOOP_MAX_WALL_CLOCK_MS` \u2014 maps to `loop.budget.maxWallClockMs`\n * - `<MODULE>_AI_LOOP_MAX_TOKENS` \u2014 maps to `loop.budget.maxTokens`\n */\nfunction readModuleLoopEnv(moduleId: string): Partial<AiAgentLoopConfig> {\n const prefix = moduleId.toUpperCase()\n const partial: Partial<AiAgentLoopConfig> = {}\n\n const maxStepsRaw = process.env[`${prefix}_AI_LOOP_MAX_STEPS`]\n if (maxStepsRaw) {\n const parsed = parseInt(maxStepsRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) partial.maxSteps = parsed\n }\n\n const maxWallClockRaw = process.env[`${prefix}_AI_LOOP_MAX_WALL_CLOCK_MS`]\n const maxTokensRaw = process.env[`${prefix}_AI_LOOP_MAX_TOKENS`]\n\n if (maxWallClockRaw || maxTokensRaw) {\n const budgetPartial: AiAgentLoopConfig['budget'] = {}\n if (maxWallClockRaw) {\n const parsed = parseInt(maxWallClockRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) budgetPartial.maxWallClockMs = parsed\n }\n if (maxTokensRaw) {\n const parsed = parseInt(maxTokensRaw.trim(), 10)\n if (!isNaN(parsed) && parsed > 0) budgetPartial.maxTokens = parsed\n }\n if (Object.keys(budgetPartial).length > 0) partial.budget = budgetPartial\n }\n\n return partial\n}\n\n/**\n * Resolves the effective loop config for a turn by walking the precedence\n * chain (highest first):\n *\n * 1. `callerLoop` \u2014 per-call `runAiAgentText({ loop })` override (Phase 1).\n * 2. Tenant override row \u2014 NOT yet implemented in DB; always `undefined` here.\n * // TODO(Phase 1782-3): hydrate loop columns from ai_agent_runtime_overrides\n * 3. `<MODULE>_AI_LOOP_*` env shorthands (Phase 3) \u2014 only MAX_STEPS,\n * MAX_WALL_CLOCK_MS, MAX_TOKENS. Lower precedence than DB override but higher\n * than the agent's code-declared defaults.\n * 4. `agent.loop` \u2014 agent's declarative loop config.\n * 5. `agent.maxSteps` (deprecated alias) \u2014 mapped to `{ maxSteps: agent.maxSteps }`.\n * 6. `wrapperDefault` \u2014 the wrapper's hardcoded fallback.\n *\n * Each source contributes only the fields it sets explicitly; fields absent at\n * a higher-priority source fall through to a lower-priority one. The merge is\n * performed left-to-right with higher-priority sources winning field-by-field.\n *\n * Throws `AgentPolicyError` code `loop_runtime_override_disabled` when the\n * agent opts out of per-call overrides and a caller loop was supplied.\n *\n * Phase 0 + Phase 1 + Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function resolveEffectiveLoopConfig(\n agent: AiAgentDefinition,\n callerLoop?: Partial<AiAgentLoopConfig> | undefined,\n wrapperDefault?: AiAgentLoopConfig,\n): AiAgentLoopConfig {\n assertLoopRuntimeOverrideAllowed(agent, callerLoop)\n\n const effectiveDefault = wrapperDefault ?? WRAPPER_DEFAULT_LOOP_CHAT\n\n // Build base from lowest-priority: wrapper default \u2192 legacy maxSteps \u2192 agent.loop\n const legacyMaxSteps: AiAgentLoopConfig | undefined =\n typeof agent.maxSteps === 'number' && agent.maxSteps > 0 && !agent.loop\n ? { maxSteps: agent.maxSteps }\n : undefined\n\n const base: AiAgentLoopConfig = {\n ...effectiveDefault,\n ...(legacyMaxSteps ?? {}),\n ...(agent.loop ?? {}),\n }\n\n // Phase 3 \u2014 env shorthands at priority 3 (above agent.loop, below DB override).\n // TODO(Phase 1782-3): hydrate loop columns from ai_agent_runtime_overrides\n // and merge tenantOverride here at priority #2 (above envOverride).\n const envOverride = readModuleLoopEnv(agent.moduleId)\n const withEnv: AiAgentLoopConfig = {\n ...base,\n ...envOverride,\n ...(envOverride.budget != null\n ? { budget: { ...(base.budget ?? {}), ...envOverride.budget } }\n : {}),\n }\n\n const withCaller: AiAgentLoopConfig = callerLoop\n ? { ...withEnv, ...callerLoop }\n : withEnv\n\n // Phase 3 \u2014 kill switch: when disabled is set to true, force maxSteps: 1 so the\n // agent executes as a single model call with no tool looping. All other loop config\n // is preserved (budget, etc.) but the step cap wins.\n if (withCaller.disabled === true) {\n return { ...withCaller, maxSteps: 1 }\n }\n\n return withCaller\n}\n\n/**\n * The reason a budget limit was hit, exposed on `LoopAbortReason` (Phase 3).\n */\nexport type LoopBudgetAbortReason =\n | 'budget-tool-calls'\n | 'budget-wall-clock'\n | 'budget-tokens'\n\n/**\n * Tracks per-turn budget usage and aborts the run when any limit is exceeded.\n *\n * Usage:\n * 1. Construct with the loop budget and the turn's `AbortController`.\n * 2. Call `wire(onStepFinish)` to get a composed `onStepFinish` that feeds\n * usage data into the enforcer on every completed step.\n * 3. The enforcer calls `abortController.abort()` with a typed\n * `LoopBudgetAbortReason` when a limit is hit.\n *\n * Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport class BudgetEnforcer {\n private toolCallsUsed = 0\n private tokensUsed = 0\n readonly turnStartMs: number\n abortReason: LoopBudgetAbortReason | null = null\n\n constructor(\n private readonly budget: AiAgentLoopConfig['budget'],\n private readonly abortController: AbortController,\n ) {\n this.turnStartMs = Date.now()\n }\n\n get hasActiveBudget(): boolean {\n const b = this.budget\n return (\n b !== undefined &&\n (b.maxToolCalls !== undefined || b.maxWallClockMs !== undefined || b.maxTokens !== undefined)\n )\n }\n\n recordStep(usage: { inputTokens?: number; outputTokens?: number; toolCalls?: number }): void {\n if (!this.budget) return\n this.toolCallsUsed += usage.toolCalls ?? 0\n this.tokensUsed += (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)\n this.checkLimits()\n }\n\n private checkLimits(): void {\n const b = this.budget\n if (!b) return\n\n if (b.maxToolCalls !== undefined && this.toolCallsUsed >= b.maxToolCalls) {\n this.abort('budget-tool-calls')\n return\n }\n\n const elapsedMs = Date.now() - this.turnStartMs\n if (b.maxWallClockMs !== undefined && elapsedMs >= b.maxWallClockMs) {\n this.abort('budget-wall-clock')\n return\n }\n\n if (b.maxTokens !== undefined && this.tokensUsed >= b.maxTokens) {\n this.abort('budget-tokens')\n }\n }\n\n private abort(reason: LoopBudgetAbortReason): void {\n if (this.abortReason !== null) return\n this.abortReason = reason\n console.info(\n `[AI Agents] Budget exceeded \u2014 aborting turn. Reason: ${reason}. ` +\n `toolCalls=${this.toolCallsUsed}, tokens=${this.tokensUsed}, ` +\n `elapsedMs=${Date.now() - this.turnStartMs}.`,\n )\n this.abortController.abort(reason)\n }\n\n wire(\n userOnStepFinish: AiAgentLoopConfig['onStepFinish'],\n ): AiAgentLoopConfig['onStepFinish'] {\n if (!this.hasActiveBudget) return userOnStepFinish\n return async (event) => {\n this.recordStep({\n inputTokens: event.usage?.inputTokens,\n outputTokens: event.usage?.outputTokens,\n toolCalls: event.toolCalls?.length,\n })\n if (userOnStepFinish) {\n try {\n await userOnStepFinish(event)\n } catch (err) {\n console.error('[AI Agents] User onStepFinish threw; ignoring:', err)\n }\n }\n }\n }\n}\n\n/**\n * Builds a wrapper-owned `onStepFinish` collector that aggregates per-step\n * usage and tool-call data into a `LoopTrace` object. The collector chains\n * the user's `onStepFinish` after it aggregates (exceptions from the user's\n * hook are caught and logged but do not abort the turn).\n *\n * Returns both the wired `onStepFinish` hook and a `finalize()` function that\n * resolves the `LoopTrace` once the turn is complete. The `budgetEnforcer`\n * is already wired into `onStepFinish` at a lower layer \u2014 this collector sits\n * above it.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function buildLoopTraceCollector(\n agentId: string,\n sessionId: string,\n turnId: string,\n userOnStepFinish: AiAgentLoopConfig['onStepFinish'],\n): {\n onStepFinish: AiAgentLoopConfig['onStepFinish']\n finalize: (abortReason: LoopBudgetAbortReason | null) => LoopTrace\n} {\n const turnStartMs = Date.now()\n const steps: LoopStepRecord[] = []\n\n const onStepFinish: AiAgentLoopConfig['onStepFinish'] = async (event) => {\n const stepIndex = steps.length\n const toolCalls = (event.toolCalls ?? []).map((tc) => {\n const raw = tc as unknown as {\n toolName?: string\n args?: unknown\n result?: unknown\n experimental_toToolResultError?: { code?: string; message?: string }\n repairAttempted?: boolean\n startTime?: number\n endTime?: number\n }\n return {\n toolName: raw.toolName\n ? desanitizeToolNameForDisplay(raw.toolName)\n : 'unknown',\n args: raw.args ?? {},\n result: raw.result,\n error: raw.experimental_toToolResultError\n ? {\n code: String(raw.experimental_toToolResultError?.code ?? 'unknown'),\n message: String(raw.experimental_toToolResultError?.message ?? ''),\n }\n : undefined,\n repairAttempted: raw.repairAttempted === true,\n durationMs:\n typeof raw.startTime === 'number' && typeof raw.endTime === 'number'\n ? raw.endTime - raw.startTime\n : 0,\n }\n })\n\n const textDelta =\n (event as unknown as { text?: string }).text ?? ''\n\n const finishReason = (\n (event as unknown as { finishReason?: string }).finishReason ?? 'stop'\n ) as LoopStepRecord['finishReason']\n\n const modelId =\n (event as unknown as { response?: { modelId?: string } }).response?.modelId ?? 'unknown'\n\n steps.push({\n stepIndex,\n modelId,\n toolCalls,\n textDelta,\n usage: {\n inputTokens: event.usage?.inputTokens ?? 0,\n outputTokens: event.usage?.outputTokens ?? 0,\n },\n finishReason,\n })\n\n if (userOnStepFinish) {\n try {\n await userOnStepFinish(event)\n } catch (err) {\n console.error('[AI Agents] User onStepFinish in LoopTrace collector threw; ignoring:', err)\n }\n }\n }\n\n function finalize(abortReason: LoopBudgetAbortReason | null): LoopTrace {\n const totalDurationMs = Date.now() - turnStartMs\n const totalUsage = steps.reduce(\n (acc, step) => ({\n inputTokens: acc.inputTokens + step.usage.inputTokens,\n outputTokens: acc.outputTokens + step.usage.outputTokens,\n }),\n { inputTokens: 0, outputTokens: 0 },\n )\n\n let stopReason: LoopTrace['stopReason'] = 'finish-reason'\n if (abortReason === 'budget-tool-calls') stopReason = 'budget-tool-calls'\n else if (abortReason === 'budget-wall-clock') stopReason = 'budget-wall-clock'\n else if (abortReason === 'budget-tokens') stopReason = 'budget-tokens'\n\n return {\n agentId,\n sessionId,\n turnId,\n steps,\n stopReason,\n totalDurationMs,\n totalUsage,\n }\n }\n\n return { onStepFinish, finalize }\n}\n\n/**\n * Translates serializable `AiAgentLoopStopCondition` items into the Vercel AI\n * SDK `StopCondition` array ready to pass to `streamText` / `generateText`.\n *\n * The wrapper ALWAYS appends `stepCountIs(maxSteps ?? 10)` as the final item\n * in the returned array (R3 mitigation). This guarantees that a misconfigured\n * `hasToolCall` for a non-existent tool can never cause an infinite loop\n * because the SDK treats `stopWhen` arrays with OR semantics \u2014 the step-count\n * fallback will always trip eventually.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function translateStopConditions(\n loopConfig: AiAgentLoopConfig,\n mapToolName: (toolName: string) => string = (toolName) => toolName,\n): StopCondition<ToolSet>[] {\n const effectiveMaxSteps = loopConfig.maxSteps ?? 10\n const userConditions: StopCondition<ToolSet>[] = []\n\n const rawStopWhen = loopConfig.stopWhen\n if (rawStopWhen) {\n const items = Array.isArray(rawStopWhen) ? rawStopWhen : [rawStopWhen]\n for (const item of items) {\n if (item.kind === 'stepCount') {\n userConditions.push(stepCountIs(item.count))\n } else if (item.kind === 'hasToolCall') {\n userConditions.push(hasToolCall(mapToolName(item.toolName)))\n } else if (item.kind === 'custom') {\n userConditions.push(item.stop as StopCondition<ToolSet>)\n }\n }\n }\n\n // Always append the hard step-count fallback (R3 mitigation).\n return [...userConditions, stepCountIs(effectiveMaxSteps)]\n}\n\n/**\n * Security-critical merge of the wrapper-owned step override with the user's\n * `prepareStep` return value.\n *\n * Guarantees (R1 mitigation \u2014 preserving the mutation-approval contract):\n * 1. Any `tools` map returned by the user is intersected with `toolRegistry`\n * (the policy-gated, mutation-approval-wrapped map). If the user returned\n * a raw mutation handler, the merged map points at the wrapped one.\n * 2. Any `activeTools` returned by the user is intersected with\n * `agent.allowedTools`. Out-of-set names are dropped with a single\n * `loop:active_tools_filtered` warning.\n * 3. A user-returned `tools` map that contains a mutation tool pointing at the\n * raw handler (not the wrapped one) is rejected with\n * `AgentPolicyError` code `loop_violates_mutation_policy`.\n * 4. Non-policy fields (`model`, `toolChoice`, `system`, `messages`) from the\n * user override are honored as-is.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n *\n * ai-sdk 6.0.177 dropped the `tools` field from `PrepareStepResult`, so the\n * map is no longer consumed by `streamText` / `generateText`. We still inspect\n * any `tools` map that a caller passes via a type-asserted result \u2014 even\n * though the SDK ignores it \u2014 to keep the defense-in-depth guard and the\n * existing test contract (R1 mitigation #3) intact.\n */\ntype StepOverrideWithTools = NonNullable<PrepareStepResult<ToolSet>> & {\n tools?: Record<string, unknown>\n}\n\nfunction normalizeAllowedToolNameForAgent(\n toolName: string,\n agent: AiAgentDefinition,\n): string | null {\n if (agent.allowedTools.includes(toolName)) return toolName\n const dottedName = desanitizeToolNameForDisplay(toolName)\n return agent.allowedTools.includes(dottedName) ? dottedName : null\n}\n\nexport function mergeStepOverrides(\n wrapperOverride: PrepareStepResult<ToolSet>,\n userOverride: PrepareStepResult<ToolSet> | undefined | null,\n agent: AiAgentDefinition,\n wrappedToolRegistry: Record<string, unknown>,\n): PrepareStepResult<ToolSet> {\n if (!userOverride) return wrapperOverride\n\n const merged: StepOverrideWithTools = { ...(wrapperOverride as StepOverrideWithTools) }\n const userWithTools = userOverride as StepOverrideWithTools\n\n if (userOverride.model !== undefined) {\n merged.model = userOverride.model\n }\n if (userOverride.toolChoice !== undefined) {\n merged.toolChoice = userOverride.toolChoice\n }\n\n if (userOverride.activeTools !== undefined) {\n const filtered = userOverride.activeTools.flatMap((name) => {\n const normalized = normalizeAllowedToolNameForAgent(name, agent)\n const allowed = normalized !== null\n if (!allowed) {\n console.warn(\n `[AI Agents] loop:active_tools_filtered \u2014 tool \"${name}\" is not in agent \"${agent.id}\" allowedTools; dropping from activeTools.`,\n )\n }\n return normalized ? [normalized] : []\n })\n merged.activeTools = filtered\n }\n\n if (userWithTools.tools !== undefined) {\n const userTools = userWithTools.tools\n const mergedTools: Record<string, unknown> = {}\n\n for (const [toolKey, userHandler] of Object.entries(userTools)) {\n const wrappedHandler = wrappedToolRegistry[toolKey]\n if (!wrappedHandler) {\n console.warn(\n `[AI Agents] mergeStepOverrides \u2014 tool \"${toolKey}\" from user prepareStep is not in the wrapper tool registry; dropping.`,\n )\n continue\n }\n if (userHandler !== wrappedHandler) {\n const toolDef = toolRegistry.getTool(\n toolKey.replace(/__/g, '.'),\n ) as { isMutation?: boolean } | undefined\n if (toolDef?.isMutation === true) {\n throw new AgentPolicyError(\n 'loop_violates_mutation_policy',\n `User prepareStep returned a tools map with raw (unwrapped) mutation handler for \"${toolKey}\". This bypasses the mutation-approval gate and is rejected.`,\n )\n }\n }\n mergedTools[toolKey] = wrappedHandler\n }\n merged.tools = mergedTools\n }\n\n return merged\n}\n\nfunction mapPrepareStepResultForModel(\n result: PrepareStepResult<ToolSet>,\n wrappedTools: Record<string, unknown>,\n): PrepareStepResult<ToolSet> {\n if (!result?.activeTools) return result\n const activeTools = result.activeTools\n .map((toolName) => sanitizeToolNameForModel(toolName))\n .filter((toolName) => wrappedTools[toolName] !== undefined)\n return { ...result, activeTools }\n}\n\nfunction mapToolChoiceForModel(\n toolChoice: AiAgentLoopConfig['toolChoice'],\n): AiAgentLoopConfig['toolChoice'] {\n if (!toolChoice || typeof toolChoice !== 'object' || toolChoice.type !== 'tool') {\n return toolChoice\n }\n return {\n ...toolChoice,\n toolName: sanitizeToolNameForModel(toolChoice.toolName),\n } as AiAgentLoopConfig['toolChoice']\n}\n\nfunction mapActiveToolsForModel(\n activeTools: string[] | undefined,\n wrappedTools: Record<string, unknown>,\n): string[] | undefined {\n if (!activeTools) return undefined\n return activeTools\n .map((toolName) => sanitizeToolNameForModel(toolName))\n .filter((toolName) => wrappedTools[toolName] !== undefined)\n}\n\n/**\n * Builds the wrapper-owned `PrepareStepFunction` that enforces the tool\n * allowlist and mutation-approval contract on every step, then composes\n * the user's `prepareStep` on top via `mergeStepOverrides`.\n *\n * This is the SECURITY-CRITICAL function for Phase 0. The wrapper `prepareStep`\n * ensures:\n * - Tool active-set is always a subset of `effectiveLoop.activeTools ?? agent.allowedTools`.\n * - Mutation tools always point at the prepareMutation-wrapped handlers.\n * - User's `prepareStep` return value cannot smuggle raw mutation handlers.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function buildWrapperPrepareStep(\n agent: AiAgentDefinition,\n effectiveLoop: AiAgentLoopConfig,\n wrappedTools: Record<string, unknown>,\n): PrepareStepFunction<ToolSet> {\n return async (state) => {\n const wrapperOverride: PrepareStepResult<ToolSet> = {}\n\n if (effectiveLoop.activeTools && effectiveLoop.activeTools.length > 0) {\n wrapperOverride.activeTools = effectiveLoop.activeTools.flatMap((name) => {\n const normalized = normalizeAllowedToolNameForAgent(name, agent)\n const allowed = normalized !== null\n if (!allowed) {\n console.warn(\n `[AI Agents] loop:active_tools_filtered \u2014 tool \"${name}\" is not in agent \"${agent.id}\" allowedTools; dropping from activeTools.`,\n )\n }\n return normalized ? [normalized] : []\n })\n }\n\n if (effectiveLoop.prepareStep) {\n let userOverride: PrepareStepResult<ToolSet> | undefined | null\n try {\n userOverride = await effectiveLoop.prepareStep(state)\n } catch (error) {\n console.error(\n `[AI Agents] User prepareStep threw for agent \"${agent.id}\"; ignoring user override:`,\n error,\n )\n return mapPrepareStepResultForModel(wrapperOverride, wrappedTools)\n }\n return mapPrepareStepResultForModel(\n mergeStepOverrides(wrapperOverride, userOverride, agent, wrappedTools),\n wrappedTools,\n )\n }\n\n return mapPrepareStepResultForModel(wrapperOverride, wrappedTools)\n }\n}\n\n/**\n * Validates that a loop config does not set any primitives that are\n * unsupported by the object-mode SDK path (`generateObject` / `streamObject`).\n *\n * Object mode accepts ONLY: `maxSteps`, `budget`, `onStepFinish`,\n * `onStepStart`, `allowRuntimeOverride`. The remaining fields\n * (`prepareStep`, `repairToolCall`, `stopWhen`, `activeTools`,\n * `toolChoice`) are chat-only and will never reach `generateObject`.\n *\n * Throws `AgentPolicyError` code `loop_unsupported_in_object_mode` if any\n * unsupported field is set. This provides an explicit, actionable error\n * rather than a silent no-op.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport function assertLoopObjectModeCompatible(loopConfig: Partial<AiAgentLoopConfig>): void {\n const unsupportedFields: string[] = []\n\n if (loopConfig.prepareStep !== undefined) unsupportedFields.push('prepareStep')\n if (loopConfig.repairToolCall !== undefined) unsupportedFields.push('repairToolCall')\n if (loopConfig.stopWhen !== undefined) unsupportedFields.push('stopWhen')\n if (loopConfig.activeTools !== undefined) unsupportedFields.push('activeTools')\n if (loopConfig.toolChoice !== undefined) unsupportedFields.push('toolChoice')\n\n if (unsupportedFields.length > 0) {\n throw new AgentPolicyError(\n 'loop_unsupported_in_object_mode',\n `Object-mode agents do not support these loop primitives: ${unsupportedFields.join(', ')}. Use runAiAgentText for agents that require these loop controls.`,\n )\n }\n}\n\ninterface ResolvedAgentModel {\n model: LanguageModel\n modelId: string\n providerId: string\n}\n\nfunction resolveAgentModel(\n agent: AiAgentDefinition,\n modelOverride: string | undefined,\n providerOverride: string | undefined,\n container: AwilixContainer | undefined,\n baseUrlOverride?: string,\n tenantOverride?: { providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null,\n requestOverride?: { providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null,\n tenantAllowlist?: TenantAllowlistSnapshot | null,\n): ResolvedAgentModel {\n const effectiveContainer = container ?? createContainer()\n const resolution = createModelFactory(effectiveContainer).resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n callerOverride: modelOverride,\n providerOverride,\n baseUrlOverride,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n tenantOverride: tenantOverride ?? undefined,\n requestOverride: requestOverride ?? undefined,\n tenantAllowlist: tenantAllowlist ?? null,\n })\n return {\n model: resolution.model as LanguageModel,\n modelId: resolution.modelId,\n providerId: resolution.providerId,\n }\n}\n\nasync function resolveTenantAllowlistSnapshot(\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<TenantAllowlistSnapshot | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiTenantModelAllowlistRepository(em)\n return await repo.getSnapshot({\n tenantId,\n organizationId: organizationId ?? null,\n })\n } catch (error) {\n console.warn(\n '[AI Agents] Tenant allowlist lookup failed; falling back to env-only enforcement.',\n error,\n )\n return null\n }\n}\n\n/**\n * Composes the effective system prompt for a run. When the agent declares a\n * `resolvePageContext` callback AND the incoming request carries both\n * `entityType` and `recordId`, the callback is invoked and its return value\n * is appended to `agent.systemPrompt`. Throwing callbacks are caught and\n * logged without failing the request \u2014 the spec allows hydration to be\n * best-effort until Step 5.2 wires a stricter contract.\n */\nexport async function composeSystemPrompt(\n agent: AiAgentDefinition,\n pageContext: AgentRequestPageContext | undefined,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<string> {\n const baseFromOverride = await resolveBaseSystemPromptWithOverride(\n agent,\n container,\n tenantId,\n organizationId,\n )\n const resolve = agent.resolvePageContext\n if (!resolve) return baseFromOverride\n const entityType = pageContext?.entityType\n const recordId = pageContext?.recordId\n if (typeof entityType !== 'string' || entityType.length === 0) return baseFromOverride\n if (typeof recordId !== 'string' || recordId.length === 0) return baseFromOverride\n if (!container) {\n console.warn(\n `[AI Agents] Agent \"${agent.id}\" declares resolvePageContext but no container was passed to runAiAgentText; skipping hydration.`,\n )\n return baseFromOverride\n }\n const hydrationInput: AiAgentPageContextInput = {\n entityType,\n recordId,\n container,\n tenantId,\n organizationId,\n }\n try {\n const hydrated = await resolve(hydrationInput)\n if (typeof hydrated === 'string' && hydrated.trim().length > 0) {\n return `${baseFromOverride}\\n\\n${hydrated}`\n }\n } catch (error) {\n console.error(\n `[AI Agents] resolvePageContext for agent \"${agent.id}\" failed; continuing without hydration:`,\n error,\n )\n }\n return baseFromOverride\n}\n\n/**\n * Fetches the latest tenant-scoped prompt override for `agent` (if any) and\n * layers it onto the built-in `systemPrompt` via the additive merge helper.\n *\n * BC + fail-open: every failure mode \u2014 missing container, missing `em`\n * registration, repository throw, missing migration \u2014 is logged at `warn`\n * and falls back to `agent.systemPrompt`. A chat turn MUST never fail on\n * override lookup (per Step 5.3 spec: \"If the repo call throws, log and\n * fall back to the built-in prompt \u2014 never fail the chat request\").\n */\nasync function resolveBaseSystemPromptWithOverride(\n agent: AiAgentDefinition,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<string> {\n const base = agent.systemPrompt\n if (!tenantId || !container) return base\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return base\n try {\n const repo = new AiAgentPromptOverrideRepository(em)\n const latest = await repo.getLatest(agent.id, {\n tenantId,\n organizationId: organizationId ?? null,\n })\n if (!latest || !latest.sections || Object.keys(latest.sections).length === 0) {\n return base\n }\n return composeSystemPromptWithOverride(base, { sections: latest.sections })\n } catch (error) {\n console.warn(\n `[AI Agents] Prompt-override lookup failed for agent \"${agent.id}\"; falling back to built-in prompt.`,\n error,\n )\n return base\n }\n}\n\n/**\n * Looks up the tenant-scoped `mutationPolicy` override for `agentId` (Step\n * 5.4). Fails SAFE: any repo error, missing container, missing `em`\n * registration, or corrupt enum value returns `null`, which causes the\n * runtime to fall back to the agent's code-declared policy. A chat turn\n * MUST never fail on override lookup.\n */\nasync function resolveMutationPolicyOverride(\n agentId: string,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<AiAgentMutationPolicy | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiAgentMutationPolicyOverrideRepository(em)\n const row = await repo.get(agentId, { tenantId, organizationId: organizationId ?? null })\n if (!row) return null\n const raw = row.mutationPolicy\n if (!isKnownMutationPolicy(raw)) {\n console.warn(\n `[AI Agents] Ignoring corrupt mutationPolicy override row for agent \"${agentId}\": \"${raw}\". Falling back to code-declared policy.`,\n )\n return null\n }\n return raw\n } catch (error) {\n console.warn(\n `[AI Agents] mutationPolicy override lookup failed for agent \"${agentId}\"; falling back to code-declared policy.`,\n error,\n )\n return null\n }\n}\n\n/**\n * Looks up the per-tenant AI runtime override (provider / model / baseURL) for\n * the given agent (Phase 4a of spec\n * `2026-04-27-ai-agents-provider-model-baseurl-overrides`).\n *\n * Mirrors the fail-open contract of {@link resolveMutationPolicyOverride}: any\n * error \u2014 missing container, missing `em`, repository throw, missing migration\n * \u2014 is logged at `warn` level and returns null so the model factory falls\n * through to lower-priority sources. A chat turn MUST never fail on override\n * lookup.\n */\nasync function resolveRuntimeModelOverride(\n agentId: string,\n container: AwilixContainer | undefined,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<{ providerId?: string | null; modelId?: string | null; baseURL?: string | null } | null> {\n if (!tenantId || !container) return null\n let em: EntityManager | null = null\n try {\n em = container.resolve<EntityManager>('em')\n } catch {\n em = null\n }\n if (!em) return null\n try {\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const row = await repo.getDefault({\n tenantId,\n organizationId: organizationId ?? null,\n agentId,\n })\n if (!row) return null\n return {\n providerId: row.providerId ?? null,\n modelId: row.modelId ?? null,\n baseURL: row.baseUrl ?? null,\n }\n } catch (error) {\n console.warn(\n `[AI Agents] Runtime model override lookup failed for agent \"${agentId}\"; falling back to lower-priority sources.`,\n error,\n )\n return null\n }\n}\n\n/**\n * Normalizes simple `{ role, content }` chat messages into the AI SDK\n * `UIMessage` shape that `convertToModelMessages` requires. When the\n * incoming message already carries a `parts` array it is left untouched;\n * otherwise a single `TextUIPart` is synthesized from `content`.\n */\nfunction ensureUiMessageShape(messages: UIMessage[]): UIMessage[] {\n return messages.map((message, index) => {\n const raw = message as unknown as { id?: string; role?: string; content?: string; parts?: unknown[] }\n if (Array.isArray(raw.parts) && raw.parts.length > 0) {\n // Already has parts \u2014 only ensure `id` is present\n return { ...message, id: raw.id ?? `msg-${index}` } as UIMessage\n }\n const textContent = typeof raw.content === 'string' ? raw.content : ''\n return {\n id: raw.id ?? `msg-${index}`,\n role: raw.role ?? 'user',\n parts: [{ type: 'text', text: textContent }],\n } as unknown as UIMessage\n })\n}\n\n/**\n * Appends AI SDK v6 `FileUIPart` entries to the last user message in the\n * request so resolved attachment bytes / signed URLs reach the model. Pure\n * helper so chat-mode and object-mode share identical behavior \u2014 any\n * divergence here breaks the Step 3.6 parity contract.\n */\nfunction attachAttachmentsToMessages(\n messages: UIMessage[],\n parts: readonly AiResolvedAttachmentPart[],\n): UIMessage[] {\n if (parts.length === 0) return messages\n const fileParts = attachmentPartsToUiFileParts(parts)\n if (fileParts.length === 0) return messages\n const next = messages.slice()\n let lastUserIndex = -1\n for (let index = next.length - 1; index >= 0; index -= 1) {\n const candidate = next[index] as unknown as { role?: string }\n if (candidate?.role === 'user') {\n lastUserIndex = index\n break\n }\n }\n if (lastUserIndex === -1) {\n next.push({\n id: 'ai-runtime-attachments',\n role: 'user',\n parts: fileParts as unknown as UIMessage['parts'],\n } as unknown as UIMessage)\n return next\n }\n const source = next[lastUserIndex] as unknown as { parts?: unknown[] }\n const existingParts = Array.isArray(source.parts) ? source.parts : []\n next[lastUserIndex] = {\n ...(next[lastUserIndex] as object),\n parts: [...existingParts, ...fileParts],\n } as UIMessage\n return next\n}\n\nfunction appendAttachmentSummary(\n systemPrompt: string,\n parts: readonly AiResolvedAttachmentPart[],\n): string {\n const summary = summarizeAttachmentPartsForPrompt(parts)\n if (!summary) return systemPrompt\n return `${systemPrompt}\\n\\n${summary}`\n}\n\n/**\n * Builds a runtime \"MUTATION POLICY (RUNTIME)\" block describing the\n * EFFECTIVE policy for this turn \u2014 what the model should expect when it\n * calls each whitelisted mutation tool. Generated dynamically because:\n *\n * - the agent's static prompt cannot know which per-tenant override is\n * in force (`destructive-confirm-required` flips most writes to\n * run-direct) and would otherwise mislead the operator with stale\n * \"this requires approval\" copy;\n * - the per-tool `isDestructive` flag determines whether each\n * whitelisted write goes through the approval card or runs inline.\n *\n * Without this block, the model parrots its hardcoded \"always route\n * through the approval card\" prompt language and tells the user \"your\n * change is awaiting approval\" when in fact the dispatcher already\n * applied the change directly. The injected block flips the model to\n * report results accurately (\"applied\", \"pending your approval\", or\n * \"blocked because read-only\") tool-by-tool.\n */\nfunction buildRuntimeMutationPolicySection(\n agent: { id: string; mutationPolicy?: string | null; allowedTools: string[] },\n mutationPolicyOverride: string | null,\n): string | null {\n const effective = resolveEffectiveMutationPolicy(\n (agent.mutationPolicy ?? null) as never,\n (mutationPolicyOverride ?? null) as never,\n agent.id,\n )\n const lines: string[] = []\n lines.push('MUTATION POLICY (RUNTIME)')\n lines.push(`Declared agent policy: ${agent.mutationPolicy ?? 'read-only'}.`)\n if (mutationPolicyOverride && mutationPolicyOverride !== agent.mutationPolicy) {\n lines.push(`Tenant override active: ${mutationPolicyOverride}.`)\n }\n lines.push(`Effective policy: ${effective}.`)\n\n // Bucket the agent's allowlisted tools into \"gated\" / \"direct\" / \"conditional\"\n // / \"blocked\" so the model can phrase outcomes correctly per tool.\n // `conditional` covers tools whose `isDestructive` is a predicate function:\n // their gate-vs-direct decision depends on the per-call input (e.g.\n // `customers.manage_deal_comment` gates only its delete branch under\n // `destructive-confirm-required`).\n const direct: string[] = []\n const gated: string[] = []\n const conditional: string[] = []\n const blocked: string[] = []\n for (const toolName of agent.allowedTools) {\n const tool = toolRegistry.getTool(toolName) as\n | { isMutation?: boolean; isDestructive?: boolean | ((input: unknown) => boolean) }\n | undefined\n if (!tool || tool.isMutation !== true) continue\n if (effective === 'read-only') {\n blocked.push(toolName)\n continue\n }\n if (effective === 'confirm-required') {\n gated.push(toolName)\n continue\n }\n // destructive-confirm-required\n if (typeof tool.isDestructive === 'function') {\n conditional.push(toolName)\n } else if (tool.isDestructive === true) {\n gated.push(toolName)\n } else {\n direct.push(toolName)\n }\n }\n\n if (\n direct.length === 0 &&\n gated.length === 0 &&\n conditional.length === 0 &&\n blocked.length === 0\n ) {\n // Read-only agent with no mutation tools \u2014 no runtime policy block needed.\n return null\n }\n if (direct.length > 0) {\n lines.push('')\n lines.push(\n `Tools that WILL RUN DIRECTLY (no approval card, no pending action) under the effective policy: ${direct.join(', ')}.`,\n )\n lines.push(\n 'When you call any of these and the call returns successfully, the change has ALREADY BEEN APPLIED. Report it in the past tense (\"Updated \u2026\", \"Added \u2026\", \"Created \u2026\"). Do NOT tell the operator the action is \"pending your approval\" or \"awaiting confirmation\" \u2014 that would be a false statement under the current policy.',\n )\n }\n if (gated.length > 0) {\n lines.push('')\n lines.push(\n `Tools that REQUIRE APPROVAL under the effective policy: ${gated.join(', ')}.`,\n )\n lines.push(\n 'When you call any of these, the dispatcher returns an \"awaiting confirmation\" envelope and renders an inline approval card. Tell the operator the change is pending their confirmation; do NOT claim it has been applied.',\n )\n }\n if (conditional.length > 0) {\n lines.push('')\n lines.push(\n `Tools whose approval requirement DEPENDS ON THE INPUT under the effective policy: ${conditional.join(', ')}.`,\n )\n lines.push(\n 'These multi-operation tools gate ONLY the destructive branches (typically `operation: \"delete\"` or similar). Read the tool result envelope: if it carries `status: \"pending-confirmation\"` then the change is pending \u2014 tell the operator it needs their approval. If it carries direct success data, the change has ALREADY BEEN APPLIED \u2014 report it in the past tense. Never assume one branch behaves like another.',\n )\n }\n if (blocked.length > 0) {\n lines.push('')\n lines.push(\n `Tools that are BLOCKED under the effective policy (read-only): ${blocked.join(', ')}.`,\n )\n lines.push(\n 'Calls to these tools are refused before the handler runs. Do not attempt them; instead direct the operator to the matching backoffice page or to switch the tenant policy if they have permission.',\n )\n }\n lines.push('')\n lines.push(\n 'This RUNTIME policy block always wins over any conflicting \"approval card\" language earlier in the prompt \u2014 the static prompt is written for the most restrictive case but real behavior depends on the per-call policy described here.',\n )\n return lines.join('\\n')\n}\n\nfunction appendRuntimeMutationPolicy(\n systemPrompt: string,\n agent: { id: string; mutationPolicy?: string | null; allowedTools: string[] },\n mutationPolicyOverride: string | null,\n): string {\n const block = buildRuntimeMutationPolicySection(agent, mutationPolicyOverride)\n if (!block) return systemPrompt\n return `${systemPrompt}\\n\\n${block}`\n}\n\nfunction appendRuntimeTaskPlanPrompt(systemPrompt: string, agent: Pick<AiAgentDefinition, 'taskPlan'>): string {\n if (!isAgentTaskPlanEnabled(agent)) return systemPrompt\n return `${systemPrompt}\\n\\n${TASK_PLAN_RUNTIME_PROMPT_SECTION}`\n}\n\n/**\n * Server-side helper that runs an Open Mercato agent in chat mode via the\n * Vercel AI SDK and returns a streaming `Response` ready to be emitted from a\n * route handler. Shares the same policy gate and tool resolution path as the\n * HTTP dispatcher \u2014 a caller using this helper can never bypass the agent's\n * `requiredFeatures`, `allowedTools`, `executionMode`, or `mutationPolicy`.\n *\n * Attachment-to-model conversion (Step 3.7): resolved\n * {@link AiResolvedAttachmentPart}s are materialized inline as AI SDK v6\n * `FileUIPart` entries on the last user message (images/PDFs) and as a\n * structured `[ATTACHMENTS]` block appended to the system prompt (text\n * extracts + metadata-only summaries). The existing `attachmentIds`\n * pass-through into `resolveAiAgentTools` is preserved \u2014 Step 3.6 parity\n * invariant #7 still holds.\n */\nexport async function runAiAgentText(input: RunAiAgentTextInput): Promise<Response> {\n const [mutationPolicyOverride, tenantRuntimeOverride, tenantAllowlistSnapshot] = await Promise.all([\n resolveMutationPolicyOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveRuntimeModelOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveTenantAllowlistSnapshot(\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n ])\n // Phase 6.2 \u2014 resolve the effective session id. `sessionId` takes precedence\n // over the deprecated `conversationId` alias. When neither is supplied, a\n // fresh UUID is generated server-side so token-usage rows and pending-action\n // idempotency hashes are always correlated within the same session.\n const effectiveSessionId = (input.sessionId ?? input.conversationId) || randomUUID()\n\n const { agent, tools } = await resolveAiAgentTools({\n agentId: input.agentId,\n authContext: input.authContext,\n pageContext: input.pageContext,\n attachmentIds: input.attachmentIds,\n mutationPolicyOverride,\n container: input.container,\n conversationId: effectiveSessionId,\n })\n\n const resolvedAttachments = await resolveAttachmentPartsForAgent({\n agent,\n attachmentIds: input.attachmentIds,\n authContext: input.authContext,\n container: input.container,\n })\n\n const baseSystemPrompt = await composeSystemPrompt(\n agent,\n input.pageContext,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n )\n const systemPrompt = appendRuntimeMutationPolicy(\n appendRuntimeTaskPlanPrompt(appendAttachmentSummary(baseSystemPrompt, resolvedAttachments), agent),\n agent,\n mutationPolicyOverride,\n )\n\n const resolvedModel = resolveAgentModel(\n agent,\n input.modelOverride,\n input.providerOverride,\n input.container,\n input.baseUrlOverride,\n tenantRuntimeOverride,\n input.requestOverride,\n tenantAllowlistSnapshot,\n )\n const { model } = resolvedModel\n const normalizedMessages = ensureUiMessageShape(input.messages)\n const hydratedMessages = attachAttachmentsToMessages(normalizedMessages, resolvedAttachments)\n const modelMessages = await convertToModelMessages(hydratedMessages)\n\n const effectiveLoop = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_CHAT)\n const stopConditions = translateStopConditions(effectiveLoop, sanitizeToolNameForModel)\n const wrapperPrepareStep = buildWrapperPrepareStep(agent, effectiveLoop, tools)\n const sdkActiveTools = mapActiveToolsForModel(effectiveLoop.activeTools, tools)\n const sdkToolChoice = mapToolChoiceForModel(effectiveLoop.toolChoice)\n\n // Phase 3 + Phase 4 \u2014 budget enforcement + LoopTrace collection.\n // Layer order (outer \u2192 inner):\n // budgetEnforcer.wire(traceOnStepFinish) \u2192 traceOnStepFinish calls userOnStepFinish\n // The trace collector builds the per-turn LoopTrace; the budget enforcer\n // aborts via AbortController when any limit is exceeded.\n // Phase 6.2 \u2014 generate a per-call turnId as a UUID.\n const turnId = randomUUID()\n const loopTraceCollector = buildLoopTraceCollector(agent.id, effectiveSessionId, turnId, effectiveLoop.onStepFinish)\n const abortController = new AbortController()\n const budgetEnforcer = new BudgetEnforcer(effectiveLoop.budget, abortController)\n const tracedOnStepFinish = budgetEnforcer.wire(loopTraceCollector.onStepFinish)\n\n // Phase 6.3 \u2014 wire the token-usage recorder into the wrapper-owned onStepFinish.\n // The recorder fires AFTER the trace collector so modelId is already in the trace.\n // It is invoked as void (detached) and MUST NEVER throw per R12.\n // resolvedModel is already computed above (model, modelId, providerId).\n\n let currentStepIndex = 0\n const wiredOnStepFinish: AiAgentLoopConfig['onStepFinish'] = async (event) => {\n const capturedStepIndex = currentStepIndex\n currentStepIndex += 1\n\n if (tracedOnStepFinish) {\n await tracedOnStepFinish(event)\n }\n if (input.container) {\n const rawEvent = event as unknown as {\n usage?: { inputTokens?: number; outputTokens?: number; cachedInputTokens?: number; reasoningTokens?: number }\n finishReason?: string\n }\n void recordTokenUsage(\n {\n authContext: input.authContext,\n agentId: agent.id,\n moduleId: agent.moduleId,\n sessionId: effectiveSessionId,\n turnId,\n stepIndex: capturedStepIndex,\n providerId: resolvedModel.providerId,\n modelId: resolvedModel.modelId,\n usage: {\n inputTokens: rawEvent.usage?.inputTokens,\n outputTokens: rawEvent.usage?.outputTokens,\n cachedInputTokens: rawEvent.usage?.cachedInputTokens,\n reasoningTokens: rawEvent.usage?.reasoningTokens,\n },\n finishReason: rawEvent.finishReason,\n loopAbortReason: budgetEnforcer.abortReason ?? undefined,\n },\n input.container,\n )\n }\n }\n\n let wallClockTimer: ReturnType<typeof setTimeout> | undefined\n if (effectiveLoop.budget?.maxWallClockMs) {\n wallClockTimer = setTimeout(() => {\n budgetEnforcer.recordStep({ toolCalls: 0 })\n }, effectiveLoop.budget.maxWallClockMs)\n }\n\n // Phase 5 \u2014 construct ToolLoopAgent when executionEngine === 'tool-loop-agent'.\n // The agent is built ONCE per turn (not pooled) with:\n // - model + tools from the wrapper-resolved registry\n // - stopWhen wired at construction (ToolLoopAgentSettings field)\n // - prepareStep wired at construction (NOT via prepareCall \u2014 it is not in\n // prepareCall's Pick list per spec \u00A7Phase 5 correction)\n // - onStepFinish wired at construction (budget + trace collector)\n // - prepareCall used only for per-turn narrowing of model/tools/stopWhen/\n // activeTools/providerOptions (per spec \u00A7Phase 5 correction)\n let builtToolLoopAgent: ToolLoopAgent<never, ToolSet> | undefined\n if (agent.executionEngine === 'tool-loop-agent') {\n const agentSettings: ToolLoopAgentSettings<never, ToolSet> = {\n model,\n tools: tools as ToolSet,\n stopWhen: stopConditions,\n prepareStep: wrapperPrepareStep,\n onStepFinish: wiredOnStepFinish,\n ...(effectiveLoop.repairToolCall !== undefined\n ? { experimental_repairToolCall: effectiveLoop.repairToolCall }\n : {}),\n ...(sdkActiveTools !== undefined ? { activeTools: sdkActiveTools } : {}),\n ...(sdkToolChoice !== undefined ? { toolChoice: sdkToolChoice } : {}),\n }\n builtToolLoopAgent = new ToolLoopAgent(agentSettings)\n }\n\n const preparedOptions: PreparedAiSdkOptions = {\n model,\n tools,\n system: systemPrompt,\n messages: modelMessages,\n maxSteps: effectiveLoop.maxSteps ?? 10,\n stopWhen: stopConditions,\n prepareStep: wrapperPrepareStep,\n onStepFinish: wiredOnStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n onToolCallStart: effectiveLoop.onToolCallStart,\n onToolCallFinish: effectiveLoop.onToolCallFinish,\n experimental_repairToolCall: effectiveLoop.repairToolCall,\n activeTools: sdkActiveTools,\n toolChoice: sdkToolChoice,\n abortSignal: abortController.signal,\n finalizeLoopTrace: () => loopTraceCollector.finalize(budgetEnforcer.abortReason),\n ...(builtToolLoopAgent !== undefined ? { toolLoopAgent: builtToolLoopAgent } : {}),\n }\n\n // Phase 1 of `2026-05-13-ai-chat-visible-task-plan` \u2014 every chat-mode\n // response stream is wrapped in a task-plan injector. The injector is\n // additive and keyed by the per-turn `turnId` so old clients that ignore\n // unknown chunks keep working; current clients render only agent-authored\n // plan rows and leave raw lifecycle progress in the tool-call details.\n const taskPlanId = `turn_${turnId}`\n\n if (input.generateText) {\n try {\n const callbackResult = await input.generateText(preparedOptions)\n const baseResponse = (callbackResult as StreamTextResult<ToolSet, never>).toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n const withTaskPlan = injectTaskPlanIntoStream(baseResponse, taskPlanId)\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(withTaskPlan, preparedOptions.finalizeLoopTrace)\n }\n return withTaskPlan\n } finally {\n if (wallClockTimer !== undefined) clearTimeout(wallClockTimer)\n }\n }\n\n // Phase 5 \u2014 engine dispatch: tool-loop-agent path vs default stream-text path.\n if (builtToolLoopAgent !== undefined) {\n // `ToolLoopAgent.stream` dispatches via the agent's own prepareCall/prepareStep\n // pipeline. prepareStep is already wired at construction (security-critical).\n const agentStreamResult = await builtToolLoopAgent.stream({\n messages: modelMessages,\n abortSignal: abortController.signal,\n onStepFinish: wiredOnStepFinish,\n })\n if (wallClockTimer !== undefined) {\n const clearTimer = () => clearTimeout(wallClockTimer!)\n Promise.resolve(agentStreamResult.consumeStream()).then(clearTimer, clearTimer)\n }\n const baseResponse = agentStreamResult.toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n const withTaskPlan = injectTaskPlanIntoStream(baseResponse, taskPlanId)\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(withTaskPlan, preparedOptions.finalizeLoopTrace)\n }\n return withTaskPlan\n }\n\n // Default stream-text path (executionEngine === 'stream-text' or unset).\n const result = streamText({\n model,\n system: systemPrompt,\n messages: modelMessages,\n tools,\n stopWhen: stopConditions as never,\n prepareStep: wrapperPrepareStep as never,\n onStepFinish: wiredOnStepFinish as never,\n experimental_onStepStart: effectiveLoop.onStepStart as never,\n experimental_onToolCallStart: effectiveLoop.onToolCallStart as never,\n experimental_onToolCallFinish: effectiveLoop.onToolCallFinish as never,\n experimental_repairToolCall: effectiveLoop.repairToolCall as never,\n ...(sdkActiveTools !== undefined ? { activeTools: sdkActiveTools } : {}),\n ...(sdkToolChoice !== undefined ? { toolChoice: sdkToolChoice } : {}),\n abortSignal: abortController.signal,\n })\n if (wallClockTimer !== undefined) {\n const clearTimer = () => clearTimeout(wallClockTimer!)\n Promise.resolve(result.consumeStream()).then(clearTimer, clearTimer)\n }\n const baseResponse = result.toUIMessageStreamResponse({\n sendReasoning: true,\n headers: {\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n })\n const withTaskPlan = injectTaskPlanIntoStream(baseResponse, taskPlanId)\n if (input.emitLoopTrace) {\n return appendLoopFinishToStream(withTaskPlan, preparedOptions.finalizeLoopTrace)\n }\n return withTaskPlan\n}\n\n/**\n * Runtime override for the structured-output schema used by {@link runAiAgentObject}.\n * When the agent itself declares no `output` block, the caller MUST supply this;\n * otherwise the helper rejects with {@link AgentPolicyError} code\n * `execution_mode_not_supported`.\n */\nexport interface RunAiAgentObjectOutputOverride<TSchema = ZodTypeAny> {\n schemaName: string\n schema: TSchema\n /**\n * `'generate'` (default) calls AI SDK `generateObject` and resolves to the\n * parsed object. `'stream'` calls `streamObject` and returns the SDK's\n * streaming handle so callers can consume partial objects / text deltas.\n */\n mode?: 'generate' | 'stream'\n}\n\nexport interface RunAiAgentObjectInput<TSchema = ZodTypeAny> {\n agentId: string\n /**\n * Accepts either a bare user prompt (wrapped as `[{ role: 'user', content }]`)\n * or a prebuilt `UIMessage[]` array \u2014 matches the source spec's\n * `RunAiAgentObjectInput` contract (\u00A71149\u20131160).\n */\n input: string | UIMessage[]\n attachmentIds?: string[]\n pageContext?: AgentRequestPageContext\n /**\n * Same Phase-1 shim as {@link RunAiAgentTextInput.authContext}. Required until\n * a global request-context resolver lands (Phase 4).\n */\n authContext: AiChatRequestContext\n modelOverride?: string\n /**\n * Optional request-time provider override. When non-empty, wins for the\n * provider axis at the same priority as `modelOverride` for the model axis.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n providerOverride?: string\n /**\n * Optional per-call base URL override. Wins over every other source in the\n * baseURL resolution chain. Intended for programmatic callers only \u2014 the\n * HTTP query-param baseUrl and the AI_RUNTIME_BASEURL_ALLOWLIST arrive in\n * Phase 4a and MUST NOT be exposed here.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n baseUrlOverride?: string\n /**\n * Per-request HTTP dispatcher override (query params `?provider=`, `?model=`,\n * `?baseUrl=`). Validated by the dispatcher route before being forwarded\n * here. Wins over tenantOverride and all lower-priority sources when\n * `agent.allowRuntimeOverride !== false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n requestOverride?: {\n providerId?: string | null\n modelId?: string | null\n baseURL?: string | null\n }\n output?: RunAiAgentObjectOutputOverride<TSchema>\n debug?: boolean\n container?: AwilixContainer\n /**\n * Optional stable per-run session id for token-usage correlation.\n * Object-mode runs are single-turn by definition but the session id lets\n * callers group multiple object runs together for reporting.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId?: string | null\n /**\n * Optional per-call loop config override for object mode. Only the\n * object-safe subset is accepted: `maxSteps`, `budget`, `onStepFinish`,\n * `onStepStart`, and `allowRuntimeOverride`. Providing any chat-only\n * field (`prepareStep`, `repairToolCall`, `stopWhen`, `activeTools`,\n * `toolChoice`) throws `AgentPolicyError` code\n * `loop_unsupported_in_object_mode`.\n *\n * Phase 1 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: Pick<AiAgentLoopConfig, 'maxSteps' | 'budget' | 'onStepFinish' | 'onStepStart' | 'allowRuntimeOverride'>\n /**\n * Optional escape-hatch callback receiving the fully prepared object-mode\n * options bag. When supplied the wrapper still enforces all policy guardrails\n * and then delegates the actual SDK call to this function. The callback MUST\n * return a value compatible with `GenerateObjectResult` or `StreamObjectResult`.\n *\n * Phase 2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n generateObject?: (\n options: PreparedAiSdkObjectOptions,\n ) => Promise<GenerateObjectResult<unknown> | StreamObjectResult<unknown, unknown, unknown>>\n}\n\nexport type RunAiAgentObjectGenerateResult<TSchema> = {\n mode: 'generate'\n object: TSchema\n finishReason?: string\n usage?: { inputTokens?: number; outputTokens?: number }\n}\n\nexport type RunAiAgentObjectStreamResult<TSchema> = {\n mode: 'stream'\n /** Full parsed object once the stream completes. */\n object: Promise<TSchema>\n /** Async iterator of partial (progressively hydrated) objects. */\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n /** Async iterator of the raw text deltas the model emitted. */\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n}\n\nexport type RunAiAgentObjectResult<TSchema> =\n | RunAiAgentObjectGenerateResult<TSchema>\n | RunAiAgentObjectStreamResult<TSchema>\n\nfunction normalizeObjectMessages(input: string | UIMessage[]): UIMessage[] {\n if (typeof input === 'string') {\n return [\n {\n id: 'user-input',\n role: 'user',\n parts: [{ type: 'text', text: input }],\n } as unknown as UIMessage,\n ]\n }\n return input\n}\n\nfunction resolveStructuredOutput<TSchema>(\n agent: AiAgentDefinition,\n override: RunAiAgentObjectOutputOverride<TSchema> | undefined,\n): { schemaName: string; schema: unknown; mode: 'generate' | 'stream' } {\n if (override) {\n return {\n schemaName: override.schemaName,\n schema: override.schema as unknown,\n mode: override.mode ?? 'generate',\n }\n }\n const declared = agent.output as AiAgentStructuredOutput | undefined\n if (!declared) {\n throw new AgentPolicyError(\n 'execution_mode_not_supported',\n `Agent \"${agent.id}\" does not declare a structured-output schema; pass runAiAgentObject({ output }) or declare agent.output.`,\n )\n }\n return {\n schemaName: declared.schemaName,\n schema: declared.schema as unknown,\n mode: declared.mode ?? 'generate',\n }\n}\n\n/**\n * Server-side helper that runs an Open Mercato agent in structured-output mode\n * via the Vercel AI SDK. Shares the same policy gate, tool resolution path,\n * system-prompt composition, and model resolution as {@link runAiAgentText} \u2014\n * object-mode and chat-mode CANNOT diverge.\n *\n * Attachment-to-model conversion (Step 3.7): resolved\n * {@link AiResolvedAttachmentPart}s are materialized inline as AI SDK v6\n * `FileUIPart` entries on the last user message (images/PDFs) and as a\n * structured `[ATTACHMENTS]` block appended to the system prompt (text\n * extracts + metadata-only summaries). Matches {@link runAiAgentText} byte-\n * for-byte so the Step 3.6 parity contract is preserved.\n */\nexport async function runAiAgentObject<TSchema = unknown>(\n input: RunAiAgentObjectInput<TSchema>,\n): Promise<RunAiAgentObjectResult<TSchema>> {\n const [mutationPolicyOverride, tenantRuntimeOverride, tenantAllowlistSnapshot] = await Promise.all([\n resolveMutationPolicyOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveRuntimeModelOverride(\n input.agentId,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n resolveTenantAllowlistSnapshot(\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n ),\n ])\n const { agent, tools } = await resolveAiAgentTools({\n agentId: input.agentId,\n authContext: input.authContext,\n pageContext: input.pageContext,\n attachmentIds: input.attachmentIds,\n requestedExecutionMode: 'object',\n mutationPolicyOverride,\n container: input.container,\n })\n\n const resolvedOutput = resolveStructuredOutput(agent, input.output)\n\n const resolvedAttachments = await resolveAttachmentPartsForAgent({\n agent,\n attachmentIds: input.attachmentIds,\n authContext: input.authContext,\n container: input.container,\n })\n\n const baseSystemPrompt = await composeSystemPrompt(\n agent,\n input.pageContext,\n input.container,\n input.authContext.tenantId,\n input.authContext.organizationId,\n )\n const systemPrompt = appendRuntimeMutationPolicy(\n appendRuntimeTaskPlanPrompt(appendAttachmentSummary(baseSystemPrompt, resolvedAttachments), agent),\n agent,\n mutationPolicyOverride,\n )\n\n const { model } = resolveAgentModel(\n agent,\n input.modelOverride,\n input.providerOverride,\n input.container,\n input.baseUrlOverride,\n tenantRuntimeOverride,\n input.requestOverride,\n tenantAllowlistSnapshot,\n )\n const normalizedMessages = ensureUiMessageShape(normalizeObjectMessages(input.input))\n const hydratedMessages = attachAttachmentsToMessages(\n normalizedMessages,\n resolvedAttachments,\n )\n const modelMessages = await convertToModelMessages(hydratedMessages)\n void tools\n\n // Phase 6.2 \u2014 resolve session id and generate per-call turn id for\n // token-usage correlation in object mode.\n const effectiveObjectSessionId = input.sessionId ?? randomUUID()\n const objectTurnId = randomUUID()\n // Expose for token recorder via closure (used by token-usage-recorder in Phase 6.3).\n void effectiveObjectSessionId\n void objectTurnId\n\n if (input.loop) {\n assertLoopObjectModeCompatible(input.loop)\n }\n const effectiveLoop = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_OBJECT)\n\n const abortController = new AbortController()\n\n const preparedObjectOptions: PreparedAiSdkObjectOptions = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schemaName: resolvedOutput.schemaName,\n schema: resolvedOutput.schema,\n maxSteps: effectiveLoop.maxSteps,\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n }\n\n if (input.generateObject) {\n const callbackResult = await input.generateObject(preparedObjectOptions)\n const typedResult = callbackResult as unknown as Record<string, unknown>\n if ('partialObjectStream' in typedResult) {\n const streamResult = typedResult as {\n object: Promise<TSchema>\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n }\n return {\n mode: 'stream',\n object: streamResult.object,\n partialObjectStream: streamResult.partialObjectStream,\n textStream: streamResult.textStream,\n finishReason: streamResult.finishReason,\n usage: streamResult.usage,\n }\n }\n const genResult = typedResult as { object: unknown; finishReason?: string; usage?: { inputTokens?: number; outputTokens?: number } }\n return {\n mode: 'generate',\n object: genResult.object as TSchema,\n finishReason: genResult.finishReason,\n usage: genResult.usage,\n }\n }\n\n if (resolvedOutput.mode === 'stream') {\n const streamArgs = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schema: resolvedOutput.schema as never,\n schemaName: resolvedOutput.schemaName,\n ...(effectiveLoop.maxSteps !== undefined ? { maxSteps: effectiveLoop.maxSteps } : {}),\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n } as Parameters<typeof streamObject>[0]\n const result = streamObject(streamArgs) as unknown as {\n object: Promise<TSchema>\n partialObjectStream: AsyncIterable<Partial<TSchema>>\n textStream: AsyncIterable<string>\n finishReason?: Promise<string | undefined>\n usage?: Promise<{ inputTokens?: number; outputTokens?: number } | undefined>\n }\n return {\n mode: 'stream',\n object: result.object,\n partialObjectStream: result.partialObjectStream,\n textStream: result.textStream,\n finishReason: result.finishReason,\n usage: result.usage,\n }\n }\n\n const generateArgs = {\n model,\n system: systemPrompt,\n messages: modelMessages,\n schema: resolvedOutput.schema as never,\n schemaName: resolvedOutput.schemaName,\n ...(effectiveLoop.maxSteps !== undefined ? { maxSteps: effectiveLoop.maxSteps } : {}),\n onStepFinish: effectiveLoop.onStepFinish,\n onStepStart: effectiveLoop.onStepStart,\n abortSignal: abortController.signal,\n } as Parameters<typeof generateObject>[0]\n\n const result = await generateObject(generateArgs)\n return {\n mode: 'generate',\n object: (result as { object: unknown }).object as TSchema,\n finishReason: (result as { finishReason?: string }).finishReason,\n usage: (result as { usage?: { inputTokens?: number; outputTokens?: number } }).usage,\n }\n}\n\nexport { AgentPolicyError }\n"],
5
+ "mappings": "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",
6
+ "names": ["baseResponse", "withTaskPlan", "result"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/ai-agent-definition.ts"],
4
- "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { ZodTypeAny } from 'zod'\nimport type {\n PrepareStepFunction,\n GenerateTextOnStepFinishCallback,\n GenerateTextOnStepStartCallback,\n GenerateTextOnToolCallStartCallback,\n GenerateTextOnToolCallFinishCallback,\n ToolCallRepairFunction,\n StopCondition,\n ToolChoice,\n ToolSet,\n} from 'ai'\n\nexport type AiAgentExecutionMode = 'chat' | 'object'\n\n/**\n * Selects the underlying Vercel AI SDK dispatch strategy for this agent.\n *\n * - `'stream-text'` (default): the runtime calls `streamText(...)` directly on\n * every turn. All loop primitives are supported: `prepareStep`, `stopWhen`,\n * `repairToolCall`, `activeTools`, `toolChoice`.\n *\n * - `'tool-loop-agent'`: the runtime constructs a `ToolLoopAgent`\n * (`Experimental_Agent`) once and dispatches via `agent.generate(...)` /\n * `agent.stream(...)` per turn. The wrapper-owned `prepareStep` (security-\n * critical for mutation-approval) is supplied at construction via\n * `settings.prepareStep`. `stopWhen` is similarly wired at construction.\n * The `prepareCall` hook is used for per-turn narrowing of `model`, `tools`,\n * `stopWhen`, `activeTools`, and `providerOptions`; `prepareStep` is NOT in\n * its `Pick` list and MUST NOT be threaded through it.\n *\n * Note: the current SDK version ships `experimental_repairToolCall` on\n * `ToolLoopAgentSettings`, so `repairToolCall` is technically reachable via\n * this engine. The `loop.repairToolCall` JSDoc retains a caveat reflecting\n * the spec's documented limitation, which was written against an earlier SDK\n * snapshot where the setting was absent \u2014 use with awareness that SDK\n * behaviour may differ across versions.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentExecutionEngine = 'stream-text' | 'tool-loop-agent'\n\n/**\n * A serializable stop condition for the agentic loop. The `kind` field\n * determines which Vercel AI SDK helper is used at runtime:\n * - `stepCount` \u2192 `stepCountIs(count)` \u2014 the loop stops after N steps.\n * - `hasToolCall` \u2192 `hasToolCall(toolName)` \u2014 the loop stops immediately\n * after the model emits a tool call for the named tool.\n * - `custom` \u2014 a raw `StopCondition<ToolSet>` predicate supplied in code.\n * NOT valid from JSON-only override sources (tenant DB overrides); only\n * accepted when declared directly in `agent.loop` or a `runAiAgentText`\n * caller override.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentLoopStopCondition =\n | { kind: 'stepCount'; count: number }\n | { kind: 'hasToolCall'; toolName: string }\n | { kind: 'custom'; stop: StopCondition<ToolSet> }\n\n/**\n * Budget limits for the agentic loop turn. When any limit is exceeded the\n * wrapper's `prepareStep`/`onStepFinish` aborts the turn via the per-turn\n * `AbortController` and the loop terminates with a `loop_budget_exceeded`\n * finish condition.\n *\n * Budget enforcement is implemented in Phase 1782-3; for Phases 0\u20132 the\n * fields are accepted and forwarded to the prepared-options bag but are not\n * actively enforced.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface AiAgentLoopBudget {\n /** Hard cap on tool calls across all steps in this turn. */\n maxToolCalls?: number\n /** Wall-clock cap (ms) per turn; runtime aborts via AbortController. */\n maxWallClockMs?: number\n /** Input+output token cap; aggregated from step `usage` fields. */\n maxTokens?: number\n}\n\n/**\n * First-class loop configuration for an AI agent. Supersedes the flat\n * `maxSteps` alias on `AiAgentDefinition`.\n *\n * All fields are optional; the runtime falls back to the wrapper default\n * (`{ maxSteps: 10 }` for chat, `{ maxSteps: undefined }` for object) when\n * neither the agent nor the caller supplies any loop config.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface AiAgentLoopConfig {\n /** Maximum number of agentic steps before the loop is forced to stop. */\n maxSteps?: number\n /**\n * Additional stop conditions. The wrapper ALWAYS composes these with\n * `stepCountIs(maxSteps ?? 10)` so a misconfigured `hasToolCall` for a\n * non-existent tool can never cause an infinite loop (R3 mitigation).\n */\n stopWhen?: AiAgentLoopStopCondition | AiAgentLoopStopCondition[]\n /**\n * Per-step preparation hook. The wrapper composes this with its own\n * security-critical `prepareStep` that re-asserts the tool allowlist and\n * mutation-approval wrapping per step.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n prepareStep?: PrepareStepFunction<ToolSet>\n /**\n * Callback fired when a step finishes. The wrapper chains its own\n * aggregation callback (LoopTrace builder) before invoking this one.\n * Exceptions thrown by this callback are caught and logged but do not\n * abort the turn (matching the SDK's own contract).\n */\n onStepFinish?: GenerateTextOnStepFinishCallback<ToolSet>\n /**\n * Callback fired when a step starts. Forwarded to the AI SDK as\n * `experimental_onStepStart`.\n */\n onStepStart?: GenerateTextOnStepStartCallback<ToolSet>\n /**\n * Callback fired when a tool call starts. Forwarded to the AI SDK as\n * `experimental_onToolCallStart`.\n */\n onToolCallStart?: GenerateTextOnToolCallStartCallback<ToolSet>\n /**\n * Callback fired when a tool call finishes. Forwarded to the AI SDK as\n * `experimental_onToolCallFinish`.\n */\n onToolCallFinish?: GenerateTextOnToolCallFinishCallback<ToolSet>\n /**\n * Tool-call repair function. Forwarded to the AI SDK as\n * `experimental_repairToolCall`.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n *\n * **Engine note**: this primitive is honored under `executionEngine: 'stream-text'`\n * (default). Agents on `'tool-loop-agent'` may not reliably support\n * `repairToolCall` across all SDK versions \u2014 if you require it, use the\n * default `stream-text` engine until support is confirmed stable on the\n * `ToolLoopAgent` class.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n repairToolCall?: ToolCallRepairFunction<ToolSet>\n /**\n * Narrow the active tool surface for each step. Names must be a subset of\n * `agent.allowedTools`; any names outside the allowlist are filtered out\n * with a `loop:active_tools_filtered` warning.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n activeTools?: string[]\n /**\n * Tool choice strategy forwarded to the AI SDK on each step.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n toolChoice?: ToolChoice<ToolSet>\n /** Budget caps for this loop turn. */\n budget?: AiAgentLoopBudget\n /**\n * When `false`, per-call `runAiAgentText({ loop })` / HTTP query-param\n * overrides are rejected with `AgentPolicyError` code\n * `loop_runtime_override_disabled`. Default is `true` (permissive).\n *\n * Agents that pin a loop policy for correctness reasons (e.g. a\n * `stopWhen: hasToolCall(...)` that must not be bypassed by callers)\n * should set this to `false`.\n */\n allowRuntimeOverride?: boolean\n /**\n * Kill switch \u2014 when `true`, the runtime forces `stopWhen: stepCountIs(1)` and\n * ignores all other loop config. Used by the per-tenant operator override to\n * collapse an agent to a single model call (no tool execution) without\n * disabling the agent entirely.\n *\n * Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n disabled?: boolean\n}\n\n/**\n * Per-step record aggregated by the wrapper-owned `onStepFinish` hook into\n * `LoopTrace`. Each completed agentic step produces one record.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface LoopStepRecord {\n stepIndex: number\n /** Model id resolved for this step (relevant when prepareStep swaps models). */\n modelId: string\n toolCalls: Array<{\n toolName: string\n args: unknown\n result?: unknown\n error?: { code: string; message: string }\n repairAttempted: boolean\n durationMs: number\n }>\n /** Raw assistant text emitted in this step. */\n textDelta: string\n usage: { inputTokens: number; outputTokens: number }\n finishReason: 'stop' | 'tool-calls' | 'length' | 'content-filter' | 'error'\n}\n\n/**\n * Per-turn trace aggregated by the wrapper-owned `buildLoopTraceCollector`.\n * Not persisted \u2014 in-memory only; surfaced via the dispatcher SSE stream and\n * the playground/`<AiChat>` debug panel.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface LoopTrace {\n agentId: string\n /**\n * Stable per-conversation id that ties every turn together. Echoed back on\n * the SSE `loop-finish` event so clients can persist it for subsequent turns.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId: string\n turnId: string\n steps: LoopStepRecord[]\n stopReason:\n | 'step-count'\n | 'has-tool-call'\n | 'custom-stop'\n | 'budget-tokens'\n | 'budget-tool-calls'\n | 'budget-wall-clock'\n | 'tenant-disabled'\n | 'finish-reason'\n | 'abort'\n totalDurationMs: number\n totalUsage: { inputTokens: number; outputTokens: number }\n}\n\nexport type AiAgentMutationPolicy =\n | 'read-only'\n | 'confirm-required'\n | 'destructive-confirm-required'\n\nexport type AiAgentAcceptedMediaType = 'image' | 'pdf' | 'file'\n\nexport type AiAgentDataOperation = 'read' | 'search' | 'aggregate'\n\nexport interface AiAgentPageContextInput {\n entityType: string\n recordId: string\n container: AwilixContainer\n tenantId: string | null\n organizationId: string | null\n}\n\nexport interface AiAgentStructuredOutput<TSchema = ZodTypeAny> {\n schemaName: string\n schema: TSchema\n mode?: 'generate' | 'stream'\n}\n\nexport interface AiAgentDataCapabilities {\n entities?: string[]\n operations?: AiAgentDataOperation[]\n searchableFields?: string[]\n}\n\nexport interface AiAgentSuggestion {\n label: string\n prompt: string\n}\n\nexport interface AiAgentDefinition {\n id: string\n moduleId: string\n label: string\n description: string\n systemPrompt: string\n allowedTools: string[]\n suggestions?: AiAgentSuggestion[]\n executionMode?: AiAgentExecutionMode\n /**\n * Selects the underlying Vercel AI SDK dispatch strategy for this agent.\n * Defaults to `'stream-text'` \u2014 the existing behavior and the only engine\n * with unconditional full primitive coverage (`repairToolCall`, all loop\n * controls).\n *\n * Set to `'tool-loop-agent'` to use the `ToolLoopAgent` (`Experimental_Agent`)\n * class, which is closer to a semantic agent abstraction and receives upcoming\n * SDK features (multi-agent handoff, streaming approval responses) first.\n *\n * **Note on `repairToolCall`**: the current SDK version ships\n * `experimental_repairToolCall` on `ToolLoopAgentSettings`, so the primitive\n * is technically available. However, SDK behaviour is not guaranteed to be\n * identical across versions \u2014 prefer `'stream-text'` when `repairToolCall`\n * correctness is critical.\n *\n * This field is opt-in: omitting it leaves the existing `stream-text` path\n * completely unchanged.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n executionEngine?: AiAgentExecutionEngine\n /**\n * Optional provider id this agent prefers (e.g. `'openai'`, `'anthropic'`).\n * Must match a registered `LlmProvider.id`. When the named provider is\n * registered but unconfigured at runtime the factory falls through\n * transparently to the next configured provider.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultProvider?: string\n /**\n * Optional model id fed through `createModelFactory` for this agent.\n * Accepts either a plain model id (`claude-haiku-4-5-20251001`) or a\n * slash-qualified `<provider>/<model>` shorthand (e.g. `openai/gpt-5-mini`).\n * When the slash form is used the prefix must match a registered provider id;\n * the registry-membership guard prevents mis-splitting model ids that already\n * contain slashes (DeepInfra: `meta-llama/Llama-3.3-70B-Instruct-Turbo`).\n *\n * A higher-priority provider source still wins over the slash hint, but a\n * lower-priority one cannot overwrite a slash-qualified model (cross-axis\n * tie-break rule from spec \u00A7Phase-1).\n *\n * Phase 0 and Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultModel?: string\n /**\n * Optional base URL this agent prefers for its chosen provider.\n * Sits between the `<MODULE>_AI_BASE_URL` env (step 2 of the public 5-step\n * baseURL hierarchy) and the preset env override (`baseURLEnvKeys`, step 4).\n * Only honoured by adapters that support baseURL (Anthropic Messages-\n * protocol relays, all OpenAI-compatible adapters, Google via\n * @ai-sdk/google \u22653.0). See `packages/ai-assistant/AGENTS.md` \u2192\n * \"baseURL override hierarchy\" for the full numbered chain.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultBaseUrl?: string\n /**\n * When false, per-request HTTP overrides (query params `provider`, `model`,\n * `baseUrl`, `loopBudget`) and the per-tenant settings override stored in\n * `ai_agent_runtime_overrides` are both suppressed. Steps 1 and 3 of the\n * model-factory resolution chain are skipped for this agent, and the\n * `loopBudget` query parameter is ignored by the chat dispatcher.\n *\n * Default is `true` (permissive). Agents that pin a specific model for\n * correctness reasons (e.g. a structured-output agent whose JSON-mode schema\n * only works with one provider) should set this to `false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n * Renamed from `allowRuntimeModelOverride` in Phase 4 of spec\n * `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n allowRuntimeOverride?: boolean\n /**\n * @deprecated Use `allowRuntimeOverride` instead. This alias is kept for\n * one minor release and will be removed in a future version. The runtime\n * checks `allowRuntimeOverride` first; if absent it falls back to this field.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n allowRuntimeModelOverride?: boolean\n acceptedMediaTypes?: AiAgentAcceptedMediaType[]\n requiredFeatures?: string[]\n uiParts?: string[]\n readOnly?: boolean\n mutationPolicy?: AiAgentMutationPolicy\n /**\n * @deprecated Use `loop.maxSteps` instead. Honored as alias when `loop` is\n * omitted. When both `maxSteps` and `loop.maxSteps` are specified, `loop.maxSteps`\n * wins. This field will be removed in a future minor release.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n maxSteps?: number\n /**\n * First-class agentic loop configuration. Supersedes the flat `maxSteps`\n * alias. The runtime walks a precedence chain (per-call override \u2192 tenant\n * DB override \u2192 this block \u2192 legacy `maxSteps` alias \u2192 wrapper default)\n * to resolve the effective loop config for each turn.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: AiAgentLoopConfig\n output?: AiAgentStructuredOutput\n resolvePageContext?: (ctx: AiAgentPageContextInput) => Promise<string | null>\n keywords?: string[]\n domain?: string\n dataCapabilities?: AiAgentDataCapabilities\n}\n\nexport interface AiAgentExtension {\n targetAgentId: string\n replaceAllowedTools?: string[]\n deleteAllowedTools?: string[]\n appendAllowedTools?: string[]\n replaceSystemPrompt?: string\n appendSystemPrompt?: string\n replaceSuggestions?: AiAgentSuggestion[]\n deleteSuggestions?: string[]\n appendSuggestions?: AiAgentSuggestion[]\n /**\n * @deprecated Use `appendSuggestions` for new code. Preserved as the\n * original append-only field for backward compatibility.\n */\n suggestions?: AiAgentSuggestion[]\n}\n\nexport function defineAiAgent(definition: AiAgentDefinition): AiAgentDefinition {\n return definition\n}\n\nexport function defineAiAgentExtension(extension: AiAgentExtension): AiAgentExtension {\n return extension\n}\n"],
5
- "mappings": "AA8ZO,SAAS,cAAc,YAAkD;AAC9E,SAAO;AACT;AAEO,SAAS,uBAAuB,WAA+C;AACpF,SAAO;AACT;",
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { ZodTypeAny } from 'zod'\nimport type {\n PrepareStepFunction,\n GenerateTextOnStepFinishCallback,\n GenerateTextOnStepStartCallback,\n GenerateTextOnToolCallStartCallback,\n GenerateTextOnToolCallFinishCallback,\n ToolCallRepairFunction,\n StopCondition,\n ToolChoice,\n ToolSet,\n} from 'ai'\n\nexport type AiAgentExecutionMode = 'chat' | 'object'\n\n/**\n * Selects the underlying Vercel AI SDK dispatch strategy for this agent.\n *\n * - `'stream-text'` (default): the runtime calls `streamText(...)` directly on\n * every turn. All loop primitives are supported: `prepareStep`, `stopWhen`,\n * `repairToolCall`, `activeTools`, `toolChoice`.\n *\n * - `'tool-loop-agent'`: the runtime constructs a `ToolLoopAgent`\n * (`Experimental_Agent`) once and dispatches via `agent.generate(...)` /\n * `agent.stream(...)` per turn. The wrapper-owned `prepareStep` (security-\n * critical for mutation-approval) is supplied at construction via\n * `settings.prepareStep`. `stopWhen` is similarly wired at construction.\n * The `prepareCall` hook is used for per-turn narrowing of `model`, `tools`,\n * `stopWhen`, `activeTools`, and `providerOptions`; `prepareStep` is NOT in\n * its `Pick` list and MUST NOT be threaded through it.\n *\n * Note: the current SDK version ships `experimental_repairToolCall` on\n * `ToolLoopAgentSettings`, so `repairToolCall` is technically reachable via\n * this engine. The `loop.repairToolCall` JSDoc retains a caveat reflecting\n * the spec's documented limitation, which was written against an earlier SDK\n * snapshot where the setting was absent \u2014 use with awareness that SDK\n * behaviour may differ across versions.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentExecutionEngine = 'stream-text' | 'tool-loop-agent'\n\n/**\n * A serializable stop condition for the agentic loop. The `kind` field\n * determines which Vercel AI SDK helper is used at runtime:\n * - `stepCount` \u2192 `stepCountIs(count)` \u2014 the loop stops after N steps.\n * - `hasToolCall` \u2192 `hasToolCall(toolName)` \u2014 the loop stops immediately\n * after the model emits a tool call for the named tool.\n * - `custom` \u2014 a raw `StopCondition<ToolSet>` predicate supplied in code.\n * NOT valid from JSON-only override sources (tenant DB overrides); only\n * accepted when declared directly in `agent.loop` or a `runAiAgentText`\n * caller override.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport type AiAgentLoopStopCondition =\n | { kind: 'stepCount'; count: number }\n | { kind: 'hasToolCall'; toolName: string }\n | { kind: 'custom'; stop: StopCondition<ToolSet> }\n\n/**\n * Budget limits for the agentic loop turn. When any limit is exceeded the\n * wrapper's `prepareStep`/`onStepFinish` aborts the turn via the per-turn\n * `AbortController` and the loop terminates with a `loop_budget_exceeded`\n * finish condition.\n *\n * Budget enforcement is implemented in Phase 1782-3; for Phases 0\u20132 the\n * fields are accepted and forwarded to the prepared-options bag but are not\n * actively enforced.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface AiAgentLoopBudget {\n /** Hard cap on tool calls across all steps in this turn. */\n maxToolCalls?: number\n /** Wall-clock cap (ms) per turn; runtime aborts via AbortController. */\n maxWallClockMs?: number\n /** Input+output token cap; aggregated from step `usage` fields. */\n maxTokens?: number\n}\n\n/**\n * First-class loop configuration for an AI agent. Supersedes the flat\n * `maxSteps` alias on `AiAgentDefinition`.\n *\n * All fields are optional; the runtime falls back to the wrapper default\n * (`{ maxSteps: 10 }` for chat, `{ maxSteps: undefined }` for object) when\n * neither the agent nor the caller supplies any loop config.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface AiAgentLoopConfig {\n /** Maximum number of agentic steps before the loop is forced to stop. */\n maxSteps?: number\n /**\n * Additional stop conditions. The wrapper ALWAYS composes these with\n * `stepCountIs(maxSteps ?? 10)` so a misconfigured `hasToolCall` for a\n * non-existent tool can never cause an infinite loop (R3 mitigation).\n */\n stopWhen?: AiAgentLoopStopCondition | AiAgentLoopStopCondition[]\n /**\n * Per-step preparation hook. The wrapper composes this with its own\n * security-critical `prepareStep` that re-asserts the tool allowlist and\n * mutation-approval wrapping per step.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n prepareStep?: PrepareStepFunction<ToolSet>\n /**\n * Callback fired when a step finishes. The wrapper chains its own\n * aggregation callback (LoopTrace builder) before invoking this one.\n * Exceptions thrown by this callback are caught and logged but do not\n * abort the turn (matching the SDK's own contract).\n */\n onStepFinish?: GenerateTextOnStepFinishCallback<ToolSet>\n /**\n * Callback fired when a step starts. Forwarded to the AI SDK as\n * `experimental_onStepStart`.\n */\n onStepStart?: GenerateTextOnStepStartCallback<ToolSet>\n /**\n * Callback fired when a tool call starts. Forwarded to the AI SDK as\n * `experimental_onToolCallStart`.\n */\n onToolCallStart?: GenerateTextOnToolCallStartCallback<ToolSet>\n /**\n * Callback fired when a tool call finishes. Forwarded to the AI SDK as\n * `experimental_onToolCallFinish`.\n */\n onToolCallFinish?: GenerateTextOnToolCallFinishCallback<ToolSet>\n /**\n * Tool-call repair function. Forwarded to the AI SDK as\n * `experimental_repairToolCall`.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n *\n * **Engine note**: this primitive is honored under `executionEngine: 'stream-text'`\n * (default). Agents on `'tool-loop-agent'` may not reliably support\n * `repairToolCall` across all SDK versions \u2014 if you require it, use the\n * default `stream-text` engine until support is confirmed stable on the\n * `ToolLoopAgent` class.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n repairToolCall?: ToolCallRepairFunction<ToolSet>\n /**\n * Narrow the active tool surface for each step. Names must be a subset of\n * `agent.allowedTools`; any names outside the allowlist are filtered out\n * with a `loop:active_tools_filtered` warning.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n activeTools?: string[]\n /**\n * Tool choice strategy forwarded to the AI SDK on each step.\n *\n * Only valid for chat agents. Rejected with `loop_unsupported_in_object_mode`\n * for object-mode agents.\n */\n toolChoice?: ToolChoice<ToolSet>\n /** Budget caps for this loop turn. */\n budget?: AiAgentLoopBudget\n /**\n * When `false`, per-call `runAiAgentText({ loop })` / HTTP query-param\n * overrides are rejected with `AgentPolicyError` code\n * `loop_runtime_override_disabled`. Default is `true` (permissive).\n *\n * Agents that pin a loop policy for correctness reasons (e.g. a\n * `stopWhen: hasToolCall(...)` that must not be bypassed by callers)\n * should set this to `false`.\n */\n allowRuntimeOverride?: boolean\n /**\n * Kill switch \u2014 when `true`, the runtime forces `stopWhen: stepCountIs(1)` and\n * ignores all other loop config. Used by the per-tenant operator override to\n * collapse an agent to a single model call (no tool execution) without\n * disabling the agent entirely.\n *\n * Phase 3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n disabled?: boolean\n}\n\n/**\n * Per-step record aggregated by the wrapper-owned `onStepFinish` hook into\n * `LoopTrace`. Each completed agentic step produces one record.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface LoopStepRecord {\n stepIndex: number\n /** Model id resolved for this step (relevant when prepareStep swaps models). */\n modelId: string\n toolCalls: Array<{\n toolName: string\n args: unknown\n result?: unknown\n error?: { code: string; message: string }\n repairAttempted: boolean\n durationMs: number\n }>\n /** Raw assistant text emitted in this step. */\n textDelta: string\n usage: { inputTokens: number; outputTokens: number }\n finishReason: 'stop' | 'tool-calls' | 'length' | 'content-filter' | 'error'\n}\n\n/**\n * Per-turn trace aggregated by the wrapper-owned `buildLoopTraceCollector`.\n * Not persisted \u2014 in-memory only; surfaced via the dispatcher SSE stream and\n * the playground/`<AiChat>` debug panel.\n *\n * Phase 4 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\nexport interface LoopTrace {\n agentId: string\n /**\n * Stable per-conversation id that ties every turn together. Echoed back on\n * the SSE `loop-finish` event so clients can persist it for subsequent turns.\n *\n * Phase 6.2 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n sessionId: string\n turnId: string\n steps: LoopStepRecord[]\n stopReason:\n | 'step-count'\n | 'has-tool-call'\n | 'custom-stop'\n | 'budget-tokens'\n | 'budget-tool-calls'\n | 'budget-wall-clock'\n | 'tenant-disabled'\n | 'finish-reason'\n | 'abort'\n totalDurationMs: number\n totalUsage: { inputTokens: number; outputTokens: number }\n}\n\nexport type AiAgentMutationPolicy =\n | 'read-only'\n | 'confirm-required'\n | 'destructive-confirm-required'\n\nexport type AiAgentAcceptedMediaType = 'image' | 'pdf' | 'file'\n\nexport type AiAgentDataOperation = 'read' | 'search' | 'aggregate'\n\nexport interface AiAgentPageContextInput {\n entityType: string\n recordId: string\n container: AwilixContainer\n tenantId: string | null\n organizationId: string | null\n}\n\nexport interface AiAgentStructuredOutput<TSchema = ZodTypeAny> {\n schemaName: string\n schema: TSchema\n mode?: 'generate' | 'stream'\n}\n\nexport interface AiAgentDataCapabilities {\n entities?: string[]\n operations?: AiAgentDataOperation[]\n searchableFields?: string[]\n}\n\nexport interface AiAgentSuggestion {\n label: string\n prompt: string\n}\n\nexport interface AiAgentTaskPlanConfig {\n /**\n * Enables the optional visible planning helper for this agent. When true,\n * the runtime exposes `meta.update_task_plan` and injects prompt guidance\n * telling the model to set a user-visible plan before domain tools.\n *\n * Defaults to false. CRM/customer agents enable this by default in core;\n * other agents can opt in from their agent definition or extension config.\n */\n enabled?: boolean\n}\n\nexport interface AiAgentDefinition {\n id: string\n moduleId: string\n label: string\n description: string\n systemPrompt: string\n allowedTools: string[]\n suggestions?: AiAgentSuggestion[]\n executionMode?: AiAgentExecutionMode\n /**\n * Selects the underlying Vercel AI SDK dispatch strategy for this agent.\n * Defaults to `'stream-text'` \u2014 the existing behavior and the only engine\n * with unconditional full primitive coverage (`repairToolCall`, all loop\n * controls).\n *\n * Set to `'tool-loop-agent'` to use the `ToolLoopAgent` (`Experimental_Agent`)\n * class, which is closer to a semantic agent abstraction and receives upcoming\n * SDK features (multi-agent handoff, streaming approval responses) first.\n *\n * **Note on `repairToolCall`**: the current SDK version ships\n * `experimental_repairToolCall` on `ToolLoopAgentSettings`, so the primitive\n * is technically available. However, SDK behaviour is not guaranteed to be\n * identical across versions \u2014 prefer `'stream-text'` when `repairToolCall`\n * correctness is critical.\n *\n * This field is opt-in: omitting it leaves the existing `stream-text` path\n * completely unchanged.\n *\n * Phase 5 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n executionEngine?: AiAgentExecutionEngine\n /**\n * Optional provider id this agent prefers (e.g. `'openai'`, `'anthropic'`).\n * Must match a registered `LlmProvider.id`. When the named provider is\n * registered but unconfigured at runtime the factory falls through\n * transparently to the next configured provider.\n *\n * Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultProvider?: string\n /**\n * Optional model id fed through `createModelFactory` for this agent.\n * Accepts either a plain model id (`claude-haiku-4-5-20251001`) or a\n * slash-qualified `<provider>/<model>` shorthand (e.g. `openai/gpt-5-mini`).\n * When the slash form is used the prefix must match a registered provider id;\n * the registry-membership guard prevents mis-splitting model ids that already\n * contain slashes (DeepInfra: `meta-llama/Llama-3.3-70B-Instruct-Turbo`).\n *\n * A higher-priority provider source still wins over the slash hint, but a\n * lower-priority one cannot overwrite a slash-qualified model (cross-axis\n * tie-break rule from spec \u00A7Phase-1).\n *\n * Phase 0 and Phase 1 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultModel?: string\n /**\n * Optional base URL this agent prefers for its chosen provider.\n * Sits between the `<MODULE>_AI_BASE_URL` env (step 2 of the public 5-step\n * baseURL hierarchy) and the preset env override (`baseURLEnvKeys`, step 4).\n * Only honoured by adapters that support baseURL (Anthropic Messages-\n * protocol relays, all OpenAI-compatible adapters, Google via\n * @ai-sdk/google \u22653.0). See `packages/ai-assistant/AGENTS.md` \u2192\n * \"baseURL override hierarchy\" for the full numbered chain.\n *\n * Phase 2 of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n defaultBaseUrl?: string\n /**\n * When false, per-request HTTP overrides (query params `provider`, `model`,\n * `baseUrl`, `loopBudget`) and the per-tenant settings override stored in\n * `ai_agent_runtime_overrides` are both suppressed. Steps 1 and 3 of the\n * model-factory resolution chain are skipped for this agent, and the\n * `loopBudget` query parameter is ignored by the chat dispatcher.\n *\n * Default is `true` (permissive). Agents that pin a specific model for\n * correctness reasons (e.g. a structured-output agent whose JSON-mode schema\n * only works with one provider) should set this to `false`.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n * Renamed from `allowRuntimeModelOverride` in Phase 4 of spec\n * `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n allowRuntimeOverride?: boolean\n /**\n * @deprecated Use `allowRuntimeOverride` instead. This alias is kept for\n * one minor release and will be removed in a future version. The runtime\n * checks `allowRuntimeOverride` first; if absent it falls back to this field.\n *\n * Phase 4a of spec `2026-04-27-ai-agents-provider-model-baseurl-overrides`.\n */\n allowRuntimeModelOverride?: boolean\n acceptedMediaTypes?: AiAgentAcceptedMediaType[]\n requiredFeatures?: string[]\n taskPlan?: AiAgentTaskPlanConfig\n uiParts?: string[]\n readOnly?: boolean\n mutationPolicy?: AiAgentMutationPolicy\n /**\n * @deprecated Use `loop.maxSteps` instead. Honored as alias when `loop` is\n * omitted. When both `maxSteps` and `loop.maxSteps` are specified, `loop.maxSteps`\n * wins. This field will be removed in a future minor release.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n maxSteps?: number\n /**\n * First-class agentic loop configuration. Supersedes the flat `maxSteps`\n * alias. The runtime walks a precedence chain (per-call override \u2192 tenant\n * DB override \u2192 this block \u2192 legacy `maxSteps` alias \u2192 wrapper default)\n * to resolve the effective loop config for each turn.\n *\n * Phase 0 of spec `2026-04-28-ai-agents-agentic-loop-controls`.\n */\n loop?: AiAgentLoopConfig\n output?: AiAgentStructuredOutput\n resolvePageContext?: (ctx: AiAgentPageContextInput) => Promise<string | null>\n keywords?: string[]\n domain?: string\n dataCapabilities?: AiAgentDataCapabilities\n}\n\nexport interface AiAgentExtension {\n targetAgentId: string\n replaceAllowedTools?: string[]\n deleteAllowedTools?: string[]\n appendAllowedTools?: string[]\n taskPlan?: AiAgentTaskPlanConfig\n replaceSystemPrompt?: string\n appendSystemPrompt?: string\n replaceSuggestions?: AiAgentSuggestion[]\n deleteSuggestions?: string[]\n appendSuggestions?: AiAgentSuggestion[]\n /**\n * @deprecated Use `appendSuggestions` for new code. Preserved as the\n * original append-only field for backward compatibility.\n */\n suggestions?: AiAgentSuggestion[]\n}\n\nexport function defineAiAgent(definition: AiAgentDefinition): AiAgentDefinition {\n return definition\n}\n\nexport function defineAiAgentExtension(extension: AiAgentExtension): AiAgentExtension {\n return extension\n}\n"],
5
+ "mappings": "AA4aO,SAAS,cAAc,YAAkD;AAC9E,SAAO;AACT;AAEO,SAAS,uBAAuB,WAA+C;AACpF,SAAO;AACT;",
6
6
  "names": []
7
7
  }