@open-mercato/ai-assistant 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +30 -4
  3. package/dist/frontend/components/AiChatButton.js +3 -2
  4. package/dist/frontend/components/AiChatButton.js.map +2 -2
  5. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js +364 -0
  6. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js.map +7 -0
  7. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js +7 -7
  8. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js.map +2 -2
  9. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js +182 -0
  10. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js.map +7 -0
  11. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js +316 -0
  12. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js.map +7 -0
  13. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js +8 -7
  14. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js.map +2 -2
  15. package/dist/modules/ai_assistant/api/ai/chat/route.js +43 -20
  16. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
  17. package/dist/modules/ai_assistant/api/settings/route.js +4 -3
  18. package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
  19. package/dist/modules/ai_assistant/api/usage/daily/route.js +111 -0
  20. package/dist/modules/ai_assistant/api/usage/daily/route.js.map +7 -0
  21. package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js +108 -0
  22. package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js.map +7 -0
  23. package/dist/modules/ai_assistant/api/usage/sessions/route.js +153 -0
  24. package/dist/modules/ai_assistant/api/usage/sessions/route.js.map +7 -0
  25. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +335 -38
  26. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
  27. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +2 -7
  28. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +2 -2
  29. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +44 -35
  30. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
  31. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js +282 -0
  32. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js.map +7 -0
  33. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js +10 -0
  34. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js.map +7 -0
  35. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js +25 -0
  36. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js.map +7 -0
  37. package/dist/modules/ai_assistant/cli.js +12 -0
  38. package/dist/modules/ai_assistant/cli.js.map +2 -2
  39. package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js.map +1 -1
  40. package/dist/modules/ai_assistant/data/entities.js +177 -1
  41. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  42. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +104 -2
  43. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +2 -2
  44. package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js +168 -0
  45. package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js.map +7 -0
  46. package/dist/modules/ai_assistant/events.js +8 -0
  47. package/dist/modules/ai_assistant/events.js.map +2 -2
  48. package/dist/modules/ai_assistant/i18n/de.json +74 -1
  49. package/dist/modules/ai_assistant/i18n/en.json +74 -1
  50. package/dist/modules/ai_assistant/i18n/es.json +75 -2
  51. package/dist/modules/ai_assistant/i18n/pl.json +74 -1
  52. package/dist/modules/ai_assistant/lib/agent-policy.js.map +2 -2
  53. package/dist/modules/ai_assistant/lib/agent-runtime.js +588 -23
  54. package/dist/modules/ai_assistant/lib/agent-runtime.js.map +3 -3
  55. package/dist/modules/ai_assistant/lib/agent-tools.js +6 -1
  56. package/dist/modules/ai_assistant/lib/agent-tools.js.map +2 -2
  57. package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
  58. package/dist/modules/ai_assistant/lib/model-factory.js +63 -22
  59. package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
  60. package/dist/modules/ai_assistant/lib/token-usage-recorder.js +78 -0
  61. package/dist/modules/ai_assistant/lib/token-usage-recorder.js.map +7 -0
  62. package/dist/modules/ai_assistant/lib/usage-serialization.js +33 -0
  63. package/dist/modules/ai_assistant/lib/usage-serialization.js.map +7 -0
  64. package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js +25 -0
  65. package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js.map +7 -0
  66. package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js +88 -0
  67. package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js.map +7 -0
  68. package/dist/modules/ai_assistant/setup.js +34 -0
  69. package/dist/modules/ai_assistant/setup.js.map +2 -2
  70. package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js +114 -0
  71. package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js.map +7 -0
  72. package/generated/entities/ai_agent_runtime_override/index.ts +7 -0
  73. package/generated/entities/ai_token_usage_daily/index.ts +16 -0
  74. package/generated/entities/ai_token_usage_event/index.ts +19 -0
  75. package/generated/entities.ids.generated.ts +2 -0
  76. package/generated/entity-fields-registry.ts +47 -1
  77. package/package.json +15 -7
  78. package/src/frontend/components/AiChatButton.tsx +3 -2
  79. package/src/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.ts +521 -0
  80. package/src/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.ts +8 -8
  81. package/src/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.ts +231 -0
  82. package/src/modules/ai_assistant/__tests__/events.test.ts +4 -3
  83. package/src/modules/ai_assistant/__tests__/settings-page-logic.test.ts +5 -5
  84. package/src/modules/ai_assistant/__tests__/token-usage-recorder.test.ts +109 -0
  85. package/src/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.ts +388 -0
  86. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/__tests__/route.test.ts +5 -0
  87. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/route.ts +8 -7
  88. package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +102 -5
  89. package/src/modules/ai_assistant/api/ai/chat/route.ts +55 -18
  90. package/src/modules/ai_assistant/api/settings/route.ts +5 -3
  91. package/src/modules/ai_assistant/api/usage/daily/__tests__/route.test.ts +159 -0
  92. package/src/modules/ai_assistant/api/usage/daily/route.ts +126 -0
  93. package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/__tests__/route.test.ts +143 -0
  94. package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/route.ts +130 -0
  95. package/src/modules/ai_assistant/api/usage/sessions/__tests__/route.test.ts +123 -0
  96. package/src/modules/ai_assistant/api/usage/sessions/route.ts +184 -0
  97. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +372 -16
  98. package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +1 -4
  99. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +26 -9
  100. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.tsx +469 -0
  101. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.ts +23 -0
  102. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.tsx +12 -0
  103. package/src/modules/ai_assistant/cli.ts +18 -0
  104. package/src/modules/ai_assistant/components/AiAssistantSettingsPageClient.tsx +1 -1
  105. package/src/modules/ai_assistant/data/entities.ts +237 -0
  106. package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +135 -3
  107. package/src/modules/ai_assistant/data/repositories/AiTokenUsageRepository.ts +213 -0
  108. package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentRuntimeOverrideRepository.test.ts +223 -0
  109. package/src/modules/ai_assistant/data/repositories/__tests__/AiTokenUsageRepository.test.ts +58 -0
  110. package/src/modules/ai_assistant/events.ts +8 -0
  111. package/src/modules/ai_assistant/i18n/de.json +74 -1
  112. package/src/modules/ai_assistant/i18n/en.json +74 -1
  113. package/src/modules/ai_assistant/i18n/es.json +75 -2
  114. package/src/modules/ai_assistant/i18n/pl.json +74 -1
  115. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase0.test.ts +439 -0
  116. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase1.test.ts +243 -0
  117. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase2.test.ts +388 -0
  118. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase3.test.ts +359 -0
  119. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-phase4a.test.ts +2 -2
  120. package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +2 -1
  121. package/src/modules/ai_assistant/lib/__tests__/max-steps-budget.integration.test.ts +12 -13
  122. package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +77 -14
  123. package/src/modules/ai_assistant/lib/agent-policy.ts +9 -0
  124. package/src/modules/ai_assistant/lib/agent-runtime.ts +1148 -43
  125. package/src/modules/ai_assistant/lib/agent-tools.ts +5 -1
  126. package/src/modules/ai_assistant/lib/ai-agent-definition.ts +289 -2
  127. package/src/modules/ai_assistant/lib/model-factory.ts +128 -43
  128. package/src/modules/ai_assistant/lib/token-usage-recorder.ts +122 -0
  129. package/src/modules/ai_assistant/lib/usage-serialization.ts +29 -0
  130. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +791 -0
  131. package/src/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.ts +25 -0
  132. package/src/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.ts +89 -0
  133. package/src/modules/ai_assistant/setup.ts +49 -0
  134. package/src/modules/ai_assistant/workers/__tests__/ai-token-usage-prune.test.ts +144 -0
  135. package/src/modules/ai_assistant/workers/ai-token-usage-prune.ts +188 -0
