@agent-native/core 0.92.1 → 0.92.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +32 -0
- package/corpus/core/docs/content/locales/ar-SA/server.mdx +35 -0
- package/corpus/core/docs/content/locales/de-DE/server.mdx +35 -0
- package/corpus/core/docs/content/locales/es-ES/server.mdx +35 -0
- package/corpus/core/docs/content/locales/fr-FR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/hi-IN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ja-JP/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ko-KR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/pt-BR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-CN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-TW/server.mdx +35 -0
- package/corpus/core/docs/content/server.mdx +35 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -4
- package/corpus/core/src/agent/engine/anthropic-engine.ts +15 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +30 -4
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/production-agent.ts +12 -1
- package/corpus/core/src/agent/run-manager.ts +57 -36
- package/corpus/core/src/agent/run-store.ts +81 -2
- package/corpus/core/src/agent/thread-data-builder.ts +33 -2
- package/corpus/core/src/cli/sync-builder-starter-manifest.ts +29 -1
- package/corpus/core/src/client/AssistantChat.tsx +314 -96
- package/corpus/core/src/client/agent-chat-adapter.ts +17 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +45 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +36 -27
- package/corpus/core/src/client/chat-model-groups.ts +1 -5
- package/corpus/core/src/client/composer/TiptapComposer.tsx +0 -2
- package/corpus/core/src/client/embed-auth.ts +20 -0
- package/corpus/core/src/client/settings/SettingsSection.tsx +66 -5
- package/corpus/core/src/client/sse-event-processor.ts +49 -17
- package/corpus/core/src/client/use-db-sync.ts +31 -9
- package/corpus/core/src/collab/agent-presence.ts +6 -1
- package/corpus/core/src/collab/awareness-store.ts +185 -0
- package/corpus/core/src/collab/awareness.ts +58 -2
- package/corpus/core/src/notifications/channels.ts +1 -22
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +7 -8
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +7 -18
- package/corpus/core/src/server/core-routes-plugin.ts +21 -2
- package/corpus/core/src/server/embed-session.ts +20 -4
- package/corpus/core/src/styles/agent-native.css +51 -0
- package/corpus/core/src/tracking/providers.ts +33 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +8 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +9 -1
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +41 -11
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +44 -0
- package/corpus/templates/analytics/changelog/2026-07-08-alert-emails-now-show-the-message-without-raw-metadata-json.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-uptime-checks-no-longer-count-ssrf-setup-time-against-the-re.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-uptime-checks-reuse-one-keep-alive-connection-so-recorded-la.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-uptime-monitors-now-confirm-transient-timeout-failures-befor.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-action-menus-now-close-cleanly-before-opening-hist.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-email-reports-still-send-when-the-screenshot-captu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-alerts-now-suppress-recovery-noise-during-flapping-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-check-history-now-shows-timing-diagnostics-so-slow-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-recovery-alerts-now-send-when-the-original-outage-not.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +47 -22
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +126 -0
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +5 -0
- package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +5 -1
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +538 -54
- package/corpus/templates/analytics/server/plugins/db.ts +15 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -1
- package/corpus/templates/analytics/server/routes/api/uptime-monitors/run.post.ts +57 -0
- package/corpus/templates/clips/actions/list-meetings.ts +12 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-reminders-now-ignore-obvious-solo-personal-calendar-.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-classification.ts +79 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +14 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +26 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +47 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +52 -33
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +24 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +64 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +28 -2
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.d.ts.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.js +24 -1
- package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +243 -36
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +15 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +43 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -3
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +0 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +14 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +32 -3
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +44 -17
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +29 -9
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +5 -2
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +1 -1
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +0 -0
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -0
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +46 -2
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/channels.js +1 -17
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +6 -6
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -13
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +17 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/styles/agent-native.css +51 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +23 -5
- package/dist/tracking/providers.js.map +1 -1
- package/docs/content/locales/ar-SA/server.mdx +35 -0
- package/docs/content/locales/de-DE/server.mdx +35 -0
- package/docs/content/locales/es-ES/server.mdx +35 -0
- package/docs/content/locales/fr-FR/server.mdx +35 -0
- package/docs/content/locales/hi-IN/server.mdx +35 -0
- package/docs/content/locales/ja-JP/server.mdx +35 -0
- package/docs/content/locales/ko-KR/server.mdx +35 -0
- package/docs/content/locales/pt-BR/server.mdx +35 -0
- package/docs/content/locales/zh-CN/server.mdx +35 -0
- package/docs/content/locales/zh-TW/server.mdx +35 -0
- package/docs/content/server.mdx +35 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"awareness.js","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-side awareness state management for collaborative editing.\n *\n * Stores per-client awareness state (cursor positions, user info) in memory.\n * Clients POST their state and receive other clients' states via polling.\n * States expire after 30 seconds of no updates.\n *\n * Fast-path: when a client POSTs new awareness state, the server emits an\n * event on the awareness emitter so SSE-connected peers receive cursor moves\n * push-style instead of waiting for the next poll cycle.\n */\n\nimport { EventEmitter } from \"node:events\";\n\nimport { defineEventHandler, setResponseStatus, getRouterParam } from \"h3\";\nimport type { H3Event } from \"h3\";\n\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n deleteAwarenessRow,\n loadAwarenessRows,\n upsertAwarenessRow,\n} from \"./awareness-store.js\";\n\nconst AWARENESS_TIMEOUT = 30_000; // 30 seconds\n\nexport interface AwarenessEntry {\n clientId: number;\n state: string; // JSON-encoded awareness state object\n lastSeen: number;\n}\n\n// ---------------------------------------------------------------------------\n// Awareness event emitter — fast-path for push delivery to SSE-connected peers.\n// The SSE handler (poll-events) subscribes and forwards events to its stream.\n// ---------------------------------------------------------------------------\n\nexport const AWARENESS_CHANGE_EVENT = \"awareness-change\" as const;\n\nexport interface AwarenessChangeEvent {\n source: \"awareness\";\n type: \"awareness-change\";\n docId: string;\n /** Array of updated states for this document (all non-expired clients). */\n states: Array<{ clientId: number; state: string }>;\n /** Owner email for access-scoped delivery (taken from session if available). */\n owner?: string;\n /** Org ID for org-scoped delivery. */\n orgId?: string;\n /** Shareable resource type this awareness event belongs to, when known. */\n resourceType?: string;\n /** Shareable resource id this awareness event belongs to, when known. */\n resourceId?: string;\n}\n\nexport interface AwarenessScope {\n owner?: string;\n orgId?: string;\n resourceType?: string;\n resourceId?: string;\n}\n\nconst _awarenessEmitter = new EventEmitter();\n_awarenessEmitter.setMaxListeners(0);\n\nconst _awarenessScopes = new Map<string, AwarenessScope>();\n\nexport function getAwarenessEmitter(): EventEmitter {\n return _awarenessEmitter;\n}\n\nexport function rememberAwarenessScope(\n docId: string,\n scope: AwarenessScope | undefined,\n): void {\n if (!scope) return;\n const next = { ...(_awarenessScopes.get(docId) ?? {}), ...scope };\n if (!next.owner && !next.orgId && !next.resourceType && !next.resourceId) {\n return;\n }\n _awarenessScopes.set(docId, next);\n}\n\nexport function emitAwarenessChange(\n docId: string,\n states: Array<{ clientId: number; state: string }>,\n scope?: AwarenessScope,\n): void {\n rememberAwarenessScope(docId, scope);\n const resolvedScope = _awarenessScopes.get(docId) ?? {};\n const event: AwarenessChangeEvent = {\n source: \"awareness\",\n type: \"awareness-change\",\n docId,\n states,\n ...(resolvedScope.owner && { owner: resolvedScope.owner }),\n ...(resolvedScope.orgId && { orgId: resolvedScope.orgId }),\n ...(resolvedScope.resourceType && {\n resourceType: resolvedScope.resourceType,\n }),\n ...(resolvedScope.resourceId && { resourceId: resolvedScope.resourceId }),\n };\n _awarenessEmitter.emit(AWARENESS_CHANGE_EVENT, event);\n}\n\n// docId → Map<clientId, AwarenessEntry>\nconst _awarenessMap = new Map<string, Map<number, AwarenessEntry>>();\n\nexport function getDocAwareness(docId: string): Map<number, AwarenessEntry> {\n let map = _awarenessMap.get(docId);\n if (!map) {\n map = new Map();\n _awarenessMap.set(docId, map);\n }\n return map;\n}\n\nexport function cleanExpired(map: Map<number, AwarenessEntry>): void {\n const now = Date.now();\n for (const [clientId, entry] of map) {\n if (now - entry.lastSeen > AWARENESS_TIMEOUT) {\n map.delete(clientId);\n }\n }\n}\n\n// Drop the per-document map from the registry once it has no entries left,\n// so the outer map does not grow unbounded with every docId ever touched.\nfunction pruneIfEmpty(docId: string, map: Map<number, AwarenessEntry>): void {\n if (map.size === 0) {\n _awarenessMap.delete(docId);\n }\n}\n\n/**\n * Merge SQL-mirrored awareness rows (written by other server instances —\n * or by an agent action running in its own serverless invocation) into the\n * in-memory map, newest lastSeen wins. Degrades to memory-only when the DB\n * is unavailable.\n */\nasync function mergeStoredAwareness(\n docId: string,\n map: Map<number, AwarenessEntry>,\n): Promise<void> {\n const rows = await loadAwarenessRows(docId);\n for (const row of rows) {\n const existing = map.get(row.clientId);\n if (!existing || row.lastSeen > existing.lastSeen) {\n map.set(row.clientId, row);\n }\n }\n}\n\n/**\n * POST /_agent-native/collab/:docId/awareness\n *\n * Client sends its awareness state and receives other clients' states.\n *\n * Body: { clientId: number, state: string | null (JSON-encoded awareness state, or null to clear) }\n * Response: { states: Array<{ clientId: number, state: string }> }\n */\nexport const postAwareness = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n const session = await getSession(event).catch(() => null);\n const contextScope = (\n event.context as { _collabAwarenessScope?: AwarenessScope } | undefined\n )?._collabAwarenessScope;\n\n const body = await readBody(event);\n const { clientId, state } = body as {\n clientId?: number;\n state?: string | null;\n };\n\n if (clientId == null || state === undefined) {\n // `!clientId` would wrongly reject clientId === 0, which is a valid\n // (if rare) Yjs client id. A null state is valid: it clears this client.\n setResponseStatus(event, 400);\n return { error: \"clientId and state required\" };\n }\n\n const map = getDocAwareness(docId);\n\n if (state === null) {\n map.delete(clientId);\n // Best-effort cross-instance removal (never blocks the response).\n void deleteAwarenessRow(docId, clientId);\n } else {\n // Store this client's state\n const entry = { clientId, state, lastSeen: Date.now() };\n map.set(clientId, entry);\n // Mirror to SQL so other instances (and serverless action invocations)\n // see this participant. Throttled internally; never throws.\n void upsertAwarenessRow(docId, clientId, state, entry.lastSeen);\n }\n\n // Pull in participants known to other instances (multi-instance serverless)\n // before building the response, so every poller sees the full set.\n await mergeStoredAwareness(docId, map);\n\n // Clean expired entries, then prune the outer-map entry if it becomes empty.\n // Without pruning, a deployment with many transient docIds (e.g. one per\n // session) would grow _awarenessMap without bound.\n cleanExpired(map);\n // Null-state clears and expiry can empty the map; prune the outer entry so\n // transient document ids do not accumulate.\n pruneIfEmpty(docId, map);\n\n // Build the full list of current states (all clients including sender).\n const allStates: Array<{ clientId: number; state: string }> = [];\n const otherStates: Array<{ clientId: number; state: string }> = [];\n for (const [id, entry] of map) {\n allStates.push({ clientId: id, state: entry.state });\n if (id !== clientId) {\n otherStates.push({ clientId: id, state: entry.state });\n }\n }\n\n // Fast-path: push the updated state set to SSE-connected peers so they\n // don't have to wait for the next poll cycle for cursor/selection updates.\n emitAwarenessChange(\n docId,\n allStates,\n contextScope ?? {\n ...(session?.email ? { owner: session.email } : {}),\n ...(session?.orgId ? { orgId: session.orgId } : {}),\n },\n );\n\n return { states: otherStates };\n});\n\n/**\n * GET /_agent-native/collab/:docId/users\n *\n * Returns the list of active users for a document (for presence bar).\n */\nexport const getActiveUsers = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const map = getDocAwareness(docId);\n await mergeStoredAwareness(docId, map);\n cleanExpired(map);\n pruneIfEmpty(docId, map);\n\n const users: Array<{ clientId: number; lastSeen: number }> = [];\n for (const [, entry] of map) {\n users.push({ clientId: entry.clientId, lastSeen: entry.lastSeen });\n }\n\n return { users };\n});\n"]}
|
|
1
|
+
{"version":3,"file":"awareness.js","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-side awareness state management for collaborative editing.\n *\n * Stores per-client awareness state (cursor positions, user info) in memory.\n * Clients POST their state and receive other clients' states via polling.\n * States expire after 30 seconds of no updates.\n *\n * Fast-path: when a client POSTs new awareness state, the server emits an\n * event on the awareness emitter so SSE-connected peers receive cursor moves\n * push-style instead of waiting for the next poll cycle.\n */\n\nimport { EventEmitter } from \"node:events\";\n\nimport { defineEventHandler, setResponseStatus, getRouterParam } from \"h3\";\nimport type { H3Event } from \"h3\";\n\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n deleteAwarenessRow,\n loadAwarenessRows,\n upsertAwarenessRow,\n} from \"./awareness-store.js\";\n\nconst AWARENESS_TIMEOUT = 30_000; // 30 seconds\nconst CLEAR_TOMBSTONE_TTL = AWARENESS_TIMEOUT + 5_000;\n\nexport interface AwarenessEntry {\n clientId: number;\n state: string; // JSON-encoded awareness state object\n lastSeen: number;\n}\n\n// ---------------------------------------------------------------------------\n// Awareness event emitter — fast-path for push delivery to SSE-connected peers.\n// The SSE handler (poll-events) subscribes and forwards events to its stream.\n// ---------------------------------------------------------------------------\n\nexport const AWARENESS_CHANGE_EVENT = \"awareness-change\" as const;\n\nexport interface AwarenessChangeEvent {\n source: \"awareness\";\n type: \"awareness-change\";\n docId: string;\n /** Array of updated states for this document (all non-expired clients). */\n states: Array<{ clientId: number; state: string }>;\n /** Owner email for access-scoped delivery (taken from session if available). */\n owner?: string;\n /** Org ID for org-scoped delivery. */\n orgId?: string;\n /** Shareable resource type this awareness event belongs to, when known. */\n resourceType?: string;\n /** Shareable resource id this awareness event belongs to, when known. */\n resourceId?: string;\n}\n\nexport interface AwarenessScope {\n owner?: string;\n orgId?: string;\n resourceType?: string;\n resourceId?: string;\n}\n\nconst _awarenessEmitter = new EventEmitter();\n_awarenessEmitter.setMaxListeners(0);\n\nconst _awarenessScopes = new Map<string, AwarenessScope>();\n\nexport function getAwarenessEmitter(): EventEmitter {\n return _awarenessEmitter;\n}\n\nexport function rememberAwarenessScope(\n docId: string,\n scope: AwarenessScope | undefined,\n): void {\n if (!scope) return;\n const next = { ...(_awarenessScopes.get(docId) ?? {}), ...scope };\n if (!next.owner && !next.orgId && !next.resourceType && !next.resourceId) {\n return;\n }\n _awarenessScopes.set(docId, next);\n}\n\nexport function emitAwarenessChange(\n docId: string,\n states: Array<{ clientId: number; state: string }>,\n scope?: AwarenessScope,\n): void {\n rememberAwarenessScope(docId, scope);\n const resolvedScope = _awarenessScopes.get(docId) ?? {};\n const event: AwarenessChangeEvent = {\n source: \"awareness\",\n type: \"awareness-change\",\n docId,\n states,\n ...(resolvedScope.owner && { owner: resolvedScope.owner }),\n ...(resolvedScope.orgId && { orgId: resolvedScope.orgId }),\n ...(resolvedScope.resourceType && {\n resourceType: resolvedScope.resourceType,\n }),\n ...(resolvedScope.resourceId && { resourceId: resolvedScope.resourceId }),\n };\n _awarenessEmitter.emit(AWARENESS_CHANGE_EVENT, event);\n}\n\n// docId → Map<clientId, AwarenessEntry>\nconst _awarenessMap = new Map<string, Map<number, AwarenessEntry>>();\n// docId + clientId -> clearedAt. Prevents a stale SQL mirror from resurrecting\n// a participant after an explicit leave/delete raced with the next poll.\nconst _awarenessClearTombstones = new Map<string, number>();\n\nfunction awarenessKey(docId: string, clientId: number): string {\n return `${docId}\\0${clientId}`;\n}\n\nfunction pruneAwarenessClearTombstones(now: number): void {\n for (const [key, clearedAt] of _awarenessClearTombstones) {\n if (now - clearedAt > CLEAR_TOMBSTONE_TTL) {\n _awarenessClearTombstones.delete(key);\n }\n }\n}\n\nexport function rememberAwarenessClear(\n docId: string,\n clientId: number,\n clearedAt: number = Date.now(),\n): void {\n pruneAwarenessClearTombstones(clearedAt);\n const key = awarenessKey(docId, clientId);\n const prev = _awarenessClearTombstones.get(key);\n if (prev == null || clearedAt > prev) {\n _awarenessClearTombstones.set(key, clearedAt);\n }\n}\n\nexport function forgetAwarenessClear(docId: string, clientId: number): void {\n _awarenessClearTombstones.delete(awarenessKey(docId, clientId));\n}\n\nfunction isBlockedByAwarenessClear(\n docId: string,\n clientId: number,\n lastSeen: number,\n now: number,\n): boolean {\n pruneAwarenessClearTombstones(now);\n const key = awarenessKey(docId, clientId);\n const clearedAt = _awarenessClearTombstones.get(key);\n if (clearedAt == null) return false;\n if (lastSeen <= clearedAt) return true;\n _awarenessClearTombstones.delete(key);\n return false;\n}\n\nexport function getDocAwareness(docId: string): Map<number, AwarenessEntry> {\n let map = _awarenessMap.get(docId);\n if (!map) {\n map = new Map();\n _awarenessMap.set(docId, map);\n }\n return map;\n}\n\nexport function cleanExpired(map: Map<number, AwarenessEntry>): void {\n const now = Date.now();\n for (const [clientId, entry] of map) {\n if (now - entry.lastSeen > AWARENESS_TIMEOUT) {\n map.delete(clientId);\n }\n }\n}\n\n// Drop the per-document map from the registry once it has no entries left,\n// so the outer map does not grow unbounded with every docId ever touched.\nfunction pruneIfEmpty(docId: string, map: Map<number, AwarenessEntry>): void {\n if (map.size === 0) {\n _awarenessMap.delete(docId);\n }\n}\n\n/**\n * Merge SQL-mirrored awareness rows (written by other server instances —\n * or by an agent action running in its own serverless invocation) into the\n * in-memory map, newest lastSeen wins. Degrades to memory-only when the DB\n * is unavailable.\n */\nasync function mergeStoredAwareness(\n docId: string,\n map: Map<number, AwarenessEntry>,\n): Promise<void> {\n const now = Date.now();\n const rows = await loadAwarenessRows(docId, now);\n for (const row of rows) {\n if (isBlockedByAwarenessClear(docId, row.clientId, row.lastSeen, now)) {\n void deleteAwarenessRow(docId, row.clientId, row.lastSeen);\n continue;\n }\n const existing = map.get(row.clientId);\n if (!existing || row.lastSeen > existing.lastSeen) {\n map.set(row.clientId, row);\n }\n }\n}\n\n/**\n * POST /_agent-native/collab/:docId/awareness\n *\n * Client sends its awareness state and receives other clients' states.\n *\n * Body: { clientId: number, state: string | null (JSON-encoded awareness state, or null to clear) }\n * Response: { states: Array<{ clientId: number, state: string }> }\n */\nexport const postAwareness = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n const session = await getSession(event).catch(() => null);\n const contextScope = (\n event.context as { _collabAwarenessScope?: AwarenessScope } | undefined\n )?._collabAwarenessScope;\n\n const body = await readBody(event);\n const { clientId, state } = body as {\n clientId?: number;\n state?: string | null;\n };\n\n if (clientId == null || state === undefined) {\n // `!clientId` would wrongly reject clientId === 0, which is a valid\n // (if rare) Yjs client id. A null state is valid: it clears this client.\n setResponseStatus(event, 400);\n return { error: \"clientId and state required\" };\n }\n\n const map = getDocAwareness(docId);\n\n if (state === null) {\n const clearedAt = Date.now();\n map.delete(clientId);\n rememberAwarenessClear(docId, clientId, clearedAt);\n // Best-effort cross-instance removal (never blocks the response).\n void deleteAwarenessRow(docId, clientId, clearedAt);\n } else {\n forgetAwarenessClear(docId, clientId);\n // Store this client's state\n const entry = { clientId, state, lastSeen: Date.now() };\n map.set(clientId, entry);\n // Mirror to SQL so other instances (and serverless action invocations)\n // see this participant. Throttled internally; never throws.\n void upsertAwarenessRow(docId, clientId, state, entry.lastSeen);\n }\n\n // Pull in participants known to other instances (multi-instance serverless)\n // before building the response, so every poller sees the full set.\n await mergeStoredAwareness(docId, map);\n\n // Clean expired entries, then prune the outer-map entry if it becomes empty.\n // Without pruning, a deployment with many transient docIds (e.g. one per\n // session) would grow _awarenessMap without bound.\n cleanExpired(map);\n // Null-state clears and expiry can empty the map; prune the outer entry so\n // transient document ids do not accumulate.\n pruneIfEmpty(docId, map);\n\n // Build the full list of current states (all clients including sender).\n const allStates: Array<{ clientId: number; state: string }> = [];\n const otherStates: Array<{ clientId: number; state: string }> = [];\n for (const [id, entry] of map) {\n allStates.push({ clientId: id, state: entry.state });\n if (id !== clientId) {\n otherStates.push({ clientId: id, state: entry.state });\n }\n }\n\n // Fast-path: push the updated state set to SSE-connected peers so they\n // don't have to wait for the next poll cycle for cursor/selection updates.\n emitAwarenessChange(\n docId,\n allStates,\n contextScope ?? {\n ...(session?.email ? { owner: session.email } : {}),\n ...(session?.orgId ? { orgId: session.orgId } : {}),\n },\n );\n\n return { states: otherStates };\n});\n\n/**\n * GET /_agent-native/collab/:docId/users\n *\n * Returns the list of active users for a document (for presence bar).\n */\nexport const getActiveUsers = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const map = getDocAwareness(docId);\n await mergeStoredAwareness(docId, map);\n cleanExpired(map);\n pruneIfEmpty(docId, map);\n\n const users: Array<{ clientId: number; lastSeen: number }> = [];\n for (const [, entry] of map) {\n users.push({ clientId: entry.clientId, lastSeen: entry.lastSeen });\n }\n\n return { users };\n});\n"]}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -138,17 +138,10 @@ function createEmailChannel() {
|
|
|
138
138
|
input.metadata.emailSubject.trim()
|
|
139
139
|
? input.metadata.emailSubject.trim()
|
|
140
140
|
: `[${input.severity}] ${input.title}`;
|
|
141
|
-
const
|
|
142
|
-
const metadataText = metadata
|
|
143
|
-
? `\n\nMetadata:\n${JSON.stringify(metadata, null, 2)}`
|
|
144
|
-
: "";
|
|
145
|
-
const text = `${input.title}\n\n${input.body ?? ""}${metadataText}`;
|
|
141
|
+
const text = `${input.title}\n\n${input.body ?? ""}`;
|
|
146
142
|
const html = [
|
|
147
143
|
`<p><strong>${escapeHtml(input.title)}</strong></p>`,
|
|
148
144
|
input.body ? `<p>${escapeHtml(input.body)}</p>` : "",
|
|
149
|
-
metadata
|
|
150
|
-
? `<pre>${escapeHtml(JSON.stringify(metadata, null, 2))}</pre>`
|
|
151
|
-
: "",
|
|
152
145
|
].join("");
|
|
153
146
|
await Promise.all(recipients.map((to) => sendEmail({
|
|
154
147
|
to,
|
|
@@ -233,15 +226,6 @@ function commaList(value) {
|
|
|
233
226
|
.filter(Boolean)
|
|
234
227
|
: [];
|
|
235
228
|
}
|
|
236
|
-
function scrubEmailMetadata(metadata) {
|
|
237
|
-
if (!metadata)
|
|
238
|
-
return null;
|
|
239
|
-
const entries = Object.entries(metadata).filter(([key]) => key !== "emailRecipients" &&
|
|
240
|
-
key !== "emailSubject" &&
|
|
241
|
-
key !== "webhookUrl" &&
|
|
242
|
-
key !== "slackWebhookUrl");
|
|
243
|
-
return entries.length ? Object.fromEntries(entries) : null;
|
|
244
|
-
}
|
|
245
229
|
function slackText(severity, title, body) {
|
|
246
230
|
return `[${severity}] ${title}${body ? `\n${body}` : ""}`;
|
|
247
231
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in notification channels.\n *\n * Webhook and Slack are always registered. Delivery uses (in order):\n * 1. per-notification `metadata.webhookUrl` / `metadata.slackWebhookUrl`\n * 2. the matching `NOTIFICATIONS_*` env URL (workspace default)\n * If neither is set, that channel no-ops for the emission.\n *\n * Extra channels can be registered at any time via\n * `registerNotificationChannel()` from a server plugin.\n *\n * NOTIFICATIONS_WEBHOOK_URL → default POST JSON URL when metadata omits one.\n * Supports `${keys.NAME}` substitution — the raw\n * value never enters the agent context.\n * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also\n * supports `${keys.NAME}`).\n * NOTIFICATIONS_SLACK_WEBHOOK_URL\n * → default Slack incoming webhook when metadata\n * omits `slackWebhookUrl`.\n * Supports `${keys.NAME}` substitution.\n * NOTIFICATIONS_EMAIL_CHANNEL=1\n * → enable the built-in email channel for\n * per-notification recipients.\n * NOTIFICATIONS_EMAIL_RECIPIENTS\n * → comma-separated fallback recipients for email\n * notifications that do not pass\n * `metadata.emailRecipients`.\n */\n\nimport { ssrfSafeFetch } from \"../extensions/url-safety.js\";\nimport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n} from \"../secrets/substitution.js\";\nimport { sendEmail } from \"../server/email.js\";\nimport { registerNotificationChannel } from \"./registry.js\";\nimport type { NotificationChannel, NotificationInput } from \"./types.js\";\n\nlet _registered = false;\n\nexport function registerBuiltinNotificationChannels(): void {\n if (_registered) return;\n _registered = true;\n\n registerNotificationChannel(\n createWebhookChannel(process.env.NOTIFICATIONS_WEBHOOK_URL),\n );\n registerNotificationChannel(\n createSlackWebhookChannel(process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL),\n );\n // Email is always registered so per-notification `metadata.emailRecipients`\n // work without a workspace-wide env flag (same pattern as webhook/slack).\n registerNotificationChannel(createEmailChannel());\n}\n\nfunction createWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_WEBHOOK_AUTH;\n return {\n name: \"webhook\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"webhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"webhookUrl\") ??\n envUrlTemplate?.trim();\n // No-op when neither a per-notification nor workspace URL is set —\n // mirrors email's empty-recipients behavior so notify-all stays quiet.\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: scrubDeliveryMetadata(input.metadata),\n owner: meta.owner,\n emittedAt: new Date().toISOString(),\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createSlackWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_SLACK_WEBHOOK_AUTH;\n return {\n name: \"slack\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"slackWebhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"slackWebhookUrl\") ??\n envUrlTemplate?.trim();\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"Slack webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n text: slackText(input.severity, input.title, input.body),\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*${escapeSlack(input.title)}*`,\n },\n },\n ...(input.body\n ? [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: escapeSlack(input.body),\n },\n },\n ]\n : []),\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `Severity: \\`${input.severity}\\` Owner: ${escapeSlack(meta.owner)}`,\n },\n ],\n },\n ],\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] Slack webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createEmailChannel(): NotificationChannel {\n return {\n name: \"email\",\n async deliver(input) {\n const recipients = notificationEmailRecipients(input.metadata);\n if (recipients.length === 0) return false;\n const subject =\n typeof input.metadata?.emailSubject === \"string\" &&\n input.metadata.emailSubject.trim()\n ? input.metadata.emailSubject.trim()\n : `[${input.severity}] ${input.title}`;\n const metadata = scrubEmailMetadata(input.metadata);\n const metadataText = metadata\n ? `\\n\\nMetadata:\\n${JSON.stringify(metadata, null, 2)}`\n : \"\";\n const text = `${input.title}\\n\\n${input.body ?? \"\"}${metadataText}`;\n const html = [\n `<p><strong>${escapeHtml(input.title)}</strong></p>`,\n input.body ? `<p>${escapeHtml(input.body)}</p>` : \"\",\n metadata\n ? `<pre>${escapeHtml(JSON.stringify(metadata, null, 2))}</pre>`\n : \"\",\n ].join(\"\");\n\n await Promise.all(\n recipients.map((to) =>\n sendEmail({\n to,\n subject,\n text,\n html,\n }),\n ),\n );\n },\n };\n}\n\nasync function resolveWebhookRequest(\n urlTemplate: string,\n authTemplate: string | undefined,\n owner: string,\n label: string,\n): Promise<{ url: string; headers: Record<string, string> }> {\n // Resolve `${keys.NAME}` references against the owner's user-scope secrets.\n // Missing keys throw — the error surfaces in logs and the channel is marked\n // un-delivered, but other channels still run.\n const { resolved: url } = await resolveKeyReferences(\n urlTemplate,\n \"user\",\n owner,\n );\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authTemplate) {\n const { resolved: auth } = await resolveKeyReferences(\n authTemplate,\n \"user\",\n owner,\n );\n headers.Authorization = auth;\n }\n\n // If the user set an allowlist on a referenced key, enforce it here —\n // origin-level check, same rule the automations fetch-tool applies.\n const keyNames = Array.from(\n new Set(\n Array.from(urlTemplate.matchAll(/\\$\\{keys\\.([A-Za-z0-9_-]+)\\}/g), (m) =>\n String(m[1]),\n ),\n ),\n );\n const allowlists = await Promise.all(\n keyNames.map((name) => getKeyAllowlist(name, \"user\", owner)),\n );\n keyNames.forEach((name, i) => {\n if (!validateUrlAllowlist(url, allowlists[i])) {\n throw new Error(\n `[notifications] ${label} URL ${new URL(url).origin} is not in the allowlist for key \"${name}\"`,\n );\n }\n });\n return { url, headers };\n}\n\nfunction metadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const value = metadata?.[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction deliveryMetadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const delivery = metadata?.delivery;\n if (!delivery || typeof delivery !== \"object\" || Array.isArray(delivery)) {\n return undefined;\n }\n const value = (delivery as Record<string, unknown>)[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction scrubDeliveryMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : undefined;\n}\n\nfunction notificationEmailRecipients(\n metadata: Record<string, unknown> | undefined,\n): string[] {\n const fromMetadata = stringArray(metadata?.emailRecipients);\n const fromEnv = commaList(process.env.NOTIFICATIONS_EMAIL_RECIPIENTS);\n const seen = new Set<string>();\n const recipients: string[] = [];\n for (const raw of [...fromMetadata, ...fromEnv]) {\n const email = raw.trim().toLowerCase();\n if (!email || seen.has(email)) continue;\n seen.add(email);\n recipients.push(email);\n }\n return recipients;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction commaList(value: string | undefined): string[] {\n return value\n ? value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n}\n\nfunction scrubEmailMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | null {\n if (!metadata) return null;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"emailRecipients\" &&\n key !== \"emailSubject\" &&\n key !== \"webhookUrl\" &&\n key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : null;\n}\n\nfunction slackText(\n severity: string,\n title: string,\n body: string | undefined,\n): string {\n return `[${severity}] ${title}${body ? `\\n${body}` : \"\"}`;\n}\n\nfunction escapeSlack(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/**\n * Read up to ~1 KB from the body for error context. Streams chunks so a\n * misbehaving endpoint returning a large error page doesn't pin that whole\n * payload in memory per failed webhook.\n */\nasync function readErrorSnippet(res: Response): Promise<string> {\n const reader = res.body?.getReader();\n if (!reader) return \"\";\n const decoder = new TextDecoder();\n const MAX = 1024;\n let buf = \"\";\n try {\n while (buf.length < MAX) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n }\n reader.cancel().catch(() => {});\n } catch {\n return \"\";\n }\n if (!buf) return \"\";\n return `: ${buf.slice(0, 200)}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in notification channels.\n *\n * Webhook and Slack are always registered. Delivery uses (in order):\n * 1. per-notification `metadata.webhookUrl` / `metadata.slackWebhookUrl`\n * 2. the matching `NOTIFICATIONS_*` env URL (workspace default)\n * If neither is set, that channel no-ops for the emission.\n *\n * Extra channels can be registered at any time via\n * `registerNotificationChannel()` from a server plugin.\n *\n * NOTIFICATIONS_WEBHOOK_URL → default POST JSON URL when metadata omits one.\n * Supports `${keys.NAME}` substitution — the raw\n * value never enters the agent context.\n * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also\n * supports `${keys.NAME}`).\n * NOTIFICATIONS_SLACK_WEBHOOK_URL\n * → default Slack incoming webhook when metadata\n * omits `slackWebhookUrl`.\n * Supports `${keys.NAME}` substitution.\n * NOTIFICATIONS_EMAIL_CHANNEL=1\n * → enable the built-in email channel for\n * per-notification recipients.\n * NOTIFICATIONS_EMAIL_RECIPIENTS\n * → comma-separated fallback recipients for email\n * notifications that do not pass\n * `metadata.emailRecipients`.\n */\n\nimport { ssrfSafeFetch } from \"../extensions/url-safety.js\";\nimport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n} from \"../secrets/substitution.js\";\nimport { sendEmail } from \"../server/email.js\";\nimport { registerNotificationChannel } from \"./registry.js\";\nimport type { NotificationChannel, NotificationInput } from \"./types.js\";\n\nlet _registered = false;\n\nexport function registerBuiltinNotificationChannels(): void {\n if (_registered) return;\n _registered = true;\n\n registerNotificationChannel(\n createWebhookChannel(process.env.NOTIFICATIONS_WEBHOOK_URL),\n );\n registerNotificationChannel(\n createSlackWebhookChannel(process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL),\n );\n // Email is always registered so per-notification `metadata.emailRecipients`\n // work without a workspace-wide env flag (same pattern as webhook/slack).\n registerNotificationChannel(createEmailChannel());\n}\n\nfunction createWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_WEBHOOK_AUTH;\n return {\n name: \"webhook\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"webhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"webhookUrl\") ??\n envUrlTemplate?.trim();\n // No-op when neither a per-notification nor workspace URL is set —\n // mirrors email's empty-recipients behavior so notify-all stays quiet.\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: scrubDeliveryMetadata(input.metadata),\n owner: meta.owner,\n emittedAt: new Date().toISOString(),\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createSlackWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_SLACK_WEBHOOK_AUTH;\n return {\n name: \"slack\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"slackWebhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"slackWebhookUrl\") ??\n envUrlTemplate?.trim();\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"Slack webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n text: slackText(input.severity, input.title, input.body),\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*${escapeSlack(input.title)}*`,\n },\n },\n ...(input.body\n ? [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: escapeSlack(input.body),\n },\n },\n ]\n : []),\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `Severity: \\`${input.severity}\\` Owner: ${escapeSlack(meta.owner)}`,\n },\n ],\n },\n ],\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] Slack webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createEmailChannel(): NotificationChannel {\n return {\n name: \"email\",\n async deliver(input) {\n const recipients = notificationEmailRecipients(input.metadata);\n if (recipients.length === 0) return false;\n const subject =\n typeof input.metadata?.emailSubject === \"string\" &&\n input.metadata.emailSubject.trim()\n ? input.metadata.emailSubject.trim()\n : `[${input.severity}] ${input.title}`;\n const text = `${input.title}\\n\\n${input.body ?? \"\"}`;\n const html = [\n `<p><strong>${escapeHtml(input.title)}</strong></p>`,\n input.body ? `<p>${escapeHtml(input.body)}</p>` : \"\",\n ].join(\"\");\n\n await Promise.all(\n recipients.map((to) =>\n sendEmail({\n to,\n subject,\n text,\n html,\n }),\n ),\n );\n },\n };\n}\n\nasync function resolveWebhookRequest(\n urlTemplate: string,\n authTemplate: string | undefined,\n owner: string,\n label: string,\n): Promise<{ url: string; headers: Record<string, string> }> {\n // Resolve `${keys.NAME}` references against the owner's user-scope secrets.\n // Missing keys throw — the error surfaces in logs and the channel is marked\n // un-delivered, but other channels still run.\n const { resolved: url } = await resolveKeyReferences(\n urlTemplate,\n \"user\",\n owner,\n );\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authTemplate) {\n const { resolved: auth } = await resolveKeyReferences(\n authTemplate,\n \"user\",\n owner,\n );\n headers.Authorization = auth;\n }\n\n // If the user set an allowlist on a referenced key, enforce it here —\n // origin-level check, same rule the automations fetch-tool applies.\n const keyNames = Array.from(\n new Set(\n Array.from(urlTemplate.matchAll(/\\$\\{keys\\.([A-Za-z0-9_-]+)\\}/g), (m) =>\n String(m[1]),\n ),\n ),\n );\n const allowlists = await Promise.all(\n keyNames.map((name) => getKeyAllowlist(name, \"user\", owner)),\n );\n keyNames.forEach((name, i) => {\n if (!validateUrlAllowlist(url, allowlists[i])) {\n throw new Error(\n `[notifications] ${label} URL ${new URL(url).origin} is not in the allowlist for key \"${name}\"`,\n );\n }\n });\n return { url, headers };\n}\n\nfunction metadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const value = metadata?.[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction deliveryMetadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const delivery = metadata?.delivery;\n if (!delivery || typeof delivery !== \"object\" || Array.isArray(delivery)) {\n return undefined;\n }\n const value = (delivery as Record<string, unknown>)[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction scrubDeliveryMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : undefined;\n}\n\nfunction notificationEmailRecipients(\n metadata: Record<string, unknown> | undefined,\n): string[] {\n const fromMetadata = stringArray(metadata?.emailRecipients);\n const fromEnv = commaList(process.env.NOTIFICATIONS_EMAIL_RECIPIENTS);\n const seen = new Set<string>();\n const recipients: string[] = [];\n for (const raw of [...fromMetadata, ...fromEnv]) {\n const email = raw.trim().toLowerCase();\n if (!email || seen.has(email)) continue;\n seen.add(email);\n recipients.push(email);\n }\n return recipients;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction commaList(value: string | undefined): string[] {\n return value\n ? value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n}\n\nfunction slackText(\n severity: string,\n title: string,\n body: string | undefined,\n): string {\n return `[${severity}] ${title}${body ? `\\n${body}` : \"\"}`;\n}\n\nfunction escapeSlack(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/**\n * Read up to ~1 KB from the body for error context. Streams chunks so a\n * misbehaving endpoint returning a large error page doesn't pin that whole\n * payload in memory per failed webhook.\n */\nasync function readErrorSnippet(res: Response): Promise<string> {\n const reader = res.body?.getReader();\n if (!reader) return \"\";\n const decoder = new TextDecoder();\n const MAX = 1024;\n let buf = \"\";\n try {\n while (buf.length < MAX) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n }\n reader.cancel().catch(() => {});\n } catch {\n return \"\";\n }\n if (!buf) return \"\";\n return `: ${buf.slice(0, 200)}`;\n}\n"]}
|
|
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
44
46
|
summary: import("./types.js").TraceSummary;
|
|
45
47
|
spans: import("./types.js").TraceSpan[];
|
|
46
48
|
id?: undefined;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
summary?: undefined;
|
|
51
53
|
spans?: undefined;
|
|
52
54
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
|
-
error?: undefined;
|
|
67
67
|
}>>;
|
|
68
68
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -49,8 +49,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
49
49
|
}>;
|
|
50
50
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
51
51
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
52
|
-
error: string;
|
|
53
52
|
ok?: undefined;
|
|
53
|
+
error: string;
|
|
54
54
|
} | {
|
|
55
55
|
error?: undefined;
|
|
56
56
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manage-agent-engine.d.ts","sourceRoot":"","sources":["../../../src/scripts/agent-engines/manage-agent-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AASvD,eAAO,MAAM,IAAI,EAAE,UA0ClB,CAAC;
|
|
1
|
+
{"version":3,"file":"manage-agent-engine.d.ts","sourceRoot":"","sources":["../../../src/scripts/agent-engines/manage-agent-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AASvD,eAAO,MAAM,IAAI,EAAE,UA0ClB,CAAC;AAwGF,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBvE"}
|
|
@@ -87,10 +87,7 @@ async function runSetAppDefault(args) {
|
|
|
87
87
|
if (!isAgentEnginePackageInstalled(entry)) {
|
|
88
88
|
return `Error: Engine "${engine}" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
normalizeModelForEngine(entry, model) !== model) {
|
|
92
|
-
return `Error: Model "${model}" is not supported by Builder. Choose one of: ${entry.supportedModels.join(", ")}`;
|
|
93
|
-
}
|
|
90
|
+
const normalizedModel = normalizeModelForEngine(entry, model);
|
|
94
91
|
const ctx = currentContext();
|
|
95
92
|
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId);
|
|
96
93
|
if (!canUpdate) {
|
|
@@ -100,13 +97,16 @@ async function runSetAppDefault(args) {
|
|
|
100
97
|
}
|
|
101
98
|
const settings = await writeAgentAppModelDefaultSettings(ctx, appId, {
|
|
102
99
|
engine,
|
|
103
|
-
model,
|
|
100
|
+
model: normalizedModel,
|
|
104
101
|
updatedBy: ctx.userEmail,
|
|
105
102
|
});
|
|
103
|
+
const normalizedNote = normalizedModel === model
|
|
104
|
+
? ""
|
|
105
|
+
: ` Requested model "${model}" is no longer supported, so "${normalizedModel}" was saved instead.`;
|
|
106
106
|
return JSON.stringify({
|
|
107
107
|
ok: true,
|
|
108
108
|
...settings,
|
|
109
|
-
message: `Default model for ${appId} set to ${
|
|
109
|
+
message: `Default model for ${appId} set to ${normalizedModel} via ${entry.label}.${normalizedNote}`,
|
|
110
110
|
}, null, 2);
|
|
111
111
|
}
|
|
112
112
|
async function runResetAppDefault(args) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manage-agent-engine.js","sourceRoot":"","sources":["../../../src/scripts/agent-engines/manage-agent-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * manage-agent-engine — unified tool for listing, setting, and testing agent engines.\n *\n * Consolidates the former list-agent-engines, set-agent-engine, and test-agent-engine\n * tools into a single tool with an `action` discriminator.\n */\n\nimport {\n canUpdateAgentAppModelDefaultSettings,\n normalizeAgentAppModelDefaultAppId,\n readAgentAppModelDefaultSettings,\n resetAgentAppModelDefaultSettings,\n writeAgentAppModelDefaultSettings,\n} from \"../../agent/app-model-defaults.js\";\nimport {\n getAgentEngineEntry,\n isAgentEnginePackageInstalled,\n normalizeModelForEngine,\n registerBuiltinEngines,\n} from \"../../agent/engine/index.js\";\nimport type { ActionTool } from \"../../agent/types.js\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"../../server/request-context.js\";\nimport { run as runList } from \"./list-agent-engines.js\";\nimport { run as runSet } from \"./set-agent-engine.js\";\nimport { run as runTest } from \"./test-agent-engine.js\";\n\nexport const tool: ActionTool = {\n description:\n 'Manage AI agent engines: list available engines, set the active global engine/model, test an engine, or manage the current app/template default model. Pass action=\"list\" to see options, action=\"set\" to change the global default, action=\"test\" to verify connectivity, action=\"get-app-default\" to inspect this app default, action=\"set-app-default\" to set this app default, or action=\"reset-app-default\" to clear it.',\n parameters: {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n enum: [\n \"list\",\n \"set\",\n \"test\",\n \"get-app-default\",\n \"set-app-default\",\n \"reset-app-default\",\n ],\n description:\n '\"list\" — show available engines and current global selection. \"set\" — change the active global engine/model. \"test\" — send a trivial prompt to verify connectivity. \"get-app-default\" — show this app/template default. \"set-app-default\" — set this app/template default. \"reset-app-default\" — clear this app/template default.',\n },\n engine: {\n type: \"string\",\n description:\n 'Engine name (e.g. \"builder\", \"anthropic\", \"ai-sdk:openai\", \"ai-sdk:google\"). Required for \"set\" and \"set-app-default\", optional for \"test\" (defaults to \"anthropic\").',\n },\n model: {\n type: \"string\",\n description:\n \"Model ID (e.g. 'gpt-5.5', 'claude-sonnet-5', 'gemini-3-1-pro'). Required for \\\"set-app-default\\\"; optional for \\\"set\\\" and \\\"test\\\" where it defaults to the engine's default model.\",\n },\n baseUrl: {\n type: \"string\",\n description:\n 'Optional OpenAI-compatible endpoint URL for action=\"test\" with engine=\"ai-sdk:openai\". Saved endpoint settings are used when omitted.',\n },\n appId: {\n type: \"string\",\n description:\n \"App/template id whose default model should be managed. Defaults to the current app.\",\n },\n },\n required: [\"action\"],\n },\n};\n\nfunction currentContext(): { userEmail?: string; orgId?: string | null } {\n try {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n } catch {\n return {};\n }\n}\n\nfunction resolveAppId(args: Record<string, string>): string | null {\n return normalizeAgentAppModelDefaultAppId(args.appId);\n}\n\nasync function runGetAppDefault(args: Record<string, string>): Promise<string> {\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n const ctx = currentContext();\n const settings = await readAgentAppModelDefaultSettings(ctx, appId);\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n return JSON.stringify({ ok: true, ...settings, canUpdate }, null, 2);\n}\n\nasync function runSetAppDefault(args: Record<string, string>): Promise<string> {\n registerBuiltinEngines();\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n\n const engine = args.engine?.trim();\n const model = args.model?.trim();\n if (!engine) return \"Error: engine is required\";\n if (!model) return \"Error: model is required\";\n\n const entry = getAgentEngineEntry(engine);\n if (!entry) return `Error: Unknown engine \"${engine}\"`;\n if (!isAgentEnginePackageInstalled(entry)) {\n return `Error: Engine \"${engine}\" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;\n }\n if (\n entry.name === \"builder\" &&\n normalizeModelForEngine(entry, model) !== model\n ) {\n return `Error: Model \"${model}\" is not supported by Builder. Choose one of: ${entry.supportedModels.join(\", \")}`;\n }\n\n const ctx = currentContext();\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n if (!canUpdate) {\n return ctx.orgId\n ? \"Error: Only organization owners and admins can change app model defaults.\"\n : \"Error: Authentication required to change app model defaults.\";\n }\n\n const settings = await writeAgentAppModelDefaultSettings(ctx, appId, {\n engine,\n model,\n updatedBy: ctx.userEmail,\n });\n return JSON.stringify(\n {\n ok: true,\n ...settings,\n message: `Default model for ${appId} set to ${model} via ${entry.label}.`,\n },\n null,\n 2,\n );\n}\n\nasync function runResetAppDefault(\n args: Record<string, string>,\n): Promise<string> {\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n const ctx = currentContext();\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n if (!canUpdate) {\n return ctx.orgId\n ? \"Error: Only organization owners and admins can reset app model defaults.\"\n : \"Error: Authentication required to reset app model defaults.\";\n }\n const settings = await resetAgentAppModelDefaultSettings(ctx, appId);\n return JSON.stringify(\n {\n ok: true,\n ...settings,\n message: `Default model for ${appId} reset to the global LLM default.`,\n },\n null,\n 2,\n );\n}\n\nexport async function run(args: Record<string, string>): Promise<string> {\n const { action } = args;\n\n switch (action) {\n case \"list\":\n return runList(args);\n case \"set\":\n return runSet(args);\n case \"test\":\n return runTest(args);\n case \"get-app-default\":\n return runGetAppDefault(args);\n case \"set-app-default\":\n return runSetAppDefault(args);\n case \"reset-app-default\":\n return runResetAppDefault(args);\n default:\n return JSON.stringify({\n error: `Unknown action \"${action}\". Must be one of: list, set, test, get-app-default, set-app-default, reset-app-default.`,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manage-agent-engine.js","sourceRoot":"","sources":["../../../src/scripts/agent-engines/manage-agent-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * manage-agent-engine — unified tool for listing, setting, and testing agent engines.\n *\n * Consolidates the former list-agent-engines, set-agent-engine, and test-agent-engine\n * tools into a single tool with an `action` discriminator.\n */\n\nimport {\n canUpdateAgentAppModelDefaultSettings,\n normalizeAgentAppModelDefaultAppId,\n readAgentAppModelDefaultSettings,\n resetAgentAppModelDefaultSettings,\n writeAgentAppModelDefaultSettings,\n} from \"../../agent/app-model-defaults.js\";\nimport {\n getAgentEngineEntry,\n isAgentEnginePackageInstalled,\n normalizeModelForEngine,\n registerBuiltinEngines,\n} from \"../../agent/engine/index.js\";\nimport type { ActionTool } from \"../../agent/types.js\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"../../server/request-context.js\";\nimport { run as runList } from \"./list-agent-engines.js\";\nimport { run as runSet } from \"./set-agent-engine.js\";\nimport { run as runTest } from \"./test-agent-engine.js\";\n\nexport const tool: ActionTool = {\n description:\n 'Manage AI agent engines: list available engines, set the active global engine/model, test an engine, or manage the current app/template default model. Pass action=\"list\" to see options, action=\"set\" to change the global default, action=\"test\" to verify connectivity, action=\"get-app-default\" to inspect this app default, action=\"set-app-default\" to set this app default, or action=\"reset-app-default\" to clear it.',\n parameters: {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n enum: [\n \"list\",\n \"set\",\n \"test\",\n \"get-app-default\",\n \"set-app-default\",\n \"reset-app-default\",\n ],\n description:\n '\"list\" — show available engines and current global selection. \"set\" — change the active global engine/model. \"test\" — send a trivial prompt to verify connectivity. \"get-app-default\" — show this app/template default. \"set-app-default\" — set this app/template default. \"reset-app-default\" — clear this app/template default.',\n },\n engine: {\n type: \"string\",\n description:\n 'Engine name (e.g. \"builder\", \"anthropic\", \"ai-sdk:openai\", \"ai-sdk:google\"). Required for \"set\" and \"set-app-default\", optional for \"test\" (defaults to \"anthropic\").',\n },\n model: {\n type: \"string\",\n description:\n \"Model ID (e.g. 'gpt-5.5', 'claude-sonnet-5', 'gemini-3-1-pro'). Required for \\\"set-app-default\\\"; optional for \\\"set\\\" and \\\"test\\\" where it defaults to the engine's default model.\",\n },\n baseUrl: {\n type: \"string\",\n description:\n 'Optional OpenAI-compatible endpoint URL for action=\"test\" with engine=\"ai-sdk:openai\". Saved endpoint settings are used when omitted.',\n },\n appId: {\n type: \"string\",\n description:\n \"App/template id whose default model should be managed. Defaults to the current app.\",\n },\n },\n required: [\"action\"],\n },\n};\n\nfunction currentContext(): { userEmail?: string; orgId?: string | null } {\n try {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n } catch {\n return {};\n }\n}\n\nfunction resolveAppId(args: Record<string, string>): string | null {\n return normalizeAgentAppModelDefaultAppId(args.appId);\n}\n\nasync function runGetAppDefault(args: Record<string, string>): Promise<string> {\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n const ctx = currentContext();\n const settings = await readAgentAppModelDefaultSettings(ctx, appId);\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n return JSON.stringify({ ok: true, ...settings, canUpdate }, null, 2);\n}\n\nasync function runSetAppDefault(args: Record<string, string>): Promise<string> {\n registerBuiltinEngines();\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n\n const engine = args.engine?.trim();\n const model = args.model?.trim();\n if (!engine) return \"Error: engine is required\";\n if (!model) return \"Error: model is required\";\n\n const entry = getAgentEngineEntry(engine);\n if (!entry) return `Error: Unknown engine \"${engine}\"`;\n if (!isAgentEnginePackageInstalled(entry)) {\n return `Error: Engine \"${engine}\" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;\n }\n const normalizedModel = normalizeModelForEngine(entry, model);\n\n const ctx = currentContext();\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n if (!canUpdate) {\n return ctx.orgId\n ? \"Error: Only organization owners and admins can change app model defaults.\"\n : \"Error: Authentication required to change app model defaults.\";\n }\n\n const settings = await writeAgentAppModelDefaultSettings(ctx, appId, {\n engine,\n model: normalizedModel,\n updatedBy: ctx.userEmail,\n });\n const normalizedNote =\n normalizedModel === model\n ? \"\"\n : ` Requested model \"${model}\" is no longer supported, so \"${normalizedModel}\" was saved instead.`;\n return JSON.stringify(\n {\n ok: true,\n ...settings,\n message: `Default model for ${appId} set to ${normalizedModel} via ${entry.label}.${normalizedNote}`,\n },\n null,\n 2,\n );\n}\n\nasync function runResetAppDefault(\n args: Record<string, string>,\n): Promise<string> {\n const appId = resolveAppId(args);\n if (!appId) return \"Error: appId is required\";\n const ctx = currentContext();\n const canUpdate = await canUpdateAgentAppModelDefaultSettings(\n ctx.userEmail,\n ctx.orgId,\n );\n if (!canUpdate) {\n return ctx.orgId\n ? \"Error: Only organization owners and admins can reset app model defaults.\"\n : \"Error: Authentication required to reset app model defaults.\";\n }\n const settings = await resetAgentAppModelDefaultSettings(ctx, appId);\n return JSON.stringify(\n {\n ok: true,\n ...settings,\n message: `Default model for ${appId} reset to the global LLM default.`,\n },\n null,\n 2,\n );\n}\n\nexport async function run(args: Record<string, string>): Promise<string> {\n const { action } = args;\n\n switch (action) {\n case \"list\":\n return runList(args);\n case \"set\":\n return runSet(args);\n case \"test\":\n return runTest(args);\n case \"get-app-default\":\n return runGetAppDefault(args);\n case \"set-app-default\":\n return runSetAppDefault(args);\n case \"reset-app-default\":\n return runResetAppDefault(args);\n default:\n return JSON.stringify({\n error: `Unknown action \"${action}\". Must be one of: list, set, test, get-app-default, set-app-default, reset-app-default.`,\n });\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-agent-engine.d.ts","sourceRoot":"","sources":["../../../src/scripts/agent-engines/set-agent-engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,eAAO,MAAM,IAAI,EAAE,UAmBlB,CAAC;AAEF,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"set-agent-engine.d.ts","sourceRoot":"","sources":["../../../src/scripts/agent-engines/set-agent-engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,eAAO,MAAM,IAAI,EAAE,UAmBlB,CAAC;AAEF,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CvE"}
|
|
@@ -35,16 +35,8 @@ export async function run(args) {
|
|
|
35
35
|
if (!isAgentEnginePackageInstalled(entry)) {
|
|
36
36
|
return `Error: Engine "${engineName}" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;
|
|
37
37
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
normalizeModelForEngine(entry, resolvedModel) !== resolvedModel) {
|
|
41
|
-
return `Error: Model "${resolvedModel}" is not supported by Builder. Choose one of: ${entry.supportedModels.join(", ")}`;
|
|
42
|
-
}
|
|
43
|
-
// supportedModels is a suggestion list, not an allowlist — accept any
|
|
44
|
-
// string for BYO/custom engines (OpenRouter / Ollama / previews) and flag
|
|
45
|
-
// uncurated saves. Builder is an enumerated gateway and is checked above.
|
|
46
|
-
const modelIsCurated = entry.supportedModels.length === 0 ||
|
|
47
|
-
entry.supportedModels.includes(resolvedModel);
|
|
38
|
+
const requestedModel = model ?? entry.defaultModel;
|
|
39
|
+
const resolvedModel = normalizeModelForEngine(entry, requestedModel);
|
|
48
40
|
const usable = await isStoredEngineUsableForRequest({ engine: engineName }, entry);
|
|
49
41
|
if (!usable) {
|
|
50
42
|
const missingEnvVars = entry.requiredEnvVars.join(", ");
|
|
@@ -54,14 +46,14 @@ export async function run(args) {
|
|
|
54
46
|
engine: engineName,
|
|
55
47
|
model: resolvedModel,
|
|
56
48
|
});
|
|
57
|
-
const
|
|
49
|
+
const normalizedNote = resolvedModel === requestedModel
|
|
58
50
|
? ""
|
|
59
|
-
: `
|
|
51
|
+
: ` Requested model "${requestedModel}" is no longer supported, so "${resolvedModel}" was saved instead.`;
|
|
60
52
|
return JSON.stringify({
|
|
61
53
|
ok: true,
|
|
62
54
|
engine: engineName,
|
|
63
55
|
model: resolvedModel,
|
|
64
|
-
message: `Agent engine set to ${entry.label} with model ${resolvedModel}. Takes effect on the next conversation.${
|
|
56
|
+
message: `Agent engine set to ${entry.label} with model ${resolvedModel}. Takes effect on the next conversation.${normalizedNote}`,
|
|
65
57
|
});
|
|
66
58
|
}
|
|
67
59
|
//# sourceMappingURL=set-agent-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-agent-engine.js","sourceRoot":"","sources":["../../../src/scripts/agent-engines/set-agent-engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,6BAA6B,EAC7B,8BAA8B,EAC9B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,MAAM,CAAC,MAAM,IAAI,GAAe;IAC9B,WAAW,EACT,oKAAoK;IACtK,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,kIAAkI;aACrI;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,0HAA0H;aAC7H;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAA4B;IACpD,sBAAsB,EAAE,CAAC;IAEzB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAE3C,IAAI,CAAC,UAAU;QAAE,OAAO,6BAA6B,CAAC;IAEtD,MAAM,KAAK,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,SAAS,GAAG,gBAAgB,EAAE;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,kBAAkB,UAAU,mCAAmC,SAAS,EAAE,CAAC;IACpF,CAAC;IAED,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,kBAAkB,UAAU,kFAAkF,KAAK,CAAC,cAAc,EAAE,CAAC;IAC9I,CAAC;IAED,MAAM,
|
|
1
|
+
{"version":3,"file":"set-agent-engine.js","sourceRoot":"","sources":["../../../src/scripts/agent-engines/set-agent-engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,6BAA6B,EAC7B,8BAA8B,EAC9B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,MAAM,CAAC,MAAM,IAAI,GAAe;IAC9B,WAAW,EACT,oKAAoK;IACtK,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,kIAAkI;aACrI;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,0HAA0H;aAC7H;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAA4B;IACpD,sBAAsB,EAAE,CAAC;IAEzB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAE3C,IAAI,CAAC,UAAU;QAAE,OAAO,6BAA6B,CAAC;IAEtD,MAAM,KAAK,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,SAAS,GAAG,gBAAgB,EAAE;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,kBAAkB,UAAU,mCAAmC,SAAS,EAAE,CAAC;IACpF,CAAC;IAED,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,kBAAkB,UAAU,kFAAkF,KAAK,CAAC,cAAc,EAAE,CAAC;IAC9I,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,IAAI,KAAK,CAAC,YAAY,CAAC;IACnD,MAAM,aAAa,GAAG,uBAAuB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAErE,MAAM,MAAM,GAAG,MAAM,8BAA8B,CACjD,EAAE,MAAM,EAAE,UAAU,EAAE,EACtB,KAAK,CACN,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,cAAc,GAAG,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,OAAO,oBAAoB,UAAU,mFAAmF,cAAc,iDAAiD,CAAC;IAC1L,CAAC;IAED,MAAM,UAAU,CAAC,cAAc,EAAE;QAC/B,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,aAAa;KACrB,CAAC,CAAC;IAEH,MAAM,cAAc,GAClB,aAAa,KAAK,cAAc;QAC9B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,qBAAqB,cAAc,iCAAiC,aAAa,sBAAsB,CAAC;IAE9G,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,aAAa;QACpB,OAAO,EAAE,uBAAuB,KAAK,CAAC,KAAK,eAAe,aAAa,2CAA2C,cAAc,EAAE;KACnI,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * set-agent-engine — validates and writes agent engine selection to settings.\n */\n\nimport {\n listAgentEngines,\n getAgentEngineEntry,\n isAgentEnginePackageInstalled,\n isStoredEngineUsableForRequest,\n normalizeModelForEngine,\n registerBuiltinEngines,\n} from \"../../agent/engine/index.js\";\nimport type { ActionTool } from \"../../agent/types.js\";\nimport { putSetting } from \"../../settings/index.js\";\n\nexport const tool: ActionTool = {\n description:\n 'Set the active AI agent engine and model. Changes take effect on the next conversation. Use manage-agent-engine with action=\"list\" first to see available options.',\n parameters: {\n type: \"object\",\n properties: {\n engine: {\n type: \"string\",\n description:\n 'Engine name (e.g. \"anthropic\", \"ai-sdk:openai\", \"ai-sdk:google\"). Use manage-agent-engine with action=\"list\" to see all options.',\n },\n model: {\n type: \"string\",\n description:\n \"Model ID to use with this engine (e.g. 'gpt-5.5', 'claude-sonnet-5'). Defaults to the engine's default model if omitted.\",\n },\n },\n required: [\"engine\"],\n },\n};\n\nexport async function run(args: Record<string, string>): Promise<string> {\n registerBuiltinEngines();\n\n const { engine: engineName, model } = args;\n\n if (!engineName) return \"Error: --engine is required\";\n\n const entry = getAgentEngineEntry(engineName);\n if (!entry) {\n const available = listAgentEngines()\n .map((e) => e.name)\n .join(\", \");\n return `Error: Engine \"${engineName}\" not found. Available engines: ${available}`;\n }\n\n if (!isAgentEnginePackageInstalled(entry)) {\n return `Error: Engine \"${engineName}\" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;\n }\n\n const requestedModel = model ?? entry.defaultModel;\n const resolvedModel = normalizeModelForEngine(entry, requestedModel);\n\n const usable = await isStoredEngineUsableForRequest(\n { engine: engineName },\n entry,\n );\n if (!usable) {\n const missingEnvVars = entry.requiredEnvVars.join(\", \");\n return `Warning: Engine \"${engineName}\" requires the following credentials which are not configured for this request: ${missingEnvVars}. The engine will fail at runtime without them.`;\n }\n\n await putSetting(\"agent-engine\", {\n engine: engineName,\n model: resolvedModel,\n });\n\n const normalizedNote =\n resolvedModel === requestedModel\n ? \"\"\n : ` Requested model \"${requestedModel}\" is no longer supported, so \"${resolvedModel}\" was saved instead.`;\n\n return JSON.stringify({\n ok: true,\n engine: engineName,\n model: resolvedModel,\n message: `Agent engine set to ${entry.label} with model ${resolvedModel}. Takes effect on the next conversation.${normalizedNote}`,\n });\n}\n"]}
|
|
@@ -10,6 +10,11 @@ import type { EnvKeyConfig } from "./create-server.js";
|
|
|
10
10
|
export declare const FRAMEWORK_ROUTE_PREFIX = "/_agent-native";
|
|
11
11
|
export declare const FRAMEWORK_EVENTS_ROUTE = "/_agent-native/events";
|
|
12
12
|
export declare const LEGACY_FRAMEWORK_EVENTS_ROUTE = "/_agent-native/poll-events";
|
|
13
|
+
export declare function normalizeAgentEngineStatusModel(entry: {
|
|
14
|
+
name: string;
|
|
15
|
+
defaultModel: string;
|
|
16
|
+
supportedModels: readonly string[];
|
|
17
|
+
} | undefined, model: string | null | undefined): string;
|
|
13
18
|
export declare function getFrameworkEnvKeys(): EnvKeyConfig[];
|
|
14
19
|
/** Result of the `/_agent-native/health` liveness + DB-warmup probe. */
|
|
15
20
|
export interface DbHealthProbeResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAqClC,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,8BAA8B,CAAC;AAgDtC,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AA+BzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAoCvD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,sBAAsB,0BAAqC,CAAC;AACzE,eAAO,MAAM,6BAA6B,+BAA0C,CAAC;AAErF,wBAAgB,+BAA+B,CAC7C,KAAK,EACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1E,SAAS,EACb,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,CAGR;AAED,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA+BpD;AAED,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,EAAE,EAAE,IAAI,CAAC;IACT,6EAA6E;IAC7E,KAAK,EAAE,OAAO,CAAC;IACf,gFAAgF;IAChF,EAAE,EAAE,OAAO,CAAC;IACZ,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,QAAQ,EAAE;QACR,UAAU,EAAE,OAAO,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,mBAAmB,EAAE,OAAO,CAAC;QAC7B,4BAA4B,EAAE,OAAO,CAAC;KACvC,CAAC;IACF,yDAAyD;IACzD,MAAM,CAAC,EAAE,0BAA0B,CAAC;CACrC;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,GAAE,MAAM;IAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAAc,EACtE,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAiC9B;AAmBD,UAAU,yBAAyB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE;AAyBD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,OAAO,GACjB,MAAM,GAAG,IAAI,CAOf;AAoCD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,GAAG,SAAa,GACf;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAoCzD;AAED,wBAAgB,qCAAqC,SAEpD;AAmBD,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,OAAO,GACb,yBAAyB,GAAG,IAAI,CAsBlC;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,mBAAmB;;;;;;;;;;;;;;;;EA2B1B;AAgDD,eAAO,MAAM,kBAAkB,QAAwC,CAAC;AAExE,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,MAAM,CAoBR;AAwHD,KAAK,6BAA6B,GAAG,CACnC,KAAK,EAAE,OAAO,KACX,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAsB,oCAAoC,CACxD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,6BAA6B,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACjE,EACN,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAsD9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CASf;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAc/D;AAUD,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;wCACoC;IACpC,eAAe,CAAC,EAAE,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAChF;2DACuD;IACvD,wBAAwB,CAAC,EAAE,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IAClG,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,6BAA6B,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,cAAc,CA8sFhB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAAyC,CAAC"}
|
|
@@ -3,7 +3,7 @@ import { readMultipartFormData } from "h3";
|
|
|
3
3
|
import { DEFAULT_MODEL } from "../agent/default-model.js";
|
|
4
4
|
import { registerBuiltinEngines } from "../agent/engine/builtin.js";
|
|
5
5
|
import { OPENAI_BASE_URL_ENV_VAR, PROVIDER_ENV_META, } from "../agent/engine/provider-env-vars.js";
|
|
6
|
-
import { isAgentEngineSettingConfigured, getAgentEngineEntry, detectEngineFromEnv, detectEngineFromUserSecrets, isStoredEngineUsableForRequest, } from "../agent/engine/registry.js";
|
|
6
|
+
import { isAgentEngineSettingConfigured, getAgentEngineEntry, detectEngineFromEnv, detectEngineFromUserSecrets, isStoredEngineUsableForRequest, normalizeModelForEngine, } from "../agent/engine/registry.js";
|
|
7
7
|
import { canUpdateAgentLoopSettings, readAgentLoopSettings, resetAgentLoopSettings, validateMaxIterationsInput, writeAgentLoopSettings, } from "../agent/loop-settings.js";
|
|
8
8
|
import { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "../application-state/handlers.js";
|
|
9
9
|
import { mountBrowserSessionRoutes } from "../browser-sessions/routes.js";
|
|
@@ -60,6 +60,11 @@ import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
|
|
|
60
60
|
export const FRAMEWORK_ROUTE_PREFIX = "/_agent-native";
|
|
61
61
|
export const FRAMEWORK_EVENTS_ROUTE = `${FRAMEWORK_ROUTE_PREFIX}/events`;
|
|
62
62
|
export const LEGACY_FRAMEWORK_EVENTS_ROUTE = `${FRAMEWORK_ROUTE_PREFIX}/poll-events`;
|
|
63
|
+
export function normalizeAgentEngineStatusModel(entry, model) {
|
|
64
|
+
if (!entry)
|
|
65
|
+
return model ?? DEFAULT_MODEL;
|
|
66
|
+
return normalizeModelForEngine(entry, model ?? entry.defaultModel);
|
|
67
|
+
}
|
|
63
68
|
export function getFrameworkEnvKeys() {
|
|
64
69
|
return [
|
|
65
70
|
{ key: "ENABLE_BUILDER", label: "Enable Builder.io features" },
|
|
@@ -1942,10 +1947,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1942
1947
|
if (isAgentEngineSettingConfigured(stored)) {
|
|
1943
1948
|
const engine = stored.engine;
|
|
1944
1949
|
const entry = getAgentEngineEntry(engine);
|
|
1950
|
+
const model = normalizeAgentEngineStatusModel(entry, stored?.model);
|
|
1945
1951
|
return {
|
|
1946
1952
|
configured: true,
|
|
1947
1953
|
engine,
|
|
1948
|
-
model
|
|
1954
|
+
model,
|
|
1949
1955
|
source: "settings",
|
|
1950
1956
|
openAiBaseUrlConfigured,
|
|
1951
1957
|
};
|
|
@@ -1976,10 +1982,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1976
1982
|
const entry = getAgentEngineEntry(stored.engine);
|
|
1977
1983
|
if (entry &&
|
|
1978
1984
|
(await runWithRequestContext({ userEmail, orgId }, () => isStoredEngineUsableForRequest(stored, entry)))) {
|
|
1985
|
+
const model = normalizeAgentEngineStatusModel(entry, stored.model);
|
|
1979
1986
|
return {
|
|
1980
1987
|
configured: true,
|
|
1981
1988
|
engine: stored.engine,
|
|
1982
|
-
model
|
|
1989
|
+
model,
|
|
1983
1990
|
source: "env",
|
|
1984
1991
|
envVar: entry.requiredEnvVars[0],
|
|
1985
1992
|
openAiBaseUrlConfigured,
|