@k2b/cloud 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -5
- package/scripts/app-favicon.ts +7 -6
- package/scripts/build.ts +3 -0
- package/scripts/installable-assets.ts +61 -0
- package/scripts/preload.ts +3 -0
- package/src/_internal/app-access-path.ts +12 -0
- package/src/_internal/app-access-request.ts +13 -0
- package/src/_internal/app-access-resources.ts +141 -0
- package/src/_internal/app-event-catalog.ts +40 -0
- package/src/_internal/app-event-schemas.ts +127 -0
- package/src/_internal/application-package.ts +40 -0
- package/src/_internal/built-in-app-ids.json +27 -0
- package/src/_internal/capabilities.ts +185 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +10 -5
- package/src/_internal/define-app.ts +159 -45
- package/src/_internal/installable-assets.ts +90 -0
- package/src/_internal/installable-head.ts +22 -0
- package/src/_internal/object-store-area.ts +9 -0
- package/src/_internal/object-store-config.ts +101 -0
- package/src/_internal/object-store-key.ts +14 -0
- package/src/_internal/page-responses.ts +34 -3
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +15 -1
- package/src/_internal/registry.ts +44 -1
- package/src/_internal/runtime-context.ts +9 -1
- package/src/_internal/runtime-watcher.ts +8 -0
- package/src/_internal/status-preserving-ssr.ts +12 -1
- package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
- package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
- package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
- package/src/access/AppAccessEditor.tsx +380 -0
- package/src/access/PermissionEditor.browser-harness.tsx +22 -2
- package/src/access/PermissionEditor.tsx +386 -77
- package/src/access/PrincipalAvatar.tsx +63 -0
- package/src/access/PrincipalPicker.tsx +29 -8
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-adapter.ts +51 -0
- package/src/access/app-access-messages.ts +172 -0
- package/src/access/messages.ts +14 -0
- package/src/access/ui.ts +4 -1
- package/src/account/Avatar.tsx +21 -3
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +8 -1
- package/src/ai/admin.ts +3 -0
- package/src/ai/app-frame.ts +12 -0
- package/src/ai/application-tasks.ts +101 -0
- package/src/ai/assistant-models.ts +126 -64
- package/src/ai/capabilities.ts +19 -4
- package/src/ai/capability-execution.ts +5 -2
- package/src/ai/chart-block.ts +63 -30
- package/src/ai/chat/chart-block.tsx +12 -3
- package/src/ai/chat/composer-adapter.ts +8 -7
- package/src/ai/chat/message-actions.tsx +1 -1
- package/src/ai/chat/message-utils.ts +2 -2
- package/src/ai/chat/presentation.tsx +22 -1
- package/src/ai/chat-quotas.ts +5 -4
- package/src/ai/chat-tasks.ts +2 -2
- package/src/ai/client/controller.ts +11 -10
- package/src/ai/client/projection.ts +3 -3
- package/src/ai/code-capability-routes.ts +1 -0
- package/src/ai/compaction.ts +3 -3
- package/src/ai/credentials.ts +13 -114
- package/src/ai/default-tool-contracts.ts +4 -3
- package/src/ai/dictation-runtime.ts +4 -4
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +38 -27
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +11 -5
- package/src/ai/inference-calls.ts +6 -4
- package/src/ai/maintenance.ts +1 -1
- package/src/ai/memory-learning-runs.ts +5 -5
- package/src/ai/memory-learning.ts +3 -3
- package/src/ai/message-queue.ts +1 -2
- package/src/ai/migrate.ts +75 -42
- package/src/ai/model-access-migrate.ts +3 -17
- package/src/ai/model-access.ts +44 -85
- package/src/ai/model-card.tsx +134 -0
- package/src/ai/model-catalog.ts +359 -0
- package/src/ai/model-chooser-messages.ts +88 -0
- package/src/ai/model-chooser.tsx +577 -0
- package/src/ai/model-delete.ts +234 -0
- package/src/ai/model-fixture.ts +39 -0
- package/src/ai/model-icon.tsx +36 -0
- package/src/ai/model-overview.ts +53 -0
- package/src/ai/model-usage.ts +45 -0
- package/src/ai/openai-api-messages.ts +0 -4
- package/src/ai/openai-api-slots.ts +10 -2
- package/src/ai/openai-api.ts +83 -16
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/projects-routes.ts +2 -1
- package/src/ai/projects.ts +13 -12
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +138 -0
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider-retry.ts +1 -1
- package/src/ai/provider.ts +2 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +11 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas-migrate.ts +1 -0
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +6 -0
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +17 -18
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- package/src/ai/store.ts +45 -23
- package/src/ai/stream.ts +1 -1
- package/src/ai/structured-runs.ts +2 -2
- package/src/ai/structured.ts +29 -46
- package/src/ai/task-contracts.ts +25 -5
- package/src/ai/task-execution.ts +15 -6
- package/src/ai/task-model.ts +9 -0
- package/src/ai/transcription.ts +37 -21
- package/src/ai/turn-failure.ts +24 -2
- package/src/ai/types.ts +50 -62
- package/src/ai/ui.tsx +13 -0
- package/src/ai/usage.ts +8 -8
- package/src/ai/validate.ts +8 -7
- package/src/ai/view-block.ts +2 -2
- package/src/api/accounts-entities.ts +40 -16
- package/src/api/admin-ai-quotas.ts +34 -56
- package/src/api/admin-ai-usage.ts +1 -1
- package/src/api/admin-ai.ts +173 -0
- package/src/api/admin-core-settings.ts +68 -244
- package/src/api/admin-directory-shares.ts +55 -0
- package/src/api/admin-identity.ts +81 -2
- package/src/api/app-access.ts +85 -0
- package/src/api/app-approval.ts +3 -1
- package/src/api/app-event-delivery.ts +51 -0
- package/src/api/app-events.ts +123 -0
- package/src/api/app-resource-access.ts +48 -0
- package/src/api/auth/account-mismatch.ts +24 -0
- package/src/api/auth.ts +26 -3
- package/src/api/capabilities.ts +127 -15
- package/src/api/capability-streams.ts +2 -5
- package/src/api/identity-child-invocation.ts +145 -0
- package/src/api/index.ts +11 -2
- package/src/api/mcp.ts +180 -5
- package/src/api/me.ts +38 -17
- package/src/api/pwa-phone.ts +87 -48
- package/src/api/pwa.ts +34 -8
- package/src/api/resource-links.ts +83 -6
- package/src/api/resource-resolve.ts +50 -0
- package/src/api/search/schemas.ts +3 -1
- package/src/api/search.ts +9 -7
- package/src/api/widgets.ts +1 -0
- package/src/app-pwa/authority.ts +75 -0
- package/src/app-pwa/contracts.ts +177 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +23 -0
- package/src/app-pwa/protocol.ts +27 -0
- package/src/app-pwa/schema.ts +59 -0
- package/src/app-pwa/service.ts +861 -0
- package/src/app-pwa/transaction.ts +338 -0
- package/src/app-pwa/transport-input.ts +57 -0
- package/src/app-pwa/transport.ts +235 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/app-access-recovery.ts +44 -0
- package/src/browser/app-session.ts +14 -3
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/live-websocket.ts +5 -0
- package/src/browser/pwa-drafts.ts +57 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/resource-picker.tsx +3 -2
- package/src/capabilities/calendar-sources.ts +302 -0
- package/src/capabilities/executions.ts +10 -3
- package/src/capabilities/migrate.ts +4 -0
- package/src/capabilities/nested.ts +43 -0
- package/src/capabilities/server-invoke.ts +202 -0
- package/src/capabilities/server.ts +26 -187
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/account.ts +57 -0
- package/src/cli/admin/agents.ts +66 -0
- package/src/cli/admin/ai-models.ts +199 -0
- package/src/cli/admin/ai-quotas.ts +6 -102
- package/src/cli/admin/ai-usage.ts +4 -4
- package/src/cli/admin/app-credentials.ts +5 -3
- package/src/cli/admin/index.ts +7 -3
- package/src/cli/app-access.ts +60 -0
- package/src/cli/apps.ts +89 -26
- package/src/cli/avatar.ts +64 -0
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/cli/index.ts +1 -1
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +52 -5
- package/src/contracts/app-approval.ts +8 -1
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +24 -5
- package/src/contracts/calendar-source.ts +274 -0
- package/src/contracts/calendar-view.ts +35 -0
- package/src/contracts/capabilities.ts +164 -3
- package/src/contracts/geo-admin.ts +2 -0
- package/src/contracts/geo-route.ts +0 -11
- package/src/contracts/geo.ts +14 -2
- package/src/contracts/index.ts +6 -0
- package/src/contracts/installable.ts +40 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/profile.ts +32 -0
- package/src/contracts/pwa-builds.ts +14 -0
- package/src/contracts/pwa-location.ts +38 -0
- package/src/contracts/pwa.ts +24 -3
- package/src/contracts/registry.ts +8 -0
- package/src/contracts/resource-clipboard.ts +35 -0
- package/src/contracts/resource-pages.ts +140 -0
- package/src/contracts/resource-resolution.ts +13 -0
- package/src/contracts/shared.ts +4 -0
- package/src/contracts/user-notifications.ts +2 -0
- package/src/events/app-event-delivery.ts +100 -0
- package/src/events/app-events.testing.ts +65 -0
- package/src/events/app-events.ts +134 -0
- package/src/events/core-user-created.ts +24 -0
- package/src/events/index.ts +9 -0
- package/src/events/testing.ts +2 -0
- package/src/index.ts +2 -1
- package/src/server/api/respond.ts +8 -3
- package/src/server/index.ts +1 -0
- package/src/server/middleware/app-access.ts +7 -6
- package/src/server/middleware/auth.ts +168 -66
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +4 -2
- package/src/server/middleware/workload.ts +3 -3
- package/src/server/oauth-scope.ts +10 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +8 -0
- package/src/services/accounts/app.ts +35 -3
- package/src/services/accounts/avatar.ts +342 -35
- package/src/services/accounts/base-group.ts +4 -5
- package/src/services/accounts/directory-shares.ts +157 -0
- package/src/services/accounts/entities.ts +50 -6
- package/src/services/accounts/groups.ts +3 -1
- package/src/services/accounts/identities.ts +64 -12
- package/src/services/accounts/local-groups.ts +1 -1
- package/src/services/accounts/users.ts +23 -15
- package/src/services/app-access-management.ts +519 -0
- package/src/services/app-access.ts +25 -7
- package/src/services/app-approval.ts +15 -4
- package/src/services/app-details.ts +224 -0
- package/src/services/app-discovery.ts +2 -1
- package/src/services/app-event-shapes.ts +139 -0
- package/src/services/app-events/accept.ts +116 -0
- package/src/services/app-events/admin.ts +250 -0
- package/src/services/app-events/declarations.ts +187 -0
- package/src/services/app-events/deliver.ts +218 -0
- package/src/services/app-events.ts +92 -0
- package/src/services/config-conflict.ts +11 -0
- package/src/services/core-user-events.ts +1 -0
- package/src/services/freeipa-config.ts +9 -0
- package/src/services/geo/admin.ts +4 -1
- package/src/services/geo/ledger.ts +47 -14
- package/src/services/geo/route-chain.ts +40 -18
- package/src/services/geo/route-estimate.ts +19 -0
- package/src/services/geo/route-plan.ts +0 -2
- package/src/services/geo/route-service.ts +57 -39
- package/src/services/geo/service.ts +51 -25
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/index.ts +2 -1
- package/src/services/identity/invocation-actor.ts +50 -2
- package/src/services/identity/invocation-authority.ts +38 -1
- package/src/services/identity/invocation-token.ts +255 -50
- package/src/services/identity/issue-invocation.ts +9 -1
- package/src/services/identity/models-only-auth.ts +13 -4
- package/src/services/identity/nested-invocation.ts +128 -0
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +24 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/policy.ts +19 -12
- package/src/services/notifications/browser.ts +4 -0
- package/src/services/notifications/catalog.ts +5 -2
- package/src/services/notifications/platform.ts +7 -2
- package/src/services/notifications/user.ts +18 -7
- package/src/services/oauth-tokens.ts +28 -2
- package/src/services/object-store.ts +217 -0
- package/src/services/providers/local/users.ts +5 -0
- package/src/services/pwa-devices.ts +147 -37
- package/src/services/service-account-credentials.ts +5 -2
- package/src/services/session/index.ts +14 -0
- package/src/services/settings/core-settings.ts +91 -47
- package/src/services/settings/index.ts +8 -0
- package/src/services/settings/versions.ts +26 -0
- package/src/shared/ai-models.ts +274 -0
- package/src/shared/ai-quotas.ts +2 -2
- package/src/shared/ai-usage.ts +2 -2
- package/src/shared/index.ts +2 -0
- package/src/shared-text/documents.ts +25 -8
- package/src/shared-text/log.ts +1 -1
- package/src/shared-text/text-diff.ts +75 -0
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +11 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +114 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +1 -1
- package/src/ssr/AppPwaRuntime.island.tsx +82 -0
- package/src/ssr/CloudShell.island.tsx +25 -4
- package/src/ssr/Layout.tsx +15 -7
- package/src/ssr/PwaLayout.tsx +73 -8
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/PwaRuntime.island.tsx +85 -6
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/ShellRuntime.island.tsx +5 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -20
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-pwa-document.tsx +16 -0
- package/src/ssr/app-pwa-messages.ts +17 -0
- package/src/ssr/index.ts +8 -0
- package/src/ssr/layout-context.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +9 -1
- package/src/ssr/pwa-messages.ts +10 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +2 -0
- package/src/styles/global.css +1 -0
- package/src/styles/profile-picture.css +270 -0
- package/src/styles/utilities-navigation.css +6 -0
- package/src/workflows/ai/policy.ts +49 -0
- package/src/workflows/ai/runtime.ts +131 -21
- package/src/workflows/ai/store.ts +283 -35
- package/src/workflows/ai/types.ts +8 -2
- package/src/workflows/ai-actions.ts +148 -120
- package/src/workflows/authoring.ts +31 -0
- package/src/workflows/contracts.ts +1 -0
- package/src/workflows/definition.ts +2 -0
- package/src/workflows/editor.ts +15 -0
- package/src/workflows/index.ts +2 -1
- package/src/workflows/language/compiler.ts +34 -3
- package/src/workflows/runtime/executor.ts +132 -33
- package/src/workflows/runtime/index.ts +2 -0
- package/src/workflows/runtime/ports.ts +5 -1
- package/src/workflows/store/actions.ts +1 -1
- package/src/workflows/store/definitions.ts +5 -0
- package/src/workflows/store/runs.ts +5 -3
- package/src/workflows/store/worker.ts +7 -2
- package/src/ai/model-pricing.ts +0 -43
- package/src/ai/model-request-settings.ts +0 -98
- package/src/ai/settings-messages.ts +0 -129
- package/src/shared/ai-model-request-settings.ts +0 -21
package/src/ai/store.ts
CHANGED
|
@@ -279,6 +279,8 @@ type TurnSteerRow = {
|
|
|
279
279
|
};
|
|
280
280
|
|
|
281
281
|
type ConversationResourceRefRow = {
|
|
282
|
+
context: string | null;
|
|
283
|
+
status: string | null;
|
|
282
284
|
resource_type: string;
|
|
283
285
|
resource_id: string;
|
|
284
286
|
title: string | null;
|
|
@@ -298,6 +300,8 @@ type ConversationResourceOccurrenceRow = ConversationResourceRefRow & {
|
|
|
298
300
|
};
|
|
299
301
|
|
|
300
302
|
type ConversationSourceRow = {
|
|
303
|
+
context: string | null;
|
|
304
|
+
status: string | null;
|
|
301
305
|
source_kind: AiConversationSource["kind"];
|
|
302
306
|
source_key: string;
|
|
303
307
|
title: string;
|
|
@@ -340,6 +344,7 @@ type InterChatMessageRow = {
|
|
|
340
344
|
};
|
|
341
345
|
|
|
342
346
|
type TimelineRow = {
|
|
347
|
+
model_replacement: { kind: "model_replaced"; previousLabel: string; nextLabel: string } | null;
|
|
343
348
|
id: string;
|
|
344
349
|
seq: number;
|
|
345
350
|
loop_id: string | null;
|
|
@@ -356,6 +361,8 @@ const iso = (value: Date | string): string => (value instanceof Date ? value.toI
|
|
|
356
361
|
|
|
357
362
|
const rowToConversationResourceRef = (row: ConversationResourceRefRow): AiConversationResourceRef => ({
|
|
358
363
|
ref: { type: row.resource_type, id: row.resource_id },
|
|
364
|
+
context: row.context,
|
|
365
|
+
status: row.status,
|
|
359
366
|
title: row.title,
|
|
360
367
|
preview: row.preview,
|
|
361
368
|
icon: row.icon,
|
|
@@ -397,6 +404,8 @@ const rowToConversationResourceOccurrence = (row: ConversationResourceOccurrence
|
|
|
397
404
|
|
|
398
405
|
const rowToConversationSource = (row: ConversationSourceRow): AiConversationSource => ({
|
|
399
406
|
kind: row.source_kind,
|
|
407
|
+
context: row.context,
|
|
408
|
+
status: row.status,
|
|
400
409
|
key: row.source_key,
|
|
401
410
|
title: row.title,
|
|
402
411
|
preview: row.preview,
|
|
@@ -649,7 +658,7 @@ const rowToEnrichmentRun = (row: EnrichmentRunRow): AiEnrichmentRun => ({
|
|
|
649
658
|
conversationId: row.conversation_id,
|
|
650
659
|
status: row.status,
|
|
651
660
|
trigger: row.trigger,
|
|
652
|
-
|
|
661
|
+
modelId: row.model_profile_id,
|
|
653
662
|
mode: row.mode,
|
|
654
663
|
durationMs: row.duration_ms === null ? null : Number(row.duration_ms),
|
|
655
664
|
titleUpdated: row.title_updated,
|
|
@@ -689,7 +698,7 @@ const rowToMessage = (row: MessageRow): AiStoredMessage => {
|
|
|
689
698
|
kind: row.kind,
|
|
690
699
|
message,
|
|
691
700
|
loopId: row.loop_id,
|
|
692
|
-
|
|
701
|
+
modelId: row.model_profile_id,
|
|
693
702
|
providerModel: row.provider_model,
|
|
694
703
|
usage: row.usage ? parseJsonValue<Usage>(row.usage) : null,
|
|
695
704
|
stopReason: row.stop_reason,
|
|
@@ -719,7 +728,7 @@ const rowToTurn = (row: TurnRow): AiTurn => ({
|
|
|
719
728
|
conversationId: row.conversation_id,
|
|
720
729
|
status: row.status,
|
|
721
730
|
attempt: Number(row.attempt ?? 0),
|
|
722
|
-
|
|
731
|
+
modelId: row.model_profile_id,
|
|
723
732
|
createdAt: iso(row.created_at),
|
|
724
733
|
completedAt: row.completed_at ? iso(row.completed_at) : null,
|
|
725
734
|
error: row.error,
|
|
@@ -1140,7 +1149,7 @@ const insertMessageLocked = async (
|
|
|
1140
1149
|
kind?: "message" | "summary";
|
|
1141
1150
|
seq?: number;
|
|
1142
1151
|
loopId?: string | null;
|
|
1143
|
-
|
|
1152
|
+
modelId?: string | null;
|
|
1144
1153
|
meta?: AiStoredMessage["meta"];
|
|
1145
1154
|
},
|
|
1146
1155
|
db: typeof sql = sql,
|
|
@@ -1181,7 +1190,7 @@ const insertMessageLocked = async (
|
|
|
1181
1190
|
${JSON.stringify(input.message)}::text::jsonb,
|
|
1182
1191
|
${messageSearchText(input.message)},
|
|
1183
1192
|
${input.loopId ?? null},
|
|
1184
|
-
${input.
|
|
1193
|
+
${input.modelId ?? null},
|
|
1185
1194
|
${providerModel},
|
|
1186
1195
|
${usage ? JSON.stringify(usage) : null}::text::jsonb,
|
|
1187
1196
|
${stopReason},
|
|
@@ -1217,7 +1226,7 @@ const appendTurnOwnedMessage = async (input: {
|
|
|
1217
1226
|
kind?: "message" | "summary";
|
|
1218
1227
|
seq?: number;
|
|
1219
1228
|
loopId: string | null;
|
|
1220
|
-
|
|
1229
|
+
modelId?: string | null;
|
|
1221
1230
|
meta?: AiStoredMessage["meta"];
|
|
1222
1231
|
}): Promise<AiStoredMessage | null> => {
|
|
1223
1232
|
return sql.begin(async (tx) => {
|
|
@@ -1252,7 +1261,7 @@ const appendTurnOwnedMessage = async (input: {
|
|
|
1252
1261
|
${input.message.role},
|
|
1253
1262
|
${JSON.stringify(input.message)}::text::jsonb,
|
|
1254
1263
|
${input.loopId},
|
|
1255
|
-
${input.
|
|
1264
|
+
${input.modelId ?? null},
|
|
1256
1265
|
${providerModel},
|
|
1257
1266
|
${usage ? JSON.stringify(usage) : null}::text::jsonb,
|
|
1258
1267
|
${stopReason},
|
|
@@ -1777,15 +1786,17 @@ export const aiConversations: AiConversationService = {
|
|
|
1777
1786
|
for (const resource of resources) {
|
|
1778
1787
|
await tx`
|
|
1779
1788
|
INSERT INTO ai.conversation_resource_refs (
|
|
1780
|
-
conversation_id, resource_type, resource_id, title, preview, icon, href, source_turn_id, source_call_id
|
|
1789
|
+
conversation_id, resource_type, resource_id, title, preview, icon, href, context, status, source_turn_id, source_call_id
|
|
1781
1790
|
) VALUES (
|
|
1782
1791
|
${input.conversationId}, ${resource.ref.type}, ${resource.ref.id}, ${resource.title ?? null}, ${resource.preview ?? null},
|
|
1783
|
-
${resource.icon ?? null}, ${resource.href ?? null}, ${input.turnId ?? null}, ${input.callId ?? null}
|
|
1792
|
+
${resource.icon ?? null}, ${resource.href ?? null}, ${resource.context ?? null}, ${resource.status ?? null}, ${input.turnId ?? null}, ${input.callId ?? null}
|
|
1784
1793
|
)
|
|
1785
1794
|
ON CONFLICT (conversation_id, resource_type, resource_id)
|
|
1786
1795
|
DO UPDATE SET
|
|
1787
1796
|
title = COALESCE(EXCLUDED.title, ai.conversation_resource_refs.title),
|
|
1788
1797
|
preview = COALESCE(EXCLUDED.preview, ai.conversation_resource_refs.preview),
|
|
1798
|
+
context = COALESCE(EXCLUDED.context, ai.conversation_resource_refs.context),
|
|
1799
|
+
status = COALESCE(EXCLUDED.status, ai.conversation_resource_refs.status),
|
|
1789
1800
|
icon = COALESCE(EXCLUDED.icon, ai.conversation_resource_refs.icon),
|
|
1790
1801
|
href = COALESCE(EXCLUDED.href, ai.conversation_resource_refs.href),
|
|
1791
1802
|
-- Project context is indexed on every turn without a call. It must not move a resource a tool call
|
|
@@ -1811,7 +1822,7 @@ export const aiConversations: AiConversationService = {
|
|
|
1811
1822
|
const pattern = searchPattern(input.search);
|
|
1812
1823
|
const cursor = decodeResourceCursor(input.before);
|
|
1813
1824
|
const rows = await sql<(ConversationResourceRefRow & { cursor_at: string })[]>`
|
|
1814
|
-
SELECT indexed.resource_type, indexed.resource_id, indexed.title, indexed.preview, indexed.icon, indexed.href,
|
|
1825
|
+
SELECT indexed.resource_type, indexed.resource_id, indexed.title, indexed.preview, indexed.icon, indexed.href, indexed.context, indexed.status,
|
|
1815
1826
|
source_turn.short_id AS source_turn_id, indexed.source_call_id, indexed.first_seen_at, indexed.last_seen_at,
|
|
1816
1827
|
to_char(indexed.last_seen_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') AS cursor_at
|
|
1817
1828
|
FROM ai.conversation_resource_refs indexed
|
|
@@ -1847,7 +1858,7 @@ export const aiConversations: AiConversationService = {
|
|
|
1847
1858
|
const pattern = searchPattern(input.search);
|
|
1848
1859
|
const cursor = decodeResourceOccurrenceCursor(input.before);
|
|
1849
1860
|
const rows = await sql<(ConversationResourceOccurrenceRow & { cursor_at: string })[]>`
|
|
1850
|
-
SELECT indexed.resource_type, indexed.resource_id, indexed.title, indexed.preview, indexed.icon, indexed.href,
|
|
1861
|
+
SELECT indexed.resource_type, indexed.resource_id, indexed.title, indexed.preview, indexed.icon, indexed.href, indexed.context, indexed.status,
|
|
1851
1862
|
source_turn.short_id AS source_turn_id, indexed.source_call_id, indexed.first_seen_at, indexed.last_seen_at,
|
|
1852
1863
|
conversation.short_id AS conversation_short_id, conversation.title AS conversation_title,
|
|
1853
1864
|
conversation.updated_at AS conversation_updated_at,
|
|
@@ -1908,6 +1919,7 @@ export const aiConversations: AiConversationService = {
|
|
|
1908
1919
|
SELECT *
|
|
1909
1920
|
FROM (
|
|
1910
1921
|
SELECT source.kind AS source_kind, source.source_key, source.title, source.preview,
|
|
1922
|
+
NULL::text AS context, NULL::text AS status,
|
|
1911
1923
|
source.icon, source.href, file.path, file.media_type, file.size,
|
|
1912
1924
|
source.resource_type, source.resource_id, source.occurrences,
|
|
1913
1925
|
turn.short_id AS source_turn_id, source.source_turn_id AS turn_uuid, source.source_call_id,
|
|
@@ -1922,6 +1934,7 @@ export const aiConversations: AiConversationService = {
|
|
|
1922
1934
|
|
|
1923
1935
|
SELECT 'file' AS source_kind, file.path AS source_key,
|
|
1924
1936
|
regexp_replace(file.path, '^.*/', '') AS title, 'Attached to the conversation' AS preview,
|
|
1937
|
+
NULL::text AS context, NULL::text AS status,
|
|
1925
1938
|
CASE WHEN file.media_type LIKE 'image/%' THEN 'ti ti-photo' ELSE 'ti ti-file' END AS icon,
|
|
1926
1939
|
NULL::text AS href, file.path, file.media_type, file.size,
|
|
1927
1940
|
NULL::text AS resource_type, NULL::text AS resource_id, 1 AS occurrences,
|
|
@@ -1934,7 +1947,7 @@ export const aiConversations: AiConversationService = {
|
|
|
1934
1947
|
|
|
1935
1948
|
SELECT 'resource' AS source_kind, indexed.resource_type || ':' || indexed.resource_id AS source_key,
|
|
1936
1949
|
COALESCE(indexed.title, indexed.resource_type || ' ' || indexed.resource_id) AS title,
|
|
1937
|
-
indexed.preview, indexed.icon, indexed.href, NULL::text AS path, NULL::text AS media_type, NULL::bigint AS size,
|
|
1950
|
+
indexed.preview, indexed.context, indexed.status, indexed.icon, indexed.href, NULL::text AS path, NULL::text AS media_type, NULL::bigint AS size,
|
|
1938
1951
|
indexed.resource_type, indexed.resource_id, 1 AS occurrences,
|
|
1939
1952
|
turn.short_id AS source_turn_id, indexed.source_turn_id AS turn_uuid, indexed.source_call_id,
|
|
1940
1953
|
indexed.first_seen_at, indexed.last_seen_at
|
|
@@ -2135,7 +2148,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2135
2148
|
"idx_ai_turns_conversation_short_id",
|
|
2136
2149
|
(attempt, shortId) => attempt<TurnRow[]>`
|
|
2137
2150
|
INSERT INTO ai.turns (short_id, conversation_id, model_profile_id, status, run_config)
|
|
2138
|
-
VALUES (${shortId}, ${message.target_conversation_id}, ${input.
|
|
2151
|
+
VALUES (${shortId}, ${message.target_conversation_id}, ${input.modelId}, 'queued', (${JSON.stringify(input.runConfig)}::text)::jsonb)
|
|
2139
2152
|
RETURNING *
|
|
2140
2153
|
`,
|
|
2141
2154
|
);
|
|
@@ -2434,7 +2447,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2434
2447
|
${input.conversationId},
|
|
2435
2448
|
${input.status},
|
|
2436
2449
|
${input.trigger},
|
|
2437
|
-
${input.
|
|
2450
|
+
${input.modelId ?? null},
|
|
2438
2451
|
${input.mode ?? null},
|
|
2439
2452
|
${input.durationMs ?? null},
|
|
2440
2453
|
${input.titleUpdated ?? false},
|
|
@@ -2691,6 +2704,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2691
2704
|
SELECT
|
|
2692
2705
|
users.short_id AS id,
|
|
2693
2706
|
users.seq,
|
|
2707
|
+
users.meta_payload->'modelReplacement' AS model_replacement,
|
|
2694
2708
|
COALESCE(timeline_turn.short_id, users.loop_id) AS loop_id,
|
|
2695
2709
|
left(
|
|
2696
2710
|
regexp_replace(
|
|
@@ -2722,6 +2736,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2722
2736
|
id: row.id,
|
|
2723
2737
|
seq: Number(row.seq),
|
|
2724
2738
|
loopId: row.loop_id,
|
|
2739
|
+
...(row.model_replacement ? { modelReplacement: row.model_replacement } : {}),
|
|
2725
2740
|
userPreview: row.user_preview?.trim() || "Message",
|
|
2726
2741
|
assistantPreview: row.assistant_preview?.trim() || "",
|
|
2727
2742
|
isSteer: Boolean(row.is_steer),
|
|
@@ -2939,7 +2954,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2939
2954
|
kind: "summary",
|
|
2940
2955
|
seq: checkpointSeq,
|
|
2941
2956
|
loopId: null,
|
|
2942
|
-
|
|
2957
|
+
modelId: input.modelId ?? null,
|
|
2943
2958
|
meta: { compactedCount: archived[0]?.count ?? 0, ...(todoPlan ? { todoPlan } : {}) },
|
|
2944
2959
|
},
|
|
2945
2960
|
tx,
|
|
@@ -2958,7 +2973,7 @@ export const aiConversations: AiConversationService = {
|
|
|
2958
2973
|
"idx_ai_turns_conversation_short_id",
|
|
2959
2974
|
(attempt, shortId) => attempt<TurnRow[]>`
|
|
2960
2975
|
INSERT INTO ai.turns (short_id, conversation_id, model_profile_id, status, run_config)
|
|
2961
|
-
VALUES (${shortId}, ${input.conversationId}, ${input.
|
|
2976
|
+
VALUES (${shortId}, ${input.conversationId}, ${input.modelId}, 'queued',
|
|
2962
2977
|
(${input.runConfig ? JSON.stringify(input.runConfig) : null}::text)::jsonb)
|
|
2963
2978
|
RETURNING *
|
|
2964
2979
|
`,
|
|
@@ -3015,7 +3030,7 @@ export const aiConversations: AiConversationService = {
|
|
|
3015
3030
|
VALUES (
|
|
3016
3031
|
${shortId},
|
|
3017
3032
|
${input.conversationId},
|
|
3018
|
-
${input.
|
|
3033
|
+
${input.modelId},
|
|
3019
3034
|
'queued',
|
|
3020
3035
|
(${JSON.stringify(input.runConfig)}::text)::jsonb
|
|
3021
3036
|
)
|
|
@@ -3064,7 +3079,12 @@ export const aiConversations: AiConversationService = {
|
|
|
3064
3079
|
conversationId: input.conversationId,
|
|
3065
3080
|
message: input.userMessage,
|
|
3066
3081
|
loopId: turn.id,
|
|
3067
|
-
meta:
|
|
3082
|
+
meta: {
|
|
3083
|
+
...(input.expectedDraftRevision === undefined ? {} : { submittedDraftRevision: input.expectedDraftRevision }),
|
|
3084
|
+
modelRequest: input.runConfig.model,
|
|
3085
|
+
modelResolved: input.runConfig.modelResolved,
|
|
3086
|
+
...(input.runConfig.modelReplacement ? { modelReplacement: input.runConfig.modelReplacement } : {}),
|
|
3087
|
+
},
|
|
3068
3088
|
},
|
|
3069
3089
|
tx,
|
|
3070
3090
|
);
|
|
@@ -3074,15 +3094,17 @@ export const aiConversations: AiConversationService = {
|
|
|
3074
3094
|
for (const resource of resources) {
|
|
3075
3095
|
await tx`
|
|
3076
3096
|
INSERT INTO ai.conversation_resource_refs (
|
|
3077
|
-
conversation_id, resource_type, resource_id, title, preview, icon, href, source_turn_id
|
|
3097
|
+
conversation_id, resource_type, resource_id, title, preview, icon, href, context, status, source_turn_id
|
|
3078
3098
|
) VALUES (
|
|
3079
3099
|
${input.conversationId}, ${resource.ref.type}, ${resource.ref.id}, ${resource.title ?? null},
|
|
3080
|
-
${resource.preview ?? null}, ${resource.icon ?? null}, ${resource.href ?? null}, ${turn.id}
|
|
3100
|
+
${resource.preview ?? null}, ${resource.icon ?? null}, ${resource.href ?? null}, ${resource.context ?? null}, ${resource.status ?? null}, ${turn.id}
|
|
3081
3101
|
)
|
|
3082
3102
|
ON CONFLICT (conversation_id, resource_type, resource_id)
|
|
3083
3103
|
DO UPDATE SET
|
|
3084
3104
|
title = COALESCE(EXCLUDED.title, ai.conversation_resource_refs.title),
|
|
3085
3105
|
preview = COALESCE(EXCLUDED.preview, ai.conversation_resource_refs.preview),
|
|
3106
|
+
context = COALESCE(EXCLUDED.context, ai.conversation_resource_refs.context),
|
|
3107
|
+
status = COALESCE(EXCLUDED.status, ai.conversation_resource_refs.status),
|
|
3086
3108
|
icon = COALESCE(EXCLUDED.icon, ai.conversation_resource_refs.icon),
|
|
3087
3109
|
href = COALESCE(EXCLUDED.href, ai.conversation_resource_refs.href),
|
|
3088
3110
|
source_turn_id = EXCLUDED.source_turn_id,
|
|
@@ -3120,7 +3142,7 @@ export const aiConversations: AiConversationService = {
|
|
|
3120
3142
|
getLatestTurn: async (input) => {
|
|
3121
3143
|
const [row] = await sql<
|
|
3122
3144
|
TurnRow[]
|
|
3123
|
-
>`SELECT * FROM ai.turns WHERE conversation_id = ${input.conversationId}::uuid AND NOT COALESCE(run_config ? 'background', false)
|
|
3145
|
+
>`SELECT * FROM ai.turns WHERE conversation_id = ${input.conversationId}::uuid AND NOT COALESCE(run_config ? 'background', false) AND (${input.kind ?? null}::text IS NULL OR run_config->>'kind'=${input.kind ?? null})
|
|
3124
3146
|
ORDER BY created_at DESC, id DESC LIMIT 1`;
|
|
3125
3147
|
return row ? rowToTurn(row) : null;
|
|
3126
3148
|
},
|
|
@@ -3978,7 +4000,7 @@ export const aiConversations: AiConversationService = {
|
|
|
3978
4000
|
kind: opts?.kind,
|
|
3979
4001
|
seq: opts?.seq,
|
|
3980
4002
|
loopId: opts?.kind === "summary" && !input.background ? null : input.turnId,
|
|
3981
|
-
|
|
4003
|
+
modelId: input.modelId,
|
|
3982
4004
|
meta,
|
|
3983
4005
|
});
|
|
3984
4006
|
if (!appended) {
|
|
@@ -3997,7 +4019,7 @@ export const aiConversations: AiConversationService = {
|
|
|
3997
4019
|
kind: opts?.kind,
|
|
3998
4020
|
seq: opts?.seq,
|
|
3999
4021
|
loopId: input.turnId && opts?.kind !== "summary" ? input.turnId : null,
|
|
4000
|
-
|
|
4022
|
+
modelId: input.modelId,
|
|
4001
4023
|
meta,
|
|
4002
4024
|
},
|
|
4003
4025
|
tx,
|
package/src/ai/stream.ts
CHANGED
|
@@ -134,7 +134,7 @@ const turnSnapshotFromActive = (active: NonNullable<Awaited<ReturnType<typeof ai
|
|
|
134
134
|
status: active.turn.status,
|
|
135
135
|
seq: active.liveSeq,
|
|
136
136
|
blocks: [...active.liveBlocks],
|
|
137
|
-
|
|
137
|
+
modelId: active.turn.modelId,
|
|
138
138
|
createdAt: active.turn.createdAt,
|
|
139
139
|
actionWaitMs: active.actionWaitMs,
|
|
140
140
|
waitingSince: active.waitingSince,
|
|
@@ -15,7 +15,7 @@ export type AiStructuredRunRecord = {
|
|
|
15
15
|
traceId?: string;
|
|
16
16
|
task: string;
|
|
17
17
|
appId?: string;
|
|
18
|
-
|
|
18
|
+
modelId?: string;
|
|
19
19
|
providerModel?: string;
|
|
20
20
|
status: "ok" | "failed";
|
|
21
21
|
durationMs: number;
|
|
@@ -40,7 +40,7 @@ export const recordAiStructuredRun = async (record: AiStructuredRunRecord): Prom
|
|
|
40
40
|
input_tokens, output_tokens, total_tokens,
|
|
41
41
|
mode, repaired, attempts, error_code, error, user_id, conversation_id, turn_id, workflow_run_id, trace_id
|
|
42
42
|
) VALUES (
|
|
43
|
-
${record.task}, ${record.appId ?? null}, ${record.
|
|
43
|
+
${record.task}, ${record.appId ?? null}, ${record.modelId ?? null}, ${record.providerModel ?? null},
|
|
44
44
|
${record.status}, ${Math.max(0, Math.round(record.durationMs))},
|
|
45
45
|
${usageNumber(record.usage, "input")}, ${usageNumber(record.usage, "output")},
|
|
46
46
|
${usageNumber(record.usage, "total")},
|
package/src/ai/structured.ts
CHANGED
|
@@ -2,49 +2,20 @@ import type { Input, LoopAggregate, StructuredMeta, Usage } from "@k2b/nessi";
|
|
|
2
2
|
import { nessi, StructuredOutputError } from "@k2b/nessi";
|
|
3
3
|
import type { z } from "zod";
|
|
4
4
|
import type { AccessSubject } from "../server";
|
|
5
|
-
import { coreSettings } from "../services";
|
|
6
5
|
import type { TraceContext } from "../services/logging";
|
|
7
6
|
import { trace } from "../services/logging";
|
|
7
|
+
import type { AiModelRequest, AiResolvedModelRef } from "../shared/ai-models";
|
|
8
8
|
import { taskReasoningEffort } from "./provider";
|
|
9
|
+
import { AiProviderError } from "./provider-errors";
|
|
9
10
|
import { inferenceProvider } from "./quota-provider";
|
|
10
11
|
import { resolveAiModel } from "./settings";
|
|
11
12
|
import { type AiUsageAttribution, safelyRecordStructuredRun } from "./structured-runs";
|
|
12
13
|
import type { AiResolvedModel } from "./types";
|
|
13
14
|
|
|
14
|
-
export const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
requestedModelId?: string;
|
|
19
|
-
workflowModelId?: string;
|
|
20
|
-
backgroundModelId?: string;
|
|
21
|
-
}): string | undefined => input.requestedModelId?.trim() || input.workflowModelId?.trim() || input.backgroundModelId?.trim() || undefined;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Resolve the model for background inference: explicit request →
|
|
25
|
-
* `ai.background_model_id` setting → platform default. Throws when AI is
|
|
26
|
-
* disabled or the model is unavailable — callers skip their work then.
|
|
27
|
-
*/
|
|
28
|
-
export const resolveAiBackgroundModel = async (requestedModelId?: string): Promise<AiResolvedModel> => {
|
|
29
|
-
const backgroundModelId = String((await coreSettings.get<string>(AI_BACKGROUND_MODEL_SETTING_KEY)) ?? "").trim();
|
|
30
|
-
return resolveAiModel({ kind: "selectable" }, requestedModelId?.trim() || backgroundModelId || undefined);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/** Resolve a workflow model: action override → workflow setting → background setting → platform default. */
|
|
34
|
-
export const resolveAiWorkflowModel = async (requestedModelId?: string): Promise<AiResolvedModel> => {
|
|
35
|
-
const [workflowModelId, backgroundModelId] = await Promise.all([
|
|
36
|
-
coreSettings.get<string>(AI_WORKFLOW_MODEL_SETTING_KEY),
|
|
37
|
-
coreSettings.get<string>(AI_BACKGROUND_MODEL_SETTING_KEY),
|
|
38
|
-
]);
|
|
39
|
-
return resolveAiModel(
|
|
40
|
-
{ kind: "selectable" },
|
|
41
|
-
selectAiWorkflowModelId({
|
|
42
|
-
requestedModelId,
|
|
43
|
-
workflowModelId: String(workflowModelId ?? ""),
|
|
44
|
-
backgroundModelId: String(backgroundModelId ?? ""),
|
|
45
|
-
}),
|
|
46
|
-
);
|
|
47
|
-
};
|
|
15
|
+
export const resolveAiBackgroundModel = (model: AiModelRequest = { class: "fast" }): Promise<AiResolvedModel> =>
|
|
16
|
+
resolveAiModel({ kind: "selectable" }, model);
|
|
17
|
+
export const resolveAiWorkflowModel = (model: AiModelRequest = { class: "balanced" }): Promise<AiResolvedModel> =>
|
|
18
|
+
resolveAiModel({ kind: "selectable" }, model);
|
|
48
19
|
|
|
49
20
|
export type RunAiStructuredInput<TOutput extends z.ZodType> = {
|
|
50
21
|
/** Short machine name for tracing, e.g. "chat-enrich". */
|
|
@@ -53,7 +24,7 @@ export type RunAiStructuredInput<TOutput extends z.ZodType> = {
|
|
|
53
24
|
output: TOutput;
|
|
54
25
|
outputName?: string;
|
|
55
26
|
systemPrompt?: string;
|
|
56
|
-
|
|
27
|
+
model?: AiModelRequest;
|
|
57
28
|
temperature?: number;
|
|
58
29
|
maxOutputTokens?: number;
|
|
59
30
|
signal?: AbortSignal;
|
|
@@ -65,17 +36,17 @@ export type RunAiStructuredInput<TOutput extends z.ZodType> = {
|
|
|
65
36
|
/** Metadata-only attribution, supplied by the authorized caller. */
|
|
66
37
|
attribution?: AiUsageAttribution;
|
|
67
38
|
/** Model resolution seam — tests inject a fake so they never touch shared settings. */
|
|
68
|
-
resolveModel?: (
|
|
39
|
+
resolveModel?: (model?: AiModelRequest) => Promise<AiResolvedModel>;
|
|
69
40
|
};
|
|
70
41
|
|
|
71
42
|
export type RunAiStructuredResult<TOutput extends z.ZodType> = {
|
|
72
43
|
output: z.infer<TOutput>;
|
|
73
|
-
|
|
44
|
+
model: AiResolvedModelRef;
|
|
74
45
|
usage?: Usage;
|
|
75
46
|
structuredMeta: StructuredMeta;
|
|
76
47
|
};
|
|
77
48
|
|
|
78
|
-
/** nessi wraps provider failures in direct structured mode;
|
|
49
|
+
/** nessi wraps classified provider failures in direct structured mode; preserve their stable code for callers. */
|
|
79
50
|
export const structuredProviderFailure = (error: unknown): unknown => {
|
|
80
51
|
if (
|
|
81
52
|
error instanceof StructuredOutputError &&
|
|
@@ -109,11 +80,17 @@ export const runAiStructured = async <TOutput extends z.ZodType>(
|
|
|
109
80
|
const startedAt = Date.now();
|
|
110
81
|
let resolved: AiResolvedModel | undefined;
|
|
111
82
|
try {
|
|
112
|
-
resolved = await (input.resolveModel ?? resolveAiBackgroundModel)(input.
|
|
83
|
+
resolved = await (input.resolveModel ?? resolveAiBackgroundModel)(input.model ?? { class: "balanced" });
|
|
113
84
|
await trace.record({
|
|
114
85
|
context: span,
|
|
115
86
|
event: "model.resolved",
|
|
116
|
-
attributes: {
|
|
87
|
+
attributes: {
|
|
88
|
+
model: resolved.profile.id,
|
|
89
|
+
providerModel: resolved.profile.model,
|
|
90
|
+
provider: resolved.profile.provider,
|
|
91
|
+
requestedClass: resolved.model.requestedClass,
|
|
92
|
+
resolvedClass: resolved.model.class,
|
|
93
|
+
},
|
|
117
94
|
});
|
|
118
95
|
|
|
119
96
|
const result = await nessi.structured({
|
|
@@ -122,6 +99,7 @@ export const runAiStructured = async <TOutput extends z.ZodType>(
|
|
|
122
99
|
kind: input.usageSubject ? "chat" : "background",
|
|
123
100
|
subject: input.usageSubject,
|
|
124
101
|
task: input.task,
|
|
102
|
+
modelRef: resolved.model,
|
|
125
103
|
appId: input.appId,
|
|
126
104
|
traceId: span.traceId,
|
|
127
105
|
...input.attribution,
|
|
@@ -155,7 +133,7 @@ export const runAiStructured = async <TOutput extends z.ZodType>(
|
|
|
155
133
|
attribution: input.attribution,
|
|
156
134
|
traceId: span.traceId,
|
|
157
135
|
appId: input.appId,
|
|
158
|
-
|
|
136
|
+
modelId: resolved.profile.id,
|
|
159
137
|
providerModel: resolved.profile.model,
|
|
160
138
|
status: "ok",
|
|
161
139
|
durationMs,
|
|
@@ -167,7 +145,7 @@ export const runAiStructured = async <TOutput extends z.ZodType>(
|
|
|
167
145
|
|
|
168
146
|
return {
|
|
169
147
|
output: result.output,
|
|
170
|
-
|
|
148
|
+
model: resolved.model,
|
|
171
149
|
usage: result.usage,
|
|
172
150
|
structuredMeta: result.structuredMeta,
|
|
173
151
|
};
|
|
@@ -182,20 +160,25 @@ export const runAiStructured = async <TOutput extends z.ZodType>(
|
|
|
182
160
|
attribution: input.attribution,
|
|
183
161
|
traceId: span.traceId,
|
|
184
162
|
appId: input.appId,
|
|
185
|
-
|
|
163
|
+
modelId: resolved?.profile.id,
|
|
186
164
|
providerModel: resolved?.profile.model,
|
|
187
165
|
status: "failed",
|
|
188
166
|
durationMs: Date.now() - startedAt,
|
|
189
167
|
usage: details?.aggregate?.usage,
|
|
190
168
|
attempts: details?.attempts,
|
|
191
|
-
errorCode:
|
|
169
|
+
errorCode:
|
|
170
|
+
failure instanceof StructuredOutputError || failure instanceof AiProviderError
|
|
171
|
+
? failure.code
|
|
172
|
+
: failure instanceof Error
|
|
173
|
+
? failure.name
|
|
174
|
+
: "unknown",
|
|
192
175
|
error: failure instanceof Error ? failure.message : String(failure),
|
|
193
176
|
});
|
|
194
177
|
throw failure;
|
|
195
178
|
}
|
|
196
179
|
},
|
|
197
180
|
{
|
|
198
|
-
summarize: (result) => ({ model: result.
|
|
181
|
+
summarize: (result) => ({ model: result.model.id, mode: result.structuredMeta.mode, repaired: result.structuredMeta.repaired }),
|
|
199
182
|
onError: (error) => (error instanceof StructuredOutputError ? structuredFailureSummary(error) : undefined),
|
|
200
183
|
},
|
|
201
184
|
);
|
package/src/ai/task-contracts.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { AiModelRequestSchema } from "../shared/ai-models";
|
|
2
3
|
|
|
3
4
|
const prompt = z.string().trim().min(1).max(20_000);
|
|
4
|
-
const
|
|
5
|
+
const model = AiModelRequestSchema.optional();
|
|
5
6
|
const choice = z.string().trim().min(1).max(200);
|
|
6
7
|
const choices = z
|
|
7
8
|
.array(choice)
|
|
@@ -20,6 +21,7 @@ const structuredField = z
|
|
|
20
21
|
required: z.boolean().default(true),
|
|
21
22
|
choices: z.array(choice).min(1).max(50).optional(),
|
|
22
23
|
maxLength: z.number().int().min(1).max(20_000).optional(),
|
|
24
|
+
minLength: z.number().int().min(1).max(20_000).optional(),
|
|
23
25
|
})
|
|
24
26
|
.strict()
|
|
25
27
|
.superRefine((field, ctx) => {
|
|
@@ -27,6 +29,8 @@ const structuredField = z
|
|
|
27
29
|
ctx.addIssue({ code: "custom", path: ["choices"], message: "Enum fields require choices." });
|
|
28
30
|
if (field.type !== "enum" && field.choices)
|
|
29
31
|
ctx.addIssue({ code: "custom", path: ["choices"], message: "Choices are only valid for enum fields." });
|
|
32
|
+
if (field.minLength !== undefined && (field.type !== "text" || field.minLength > (field.maxLength ?? 20_000)))
|
|
33
|
+
ctx.addIssue({ code: "custom", path: ["minLength"], message: "minLength requires a text field and cannot exceed maxLength." });
|
|
30
34
|
if (field.type !== "text" && field.maxLength)
|
|
31
35
|
ctx.addIssue({ code: "custom", path: ["maxLength"], message: "maxLength is only valid for text fields." });
|
|
32
36
|
});
|
|
@@ -42,7 +46,7 @@ export const AiTaskRequestSchema = z.discriminatedUnion("kind", [
|
|
|
42
46
|
kind: z.literal("generate_text"),
|
|
43
47
|
prompt,
|
|
44
48
|
input: z.unknown().optional(),
|
|
45
|
-
|
|
49
|
+
model,
|
|
46
50
|
maxOutputChars: z.number().int().min(1).max(20_000).default(4_000),
|
|
47
51
|
}),
|
|
48
52
|
z.object({
|
|
@@ -50,7 +54,7 @@ export const AiTaskRequestSchema = z.discriminatedUnion("kind", [
|
|
|
50
54
|
prompt,
|
|
51
55
|
input: z.unknown(),
|
|
52
56
|
choices,
|
|
53
|
-
|
|
57
|
+
model,
|
|
54
58
|
}),
|
|
55
59
|
z
|
|
56
60
|
.object({
|
|
@@ -60,7 +64,7 @@ export const AiTaskRequestSchema = z.discriminatedUnion("kind", [
|
|
|
60
64
|
choices,
|
|
61
65
|
minChoices: z.number().int().min(0).max(50).default(0),
|
|
62
66
|
maxChoices: z.number().int().min(0).max(50).optional(),
|
|
63
|
-
|
|
67
|
+
model,
|
|
64
68
|
})
|
|
65
69
|
.superRefine((value, ctx) => {
|
|
66
70
|
const maximum = value.maxChoices ?? value.choices.length;
|
|
@@ -76,9 +80,25 @@ export const AiTaskRequestSchema = z.discriminatedUnion("kind", [
|
|
|
76
80
|
prompt,
|
|
77
81
|
input: z.unknown(),
|
|
78
82
|
fields: structuredFields,
|
|
79
|
-
|
|
83
|
+
model,
|
|
80
84
|
}),
|
|
81
85
|
]);
|
|
82
86
|
|
|
83
87
|
export type AiTaskRequest = z.infer<typeof AiTaskRequestSchema>;
|
|
84
88
|
export type AiTaskRequestInput = z.input<typeof AiTaskRequestSchema>;
|
|
89
|
+
|
|
90
|
+
/** Existing durable task storage contract; measured in UTF-8 bytes, not characters. */
|
|
91
|
+
export const AI_TASK_REQUEST_MAX_BYTES = 100_000;
|
|
92
|
+
export class AiTaskInputTooLargeError extends Error {
|
|
93
|
+
readonly code = "WORKFLOW_AI_INPUT_TOO_LARGE";
|
|
94
|
+
readonly retryable = false;
|
|
95
|
+
constructor() {
|
|
96
|
+
super(
|
|
97
|
+
"AI input exceeds the 100,000-byte task budget. Narrow the supplied content or inspect the original source manually; nothing was truncated.",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const assertAiTaskRequestSize = (request: unknown): void => {
|
|
103
|
+
if (Buffer.byteLength(JSON.stringify(request), "utf8") > AI_TASK_REQUEST_MAX_BYTES) throw new AiTaskInputTooLargeError();
|
|
104
|
+
};
|
package/src/ai/task-execution.ts
CHANGED
|
@@ -11,7 +11,13 @@ const structuredOutputSchema = (fields: Extract<AiTaskRequest, { kind: "extract_
|
|
|
11
11
|
for (const field of fields) {
|
|
12
12
|
let schema: z.ZodType =
|
|
13
13
|
field.type === "text"
|
|
14
|
-
?
|
|
14
|
+
? field.minLength === undefined
|
|
15
|
+
? z.string().max(field.maxLength ?? 20_000)
|
|
16
|
+
: z
|
|
17
|
+
.string()
|
|
18
|
+
.trim()
|
|
19
|
+
.min(field.minLength)
|
|
20
|
+
.max(field.maxLength ?? 20_000)
|
|
15
21
|
: field.type === "number"
|
|
16
22
|
? z.number()
|
|
17
23
|
: field.type === "boolean"
|
|
@@ -28,13 +34,16 @@ const structuredOutputSchema = (fields: Extract<AiTaskRequest, { kind: "extract_
|
|
|
28
34
|
/** Schema-validated AI calculations shared by workflows and interactive code. */
|
|
29
35
|
export const executeAiTask = async (
|
|
30
36
|
request: AiTaskRequest,
|
|
31
|
-
options: Pick<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
> & { taskPrefix: string },
|
|
37
|
+
options: Pick<RunAiStructuredInput<z.ZodType>, "appId" | "attribution" | "signal" | "model" | "resolveModel" | "usageSubject"> & {
|
|
38
|
+
taskPrefix: string;
|
|
39
|
+
},
|
|
35
40
|
runStructured: typeof runAiStructured = runAiStructured,
|
|
36
41
|
) => {
|
|
37
|
-
const { taskPrefix, ...
|
|
42
|
+
const { taskPrefix, ...provided } = options;
|
|
43
|
+
const common = {
|
|
44
|
+
...provided,
|
|
45
|
+
model: provided.model ?? request.model ?? ({ class: "balanced" } satisfies import("../shared/ai-models").AiModelRequest),
|
|
46
|
+
};
|
|
38
47
|
if (request.kind === "generate_text") {
|
|
39
48
|
const result = await runStructured({
|
|
40
49
|
...common,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AccessSubject } from "../server/services/access";
|
|
2
|
+
import { selectAssistantAiModel } from "./assistant-models";
|
|
3
|
+
import { personalAiModelPolicy } from "./personal-agent";
|
|
4
|
+
import { resolveAiModel } from "./settings";
|
|
5
|
+
import { resolveAiWorkflowModel } from "./structured";
|
|
6
|
+
|
|
7
|
+
/** Interactive application tasks retain Assistant grants and data-boundary policy. */
|
|
8
|
+
export const resolveApplicationAiTaskModel = async (subject?: AccessSubject, model?: import("../shared/ai-models").AiModelRequest) =>
|
|
9
|
+
subject ? resolveAiModel(personalAiModelPolicy, await selectAssistantAiModel(subject, model)) : resolveAiWorkflowModel(model);
|