@open-mercato/core 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2
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,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/ai-tools/activities-tasks-pack.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * `customers.list_activities` + `customers.list_tasks` (Phase 1 WS-C, Step 3.9)\n * plus the deal comment / activity manage tools (Phase 3 WS-C, follow-up).\n *\n * The mutation tools route every write through the AI pending-action approval\n * gate via `createAiApiOperationRunner` \u2014 same contract as\n * `customers.update_deal_stage`.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport {\n createAiApiOperationRunner,\n type AiToolExecutionContext,\n} from '@open-mercato/ai-assistant/modules/ai_assistant/lib/ai-api-operation-runner'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n CustomerActivity,\n CustomerComment,\n CustomerDeal,\n CustomerDealCompanyLink,\n CustomerDealPersonLink,\n CustomerInteraction,\n CustomerTodoLink,\n} from '../data/entities'\nimport {\n assertTenantScope,\n type CustomersAiToolDefinition,\n type CustomersToolContext,\n type CustomersToolLoadBeforeSingleRecord,\n} from './types'\n\nfunction resolveEm(ctx: CustomersToolContext | AiToolExecutionContext): EntityManager {\n return ctx.container.resolve<EntityManager>('em')\n}\n\nfunction buildScope(ctx: CustomersToolContext | AiToolExecutionContext, tenantId: string) {\n return { tenantId, organizationId: ctx.organizationId }\n}\n\nfunction recordVersionFromUpdatedAt(updatedAt: Date | null | undefined): string | null {\n if (!updatedAt) return null\n const value = updatedAt instanceof Date ? updatedAt : new Date(updatedAt)\n if (Number.isNaN(value.getTime())) return null\n return value.toISOString()\n}\n\n// LLMs frequently emit `\"\"` for \"not provided\" \u2014 coerce blanks (and surrounding\n// whitespace) to `undefined` BEFORE per-field validators run. Mirrors the\n// `blankToUndefined` helper in deals-pack.ts.\nconst blankToUndefined = (value: unknown): unknown => {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length === 0 ? undefined : trimmed\n}\n\nasync function loadDealForScope(\n em: EntityManager,\n ctx: CustomersToolContext,\n tenantId: string,\n dealId: string,\n): Promise<CustomerDeal | null> {\n const where: Record<string, unknown> = { id: dealId, tenantId, deletedAt: null }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const deal = await findOneWithDecryption<CustomerDeal>(\n em,\n CustomerDeal,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!deal || deal.tenantId !== tenantId) return null\n if (ctx.organizationId && deal.organizationId !== ctx.organizationId) return null\n return deal\n}\n\n/**\n * `CustomerDeal` does NOT carry a direct `entity` field \u2014 deals are linked\n * to people / companies via the `customer_deal_person_links` and\n * `customer_deal_company_links` tables. Comments however need a non-null\n * `entity_id` (the timeline owner) so this helper resolves the deal's\n * first linked person, then falls back to its first linked company. When\n * neither exists, the caller MUST instruct the operator to link a contact\n * before commenting on the deal.\n */\nasync function resolveDealCommentEntityId(\n em: EntityManager,\n ctx: CustomersToolContext,\n tenantId: string,\n dealId: string,\n): Promise<string | null> {\n const personLink = await em.findOne(\n CustomerDealPersonLink,\n { deal: dealId, tenantId } as never,\n { populate: ['personEntity'] as never },\n )\n if (personLink) {\n const linked = (personLink as unknown as { personEntity?: { id?: string | null } | null }).personEntity\n if (linked && typeof linked === 'object' && typeof linked.id === 'string') return linked.id\n const raw = (personLink as unknown as { personEntity?: unknown }).personEntity\n if (typeof raw === 'string') return raw\n }\n const companyLink = await em.findOne(\n CustomerDealCompanyLink,\n { deal: dealId, tenantId } as never,\n { populate: ['companyEntity'] as never },\n )\n if (companyLink) {\n const linked = (companyLink as unknown as { companyEntity?: { id?: string | null } | null }).companyEntity\n if (linked && typeof linked === 'object' && typeof linked.id === 'string') return linked.id\n const raw = (companyLink as unknown as { companyEntity?: unknown }).companyEntity\n if (typeof raw === 'string') return raw\n }\n return null\n}\n\nasync function loadCommentForScope(\n em: EntityManager,\n ctx: CustomersToolContext,\n tenantId: string,\n commentId: string,\n): Promise<CustomerComment | null> {\n const where: Record<string, unknown> = { id: commentId, tenantId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const row = await findOneWithDecryption<CustomerComment>(\n em,\n CustomerComment,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!row || row.tenantId !== tenantId) return null\n if (ctx.organizationId && row.organizationId !== ctx.organizationId) return null\n return row\n}\n\nasync function loadActivityForScope(\n em: EntityManager,\n ctx: CustomersToolContext,\n tenantId: string,\n activityId: string,\n): Promise<CustomerActivity | null> {\n const where: Record<string, unknown> = { id: activityId, tenantId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const row = await findOneWithDecryption<CustomerActivity>(\n em,\n CustomerActivity,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!row || row.tenantId !== tenantId) return null\n if (ctx.organizationId && row.organizationId !== ctx.organizationId) return null\n return row\n}\n\nfunction commentEntityIdOf(row: CustomerComment): string | null {\n const ent = (row as any).entity\n if (!ent) return null\n if (typeof ent === 'string') return ent\n if (typeof ent === 'object' && typeof ent.id === 'string') return ent.id\n return null\n}\n\nfunction activityEntityIdOf(row: CustomerActivity): string | null {\n const ent = (row as any).entity\n if (!ent) return null\n if (typeof ent === 'string') return ent\n if (typeof ent === 'object' && typeof ent.id === 'string') return ent.id\n return null\n}\n\nconst listActivitiesInput = z\n .object({\n personId: z.string().uuid().optional().describe('Restrict to activities attached to this person entity id.'),\n companyId: z.string().uuid().optional().describe('Restrict to activities attached to this company entity id.'),\n dealId: z.string().uuid().optional().describe('Restrict to activities attached to this deal id.'),\n activityType: z.string().optional().describe('Filter by activity type (e.g. \"call\", \"email\").'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listActivitiesTool: CustomersAiToolDefinition = {\n name: 'customers.list_activities',\n displayName: 'List activities',\n description:\n 'List logged customer activities (calls, emails, meetings, notes, etc.) scoped to tenant + organization. Supply `personId` / `companyId` / `dealId` to narrow; otherwise returns the most recent activities across the tenant.',\n inputSchema: listActivitiesInput,\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listActivitiesInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const entityId = input.personId ?? input.companyId ?? null\n if (entityId) where.entity = entityId\n if (input.dealId) where.deal = input.dealId\n if (input.activityType) where.activityType = input.activityType\n const [rows, total] = await Promise.all([\n findWithDecryption<CustomerActivity>(\n em,\n CustomerActivity,\n where as any,\n { limit, offset, orderBy: { occurredAt: 'desc', createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CustomerActivity, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n activityType: row.activityType,\n subject: row.subject ?? null,\n body: row.body ?? null,\n occurredAt: row.occurredAt ? new Date(row.occurredAt).toISOString() : null,\n authorUserId: row.authorUserId ?? null,\n entityId: activityEntityIdOf(row),\n dealId: (row as any).deal && typeof (row as any).deal === 'object' ? (row as any).deal.id : (row as any).deal ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\nconst listTasksInput = z\n .object({\n personId: z.string().uuid().optional().describe('Restrict to tasks linked to this person entity id.'),\n companyId: z.string().uuid().optional().describe('Restrict to tasks linked to this company entity id.'),\n dealId: z.string().uuid().optional().describe('Restrict to tasks connected to this deal id.'),\n status: z\n .enum(['open', 'done', 'cancelled'])\n .optional()\n .describe('Filter canonical interaction tasks by status. Ignored when listing legacy todo links.'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listTasksTool: CustomersAiToolDefinition = {\n name: 'customers.list_tasks',\n displayName: 'List tasks',\n description:\n 'List customer tasks scoped to tenant + organization. Returns canonical interaction tasks (interactionType=\"task\") merged with legacy todo links for compatibility.',\n inputSchema: listTasksInput,\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listTasksInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const entityId = input.personId ?? input.companyId ?? null\n const interactionWhere: Record<string, unknown> = {\n tenantId,\n interactionType: 'task',\n deletedAt: null,\n }\n if (ctx.organizationId) interactionWhere.organizationId = ctx.organizationId\n if (entityId) interactionWhere.entity = entityId\n if (input.dealId) interactionWhere.dealId = input.dealId\n if (input.status) interactionWhere.status = input.status === 'open' ? 'planned' : input.status === 'done' ? 'completed' : 'cancelled'\n const interactionRows = await findWithDecryption<CustomerInteraction>(\n em,\n CustomerInteraction,\n interactionWhere as any,\n { limit, offset, orderBy: { scheduledAt: 'desc', createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n )\n const legacyWhere: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) legacyWhere.organizationId = ctx.organizationId\n if (entityId) legacyWhere.entity = entityId\n const legacyRows =\n input.status || input.dealId\n ? []\n : await findWithDecryption<CustomerTodoLink>(\n em,\n CustomerTodoLink,\n legacyWhere as any,\n { limit, offset, orderBy: { createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n )\n const filteredInteractions = interactionRows.filter((row) => row.tenantId === tenantId)\n const filteredLegacy = legacyRows.filter((row) => row.tenantId === tenantId)\n const items = [\n ...filteredInteractions.map((row) => ({\n kind: 'interaction' as const,\n id: row.id,\n title: row.title ?? null,\n body: row.body ?? null,\n status: row.status,\n scheduledAt: row.scheduledAt ? new Date(row.scheduledAt).toISOString() : null,\n occurredAt: row.occurredAt ? new Date(row.occurredAt).toISOString() : null,\n dealId: row.dealId ?? null,\n ownerUserId: row.ownerUserId ?? null,\n priority: row.priority ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n })),\n ...filteredLegacy.map((row) => ({\n kind: 'todo_link' as const,\n id: row.id,\n todoId: row.todoId,\n todoSource: row.todoSource,\n entityId: (row as any).entity && typeof (row as any).entity === 'object' ? (row as any).entity.id : (row as any).entity ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n })),\n ]\n return {\n items,\n total: items.length,\n limit,\n offset,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.list_deal_comments \u2014 read-only dedicated comment listing\n// ---------------------------------------------------------------------------\n\nconst listDealCommentsInput = z\n .object({\n dealId: z.string().uuid().describe('Deal id whose comments should be listed.'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listDealCommentsTool: CustomersAiToolDefinition = {\n name: 'customers.list_deal_comments',\n displayName: 'List deal comments',\n description:\n 'List comments left on a specific deal, ordered by most recent first. Read-only; tenant + organization scoped.',\n inputSchema: listDealCommentsInput,\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listDealCommentsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId, deal: input.dealId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<CustomerComment>(\n em,\n CustomerComment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CustomerComment, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n body: row.body ?? null,\n entityId: commentEntityIdOf(row),\n dealId: (row as any).deal && typeof (row as any).deal === 'object' ? (row as any).deal.id : (row as any).deal ?? null,\n authorUserId: row.authorUserId ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.manage_deal_comment \u2014 create / update / delete a deal comment\n// ---------------------------------------------------------------------------\n\nconst manageDealCommentInput = z\n .object({\n operation: z\n .enum(['create', 'update', 'delete'])\n .describe('Which write to perform: create a new comment, update an existing one, or delete it.'),\n dealId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` \u2014 the deal the comment is attached to.'),\n commentId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `update` and `delete` \u2014 id of the existing comment row.'),\n body: z\n .preprocess(blankToUndefined, z.string().min(1).max(8000).optional())\n .describe('Comment text. Required for `create`; optional on `update`.'),\n })\n .superRefine((value, ctx) => {\n if (value.operation === 'create') {\n if (!value.dealId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'dealId is required for create.', path: ['dealId'] })\n if (!value.body) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'body is required for create.', path: ['body'] })\n }\n if (value.operation === 'update') {\n if (!value.commentId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'commentId is required for update.', path: ['commentId'] })\n if (!value.body) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'body is required for update.', path: ['body'] })\n }\n if (value.operation === 'delete') {\n if (!value.commentId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'commentId is required for delete.', path: ['commentId'] })\n }\n })\n\ntype ManageDealCommentInput = z.infer<typeof manageDealCommentInput>\n\nconst manageDealCommentTool: CustomersAiToolDefinition = {\n name: 'customers.manage_deal_comment',\n displayName: 'Manage deal comment',\n description:\n 'Create, update, or delete a comment on a deal. Use `operation` to pick the action. Under `destructive-confirm-required` policy, only the `delete` branch routes through the approval card; `create` and `update` execute directly.',\n inputSchema: manageDealCommentInput as z.ZodType<unknown>,\n requiredFeatures: ['customers.activities.manage'],\n tags: ['write', 'customers'],\n isMutation: true,\n // Predicate `isDestructive`: only the `delete` branch counts as\n // destructive. Under `confirm-required` policy every branch still\n // gates (the framework ignores this flag); under\n // `destructive-confirm-required` only deletes go through the approval\n // card while creates/updates run directly.\n isDestructive: (input: unknown) => {\n if (!input || typeof input !== 'object') return false\n return (input as { operation?: unknown }).operation === 'delete'\n },\n loadBeforeRecord: async (rawInput, ctx): Promise<CustomersToolLoadBeforeSingleRecord | null> => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageDealCommentInput = manageDealCommentInput.parse(rawInput)\n const em = resolveEm(ctx)\n if (input.operation === 'create') {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId!)\n if (!deal) return null\n return {\n recordId: deal.id,\n entityType: 'customers.deal',\n recordVersion: recordVersionFromUpdatedAt(deal.updatedAt),\n before: { commentId: null, body: null, dealId: deal.id },\n }\n }\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) return null\n return {\n recordId: existing.id,\n entityType: 'customers.customer_comment',\n recordVersion: recordVersionFromUpdatedAt(existing.updatedAt),\n before: {\n body: existing.body ?? null,\n dealId: (existing as any).deal && typeof (existing as any).deal === 'object'\n ? (existing as any).deal.id\n : (existing as any).deal ?? null,\n entityId: commentEntityIdOf(existing),\n authorUserId: existing.authorUserId ?? null,\n },\n }\n },\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageDealCommentInput = manageDealCommentInput.parse(rawInput)\n const em = resolveEm(ctx)\n const runner = createAiApiOperationRunner(ctx as unknown as AiToolExecutionContext)\n\n if (input.operation === 'create') {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId!)\n if (!deal) throw new Error(`Deal \"${input.dealId}\" is not accessible to the caller.`)\n const organizationId = deal.organizationId\n if (!organizationId) throw new Error(`Deal \"${deal.id}\" has no organization scope.`)\n // `CustomerDeal` has no direct `.entity` field \u2014 deals link to\n // people/companies via two link tables. Resolve the first available\n // person, then fall back to the first linked company. Only fail the\n // operation when the deal has no linked contacts at all.\n const dealEntityId = await resolveDealCommentEntityId(em, ctx, tenantId, deal.id)\n if (!dealEntityId) {\n throw new Error(\n `Deal \"${deal.id}\" has no linked person or company. Link a contact to the deal in the backoffice before adding a comment, or post the comment directly on the person/company record instead.`,\n )\n }\n const body: Record<string, unknown> = {\n tenantId,\n organizationId,\n dealId: deal.id,\n // Comments require an `entityId` (the person/company on the timeline).\n entityId: dealEntityId,\n body: input.body,\n }\n const response = await runner.run({ method: 'POST', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? 'Failed to create comment')\n }\n const result = (response.data ?? {}) as { id?: string | null }\n return {\n operation: 'create' as const,\n commentId: result.id ?? null,\n dealId: deal.id,\n commandName: 'customers.comments.create',\n before: null,\n after: { body: input.body ?? null, dealId: deal.id },\n }\n }\n\n if (input.operation === 'update') {\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) throw new Error(`Comment \"${input.commentId}\" is not accessible to the caller.`)\n const organizationId = existing.organizationId\n if (!organizationId) throw new Error(`Comment \"${existing.id}\" has no organization scope.`)\n const body: Record<string, unknown> = {\n id: existing.id,\n tenantId,\n organizationId,\n body: input.body,\n }\n const response = await runner.run({ method: 'PUT', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to update comment \"${existing.id}\"`)\n }\n const after = await loadCommentForScope(em, ctx, tenantId, existing.id)\n return {\n operation: 'update' as const,\n commentId: existing.id,\n commandName: 'customers.comments.update',\n before: { body: existing.body ?? null },\n after: after ? { body: after.body ?? null } : null,\n }\n }\n\n // delete\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) throw new Error(`Comment \"${input.commentId}\" is not accessible to the caller.`)\n const body: Record<string, unknown> = { id: existing.id }\n const response = await runner.run({ method: 'DELETE', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to delete comment \"${existing.id}\"`)\n }\n return {\n operation: 'delete' as const,\n commentId: existing.id,\n commandName: 'customers.comments.delete',\n before: { body: existing.body ?? null },\n after: null,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.manage_deal_activity \u2014 create / update / delete an activity\n// ---------------------------------------------------------------------------\n\nconst manageDealActivityInput = z\n .object({\n operation: z\n .enum(['create', 'update', 'delete'])\n .describe('Which write to perform: create a new activity, update an existing one, or delete it.'),\n activityId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `update` and `delete`.'),\n dealId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` \u2014 the deal the activity is logged against.'),\n activityType: z\n .preprocess(blankToUndefined, z.string().min(1).max(100).optional())\n .describe('Required for `create` \u2014 e.g. \"call\", \"email\", \"meeting\", \"note\".'),\n subject: z\n .preprocess(blankToUndefined, z.string().max(200).optional())\n .describe('Optional short subject line.'),\n body: z\n .preprocess(blankToUndefined, z.string().max(8000).optional())\n .describe('Optional free-text body.'),\n occurredAt: z\n .preprocess(blankToUndefined, z.string().datetime().optional())\n .describe('ISO-8601 timestamp when the activity occurred. Omit for \"now\" (server-side default applies).'),\n })\n .superRefine((value, ctx) => {\n if (value.operation === 'create') {\n if (!value.dealId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'dealId is required for create.', path: ['dealId'] })\n if (!value.activityType) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'activityType is required for create.', path: ['activityType'] })\n }\n if (value.operation === 'update' || value.operation === 'delete') {\n if (!value.activityId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'activityId is required.', path: ['activityId'] })\n }\n })\n\ntype ManageDealActivityInput = z.infer<typeof manageDealActivityInput>\n\nconst manageDealActivityTool: CustomersAiToolDefinition = {\n name: 'customers.manage_deal_activity',\n displayName: 'Manage deal activity',\n description:\n 'Create, update, or delete a deal activity (call, email, meeting, note, etc.). Mutation tool \u2014 every call routes through the AI pending-action approval gate. Use `operation` to pick the action.',\n inputSchema: manageDealActivityInput as z.ZodType<unknown>,\n requiredFeatures: ['customers.activities.manage'],\n tags: ['write', 'customers'],\n isMutation: true,\n loadBeforeRecord: async (rawInput, ctx): Promise<CustomersToolLoadBeforeSingleRecord | null> => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageDealActivityInput = manageDealActivityInput.parse(rawInput)\n const em = resolveEm(ctx)\n if (input.operation === 'create') {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId!)\n if (!deal) return null\n return {\n recordId: deal.id,\n entityType: 'customers.deal',\n recordVersion: recordVersionFromUpdatedAt(deal.updatedAt),\n before: { activityId: null, dealId: deal.id },\n }\n }\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) return null\n return {\n recordId: existing.id,\n entityType: 'customers.customer_activity',\n recordVersion: recordVersionFromUpdatedAt(existing.updatedAt),\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n occurredAt: existing.occurredAt ? new Date(existing.occurredAt).toISOString() : null,\n dealId: (existing as any).deal && typeof (existing as any).deal === 'object'\n ? (existing as any).deal.id\n : (existing as any).deal ?? null,\n },\n }\n },\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageDealActivityInput = manageDealActivityInput.parse(rawInput)\n const em = resolveEm(ctx)\n const runner = createAiApiOperationRunner(ctx as unknown as AiToolExecutionContext)\n\n if (input.operation === 'create') {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId!)\n if (!deal) throw new Error(`Deal \"${input.dealId}\" is not accessible to the caller.`)\n const organizationId = deal.organizationId\n if (!organizationId) throw new Error(`Deal \"${deal.id}\" has no organization scope.`)\n const dealEntity = (deal as unknown as { entity?: unknown }).entity\n const entityId =\n dealEntity && typeof dealEntity === 'object'\n ? (dealEntity as { id?: string | null }).id ?? null\n : typeof dealEntity === 'string'\n ? dealEntity\n : null\n if (!entityId) {\n throw new Error(`Deal \"${deal.id}\" has no associated person/company; cannot attach an activity.`)\n }\n const body: Record<string, unknown> = {\n tenantId,\n organizationId,\n entityId,\n dealId: deal.id,\n activityType: input.activityType,\n }\n if (input.subject) body.subject = input.subject\n if (input.body) body.body = input.body\n if (input.occurredAt) body.occurredAt = input.occurredAt\n const response = await runner.run({ method: 'POST', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? 'Failed to create activity')\n }\n const result = (response.data ?? {}) as { id?: string | null }\n return {\n operation: 'create' as const,\n activityId: result.id ?? null,\n dealId: deal.id,\n commandName: 'customers.activities.create',\n before: null,\n after: {\n activityType: input.activityType ?? null,\n subject: input.subject ?? null,\n body: input.body ?? null,\n occurredAt: input.occurredAt ?? null,\n },\n }\n }\n\n if (input.operation === 'update') {\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) throw new Error(`Activity \"${input.activityId}\" is not accessible to the caller.`)\n const organizationId = existing.organizationId\n if (!organizationId) throw new Error(`Activity \"${existing.id}\" has no organization scope.`)\n const body: Record<string, unknown> = { id: existing.id, tenantId, organizationId }\n if (input.activityType) body.activityType = input.activityType\n if (input.subject !== undefined) body.subject = input.subject\n if (input.body !== undefined) body.body = input.body\n if (input.occurredAt !== undefined) body.occurredAt = input.occurredAt\n const response = await runner.run({ method: 'PUT', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to update activity \"${existing.id}\"`)\n }\n const after = await loadActivityForScope(em, ctx, tenantId, existing.id)\n return {\n operation: 'update' as const,\n activityId: existing.id,\n commandName: 'customers.activities.update',\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n occurredAt: existing.occurredAt ? new Date(existing.occurredAt).toISOString() : null,\n },\n after: after\n ? {\n activityType: after.activityType,\n subject: after.subject ?? null,\n body: after.body ?? null,\n occurredAt: after.occurredAt ? new Date(after.occurredAt).toISOString() : null,\n }\n : null,\n }\n }\n\n // delete\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) throw new Error(`Activity \"${input.activityId}\" is not accessible to the caller.`)\n const body: Record<string, unknown> = { id: existing.id }\n const response = await runner.run({ method: 'DELETE', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to delete activity \"${existing.id}\"`)\n }\n return {\n operation: 'delete' as const,\n activityId: existing.id,\n commandName: 'customers.activities.delete',\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n },\n after: null,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.list_record_comments \u2014 list comments on a person / company / deal\n// ---------------------------------------------------------------------------\n\nconst listRecordCommentsInput = z\n .object({\n personId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Restrict to comments on this person entity id.'),\n companyId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Restrict to comments on this company entity id.'),\n dealId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Restrict to comments attached to this deal id.'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .superRefine((value, ctx) => {\n if (!value.personId && !value.companyId && !value.dealId) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Provide at least one of personId, companyId, or dealId.',\n path: ['personId'],\n })\n }\n })\n\nconst listRecordCommentsTool: CustomersAiToolDefinition = {\n name: 'customers.list_record_comments',\n displayName: 'List record comments',\n description:\n 'List comments left on a person, company, or deal record. Read-only; tenant + organization scoped. Provide at least one of `personId`, `companyId`, or `dealId`.',\n inputSchema: listRecordCommentsInput as z.ZodType<unknown>,\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listRecordCommentsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const entityId = input.personId ?? input.companyId ?? null\n if (entityId) where.entity = entityId\n if (input.dealId) where.deal = input.dealId\n const [rows, total] = await Promise.all([\n findWithDecryption<CustomerComment>(\n em,\n CustomerComment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CustomerComment, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n body: row.body ?? null,\n entityId: commentEntityIdOf(row),\n dealId: (row as any).deal && typeof (row as any).deal === 'object' ? (row as any).deal.id : (row as any).deal ?? null,\n authorUserId: row.authorUserId ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.manage_record_comment \u2014 create / update / delete a comment on a\n// person, company, or deal record. Mutation tool.\n// ---------------------------------------------------------------------------\n\nconst manageRecordCommentInput = z\n .object({\n operation: z\n .enum(['create', 'update', 'delete'])\n .describe('Which write to perform: create a new comment, update an existing one, or delete it.'),\n personId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` (or supply `companyId`) \u2014 the person entity the comment is attached to.'),\n companyId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` (or supply `personId`) \u2014 the company entity the comment is attached to.'),\n dealId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Optional on `create` \u2014 when set, the comment also shows up under that deal.'),\n commentId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `update` and `delete` \u2014 id of the existing comment row.'),\n body: z\n .preprocess(blankToUndefined, z.string().min(1).max(8000).optional())\n .describe('Comment text. Required for `create`; optional on `update`.'),\n })\n .superRefine((value, ctx) => {\n if (value.operation === 'create') {\n if (!value.personId && !value.companyId) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Provide personId or companyId for create.',\n path: ['personId'],\n })\n }\n if (value.personId && value.companyId) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Provide only one of personId or companyId.',\n path: ['personId'],\n })\n }\n if (!value.body) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'body is required for create.', path: ['body'] })\n }\n if (value.operation === 'update') {\n if (!value.commentId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'commentId is required for update.', path: ['commentId'] })\n if (!value.body) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'body is required for update.', path: ['body'] })\n }\n if (value.operation === 'delete') {\n if (!value.commentId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'commentId is required for delete.', path: ['commentId'] })\n }\n })\n\ntype ManageRecordCommentInput = z.infer<typeof manageRecordCommentInput>\n\nconst manageRecordCommentTool: CustomersAiToolDefinition = {\n name: 'customers.manage_record_comment',\n displayName: 'Manage record comment',\n description:\n 'Create, update, or delete a comment on a person, company, or deal record. Mutation tool \u2014 every call routes through the AI pending-action approval gate. Use `operation` to pick the action; for `create` provide `personId` OR `companyId` (and optionally `dealId`).',\n inputSchema: manageRecordCommentInput as z.ZodType<unknown>,\n requiredFeatures: ['customers.activities.manage'],\n tags: ['write', 'customers'],\n isMutation: true,\n loadBeforeRecord: async (rawInput, ctx): Promise<CustomersToolLoadBeforeSingleRecord | null> => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageRecordCommentInput = manageRecordCommentInput.parse(rawInput)\n const em = resolveEm(ctx)\n if (input.operation === 'create') {\n const entityId = input.personId ?? input.companyId\n // We do not load the host person/company entity here \u2014 the\n // `customers/comments` POST handler validates its existence and tenant\n // scope. We do hydrate the deal when supplied so the approval card has\n // a stable record-version anchor.\n if (input.dealId) {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId)\n if (!deal) return null\n return {\n recordId: deal.id,\n entityType: 'customers.deal',\n recordVersion: recordVersionFromUpdatedAt(deal.updatedAt),\n before: { commentId: null, body: null, entityId, dealId: deal.id },\n }\n }\n return {\n recordId: entityId!,\n entityType: input.personId ? 'customers.person' : 'customers.company',\n recordVersion: null,\n before: { commentId: null, body: null, entityId, dealId: null },\n }\n }\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) return null\n return {\n recordId: existing.id,\n entityType: 'customers.customer_comment',\n recordVersion: recordVersionFromUpdatedAt(existing.updatedAt),\n before: {\n body: existing.body ?? null,\n dealId: (existing as any).deal && typeof (existing as any).deal === 'object'\n ? (existing as any).deal.id\n : (existing as any).deal ?? null,\n entityId: commentEntityIdOf(existing),\n authorUserId: existing.authorUserId ?? null,\n },\n }\n },\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageRecordCommentInput = manageRecordCommentInput.parse(rawInput)\n const em = resolveEm(ctx)\n const runner = createAiApiOperationRunner(ctx as unknown as AiToolExecutionContext)\n\n if (input.operation === 'create') {\n const entityId = input.personId ?? input.companyId!\n // Resolve organization scope: prefer the deal's org when one is supplied,\n // otherwise fall back to the caller context's org.\n let organizationId: string | null = ctx.organizationId\n let dealId: string | null = null\n if (input.dealId) {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId)\n if (!deal) throw new Error(`Deal \"${input.dealId}\" is not accessible to the caller.`)\n organizationId = deal.organizationId ?? organizationId\n dealId = deal.id\n }\n if (!organizationId) {\n throw new Error('Organization scope is required to create a comment.')\n }\n const body: Record<string, unknown> = {\n tenantId,\n organizationId,\n entityId,\n body: input.body,\n }\n if (dealId) body.dealId = dealId\n const response = await runner.run({ method: 'POST', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? 'Failed to create comment')\n }\n const result = (response.data ?? {}) as { id?: string | null }\n return {\n operation: 'create' as const,\n commentId: result.id ?? null,\n entityId,\n dealId,\n commandName: 'customers.comments.create',\n before: null,\n after: { body: input.body ?? null, entityId, dealId },\n }\n }\n\n if (input.operation === 'update') {\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) throw new Error(`Comment \"${input.commentId}\" is not accessible to the caller.`)\n const organizationId = existing.organizationId\n if (!organizationId) throw new Error(`Comment \"${existing.id}\" has no organization scope.`)\n const body: Record<string, unknown> = {\n id: existing.id,\n tenantId,\n organizationId,\n body: input.body,\n }\n const response = await runner.run({ method: 'PUT', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to update comment \"${existing.id}\"`)\n }\n const after = await loadCommentForScope(em, ctx, tenantId, existing.id)\n return {\n operation: 'update' as const,\n commentId: existing.id,\n commandName: 'customers.comments.update',\n before: { body: existing.body ?? null },\n after: after ? { body: after.body ?? null } : null,\n }\n }\n\n // delete\n const existing = await loadCommentForScope(em, ctx, tenantId, input.commentId!)\n if (!existing) throw new Error(`Comment \"${input.commentId}\" is not accessible to the caller.`)\n const body: Record<string, unknown> = { id: existing.id }\n const response = await runner.run({ method: 'DELETE', path: '/customers/comments', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to delete comment \"${existing.id}\"`)\n }\n return {\n operation: 'delete' as const,\n commentId: existing.id,\n commandName: 'customers.comments.delete',\n before: { body: existing.body ?? null },\n after: null,\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// customers.manage_record_activity \u2014 create / update / delete an activity on\n// a person, company, or deal record. Mutation tool.\n// ---------------------------------------------------------------------------\n\nconst manageRecordActivityInput = z\n .object({\n operation: z\n .enum(['create', 'update', 'delete'])\n .describe('Which write to perform: create a new activity, update an existing one, or delete it.'),\n activityId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `update` and `delete`.'),\n personId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` (or supply `companyId`) \u2014 the person entity the activity is logged on.'),\n companyId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Required for `create` (or supply `personId`) \u2014 the company entity the activity is logged on.'),\n dealId: z\n .preprocess(blankToUndefined, z.string().uuid().optional())\n .describe('Optional on `create` \u2014 when set, the activity is also linked to that deal.'),\n activityType: z\n .preprocess(blankToUndefined, z.string().min(1).max(100).optional())\n .describe('Required for `create` \u2014 e.g. \"call\", \"email\", \"meeting\", \"note\".'),\n subject: z\n .preprocess(blankToUndefined, z.string().max(200).optional())\n .describe('Optional short subject line.'),\n body: z\n .preprocess(blankToUndefined, z.string().max(8000).optional())\n .describe('Optional free-text body.'),\n occurredAt: z\n .preprocess(blankToUndefined, z.string().datetime().optional())\n .describe('ISO-8601 timestamp when the activity occurred. Omit for \"now\" (server-side default applies).'),\n })\n .superRefine((value, ctx) => {\n if (value.operation === 'create') {\n if (!value.personId && !value.companyId) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Provide personId or companyId for create.',\n path: ['personId'],\n })\n }\n if (value.personId && value.companyId) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Provide only one of personId or companyId.',\n path: ['personId'],\n })\n }\n if (!value.activityType) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'activityType is required for create.', path: ['activityType'] })\n }\n if (value.operation === 'update' || value.operation === 'delete') {\n if (!value.activityId) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'activityId is required.', path: ['activityId'] })\n }\n })\n\ntype ManageRecordActivityInput = z.infer<typeof manageRecordActivityInput>\n\nconst manageRecordActivityTool: CustomersAiToolDefinition = {\n name: 'customers.manage_record_activity',\n displayName: 'Manage record activity',\n description:\n 'Create, update, or delete an activity (call, email, meeting, note) on a person, company, or deal record. Mutation tool \u2014 every call routes through the AI pending-action approval gate. For `create` provide `personId` OR `companyId` (and optionally `dealId`).',\n inputSchema: manageRecordActivityInput as z.ZodType<unknown>,\n requiredFeatures: ['customers.activities.manage'],\n tags: ['write', 'customers'],\n isMutation: true,\n loadBeforeRecord: async (rawInput, ctx): Promise<CustomersToolLoadBeforeSingleRecord | null> => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageRecordActivityInput = manageRecordActivityInput.parse(rawInput)\n const em = resolveEm(ctx)\n if (input.operation === 'create') {\n const entityId = input.personId ?? input.companyId\n if (input.dealId) {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId)\n if (!deal) return null\n return {\n recordId: deal.id,\n entityType: 'customers.deal',\n recordVersion: recordVersionFromUpdatedAt(deal.updatedAt),\n before: { activityId: null, dealId: deal.id, entityId },\n }\n }\n return {\n recordId: entityId!,\n entityType: input.personId ? 'customers.person' : 'customers.company',\n recordVersion: null,\n before: { activityId: null, entityId, dealId: null },\n }\n }\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) return null\n return {\n recordId: existing.id,\n entityType: 'customers.customer_activity',\n recordVersion: recordVersionFromUpdatedAt(existing.updatedAt),\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n occurredAt: existing.occurredAt ? new Date(existing.occurredAt).toISOString() : null,\n entityId: activityEntityIdOf(existing),\n dealId: (existing as any).deal && typeof (existing as any).deal === 'object'\n ? (existing as any).deal.id\n : (existing as any).deal ?? null,\n },\n }\n },\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input: ManageRecordActivityInput = manageRecordActivityInput.parse(rawInput)\n const em = resolveEm(ctx)\n const runner = createAiApiOperationRunner(ctx as unknown as AiToolExecutionContext)\n\n if (input.operation === 'create') {\n const entityId = input.personId ?? input.companyId!\n let organizationId: string | null = ctx.organizationId\n let dealId: string | null = null\n if (input.dealId) {\n const deal = await loadDealForScope(em, ctx, tenantId, input.dealId)\n if (!deal) throw new Error(`Deal \"${input.dealId}\" is not accessible to the caller.`)\n organizationId = deal.organizationId ?? organizationId\n dealId = deal.id\n }\n if (!organizationId) {\n throw new Error('Organization scope is required to create an activity.')\n }\n const body: Record<string, unknown> = {\n tenantId,\n organizationId,\n entityId,\n activityType: input.activityType,\n }\n if (dealId) body.dealId = dealId\n if (input.subject) body.subject = input.subject\n if (input.body) body.body = input.body\n if (input.occurredAt) body.occurredAt = input.occurredAt\n const response = await runner.run({ method: 'POST', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? 'Failed to create activity')\n }\n const result = (response.data ?? {}) as { id?: string | null }\n return {\n operation: 'create' as const,\n activityId: result.id ?? null,\n entityId,\n dealId,\n commandName: 'customers.activities.create',\n before: null,\n after: {\n activityType: input.activityType ?? null,\n subject: input.subject ?? null,\n body: input.body ?? null,\n occurredAt: input.occurredAt ?? null,\n },\n }\n }\n\n if (input.operation === 'update') {\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) throw new Error(`Activity \"${input.activityId}\" is not accessible to the caller.`)\n const organizationId = existing.organizationId\n if (!organizationId) throw new Error(`Activity \"${existing.id}\" has no organization scope.`)\n const body: Record<string, unknown> = { id: existing.id, tenantId, organizationId }\n if (input.activityType) body.activityType = input.activityType\n if (input.subject !== undefined) body.subject = input.subject\n if (input.body !== undefined) body.body = input.body\n if (input.occurredAt !== undefined) body.occurredAt = input.occurredAt\n const response = await runner.run({ method: 'PUT', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to update activity \"${existing.id}\"`)\n }\n const after = await loadActivityForScope(em, ctx, tenantId, existing.id)\n return {\n operation: 'update' as const,\n activityId: existing.id,\n commandName: 'customers.activities.update',\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n occurredAt: existing.occurredAt ? new Date(existing.occurredAt).toISOString() : null,\n },\n after: after\n ? {\n activityType: after.activityType,\n subject: after.subject ?? null,\n body: after.body ?? null,\n occurredAt: after.occurredAt ? new Date(after.occurredAt).toISOString() : null,\n }\n : null,\n }\n }\n\n // delete\n const existing = await loadActivityForScope(em, ctx, tenantId, input.activityId!)\n if (!existing) throw new Error(`Activity \"${input.activityId}\" is not accessible to the caller.`)\n const body: Record<string, unknown> = { id: existing.id }\n const response = await runner.run({ method: 'DELETE', path: '/customers/activities', body })\n if (!response.success) {\n throw new Error(response.error ?? `Failed to delete activity \"${existing.id}\"`)\n }\n return {\n operation: 'delete' as const,\n activityId: existing.id,\n commandName: 'customers.activities.delete',\n before: {\n activityType: existing.activityType,\n subject: existing.subject ?? null,\n body: existing.body ?? null,\n },\n after: null,\n }\n },\n}\n\nexport const activitiesTasksAiTools: CustomersAiToolDefinition[] = [\n listActivitiesTool,\n listTasksTool,\n listDealCommentsTool,\n manageDealCommentTool,\n manageDealActivityTool,\n listRecordCommentsTool,\n manageRecordCommentTool,\n manageRecordActivityTool,\n]\n\nexport default activitiesTasksAiTools\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["body", "response", "existing"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
|
|
4
|
+
import { CustomerAddress, CustomerTag } from "../data/entities.js";
|
|
5
|
+
import { assertTenantScope } from "./types.js";
|
|
6
|
+
function resolveEm(ctx) {
|
|
7
|
+
return ctx.container.resolve("em");
|
|
8
|
+
}
|
|
9
|
+
function buildScope(ctx, tenantId) {
|
|
10
|
+
return { tenantId, organizationId: ctx.organizationId };
|
|
11
|
+
}
|
|
12
|
+
const listAddressesInput = z.object({
|
|
13
|
+
entityType: z.enum(["person", "company"]).describe("Parent entity kind."),
|
|
14
|
+
entityId: z.string().uuid().describe("Parent person/company entity id."),
|
|
15
|
+
limit: z.number().int().min(1).max(100).optional().describe("Max rows (default 100)."),
|
|
16
|
+
offset: z.number().int().min(0).optional().describe("Rows to skip (default 0).")
|
|
17
|
+
});
|
|
18
|
+
const listAddressesTool = {
|
|
19
|
+
name: "customers.list_addresses",
|
|
20
|
+
displayName: "List addresses",
|
|
21
|
+
description: "List addresses attached to a person or company (tenant + organization scoped). `entityType` is informational; the actual filter is by `entityId`.",
|
|
22
|
+
inputSchema: listAddressesInput,
|
|
23
|
+
// Addresses share the same route-level guard as activities in the existing
|
|
24
|
+
// route handler (`customers.activities.view`).
|
|
25
|
+
requiredFeatures: ["customers.activities.view"],
|
|
26
|
+
tags: ["read", "customers"],
|
|
27
|
+
handler: async (rawInput, ctx) => {
|
|
28
|
+
const { tenantId } = assertTenantScope(ctx);
|
|
29
|
+
const input = listAddressesInput.parse(rawInput);
|
|
30
|
+
const em = resolveEm(ctx);
|
|
31
|
+
const limit = input.limit ?? 100;
|
|
32
|
+
const offset = input.offset ?? 0;
|
|
33
|
+
const where = { tenantId, entity: input.entityId };
|
|
34
|
+
if (ctx.organizationId) where.organizationId = ctx.organizationId;
|
|
35
|
+
const [rows, total] = await Promise.all([
|
|
36
|
+
findWithDecryption(
|
|
37
|
+
em,
|
|
38
|
+
CustomerAddress,
|
|
39
|
+
where,
|
|
40
|
+
{ limit, offset, orderBy: { isPrimary: "desc", createdAt: "desc" } },
|
|
41
|
+
buildScope(ctx, tenantId)
|
|
42
|
+
),
|
|
43
|
+
em.count(CustomerAddress, where)
|
|
44
|
+
]);
|
|
45
|
+
const filtered = rows.filter((row) => row.tenantId === tenantId);
|
|
46
|
+
return {
|
|
47
|
+
entityType: input.entityType,
|
|
48
|
+
entityId: input.entityId,
|
|
49
|
+
items: filtered.map((row) => ({
|
|
50
|
+
id: row.id,
|
|
51
|
+
name: row.name ?? null,
|
|
52
|
+
purpose: row.purpose ?? null,
|
|
53
|
+
companyName: row.companyName ?? null,
|
|
54
|
+
addressLine1: row.addressLine1,
|
|
55
|
+
addressLine2: row.addressLine2 ?? null,
|
|
56
|
+
buildingNumber: row.buildingNumber ?? null,
|
|
57
|
+
flatNumber: row.flatNumber ?? null,
|
|
58
|
+
city: row.city ?? null,
|
|
59
|
+
region: row.region ?? null,
|
|
60
|
+
postalCode: row.postalCode ?? null,
|
|
61
|
+
country: row.country ?? null,
|
|
62
|
+
latitude: row.latitude ?? null,
|
|
63
|
+
longitude: row.longitude ?? null,
|
|
64
|
+
isPrimary: !!row.isPrimary,
|
|
65
|
+
organizationId: row.organizationId ?? null,
|
|
66
|
+
tenantId: row.tenantId ?? null,
|
|
67
|
+
createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null
|
|
68
|
+
})),
|
|
69
|
+
total,
|
|
70
|
+
limit,
|
|
71
|
+
offset
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const listTagsInput = z.object({
|
|
76
|
+
q: z.string().trim().optional().describe("Fuzzy search against tag label / slug. Omit or leave empty to list all."),
|
|
77
|
+
limit: z.number().int().min(1).max(100).optional().describe("Max rows (default 100)."),
|
|
78
|
+
offset: z.number().int().min(0).optional().describe("Rows to skip (default 0).")
|
|
79
|
+
}).passthrough();
|
|
80
|
+
const listTagsTool = {
|
|
81
|
+
name: "customers.list_tags",
|
|
82
|
+
displayName: "List tags",
|
|
83
|
+
description: "List available customer tags (slug, label, color, description) scoped to tenant + organization.",
|
|
84
|
+
inputSchema: listTagsInput,
|
|
85
|
+
// Tag administration routes require `customers.activities.*` in the
|
|
86
|
+
// existing codebase; keep the same least-privilege view feature here.
|
|
87
|
+
requiredFeatures: ["customers.activities.view"],
|
|
88
|
+
tags: ["read", "customers"],
|
|
89
|
+
handler: async (rawInput, ctx) => {
|
|
90
|
+
const { tenantId } = assertTenantScope(ctx);
|
|
91
|
+
const input = listTagsInput.parse(rawInput);
|
|
92
|
+
const em = resolveEm(ctx);
|
|
93
|
+
const limit = input.limit ?? 100;
|
|
94
|
+
const offset = input.offset ?? 0;
|
|
95
|
+
const where = { tenantId };
|
|
96
|
+
if (ctx.organizationId) where.organizationId = ctx.organizationId;
|
|
97
|
+
if (input.q) {
|
|
98
|
+
const pattern = `%${escapeLikePattern(input.q)}%`;
|
|
99
|
+
where.$or = [{ label: { $ilike: pattern } }, { slug: { $ilike: pattern } }];
|
|
100
|
+
}
|
|
101
|
+
const [rows, total] = await Promise.all([
|
|
102
|
+
findWithDecryption(
|
|
103
|
+
em,
|
|
104
|
+
CustomerTag,
|
|
105
|
+
where,
|
|
106
|
+
{ limit, offset, orderBy: { label: "asc" } },
|
|
107
|
+
buildScope(ctx, tenantId)
|
|
108
|
+
),
|
|
109
|
+
em.count(CustomerTag, where)
|
|
110
|
+
]);
|
|
111
|
+
const filtered = rows.filter((row) => row.tenantId === tenantId);
|
|
112
|
+
return {
|
|
113
|
+
items: filtered.map((row) => ({
|
|
114
|
+
id: row.id,
|
|
115
|
+
slug: row.slug,
|
|
116
|
+
label: row.label,
|
|
117
|
+
color: row.color ?? null,
|
|
118
|
+
description: row.description ?? null,
|
|
119
|
+
organizationId: row.organizationId ?? null,
|
|
120
|
+
tenantId: row.tenantId ?? null
|
|
121
|
+
})),
|
|
122
|
+
total,
|
|
123
|
+
limit,
|
|
124
|
+
offset
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const addressesTagsAiTools = [listAddressesTool, listTagsTool];
|
|
129
|
+
var addresses_tags_pack_default = addressesTagsAiTools;
|
|
130
|
+
export {
|
|
131
|
+
addressesTagsAiTools,
|
|
132
|
+
addresses_tags_pack_default as default
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=addresses-tags-pack.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/ai-tools/addresses-tags-pack.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * `customers.list_addresses` + `customers.list_tags` (Phase 1 WS-C, Step 3.9).\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { CustomerAddress, CustomerTag } from '../data/entities'\nimport { assertTenantScope, type CustomersAiToolDefinition, type CustomersToolContext } from './types'\n\nfunction resolveEm(ctx: CustomersToolContext): EntityManager {\n return ctx.container.resolve<EntityManager>('em')\n}\n\nfunction buildScope(ctx: CustomersToolContext, tenantId: string) {\n return { tenantId, organizationId: ctx.organizationId }\n}\n\nconst listAddressesInput = z.object({\n entityType: z.enum(['person', 'company']).describe('Parent entity kind.'),\n entityId: z.string().uuid().describe('Parent person/company entity id.'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n})\n\nconst listAddressesTool: CustomersAiToolDefinition = {\n name: 'customers.list_addresses',\n displayName: 'List addresses',\n description:\n 'List addresses attached to a person or company (tenant + organization scoped). `entityType` is informational; the actual filter is by `entityId`.',\n inputSchema: listAddressesInput,\n // Addresses share the same route-level guard as activities in the existing\n // route handler (`customers.activities.view`).\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listAddressesInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 100\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId, entity: input.entityId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<CustomerAddress>(\n em,\n CustomerAddress,\n where as any,\n { limit, offset, orderBy: { isPrimary: 'desc', createdAt: 'desc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CustomerAddress, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n entityType: input.entityType,\n entityId: input.entityId,\n items: filtered.map((row) => ({\n id: row.id,\n name: row.name ?? null,\n purpose: row.purpose ?? null,\n companyName: row.companyName ?? null,\n addressLine1: row.addressLine1,\n addressLine2: row.addressLine2 ?? null,\n buildingNumber: row.buildingNumber ?? null,\n flatNumber: row.flatNumber ?? null,\n city: row.city ?? null,\n region: row.region ?? null,\n postalCode: row.postalCode ?? null,\n country: row.country ?? null,\n latitude: row.latitude ?? null,\n longitude: row.longitude ?? null,\n isPrimary: !!row.isPrimary,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\nconst listTagsInput = z\n .object({\n q: z.string().trim().optional().describe('Fuzzy search against tag label / slug. Omit or leave empty to list all.'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listTagsTool: CustomersAiToolDefinition = {\n name: 'customers.list_tags',\n displayName: 'List tags',\n description:\n 'List available customer tags (slug, label, color, description) scoped to tenant + organization.',\n inputSchema: listTagsInput,\n // Tag administration routes require `customers.activities.*` in the\n // existing codebase; keep the same least-privilege view feature here.\n requiredFeatures: ['customers.activities.view'],\n tags: ['read', 'customers'],\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listTagsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 100\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n if (input.q) {\n const pattern = `%${escapeLikePattern(input.q)}%`\n where.$or = [{ label: { $ilike: pattern } }, { slug: { $ilike: pattern } }]\n }\n const [rows, total] = await Promise.all([\n findWithDecryption<CustomerTag>(\n em,\n CustomerTag,\n where as any,\n { limit, offset, orderBy: { label: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CustomerTag, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n slug: row.slug,\n label: row.label,\n color: row.color ?? null,\n description: row.description ?? null,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\nexport const addressesTagsAiTools: CustomersAiToolDefinition[] = [listAddressesTool, listTagsTool]\n\nexport default addressesTagsAiTools\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineApiBackedAiTool } from "@open-mercato/ai-assistant/modules/ai_assistant/lib/api-backed-tool";
|
|
3
|
+
import {
|
|
4
|
+
createAiApiOperationRunner
|
|
5
|
+
} from "@open-mercato/ai-assistant/modules/ai_assistant/lib/ai-api-operation-runner";
|
|
6
|
+
import { assertTenantScope } from "./types.js";
|
|
7
|
+
const listCompaniesInput = z.object({
|
|
8
|
+
q: z.string().trim().optional().describe("Search text matched against display name / email / domain. Omit or leave empty to list all."),
|
|
9
|
+
limit: z.number().int().min(1).max(100).optional().describe("Maximum rows to return (default 50, max 100)."),
|
|
10
|
+
offset: z.number().int().min(0).optional().describe("Number of rows to skip (default 0)."),
|
|
11
|
+
tags: z.array(z.string().uuid()).optional().describe("Restrict to companies carrying at least one of these tag ids.")
|
|
12
|
+
}).passthrough();
|
|
13
|
+
const listCompaniesTool = defineApiBackedAiTool({
|
|
14
|
+
name: "customers.list_companies",
|
|
15
|
+
displayName: "List companies",
|
|
16
|
+
description: "Search / list companies for the caller tenant + organization. Returns { items, total, limit, offset }.",
|
|
17
|
+
inputSchema: listCompaniesInput,
|
|
18
|
+
requiredFeatures: ["customers.companies.view"],
|
|
19
|
+
toOperation: (input, ctx) => {
|
|
20
|
+
assertTenantScope(ctx);
|
|
21
|
+
const limit = input.limit ?? 50;
|
|
22
|
+
const offset = input.offset ?? 0;
|
|
23
|
+
const page = Math.floor(offset / limit) + 1;
|
|
24
|
+
const query = {
|
|
25
|
+
page,
|
|
26
|
+
pageSize: limit
|
|
27
|
+
};
|
|
28
|
+
if (input.q?.trim()) query.search = input.q.trim();
|
|
29
|
+
if (input.tags && input.tags.length > 0) query.tagIds = input.tags.join(",");
|
|
30
|
+
const operation = {
|
|
31
|
+
method: "GET",
|
|
32
|
+
path: "/customers/companies",
|
|
33
|
+
query
|
|
34
|
+
};
|
|
35
|
+
return operation;
|
|
36
|
+
},
|
|
37
|
+
mapResponse: (response, input) => {
|
|
38
|
+
const limit = input.limit ?? 50;
|
|
39
|
+
const offset = input.offset ?? 0;
|
|
40
|
+
const data = response.data ?? {};
|
|
41
|
+
const rawItems = Array.isArray(data.items) ? data.items : [];
|
|
42
|
+
return {
|
|
43
|
+
items: rawItems.map((row) => {
|
|
44
|
+
const createdAtRaw = row.created_at ?? row.createdAt ?? null;
|
|
45
|
+
const createdAt = createdAtRaw ? new Date(String(createdAtRaw)).toISOString() : null;
|
|
46
|
+
return {
|
|
47
|
+
id: row.id,
|
|
48
|
+
displayName: row.display_name ?? row.displayName ?? null,
|
|
49
|
+
primaryEmail: row.primary_email ?? row.primaryEmail ?? null,
|
|
50
|
+
primaryPhone: row.primary_phone ?? row.primaryPhone ?? null,
|
|
51
|
+
status: row.status ?? null,
|
|
52
|
+
lifecycleStage: row.lifecycle_stage ?? row.lifecycleStage ?? null,
|
|
53
|
+
source: row.source ?? null,
|
|
54
|
+
ownerUserId: row.owner_user_id ?? row.ownerUserId ?? null,
|
|
55
|
+
organizationId: row.organization_id ?? row.organizationId ?? null,
|
|
56
|
+
tenantId: row.tenant_id ?? row.tenantId ?? null,
|
|
57
|
+
domain: row.domain ?? null,
|
|
58
|
+
websiteUrl: row.website_url ?? row.websiteUrl ?? null,
|
|
59
|
+
industry: row.industry ?? null,
|
|
60
|
+
sizeBucket: row.size_bucket ?? row.sizeBucket ?? null,
|
|
61
|
+
createdAt
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
64
|
+
total: typeof data.total === "number" ? data.total : 0,
|
|
65
|
+
limit,
|
|
66
|
+
offset
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const getCompanyInput = z.object({
|
|
71
|
+
companyId: z.string().uuid().describe("Company entity id (UUID)."),
|
|
72
|
+
includeRelated: z.boolean().optional().describe("When true, include notes, activities, deals, people, addresses, tasks, and tags (each capped at 100).")
|
|
73
|
+
});
|
|
74
|
+
function toIsoCompany(value) {
|
|
75
|
+
if (!value) return null;
|
|
76
|
+
const dt = value instanceof Date ? value : new Date(String(value));
|
|
77
|
+
if (Number.isNaN(dt.getTime())) return null;
|
|
78
|
+
return dt.toISOString();
|
|
79
|
+
}
|
|
80
|
+
const getCompanyTool = {
|
|
81
|
+
name: "customers.get_company",
|
|
82
|
+
displayName: "Get company",
|
|
83
|
+
description: "Fetch a company customer record by id with profile fields and (optionally) notes, activities, deals, people, addresses, tasks, tags, and custom fields. Returns { found: false } when outside tenant/org scope.",
|
|
84
|
+
inputSchema: getCompanyInput,
|
|
85
|
+
requiredFeatures: ["customers.companies.view"],
|
|
86
|
+
tags: ["read", "customers"],
|
|
87
|
+
handler: async (rawInput, ctx) => {
|
|
88
|
+
const { tenantId: _tenantId } = assertTenantScope(ctx);
|
|
89
|
+
void _tenantId;
|
|
90
|
+
const input = getCompanyInput.parse(rawInput);
|
|
91
|
+
const includeRelated = !!input.includeRelated;
|
|
92
|
+
const operation = {
|
|
93
|
+
method: "GET",
|
|
94
|
+
path: `/customers/companies/${input.companyId}`
|
|
95
|
+
};
|
|
96
|
+
if (includeRelated) {
|
|
97
|
+
operation.query = {
|
|
98
|
+
include: "addresses,comments,activities,interactions,deals,todos,people"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const runner = createAiApiOperationRunner(ctx);
|
|
102
|
+
const response = await runner.run(operation);
|
|
103
|
+
if (!response.success) {
|
|
104
|
+
if (response.statusCode === 404 || response.statusCode === 403) {
|
|
105
|
+
return { found: false, companyId: input.companyId };
|
|
106
|
+
}
|
|
107
|
+
throw new Error(response.error ?? `Failed to fetch company ${input.companyId}`);
|
|
108
|
+
}
|
|
109
|
+
const data = response.data ?? {};
|
|
110
|
+
const companyRow = data.company ?? null;
|
|
111
|
+
if (!companyRow) {
|
|
112
|
+
return { found: false, companyId: input.companyId };
|
|
113
|
+
}
|
|
114
|
+
const profileRow = data.profile ?? null;
|
|
115
|
+
const customFields = data.customFields ?? {};
|
|
116
|
+
let related = null;
|
|
117
|
+
if (includeRelated) {
|
|
118
|
+
const addresses = Array.isArray(data.addresses) ? data.addresses : [];
|
|
119
|
+
const activities = Array.isArray(data.activities) ? data.activities : [];
|
|
120
|
+
const notes = Array.isArray(data.comments) ? data.comments : [];
|
|
121
|
+
const todos = Array.isArray(data.todos) ? data.todos : [];
|
|
122
|
+
const interactions = Array.isArray(data.interactions) ? data.interactions : [];
|
|
123
|
+
const tagsRows = Array.isArray(data.tags) ? data.tags : [];
|
|
124
|
+
const dealsRows = Array.isArray(data.deals) ? data.deals : [];
|
|
125
|
+
const peopleRows = Array.isArray(data.people) ? data.people : [];
|
|
126
|
+
related = {
|
|
127
|
+
addresses: addresses.map((address) => ({
|
|
128
|
+
id: address.id,
|
|
129
|
+
name: address.name ?? null,
|
|
130
|
+
purpose: address.purpose ?? null,
|
|
131
|
+
addressLine1: address.addressLine1 ?? null,
|
|
132
|
+
addressLine2: address.addressLine2 ?? null,
|
|
133
|
+
city: address.city ?? null,
|
|
134
|
+
region: address.region ?? null,
|
|
135
|
+
postalCode: address.postalCode ?? null,
|
|
136
|
+
country: address.country ?? null,
|
|
137
|
+
isPrimary: !!address.isPrimary
|
|
138
|
+
})),
|
|
139
|
+
activities: activities.map((activity) => ({
|
|
140
|
+
id: activity.id,
|
|
141
|
+
activityType: activity.activityType,
|
|
142
|
+
subject: activity.subject ?? null,
|
|
143
|
+
body: activity.body ?? null,
|
|
144
|
+
occurredAt: toIsoCompany(activity.occurredAt),
|
|
145
|
+
createdAt: toIsoCompany(activity.createdAt)
|
|
146
|
+
})),
|
|
147
|
+
notes: notes.map((comment) => ({
|
|
148
|
+
id: comment.id,
|
|
149
|
+
body: comment.body,
|
|
150
|
+
authorUserId: comment.authorUserId ?? null,
|
|
151
|
+
createdAt: toIsoCompany(comment.createdAt)
|
|
152
|
+
})),
|
|
153
|
+
tasks: todos.map((task) => ({
|
|
154
|
+
id: task.id,
|
|
155
|
+
todoId: task.todoId ?? task.id,
|
|
156
|
+
todoSource: task.todoSource ?? null,
|
|
157
|
+
createdAt: toIsoCompany(task.createdAt)
|
|
158
|
+
})),
|
|
159
|
+
interactions: interactions.map((interaction) => ({
|
|
160
|
+
id: interaction.id,
|
|
161
|
+
interactionType: interaction.interactionType,
|
|
162
|
+
title: interaction.title ?? null,
|
|
163
|
+
status: interaction.status,
|
|
164
|
+
scheduledAt: toIsoCompany(interaction.scheduledAt),
|
|
165
|
+
occurredAt: toIsoCompany(interaction.occurredAt)
|
|
166
|
+
})),
|
|
167
|
+
tags: tagsRows.map((tag) => {
|
|
168
|
+
if (!tag || typeof tag !== "object") return null;
|
|
169
|
+
const id = typeof tag.id === "string" ? tag.id : null;
|
|
170
|
+
const label = typeof tag.label === "string" ? tag.label : null;
|
|
171
|
+
if (!id || !label) return null;
|
|
172
|
+
const slug = typeof tag.slug === "string" ? tag.slug : label;
|
|
173
|
+
const color = typeof tag.color === "string" ? tag.color : null;
|
|
174
|
+
return { id, slug, label, color };
|
|
175
|
+
}).filter(
|
|
176
|
+
(entry) => entry !== null
|
|
177
|
+
),
|
|
178
|
+
deals: dealsRows.map((deal) => {
|
|
179
|
+
if (!deal || typeof deal !== "object") return null;
|
|
180
|
+
const id = typeof deal.id === "string" ? deal.id : null;
|
|
181
|
+
if (!id) return null;
|
|
182
|
+
return {
|
|
183
|
+
id,
|
|
184
|
+
title: typeof deal.title === "string" ? deal.title : "",
|
|
185
|
+
status: typeof deal.status === "string" ? deal.status : null,
|
|
186
|
+
pipelineStageId: typeof deal.pipelineStageId === "string" ? deal.pipelineStageId : null,
|
|
187
|
+
valueAmount: typeof deal.valueAmount === "string" ? deal.valueAmount : deal.valueAmount === null || deal.valueAmount === void 0 ? null : String(deal.valueAmount),
|
|
188
|
+
valueCurrency: typeof deal.valueCurrency === "string" ? deal.valueCurrency : null
|
|
189
|
+
};
|
|
190
|
+
}).filter(
|
|
191
|
+
(value) => value !== null
|
|
192
|
+
),
|
|
193
|
+
people: peopleRows.map((person) => {
|
|
194
|
+
if (!person || typeof person !== "object") return null;
|
|
195
|
+
const id = typeof person.id === "string" ? person.id : null;
|
|
196
|
+
const displayName = typeof person.displayName === "string" ? person.displayName : null;
|
|
197
|
+
if (!id || !displayName) return null;
|
|
198
|
+
return {
|
|
199
|
+
id,
|
|
200
|
+
displayName,
|
|
201
|
+
primaryEmail: typeof person.primaryEmail === "string" ? person.primaryEmail : null,
|
|
202
|
+
primaryPhone: typeof person.primaryPhone === "string" ? person.primaryPhone : null,
|
|
203
|
+
jobTitle: typeof person.jobTitle === "string" ? person.jobTitle : null,
|
|
204
|
+
department: typeof person.department === "string" ? person.department : null
|
|
205
|
+
};
|
|
206
|
+
}).filter(
|
|
207
|
+
(value) => value !== null
|
|
208
|
+
)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
found: true,
|
|
213
|
+
company: {
|
|
214
|
+
id: companyRow.id,
|
|
215
|
+
displayName: companyRow.displayName ?? null,
|
|
216
|
+
description: companyRow.description ?? null,
|
|
217
|
+
primaryEmail: companyRow.primaryEmail ?? null,
|
|
218
|
+
primaryPhone: companyRow.primaryPhone ?? null,
|
|
219
|
+
status: companyRow.status ?? null,
|
|
220
|
+
lifecycleStage: companyRow.lifecycleStage ?? null,
|
|
221
|
+
source: companyRow.source ?? null,
|
|
222
|
+
ownerUserId: companyRow.ownerUserId ?? null,
|
|
223
|
+
organizationId: companyRow.organizationId ?? null,
|
|
224
|
+
tenantId: companyRow.tenantId ?? null,
|
|
225
|
+
createdAt: toIsoCompany(companyRow.createdAt),
|
|
226
|
+
updatedAt: toIsoCompany(companyRow.updatedAt)
|
|
227
|
+
},
|
|
228
|
+
profile: profileRow ? {
|
|
229
|
+
id: profileRow.id,
|
|
230
|
+
legalName: profileRow.legalName ?? null,
|
|
231
|
+
brandName: profileRow.brandName ?? null,
|
|
232
|
+
domain: profileRow.domain ?? null,
|
|
233
|
+
websiteUrl: profileRow.websiteUrl ?? null,
|
|
234
|
+
industry: profileRow.industry ?? null,
|
|
235
|
+
sizeBucket: profileRow.sizeBucket ?? null,
|
|
236
|
+
annualRevenue: profileRow.annualRevenue ?? null
|
|
237
|
+
} : null,
|
|
238
|
+
customFields,
|
|
239
|
+
related
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const companiesAiTools = [listCompaniesTool, getCompanyTool];
|
|
244
|
+
var companies_pack_default = companiesAiTools;
|
|
245
|
+
export {
|
|
246
|
+
companiesAiTools,
|
|
247
|
+
companies_pack_default as default
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=companies-pack.js.map
|