@open-mercato/core 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3043.1a796c3920
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +13 -1
- package/dist/helpers/integration/api.js +29 -16
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/auth.js +11 -6
- package/dist/helpers/integration/auth.js.map +3 -3
- package/dist/modules/auth/commands/roles.js +9 -12
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/catalog/ai-agents-context.js +147 -0
- package/dist/modules/catalog/ai-agents-context.js.map +7 -0
- package/dist/modules/catalog/ai-agents.js +383 -0
- package/dist/modules/catalog/ai-agents.js.map +7 -0
- package/dist/modules/catalog/ai-tools/_shared.js +318 -0
- package/dist/modules/catalog/ai-tools/_shared.js.map +7 -0
- package/dist/modules/catalog/ai-tools/authoring-pack.js +391 -0
- package/dist/modules/catalog/ai-tools/authoring-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/categories-pack.js +167 -0
- package/dist/modules/catalog/ai-tools/categories-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/configuration-pack.js +120 -0
- package/dist/modules/catalog/ai-tools/configuration-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/media-tags-pack.js +107 -0
- package/dist/modules/catalog/ai-tools/media-tags-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +429 -0
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/mutation-pack.js +576 -0
- package/dist/modules/catalog/ai-tools/mutation-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js +208 -0
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/products-pack.js +298 -0
- package/dist/modules/catalog/ai-tools/products-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/stats-pack.js +57 -0
- package/dist/modules/catalog/ai-tools/stats-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/types.js +10 -0
- package/dist/modules/catalog/ai-tools/types.js.map +7 -0
- package/dist/modules/catalog/ai-tools/variants-pack.js +75 -0
- package/dist/modules/catalog/ai-tools/variants-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools.js +28 -0
- package/dist/modules/catalog/ai-tools.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +466 -0
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/products/page.js +7 -1
- package/dist/modules/catalog/backend/catalog/products/page.js.map +2 -2
- package/dist/modules/catalog/components/CatalogStatsCard.js +91 -0
- package/dist/modules/catalog/components/CatalogStatsCard.js.map +7 -0
- package/dist/modules/catalog/components/products/ProductsDataTable.js +23 -3
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/events.js +7 -4
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +59 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js +17 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +1 -1
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
- package/dist/modules/catalog/widgets/injection-table.js +13 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js +94 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js +17 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/customer_accounts/widgets/injection-table.js +9 -0
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/ai-agents-context.js +96 -0
- package/dist/modules/customers/ai-agents-context.js.map +7 -0
- package/dist/modules/customers/ai-agents.js +244 -0
- package/dist/modules/customers/ai-agents.js.map +7 -0
- package/dist/modules/customers/ai-tools/activities-tasks-pack.js +1015 -0
- package/dist/modules/customers/ai-tools/activities-tasks-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/addresses-tags-pack.js +134 -0
- package/dist/modules/customers/ai-tools/addresses-tags-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/companies-pack.js +249 -0
- package/dist/modules/customers/ai-tools/companies-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/deals-pack.js +348 -0
- package/dist/modules/customers/ai-tools/deals-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/people-pack.js +261 -0
- package/dist/modules/customers/ai-tools/people-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/settings-pack.js +102 -0
- package/dist/modules/customers/ai-tools/settings-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/types.js +10 -0
- package/dist/modules/customers/ai-tools/types.js.map +7 -0
- package/dist/modules/customers/ai-tools.js +20 -0
- package/dist/modules/customers/ai-tools.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +469 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +17 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js +117 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js +17 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js.map +7 -0
- package/dist/modules/customers/widgets/injection-table.js +26 -0
- package/dist/modules/customers/widgets/injection-table.js.map +7 -0
- package/dist/modules/inbox_ops/ai-tools.js +4 -0
- package/dist/modules/inbox_ops/ai-tools.js.map +2 -2
- package/dist/modules/inbox_ops/lib/llmProvider.js +52 -7
- package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
- package/dist/modules/notifications/setup.js +13 -0
- package/dist/modules/notifications/setup.js.map +7 -0
- package/jest.config.cjs +1 -0
- package/jest.setup.ts +18 -0
- package/package.json +5 -3
- package/src/helpers/integration/api.ts +38 -16
- package/src/helpers/integration/auth.ts +13 -6
- package/src/modules/auth/commands/roles.ts +10 -12
- package/src/modules/catalog/AGENTS.md +11 -0
- package/src/modules/catalog/ai-agents-context.ts +239 -0
- package/src/modules/catalog/ai-agents.ts +525 -0
- package/src/modules/catalog/ai-tools/_shared.ts +487 -0
- package/src/modules/catalog/ai-tools/authoring-pack.ts +600 -0
- package/src/modules/catalog/ai-tools/categories-pack.ts +192 -0
- package/src/modules/catalog/ai-tools/configuration-pack.ts +218 -0
- package/src/modules/catalog/ai-tools/media-tags-pack.ts +127 -0
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +608 -0
- package/src/modules/catalog/ai-tools/mutation-pack.ts +761 -0
- package/src/modules/catalog/ai-tools/prices-offers-pack.ts +376 -0
- package/src/modules/catalog/ai-tools/products-pack.ts +387 -0
- package/src/modules/catalog/ai-tools/stats-pack.ts +84 -0
- package/src/modules/catalog/ai-tools/types.ts +81 -0
- package/src/modules/catalog/ai-tools/variants-pack.ts +147 -0
- package/src/modules/catalog/ai-tools.ts +78 -0
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +597 -0
- package/src/modules/catalog/backend/catalog/products/page.tsx +23 -2
- package/src/modules/catalog/components/CatalogStatsCard.tsx +118 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +54 -6
- package/src/modules/catalog/events.ts +7 -4
- package/src/modules/catalog/i18n/de.json +17 -0
- package/src/modules/catalog/i18n/en.json +17 -0
- package/src/modules/catalog/i18n/es.json +17 -0
- package/src/modules/catalog/i18n/pl.json +17 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +109 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.ts +29 -0
- package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +1 -1
- package/src/modules/catalog/widgets/injection-table.ts +12 -0
- package/src/modules/customer_accounts/i18n/de.json +5 -0
- package/src/modules/customer_accounts/i18n/en.json +5 -0
- package/src/modules/customer_accounts/i18n/es.json +5 -0
- package/src/modules/customer_accounts/i18n/pl.json +5 -0
- package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.tsx +136 -0
- package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.ts +43 -0
- package/src/modules/customer_accounts/widgets/injection-table.ts +9 -0
- package/src/modules/customers/AGENTS.md +13 -0
- package/src/modules/customers/ai-agents-context.ts +150 -0
- package/src/modules/customers/ai-agents.ts +355 -0
- package/src/modules/customers/ai-tools/activities-tasks-pack.ts +1248 -0
- package/src/modules/customers/ai-tools/addresses-tags-pack.ts +145 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +362 -0
- package/src/modules/customers/ai-tools/deals-pack.ts +505 -0
- package/src/modules/customers/ai-tools/people-pack.ts +369 -0
- package/src/modules/customers/ai-tools/settings-pack.ts +121 -0
- package/src/modules/customers/ai-tools/types.ts +76 -0
- package/src/modules/customers/ai-tools.ts +34 -0
- package/src/modules/customers/i18n/de.json +25 -0
- package/src/modules/customers/i18n/en.json +25 -0
- package/src/modules/customers/i18n/es.json +25 -0
- package/src/modules/customers/i18n/pl.json +25 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +580 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +36 -0
- package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.tsx +191 -0
- package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.ts +37 -0
- package/src/modules/customers/widgets/injection-table.ts +41 -0
- package/src/modules/inbox_ops/ai-tools.ts +4 -0
- package/src/modules/inbox_ops/lib/llmProvider.ts +83 -7
- package/src/modules/notifications/setup.ts +11 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step 5.15 — Backend AiChat injection widget for the deal detail page.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the Step 4.10 People-list trigger, but targets a record-level
|
|
7
|
+
* surface: the deal detail page's header injection spot
|
|
8
|
+
* (`detail:customers.deal:header`). The page is NOT modified beyond
|
|
9
|
+
* adding the shared `<InjectionSpot>` mount point.
|
|
10
|
+
*
|
|
11
|
+
* `pageContext` shape (spec §10.1):
|
|
12
|
+
*
|
|
13
|
+
* { view: 'customers.deal.detail',
|
|
14
|
+
* recordType: 'deal',
|
|
15
|
+
* recordId: <dealId>,
|
|
16
|
+
* extra: { stage: string | null, pipelineStageId: string | null } }
|
|
17
|
+
*
|
|
18
|
+
* The agent is `customers.account_assistant`, feature-gated at the widget
|
|
19
|
+
* metadata layer behind `customers.deals.view` + `ai_assistant.view`.
|
|
20
|
+
* The read-only agent serves information requests today; when the tenant
|
|
21
|
+
* opts into Step 5.4's mutation-policy override, the agent unlocks the
|
|
22
|
+
* Step 5.13 `customers.update_deal_stage` tool behind the pending-action
|
|
23
|
+
* contract.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import * as React from 'react'
|
|
27
|
+
import { Sparkles } from 'lucide-react'
|
|
28
|
+
import { AiChat } from '@open-mercato/ui/ai/AiChat'
|
|
29
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
30
|
+
import {
|
|
31
|
+
Dialog,
|
|
32
|
+
DialogContent,
|
|
33
|
+
DialogDescription,
|
|
34
|
+
DialogHeader,
|
|
35
|
+
DialogTitle,
|
|
36
|
+
} from '@open-mercato/ui/primitives/dialog'
|
|
37
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
38
|
+
import { cn } from '@open-mercato/shared/lib/utils'
|
|
39
|
+
|
|
40
|
+
export const CUSTOMERS_AI_DEAL_DETAIL_AGENT_ID = 'customers.account_assistant'
|
|
41
|
+
|
|
42
|
+
export interface CustomersAiDealDetailPageContext {
|
|
43
|
+
view: 'customers.deal.detail'
|
|
44
|
+
recordType: 'deal'
|
|
45
|
+
recordId: string
|
|
46
|
+
extra: {
|
|
47
|
+
stage: string | null
|
|
48
|
+
pipelineStageId: string | null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface HostInjectionContext {
|
|
53
|
+
dealId?: string
|
|
54
|
+
recordId?: string
|
|
55
|
+
stage?: string | null
|
|
56
|
+
pipelineStageId?: string | null
|
|
57
|
+
data?: {
|
|
58
|
+
deal?: {
|
|
59
|
+
id?: string
|
|
60
|
+
status?: string | null
|
|
61
|
+
pipelineStage?: string | null
|
|
62
|
+
pipelineStageId?: string | null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface AiDealDetailTriggerProps {
|
|
68
|
+
context?: HostInjectionContext
|
|
69
|
+
data?: HostInjectionContext['data']
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readString(value: unknown): string | null {
|
|
73
|
+
return typeof value === 'string' && value.length > 0 ? value : null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildPageContext(
|
|
77
|
+
context: HostInjectionContext | undefined,
|
|
78
|
+
data: HostInjectionContext['data'] | undefined,
|
|
79
|
+
): CustomersAiDealDetailPageContext | null {
|
|
80
|
+
const dealRecord = data?.deal ?? context?.data?.deal
|
|
81
|
+
const dealId =
|
|
82
|
+
readString(context?.dealId) ??
|
|
83
|
+
readString(context?.recordId) ??
|
|
84
|
+
readString(dealRecord?.id) ??
|
|
85
|
+
null
|
|
86
|
+
if (!dealId) return null
|
|
87
|
+
const stage =
|
|
88
|
+
readString(context?.stage) ??
|
|
89
|
+
readString(dealRecord?.status) ??
|
|
90
|
+
readString(dealRecord?.pipelineStage) ??
|
|
91
|
+
null
|
|
92
|
+
const pipelineStageId =
|
|
93
|
+
readString(context?.pipelineStageId) ??
|
|
94
|
+
readString(dealRecord?.pipelineStageId) ??
|
|
95
|
+
null
|
|
96
|
+
return {
|
|
97
|
+
view: 'customers.deal.detail',
|
|
98
|
+
recordType: 'deal',
|
|
99
|
+
recordId: dealId,
|
|
100
|
+
extra: { stage, pipelineStageId },
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Exposed for unit tests so the page-context derivation is exercisable
|
|
106
|
+
* without mounting the widget.
|
|
107
|
+
*/
|
|
108
|
+
export function computeCustomersAiDealDetailPageContext(
|
|
109
|
+
context: HostInjectionContext | undefined,
|
|
110
|
+
data?: HostInjectionContext['data'],
|
|
111
|
+
): CustomersAiDealDetailPageContext | null {
|
|
112
|
+
return buildPageContext(context, data)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default function AiDealDetailTriggerWidget({ context, data }: AiDealDetailTriggerProps) {
|
|
116
|
+
const t = useT()
|
|
117
|
+
const [open, setOpen] = React.useState(false)
|
|
118
|
+
const pageContext = React.useMemo(() => buildPageContext(context, data), [context, data])
|
|
119
|
+
const handleClick = React.useCallback(() => {
|
|
120
|
+
setOpen(true)
|
|
121
|
+
}, [])
|
|
122
|
+
|
|
123
|
+
if (!pageContext) return null
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
<Button
|
|
128
|
+
type="button"
|
|
129
|
+
variant="outline"
|
|
130
|
+
size="sm"
|
|
131
|
+
onClick={handleClick}
|
|
132
|
+
data-ai-customers-deal-trigger=""
|
|
133
|
+
data-ai-customers-deal-id={pageContext.recordId}
|
|
134
|
+
aria-label={t(
|
|
135
|
+
'customers.ai_assistant.dealDetail.trigger.ariaLabel',
|
|
136
|
+
'Open AI assistant for this deal',
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
<Sparkles className="size-4" aria-hidden />
|
|
140
|
+
<span>{t('customers.ai_assistant.dealDetail.trigger.label', 'Ask AI')}</span>
|
|
141
|
+
</Button>
|
|
142
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
143
|
+
<DialogContent
|
|
144
|
+
className={cn(
|
|
145
|
+
'sm:max-w-xl sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',
|
|
146
|
+
'sm:h-screen sm:max-h-screen sm:rounded-none sm:rounded-l-2xl',
|
|
147
|
+
'flex flex-col gap-3 p-4 z-[70]',
|
|
148
|
+
)}
|
|
149
|
+
data-ai-customers-deal-sheet=""
|
|
150
|
+
>
|
|
151
|
+
<DialogHeader>
|
|
152
|
+
<div className="flex items-center justify-between gap-3">
|
|
153
|
+
<DialogTitle>
|
|
154
|
+
{t(
|
|
155
|
+
'customers.ai_assistant.dealDetail.sheet.title',
|
|
156
|
+
'Customers AI assistant — deal',
|
|
157
|
+
)}
|
|
158
|
+
</DialogTitle>
|
|
159
|
+
{pageContext.extra.stage ? (
|
|
160
|
+
<span
|
|
161
|
+
className="inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground"
|
|
162
|
+
data-ai-customers-deal-stage-pill=""
|
|
163
|
+
data-ai-customers-deal-stage={pageContext.extra.stage}
|
|
164
|
+
>
|
|
165
|
+
{pageContext.extra.stage}
|
|
166
|
+
</span>
|
|
167
|
+
) : null}
|
|
168
|
+
</div>
|
|
169
|
+
<DialogDescription>
|
|
170
|
+
{t(
|
|
171
|
+
'customers.ai_assistant.dealDetail.sheet.description',
|
|
172
|
+
'Ask about this deal. With the per-tenant mutation-policy override enabled, the assistant can also propose a stage change that you confirm before anything is saved.',
|
|
173
|
+
)}
|
|
174
|
+
</DialogDescription>
|
|
175
|
+
</DialogHeader>
|
|
176
|
+
<div className="min-h-0 flex-1" data-ai-customers-deal-chat-container="">
|
|
177
|
+
<AiChat
|
|
178
|
+
agent={CUSTOMERS_AI_DEAL_DETAIL_AGENT_ID}
|
|
179
|
+
pageContext={pageContext as unknown as Record<string, unknown>}
|
|
180
|
+
className="h-full"
|
|
181
|
+
placeholder={t(
|
|
182
|
+
'customers.ai_assistant.dealDetail.sheet.composerPlaceholder',
|
|
183
|
+
'Ask about this deal, the stage, pipeline...',
|
|
184
|
+
)}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
</DialogContent>
|
|
188
|
+
</Dialog>
|
|
189
|
+
</>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
import AiDealDetailTriggerWidget from './widget.client'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Step 5.15 (Phase 3 WS-D) — Customers Deal detail AiChat injection.
|
|
6
|
+
*
|
|
7
|
+
* Extends the Step 4.10 pattern to a record-level surface. Drops an
|
|
8
|
+
* "Ask AI" trigger on the deal detail page (`detail:customers.deal:header`)
|
|
9
|
+
* and opens a sheet embedding
|
|
10
|
+
* `<AiChat agent="customers.account_assistant" pageContext={…} />` with a
|
|
11
|
+
* deal-scoped `pageContext` shape:
|
|
12
|
+
*
|
|
13
|
+
* { view: 'customers.deal.detail',
|
|
14
|
+
* recordType: 'deal',
|
|
15
|
+
* recordId: <dealId>,
|
|
16
|
+
* extra: { stage, pipelineStageId } }
|
|
17
|
+
*
|
|
18
|
+
* Wires the stable conversation id so the Step 5.13
|
|
19
|
+
* `customers.update_deal_stage` mutation tool's idempotency hash stays
|
|
20
|
+
* constant across repeated confirms / retries within the same chat.
|
|
21
|
+
*
|
|
22
|
+
* Feature-gated behind `customers.deals.view` + `ai_assistant.view`.
|
|
23
|
+
*/
|
|
24
|
+
const widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {
|
|
25
|
+
metadata: {
|
|
26
|
+
id: 'customers.injection.ai-deal-detail-trigger',
|
|
27
|
+
title: 'Customers AI Deal Detail Trigger',
|
|
28
|
+
description:
|
|
29
|
+
'Renders an "Ask AI" button in the deal detail header that opens a sheet embedding the customers account assistant with deal-scoped page context.',
|
|
30
|
+
features: ['customers.deals.view', 'ai_assistant.view'],
|
|
31
|
+
priority: 100,
|
|
32
|
+
enabled: true,
|
|
33
|
+
},
|
|
34
|
+
Widget: AiDealDetailTriggerWidget,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default widget
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step 4.10 / Step 5.15 — customers module injection table.
|
|
5
|
+
*
|
|
6
|
+
* - Step 4.10 drops the `ai-assistant-trigger` widget on the People-list
|
|
7
|
+
* `DataTable` `:search-trailing` slot, which renders adjacent to the
|
|
8
|
+
* list search input. The previous mount point was the `:header` slot
|
|
9
|
+
* (separate row); the round icon-only trigger now lives next to the
|
|
10
|
+
* search box for a tighter, single-row toolbar.
|
|
11
|
+
* - Step 5.15 (Phase 3 WS-D) adds the `ai-deal-detail-trigger` widget on
|
|
12
|
+
* the Deal detail page header spot (`detail:customers.deal:header`).
|
|
13
|
+
*
|
|
14
|
+
* Both widgets embed `<AiChat agent="customers.account_assistant" …>`
|
|
15
|
+
* with a selection- or record-aware `pageContext`. The page files
|
|
16
|
+
* themselves only register the shared `<InjectionSpot>` mount point —
|
|
17
|
+
* the trigger, sheet, and chat surface live entirely in the injection
|
|
18
|
+
* widgets so third-party modules can copy the pattern unchanged.
|
|
19
|
+
*/
|
|
20
|
+
export const injectionTable: ModuleInjectionTable = {
|
|
21
|
+
'data-table:customers.people.list:search-trailing': [
|
|
22
|
+
{
|
|
23
|
+
widgetId: 'customers.injection.ai-assistant-trigger',
|
|
24
|
+
priority: 100,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
'data-table:customers.companies.list:search-trailing': [
|
|
28
|
+
{
|
|
29
|
+
widgetId: 'customers.injection.ai-assistant-trigger',
|
|
30
|
+
priority: 100,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
'detail:customers.deal:header': [
|
|
34
|
+
{
|
|
35
|
+
widgetId: 'customers.injection.ai-deal-detail-trigger',
|
|
36
|
+
priority: 100,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default injectionTable
|
|
@@ -278,6 +278,10 @@ Returns on error: error message with appropriate detail.`,
|
|
|
278
278
|
actionId: z.string().uuid().describe('The UUID of the action to accept'),
|
|
279
279
|
}),
|
|
280
280
|
requiredFeatures: ['inbox_ops.proposals.manage'],
|
|
281
|
+
// Accepting an inbox action creates downstream entities (orders, contacts,
|
|
282
|
+
// etc.) in target modules — must surface as a write so any agent that
|
|
283
|
+
// whitelists it routes through the approval card.
|
|
284
|
+
isMutation: true,
|
|
281
285
|
handler: async (input: { proposalId: string; actionId: string }, ctx: ToolContext) => {
|
|
282
286
|
const scope = requireTenantContext(ctx)
|
|
283
287
|
if (!ctx.userId) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { generateObject } from 'ai'
|
|
2
|
+
import type { AwilixContainer } from 'awilix'
|
|
3
|
+
import { createContainer } from 'awilix'
|
|
2
4
|
import {
|
|
3
5
|
resolveFirstConfiguredOpenCodeProvider,
|
|
4
6
|
resolveOpenCodeModel,
|
|
@@ -6,6 +8,11 @@ import {
|
|
|
6
8
|
resolveOpenCodeProviderId,
|
|
7
9
|
type OpenCodeProviderId,
|
|
8
10
|
} from '@open-mercato/shared/lib/ai/opencode-provider'
|
|
11
|
+
import {
|
|
12
|
+
AiModelFactoryError,
|
|
13
|
+
createModelFactory,
|
|
14
|
+
type AiModelFactory,
|
|
15
|
+
} from '@open-mercato/ai-assistant/modules/ai_assistant/lib/model-factory'
|
|
9
16
|
import { extractionOutputSchema } from '../data/validators'
|
|
10
17
|
|
|
11
18
|
// Vercel AI SDK provider factories return LanguageModelV1 but generateObject()
|
|
@@ -16,6 +23,24 @@ function asAiModel(model: unknown): AiModel {
|
|
|
16
23
|
return model as AiModel
|
|
17
24
|
}
|
|
18
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Step 5.1 — thin backward-compatibility shim. The public surface of this
|
|
28
|
+
* module (`resolveExtractionProviderId`, `createStructuredModel`,
|
|
29
|
+
* `withTimeout`, `runExtractionWithConfiguredProvider`) is unchanged so
|
|
30
|
+
* `ai-tools.ts`, `translationProvider.ts`, and `extractionWorker.ts` continue
|
|
31
|
+
* to compile and pass their existing tests.
|
|
32
|
+
*
|
|
33
|
+
* The model-instantiation path inside {@link runExtractionWithConfiguredProvider}
|
|
34
|
+
* now delegates to the shared {@link createModelFactory} so every
|
|
35
|
+
* AI-runtime caller shares one resolution order. The legacy
|
|
36
|
+
* `OPENCODE_MODEL` / `OPENCODE_PROVIDER` envs remain honored via
|
|
37
|
+
* {@link resolveExtractionProviderId} and {@link resolveOpenCodeModel} so
|
|
38
|
+
* inbox_ops deployments do not see a behavior change — the factory is
|
|
39
|
+
* consulted first (honoring `INBOX_OPS_AI_MODEL` + `input.modelOverride`),
|
|
40
|
+
* with the legacy path as the fallback when no registry provider is
|
|
41
|
+
* configured (preserving the historical error messages).
|
|
42
|
+
*/
|
|
43
|
+
|
|
19
44
|
export function resolveExtractionProviderId(): OpenCodeProviderId {
|
|
20
45
|
const configuredProvider = process.env.OPENCODE_PROVIDER
|
|
21
46
|
if (configuredProvider && configuredProvider.trim().length > 0) {
|
|
@@ -73,6 +98,48 @@ export async function withTimeout<T>(
|
|
|
73
98
|
}
|
|
74
99
|
}
|
|
75
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Test-only seam for the factory-delegation regression suite. Production
|
|
103
|
+
* callers MUST use {@link runExtractionWithConfiguredProvider} directly; the
|
|
104
|
+
* suite overrides this binding via `jest.spyOn` to assert the shim actually
|
|
105
|
+
* reaches `createModelFactory` without stubbing `@open-mercato/ai-assistant`.
|
|
106
|
+
*/
|
|
107
|
+
export const __inboxOpsLlmProviderInternal = {
|
|
108
|
+
createModelFactory,
|
|
109
|
+
createContainer,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function tryFactoryResolution(input: {
|
|
113
|
+
modelOverride?: string | null
|
|
114
|
+
}): { modelId: string; providerId: OpenCodeProviderId; model: AiModel } | null {
|
|
115
|
+
let factory: AiModelFactory
|
|
116
|
+
try {
|
|
117
|
+
const container = __inboxOpsLlmProviderInternal.createContainer()
|
|
118
|
+
factory = __inboxOpsLlmProviderInternal.createModelFactory(container as AwilixContainer)
|
|
119
|
+
} catch {
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const resolution = factory.resolveModel({
|
|
124
|
+
moduleId: 'inbox_ops',
|
|
125
|
+
callerOverride: input.modelOverride ?? undefined,
|
|
126
|
+
})
|
|
127
|
+
const providerId = resolveOpenCodeProviderId(resolution.providerId)
|
|
128
|
+
return {
|
|
129
|
+
modelId: resolution.modelId,
|
|
130
|
+
providerId,
|
|
131
|
+
model: asAiModel(resolution.model),
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (err instanceof AiModelFactoryError) {
|
|
135
|
+
// Fall back to the legacy path so the shim keeps throwing the original
|
|
136
|
+
// OPENCODE_*-era error messages existing tests/consumers rely on.
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
throw err
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
76
143
|
export async function runExtractionWithConfiguredProvider(input: {
|
|
77
144
|
systemPrompt: string
|
|
78
145
|
userPrompt: string
|
|
@@ -83,13 +150,22 @@ export async function runExtractionWithConfiguredProvider(input: {
|
|
|
83
150
|
totalTokens: number
|
|
84
151
|
modelWithProvider: string
|
|
85
152
|
}> {
|
|
86
|
-
const
|
|
87
|
-
const apiKey = requireOpenCodeProviderApiKey(providerId)
|
|
153
|
+
const factoryResolution = tryFactoryResolution({ modelOverride: input.modelOverride })
|
|
88
154
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
155
|
+
let model: AiModel
|
|
156
|
+
let modelWithProvider: string
|
|
157
|
+
if (factoryResolution) {
|
|
158
|
+
model = factoryResolution.model
|
|
159
|
+
modelWithProvider = `${factoryResolution.providerId}/${factoryResolution.modelId}`
|
|
160
|
+
} else {
|
|
161
|
+
const providerId = resolveExtractionProviderId()
|
|
162
|
+
const apiKey = requireOpenCodeProviderApiKey(providerId)
|
|
163
|
+
const modelConfig = resolveOpenCodeModel(providerId, {
|
|
164
|
+
overrideModel: input.modelOverride,
|
|
165
|
+
})
|
|
166
|
+
model = await createStructuredModel(providerId, apiKey, modelConfig.modelId)
|
|
167
|
+
modelWithProvider = modelConfig.modelWithProvider
|
|
168
|
+
}
|
|
93
169
|
|
|
94
170
|
const result = await withTimeout(
|
|
95
171
|
generateObject({
|
|
@@ -106,6 +182,6 @@ export async function runExtractionWithConfiguredProvider(input: {
|
|
|
106
182
|
return {
|
|
107
183
|
object: result.object,
|
|
108
184
|
totalTokens: Number(result.usage?.totalTokens ?? 0) || 0,
|
|
109
|
-
modelWithProvider
|
|
185
|
+
modelWithProvider,
|
|
110
186
|
}
|
|
111
187
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
|
|
2
|
+
|
|
3
|
+
export const setup: ModuleSetupConfig = {
|
|
4
|
+
defaultRoleFeatures: {
|
|
5
|
+
superadmin: ['notifications.*'],
|
|
6
|
+
admin: ['notifications.*'],
|
|
7
|
+
employee: ['notifications.view'],
|
|
8
|
+
},
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default setup
|