@@ -0,0 +1,122 @@
1
+ import type { AwilixContainer } from 'awilix'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
3
+ import type { AiChatRequestContext } from './attachment-bridge-types'
4
+ import { AiTokenUsageRepository } from '../data/repositories/AiTokenUsageRepository'
5
+ import { emitAiAssistantEvent } from '../events'
6
+
7
+ export interface RecordTokenUsageInput {
8
+ authContext: AiChatRequestContext
9
+ agentId: string
10
+ moduleId: string
11
+ sessionId: string
12
+ turnId: string
13
+ stepIndex: number
14
+ providerId: string
15
+ modelId: string
16
+ usage: {
17
+ inputTokens?: number
18
+ outputTokens?: number
19
+ cachedInputTokens?: number
20
+ reasoningTokens?: number
21
+ }
22
+ finishReason?: string
23
+ loopAbortReason?: string
24
+ }
25
+
26
+ /**
27
+ * Thin fire-and-forget collector that persists one row in `ai_token_usage_events`
28
+ * and upserts the matching `ai_token_usage_daily` row inside a single transaction.
29
+ *
30
+ * CRITICAL SAFETY CONTRACT (R12):
31
+ * - This function MUST NEVER throw — any failure is caught, logged at `warn`,
32
+ * and silently swallowed so the agent turn is never interrupted.
33
+ * - Callers MUST invoke as `void recordTokenUsage(...)` without awaiting.
34
+ *
35
+ * After writing the row the function emits `ai.token_usage.recorded` so
36
+ * downstream subscribers (cost dashboards, metering) can react without polling.
37
+ *
38
+ * Phase 6.3 of spec `2026-04-28-ai-agents-agentic-loop-controls`.
39
+ */
40
+ export async function recordTokenUsage(
41
+ input: RecordTokenUsageInput,
42
+ container: AwilixContainer,
43
+ ): Promise<void> {
44
+ const tenantId = input.authContext.tenantId
45
+ if (!tenantId) return
46
+
47
+ try {
48
+ const em = container.resolve<EntityManager>('em')
49
+ const repo = new AiTokenUsageRepository(em.fork())
50
+
51
+ const inputTokens = input.usage.inputTokens ?? 0
52
+ const outputTokens = input.usage.outputTokens ?? 0
53
+ const cachedInputTokens = input.usage.cachedInputTokens ?? 0
54
+ const reasoningTokens = input.usage.reasoningTokens ?? 0
55
+
56
+ const now = new Date()
57
+ const day = now.toISOString().slice(0, 10)
58
+
59
+ await repo.createEvent({
60
+ tenantId,
61
+ organizationId: input.authContext.organizationId ?? null,
62
+ userId: input.authContext.userId,
63
+ agentId: input.agentId,
64
+ moduleId: input.moduleId,
65
+ sessionId: input.sessionId,
66
+ turnId: input.turnId,
67
+ stepIndex: input.stepIndex,
68
+ providerId: input.providerId,
69
+ modelId: input.modelId,
70
+ inputTokens,
71
+ outputTokens,
72
+ cachedInputTokens: input.usage.cachedInputTokens ?? null,
73
+ reasoningTokens: input.usage.reasoningTokens ?? null,
74
+ finishReason: input.finishReason ?? null,
75
+ loopAbortReason: input.loopAbortReason ?? null,
76
+ })
77
+
78
+ await repo.upsertDaily({
79
+ tenantId,
80
+ organizationId: input.authContext.organizationId ?? null,
81
+ day,
82
+ agentId: input.agentId,
83
+ modelId: input.modelId,
84
+ providerId: input.providerId,
85
+ sessionId: input.sessionId,
86
+ inputTokens,
87
+ outputTokens,
88
+ cachedInputTokens,
89
+ reasoningTokens,
90
+ })
91
+ } catch (error) {
92
+ console.warn(
93
+ '[AI token-usage] recordTokenUsage failed (turn continues unaffected):',
94
+ error instanceof Error ? error.message : error,
95
+ )
96
+ return
97
+ }
98
+
99
+ // Emit event AFTER successful write — detached from the try/catch so a
100
+ // failed event emission does not retroactively fail the DB write.
101
+ try {
102
+ await emitAiAssistantEvent(
103
+ 'ai.token_usage.recorded',
104
+ {
105
+ tenantId,
106
+ agentId: input.agentId,
107
+ sessionId: input.sessionId,
108
+ turnId: input.turnId,
109
+ stepIndex: input.stepIndex,
110
+ modelId: input.modelId,
111
+ inputTokens: input.usage.inputTokens ?? 0,
112
+ outputTokens: input.usage.outputTokens ?? 0,
113
+ },
114
+ { persistent: false },
115
+ )
116
+ } catch (emitError) {
117
+ console.warn(
118
+ '[AI token-usage] Event emit failed (non-fatal):',
119
+ emitError instanceof Error ? emitError.message : emitError,
120
+ )
121
+ }
122
+ }
@@ -0,0 +1,29 @@
1
+ export function toInteger(value: unknown): number {
2
+ if (typeof value === 'number') return Number.isFinite(value) ? value : 0
3
+ if (typeof value === 'bigint') return Number(value)
4
+ if (typeof value === 'string') {
5
+ const parsed = parseInt(value, 10)
6
+ return Number.isFinite(parsed) ? parsed : 0
7
+ }
8
+ return 0
9
+ }
10
+
11
+ export function toIntegerString(value: unknown): string {
12
+ if (typeof value === 'bigint') return value.toString()
13
+ if (typeof value === 'number') return String(value)
14
+ if (typeof value === 'string') return value
15
+ return '0'
16
+ }
17
+
18
+ export function toIsoString(value: unknown): string {
19
+ if (value instanceof Date) return value.toISOString()
20
+ if (typeof value === 'string' || typeof value === 'number') {
21
+ return new Date(value).toISOString()
22
+ }
23
+ throw new TypeError('Expected a Date-compatible value')
24
+ }
25
+
26
+ export function toDateString(value: unknown): string {
27
+ if (value instanceof Date) return value.toISOString().slice(0, 10)
28
+ return String(value)
29
+ }