robotrock 1.3.1 → 1.3.2
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/dist/agent-admin.d.ts +2 -1
- package/dist/agent-admin.js +44 -0
- package/dist/agent-admin.js.map +1 -1
- package/dist/ai/index.d.ts +6 -6
- package/dist/ai/index.js +34 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/trigger.d.ts +4 -4
- package/dist/ai/trigger.js +34 -2
- package/dist/ai/trigger.js.map +1 -1
- package/dist/ai/workflow.d.ts +4 -4
- package/dist/ai/workflow.js +34 -2
- package/dist/ai/workflow.js.map +1 -1
- package/dist/{auth-headers-qL-ZeEtd.d.ts → chat-correlation-BsHD-tOQ.d.ts} +6 -1
- package/dist/{client-YO9Y1rkH.d.ts → client-CZ5eWH5v.d.ts} +6 -1
- package/dist/eve/agent/index.d.ts +4 -4
- package/dist/eve/agent/index.js +127 -238
- package/dist/eve/agent/index.js.map +1 -1
- package/dist/eve/index.d.ts +3 -3
- package/dist/eve/index.js +1 -26
- package/dist/eve/index.js.map +1 -1
- package/dist/eve/tools/admin/assign-tasks.js +44 -0
- package/dist/eve/tools/admin/assign-tasks.js.map +1 -1
- package/dist/eve/tools/admin/manage-groups.js +44 -0
- package/dist/eve/tools/admin/manage-groups.js.map +1 -1
- package/dist/eve/tools/admin/manage-team-members.js +44 -0
- package/dist/eve/tools/admin/manage-team-members.js.map +1 -1
- package/dist/eve/tools/admin/query-tasks.js +44 -0
- package/dist/eve/tools/admin/query-tasks.js.map +1 -1
- package/dist/eve/tools/identity/index.d.ts +1 -1
- package/dist/eve/tools/identity/index.js +2 -0
- package/dist/eve/tools/identity/index.js.map +1 -1
- package/dist/eve/tools/identity/my-access.js +2 -0
- package/dist/eve/tools/identity/my-access.js.map +1 -1
- package/dist/eve/tools/identity/whoami.d.ts +1 -1
- package/dist/eve/tools/identity/whoami.js +2 -0
- package/dist/eve/tools/identity/whoami.js.map +1 -1
- package/dist/eve/tools/inbox/create-task.d.ts +1 -1
- package/dist/eve/tools/inbox/create-task.js +56 -31
- package/dist/eve/tools/inbox/create-task.js.map +1 -1
- package/dist/eve/tools/inbox/index.d.ts +1 -1
- package/dist/eve/tools/inbox/index.js +56 -31
- package/dist/eve/tools/inbox/index.js.map +1 -1
- package/dist/eve/tools/index.d.ts +1 -1
- package/dist/eve/tools/index.js +68 -31
- package/dist/eve/tools/index.js.map +1 -1
- package/dist/{index-DoQN48Bm.d.ts → index-DePyAbTI.d.ts} +6 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/dist/{tenant-5YKDrdC-.d.ts → tenant-DmN0MMyC.d.ts} +1 -0
- package/dist/{tool-approval-bridge-aMA79Z1B.d.ts → tool-approval-bridge-DnGaRES4.d.ts} +1 -1
- package/dist/trigger/index.d.ts +2 -2
- package/dist/trigger/index.js +26 -2
- package/dist/trigger/index.js.map +1 -1
- package/dist/{trigger-CXrbKVCL.d.ts → trigger--qTIK0s6.d.ts} +2 -2
- package/dist/workflow/index.d.ts +2 -2
- package/dist/workflow/index.js +26 -2
- package/dist/workflow/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/eve/tools/identity/whoami.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts","../../../../src/eve/tools/identity/my-access.ts","../../../../src/eve/tool-result-display.ts","../../../../src/eve/tool-display-format.ts","../../../../src/eve/tools/identity/format-my-access.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n WHOAMI_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\n\nexport const WHOAMI_TOOL_NAME = \"whoami\";\n\nexport const whoamiInputSchema = z.object({});\n\nexport function defineWhoamiTool() {\n return defineTool({\n description:\n \"Return the authenticated RobotRock user, tenant, role, and group memberships for the current chat session. \" +\n \"The dashboard renders a profile card — do not repeat name, email, workspace, role, or groups in your reply; only add context not shown in the card.\",\n inputSchema: whoamiInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent identity details.\"\n );\n }\n\n return withReplyGuidance(\n {\n authenticated: true as const,\n userId: caller.userId,\n name: caller.name,\n email: caller.email,\n tenantSlug: caller.tenantSlug,\n role: caller.role,\n isAdmin: caller.isAdmin,\n groups: caller.groups.map((slug) => ({ slug })),\n ...(caller.workosUserId ? { workosUserId: caller.workosUserId } : {}),\n sessionId: ctx.session.id,\n },\n WHOAMI_REPLY_GUIDANCE\n );\n },\n });\n}\n\nexport const whoamiTool = defineWhoamiTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n","import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n MY_ACCESS_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\nimport { formatMyAccessResult } from \"./format-my-access.js\";\n\n/** Eve tool slug — must match the agent tool filename `get_my_access.ts`. */\nexport const MY_ACCESS_TOOL_NAME = \"get_my_access\";\n\nexport const myAccessInputSchema = z.object({});\n\nfunction tenantCapabilities(isAdmin: boolean) {\n return {\n manageSettings: isAdmin,\n manageBilling: isAdmin,\n manageTeam: isAdmin,\n manageIntegrations: isAdmin,\n };\n}\n\nexport function defineMyAccessTool() {\n return defineTool({\n description:\n \"Check the authenticated user's tenant role, group memberships, and admin capabilities in RobotRock. \" +\n \"Results render as an access card in chat — do not restate role, groups, or capabilities in your reply.\",\n inputSchema: myAccessInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent access details.\"\n );\n }\n\n return formatMyAccessResult({\n tenantSlug: caller.tenantSlug,\n tenantRole: caller.role,\n isTenantAdmin: caller.isAdmin,\n groupSlugs: caller.groups,\n capabilities: tenantCapabilities(caller.isAdmin),\n email: caller.email,\n });\n },\n });\n}\n\nexport const myAccessTool = defineMyAccessTool();\n","import type {\n ToolDisplayEnvelope,\n ToolDisplayMetadata,\n UiSchema,\n} from \"@robotrock/core/schemas\";\n\nexport type ToolDisplayResultOptions = {\n widget?: string;\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n};\n\n/**\n * Wrap tool execute output with optional dashboard display hints.\n * Agents return plain JSON by default; use this when schema-driven widgets help.\n */\nexport function toolDisplayResult<T extends Record<string, unknown>>(\n data: T,\n options?: ToolDisplayResultOptions\n): ToolDisplayEnvelope<T> {\n const display: ToolDisplayMetadata | undefined =\n options?.widget || options?.title || options?.description\n ? {\n ...(options.widget ? { widget: options.widget } : {}),\n ...(options.title ? { title: options.title } : {}),\n ...(options.description ? { description: options.description } : {}),\n }\n : undefined;\n\n return {\n ...(display ? { display } : {}),\n data,\n ...(options?.ui ? { ui: options.ui } : {}),\n };\n}\n","import type { UiSchema } from \"@robotrock/core/schemas\";\nimport { toolDisplayResult } from \"./tool-result-display.js\";\nimport { withReplyGuidance } from \"./tool-reply-guidance.js\";\n\nexport type FormatToolObjectResultOptions = {\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n replyGuidance: string;\n};\n\nexport type FormatToolListResultOptions = {\n title: string;\n itemUi?: UiSchema;\n /** Composite widget for each row (uses list layout instead of table). */\n itemWidget?: string;\n replyGuidance: string;\n};\n\n/** Wrap a flat object for schema-driven chat rendering. */\nexport function formatToolObjectResult(\n data: Record<string, unknown>,\n options: FormatToolObjectResultOptions\n) {\n return withReplyGuidance(\n toolDisplayResult(data, {\n title: options.title,\n description: options.description,\n ui: options.ui,\n }),\n options.replyGuidance\n );\n}\n\n/** Wrap an array field as a list widget inside a display envelope. */\nexport function formatToolListResult(\n listKey: string,\n items: Record<string, unknown>[],\n options: FormatToolListResultOptions\n) {\n const listFieldUi = options.itemWidget\n ? {\n \"ui:widget\": \"list\" as const,\n items: {\n \"ui:widget\": options.itemWidget,\n ...(options.itemUi ?? {}),\n },\n }\n : {\n \"ui:widget\": \"table\" as const,\n ...(options.itemUi ? { items: options.itemUi } : {}),\n };\n\n return formatToolObjectResult(\n { [listKey]: items },\n {\n title: options.title,\n ui: {\n [listKey]: listFieldUi,\n },\n replyGuidance: options.replyGuidance,\n }\n );\n}\n\nexport function isAgentAdminErrorResult(\n result: unknown\n): result is { ok: false; message: string } {\n return (\n typeof result === \"object\" &&\n result !== null &&\n (result as { ok?: unknown }).ok === false &&\n typeof (result as { message?: unknown }).message === \"string\"\n );\n}\n","import { formatToolObjectResult } from \"../../tool-display-format.js\";\nimport { MY_ACCESS_REPLY_GUIDANCE } from \"../../tool-reply-guidance.js\";\n\ntype MyAccessResultInput = {\n tenantSlug: string;\n tenantRole?: string;\n isTenantAdmin: boolean;\n groupSlugs: string[];\n capabilities: {\n manageSettings: boolean;\n manageBilling: boolean;\n manageTeam: boolean;\n manageIntegrations: boolean;\n };\n email?: string;\n};\n\nconst ACCESS_FIELD_UI = {\n workspace: { \"ui:title\": \"Workspace\" },\n role: { \"ui:title\": \"Role\" },\n tenantAdmin: { \"ui:title\": \"Tenant admin\", \"ui:widget\": \"boolean\" },\n groups: { \"ui:title\": \"Groups\" },\n manageSettings: { \"ui:title\": \"Manage settings\", \"ui:widget\": \"boolean\" },\n manageBilling: { \"ui:title\": \"Manage billing\", \"ui:widget\": \"boolean\" },\n manageTeam: { \"ui:title\": \"Manage team\", \"ui:widget\": \"boolean\" },\n manageIntegrations: {\n \"ui:title\": \"Manage integrations\",\n \"ui:widget\": \"boolean\",\n },\n email: { \"ui:title\": \"Email\" },\n} as const;\n\nexport function formatMyAccessResult(input: MyAccessResultInput) {\n return formatToolObjectResult(\n {\n access: {\n workspace: input.tenantSlug,\n role: input.tenantRole ?? \"member\",\n tenantAdmin: input.isTenantAdmin,\n groups: input.groupSlugs.join(\", \") || \"—\",\n manageSettings: input.capabilities.manageSettings,\n manageBilling: input.capabilities.manageBilling,\n manageTeam: input.capabilities.manageTeam,\n manageIntegrations: input.capabilities.manageIntegrations,\n ...(input.email ? { email: input.email } : {}),\n },\n },\n {\n ui: {\n access: {\n \"ui:widget\": \"access\",\n \"ui:title\": \"Your access\",\n items: ACCESS_FIELD_UI,\n },\n },\n replyGuidance: MY_ACCESS_REPLY_GUIDANCE,\n }\n );\n}\n"],"mappings":"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","names":["defineTool","z","z","defineTool"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/eve/tools/identity/whoami.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts","../../../../src/eve/tools/identity/my-access.ts","../../../../src/eve/tool-result-display.ts","../../../../src/eve/tool-display-format.ts","../../../../src/eve/tools/identity/format-my-access.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n WHOAMI_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\n\nexport const WHOAMI_TOOL_NAME = \"whoami\";\n\nexport const whoamiInputSchema = z.object({});\n\nexport function defineWhoamiTool() {\n return defineTool({\n description:\n \"Return the authenticated RobotRock user, tenant, role, and group memberships for the current chat session. \" +\n \"The dashboard renders a profile card — do not repeat name, email, workspace, role, or groups in your reply; only add context not shown in the card.\",\n inputSchema: whoamiInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent identity details.\"\n );\n }\n\n return withReplyGuidance(\n {\n authenticated: true as const,\n userId: caller.userId,\n name: caller.name,\n email: caller.email,\n tenantSlug: caller.tenantSlug,\n role: caller.role,\n isAdmin: caller.isAdmin,\n groups: caller.groups.map((slug) => ({ slug })),\n ...(caller.workosUserId ? { workosUserId: caller.workosUserId } : {}),\n sessionId: ctx.session.id,\n },\n WHOAMI_REPLY_GUIDANCE\n );\n },\n });\n}\n\nexport const whoamiTool = defineWhoamiTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n chatId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const chatId = readStringAttribute(caller.attributes, \"chatId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n ...(chatId ? { chatId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n","import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n MY_ACCESS_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\nimport { formatMyAccessResult } from \"./format-my-access.js\";\n\n/** Eve tool slug — must match the agent tool filename `get_my_access.ts`. */\nexport const MY_ACCESS_TOOL_NAME = \"get_my_access\";\n\nexport const myAccessInputSchema = z.object({});\n\nfunction tenantCapabilities(isAdmin: boolean) {\n return {\n manageSettings: isAdmin,\n manageBilling: isAdmin,\n manageTeam: isAdmin,\n manageIntegrations: isAdmin,\n };\n}\n\nexport function defineMyAccessTool() {\n return defineTool({\n description:\n \"Check the authenticated user's tenant role, group memberships, and admin capabilities in RobotRock. \" +\n \"Results render as an access card in chat — do not restate role, groups, or capabilities in your reply.\",\n inputSchema: myAccessInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent access details.\"\n );\n }\n\n return formatMyAccessResult({\n tenantSlug: caller.tenantSlug,\n tenantRole: caller.role,\n isTenantAdmin: caller.isAdmin,\n groupSlugs: caller.groups,\n capabilities: tenantCapabilities(caller.isAdmin),\n email: caller.email,\n });\n },\n });\n}\n\nexport const myAccessTool = defineMyAccessTool();\n","import type {\n ToolDisplayEnvelope,\n ToolDisplayMetadata,\n UiSchema,\n} from \"@robotrock/core/schemas\";\n\nexport type ToolDisplayResultOptions = {\n widget?: string;\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n};\n\n/**\n * Wrap tool execute output with optional dashboard display hints.\n * Agents return plain JSON by default; use this when schema-driven widgets help.\n */\nexport function toolDisplayResult<T extends Record<string, unknown>>(\n data: T,\n options?: ToolDisplayResultOptions\n): ToolDisplayEnvelope<T> {\n const display: ToolDisplayMetadata | undefined =\n options?.widget || options?.title || options?.description\n ? {\n ...(options.widget ? { widget: options.widget } : {}),\n ...(options.title ? { title: options.title } : {}),\n ...(options.description ? { description: options.description } : {}),\n }\n : undefined;\n\n return {\n ...(display ? { display } : {}),\n data,\n ...(options?.ui ? { ui: options.ui } : {}),\n };\n}\n","import type { UiSchema } from \"@robotrock/core/schemas\";\nimport { toolDisplayResult } from \"./tool-result-display.js\";\nimport { withReplyGuidance } from \"./tool-reply-guidance.js\";\n\nexport type FormatToolObjectResultOptions = {\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n replyGuidance: string;\n};\n\nexport type FormatToolListResultOptions = {\n title: string;\n itemUi?: UiSchema;\n /** Composite widget for each row (uses list layout instead of table). */\n itemWidget?: string;\n replyGuidance: string;\n};\n\n/** Wrap a flat object for schema-driven chat rendering. */\nexport function formatToolObjectResult(\n data: Record<string, unknown>,\n options: FormatToolObjectResultOptions\n) {\n return withReplyGuidance(\n toolDisplayResult(data, {\n title: options.title,\n description: options.description,\n ui: options.ui,\n }),\n options.replyGuidance\n );\n}\n\n/** Wrap an array field as a list widget inside a display envelope. */\nexport function formatToolListResult(\n listKey: string,\n items: Record<string, unknown>[],\n options: FormatToolListResultOptions\n) {\n const listFieldUi = options.itemWidget\n ? {\n \"ui:widget\": \"list\" as const,\n items: {\n \"ui:widget\": options.itemWidget,\n ...(options.itemUi ?? {}),\n },\n }\n : {\n \"ui:widget\": \"table\" as const,\n ...(options.itemUi ? { items: options.itemUi } : {}),\n };\n\n return formatToolObjectResult(\n { [listKey]: items },\n {\n title: options.title,\n ui: {\n [listKey]: listFieldUi,\n },\n replyGuidance: options.replyGuidance,\n }\n );\n}\n\nexport function isAgentAdminErrorResult(\n result: unknown\n): result is { ok: false; message: string } {\n return (\n typeof result === \"object\" &&\n result !== null &&\n (result as { ok?: unknown }).ok === false &&\n typeof (result as { message?: unknown }).message === \"string\"\n );\n}\n","import { formatToolObjectResult } from \"../../tool-display-format.js\";\nimport { MY_ACCESS_REPLY_GUIDANCE } from \"../../tool-reply-guidance.js\";\n\ntype MyAccessResultInput = {\n tenantSlug: string;\n tenantRole?: string;\n isTenantAdmin: boolean;\n groupSlugs: string[];\n capabilities: {\n manageSettings: boolean;\n manageBilling: boolean;\n manageTeam: boolean;\n manageIntegrations: boolean;\n };\n email?: string;\n};\n\nconst ACCESS_FIELD_UI = {\n workspace: { \"ui:title\": \"Workspace\" },\n role: { \"ui:title\": \"Role\" },\n tenantAdmin: { \"ui:title\": \"Tenant admin\", \"ui:widget\": \"boolean\" },\n groups: { \"ui:title\": \"Groups\" },\n manageSettings: { \"ui:title\": \"Manage settings\", \"ui:widget\": \"boolean\" },\n manageBilling: { \"ui:title\": \"Manage billing\", \"ui:widget\": \"boolean\" },\n manageTeam: { \"ui:title\": \"Manage team\", \"ui:widget\": \"boolean\" },\n manageIntegrations: {\n \"ui:title\": \"Manage integrations\",\n \"ui:widget\": \"boolean\",\n },\n email: { \"ui:title\": \"Email\" },\n} as const;\n\nexport function formatMyAccessResult(input: MyAccessResultInput) {\n return formatToolObjectResult(\n {\n access: {\n workspace: input.tenantSlug,\n role: input.tenantRole ?? \"member\",\n tenantAdmin: input.isTenantAdmin,\n groups: input.groupSlugs.join(\", \") || \"—\",\n manageSettings: input.capabilities.manageSettings,\n manageBilling: input.capabilities.manageBilling,\n manageTeam: input.capabilities.manageTeam,\n manageIntegrations: input.capabilities.manageIntegrations,\n ...(input.email ? { email: input.email } : {}),\n },\n },\n {\n ui: {\n access: {\n \"ui:widget\": \"access\",\n \"ui:title\": \"Your access\",\n items: ACCESS_FIELD_UI,\n },\n },\n replyGuidance: MY_ACCESS_REPLY_GUIDANCE,\n }\n );\n}\n"],"mappings":"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","names":["defineTool","z","z","defineTool"]}
|
|
@@ -47,6 +47,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
47
47
|
}
|
|
48
48
|
const workosUserId = readStringAttribute(caller.attributes, "workosUserId");
|
|
49
49
|
const connectionId = readStringAttribute(caller.attributes, "connectionId");
|
|
50
|
+
const chatId = readStringAttribute(caller.attributes, "chatId");
|
|
50
51
|
const groups = readStringArrayAttribute(caller.attributes, "groups");
|
|
51
52
|
return {
|
|
52
53
|
userId: caller.principalId,
|
|
@@ -54,6 +55,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
54
55
|
name: name ?? email.split("@")[0] ?? email,
|
|
55
56
|
tenantSlug,
|
|
56
57
|
...connectionId ? { connectionId } : {},
|
|
58
|
+
...chatId ? { chatId } : {},
|
|
57
59
|
role,
|
|
58
60
|
isAdmin: role === "admin",
|
|
59
61
|
groups,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/eve/tools/identity/my-access.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts","../../../../src/eve/tool-result-display.ts","../../../../src/eve/tool-display-format.ts","../../../../src/eve/tools/identity/format-my-access.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n MY_ACCESS_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\nimport { formatMyAccessResult } from \"./format-my-access.js\";\n\n/** Eve tool slug — must match the agent tool filename `get_my_access.ts`. */\nexport const MY_ACCESS_TOOL_NAME = \"get_my_access\";\n\nexport const myAccessInputSchema = z.object({});\n\nfunction tenantCapabilities(isAdmin: boolean) {\n return {\n manageSettings: isAdmin,\n manageBilling: isAdmin,\n manageTeam: isAdmin,\n manageIntegrations: isAdmin,\n };\n}\n\nexport function defineMyAccessTool() {\n return defineTool({\n description:\n \"Check the authenticated user's tenant role, group memberships, and admin capabilities in RobotRock. \" +\n \"Results render as an access card in chat — do not restate role, groups, or capabilities in your reply.\",\n inputSchema: myAccessInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent access details.\"\n );\n }\n\n return formatMyAccessResult({\n tenantSlug: caller.tenantSlug,\n tenantRole: caller.role,\n isTenantAdmin: caller.isAdmin,\n groupSlugs: caller.groups,\n capabilities: tenantCapabilities(caller.isAdmin),\n email: caller.email,\n });\n },\n });\n}\n\nexport const myAccessTool = defineMyAccessTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n","import type {\n ToolDisplayEnvelope,\n ToolDisplayMetadata,\n UiSchema,\n} from \"@robotrock/core/schemas\";\n\nexport type ToolDisplayResultOptions = {\n widget?: string;\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n};\n\n/**\n * Wrap tool execute output with optional dashboard display hints.\n * Agents return plain JSON by default; use this when schema-driven widgets help.\n */\nexport function toolDisplayResult<T extends Record<string, unknown>>(\n data: T,\n options?: ToolDisplayResultOptions\n): ToolDisplayEnvelope<T> {\n const display: ToolDisplayMetadata | undefined =\n options?.widget || options?.title || options?.description\n ? {\n ...(options.widget ? { widget: options.widget } : {}),\n ...(options.title ? { title: options.title } : {}),\n ...(options.description ? { description: options.description } : {}),\n }\n : undefined;\n\n return {\n ...(display ? { display } : {}),\n data,\n ...(options?.ui ? { ui: options.ui } : {}),\n };\n}\n","import type { UiSchema } from \"@robotrock/core/schemas\";\nimport { toolDisplayResult } from \"./tool-result-display.js\";\nimport { withReplyGuidance } from \"./tool-reply-guidance.js\";\n\nexport type FormatToolObjectResultOptions = {\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n replyGuidance: string;\n};\n\nexport type FormatToolListResultOptions = {\n title: string;\n itemUi?: UiSchema;\n /** Composite widget for each row (uses list layout instead of table). */\n itemWidget?: string;\n replyGuidance: string;\n};\n\n/** Wrap a flat object for schema-driven chat rendering. */\nexport function formatToolObjectResult(\n data: Record<string, unknown>,\n options: FormatToolObjectResultOptions\n) {\n return withReplyGuidance(\n toolDisplayResult(data, {\n title: options.title,\n description: options.description,\n ui: options.ui,\n }),\n options.replyGuidance\n );\n}\n\n/** Wrap an array field as a list widget inside a display envelope. */\nexport function formatToolListResult(\n listKey: string,\n items: Record<string, unknown>[],\n options: FormatToolListResultOptions\n) {\n const listFieldUi = options.itemWidget\n ? {\n \"ui:widget\": \"list\" as const,\n items: {\n \"ui:widget\": options.itemWidget,\n ...(options.itemUi ?? {}),\n },\n }\n : {\n \"ui:widget\": \"table\" as const,\n ...(options.itemUi ? { items: options.itemUi } : {}),\n };\n\n return formatToolObjectResult(\n { [listKey]: items },\n {\n title: options.title,\n ui: {\n [listKey]: listFieldUi,\n },\n replyGuidance: options.replyGuidance,\n }\n );\n}\n\nexport function isAgentAdminErrorResult(\n result: unknown\n): result is { ok: false; message: string } {\n return (\n typeof result === \"object\" &&\n result !== null &&\n (result as { ok?: unknown }).ok === false &&\n typeof (result as { message?: unknown }).message === \"string\"\n );\n}\n","import { formatToolObjectResult } from \"../../tool-display-format.js\";\nimport { MY_ACCESS_REPLY_GUIDANCE } from \"../../tool-reply-guidance.js\";\n\ntype MyAccessResultInput = {\n tenantSlug: string;\n tenantRole?: string;\n isTenantAdmin: boolean;\n groupSlugs: string[];\n capabilities: {\n manageSettings: boolean;\n manageBilling: boolean;\n manageTeam: boolean;\n manageIntegrations: boolean;\n };\n email?: string;\n};\n\nconst ACCESS_FIELD_UI = {\n workspace: { \"ui:title\": \"Workspace\" },\n role: { \"ui:title\": \"Role\" },\n tenantAdmin: { \"ui:title\": \"Tenant admin\", \"ui:widget\": \"boolean\" },\n groups: { \"ui:title\": \"Groups\" },\n manageSettings: { \"ui:title\": \"Manage settings\", \"ui:widget\": \"boolean\" },\n manageBilling: { \"ui:title\": \"Manage billing\", \"ui:widget\": \"boolean\" },\n manageTeam: { \"ui:title\": \"Manage team\", \"ui:widget\": \"boolean\" },\n manageIntegrations: {\n \"ui:title\": \"Manage integrations\",\n \"ui:widget\": \"boolean\",\n },\n email: { \"ui:title\": \"Email\" },\n} as const;\n\nexport function formatMyAccessResult(input: MyAccessResultInput) {\n return formatToolObjectResult(\n {\n access: {\n workspace: input.tenantSlug,\n role: input.tenantRole ?? \"member\",\n tenantAdmin: input.isTenantAdmin,\n groups: input.groupSlugs.join(\", \") || \"—\",\n manageSettings: input.capabilities.manageSettings,\n manageBilling: input.capabilities.manageBilling,\n manageTeam: input.capabilities.manageTeam,\n manageIntegrations: input.capabilities.manageIntegrations,\n ...(input.email ? { email: input.email } : {}),\n },\n },\n {\n ui: {\n access: {\n \"ui:widget\": \"access\",\n \"ui:title\": \"Your access\",\n items: ACCESS_FIELD_UI,\n },\n },\n replyGuidance: MY_ACCESS_REPLY_GUIDANCE,\n }\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/eve/tools/identity/my-access.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts","../../../../src/eve/tool-result-display.ts","../../../../src/eve/tool-display-format.ts","../../../../src/eve/tools/identity/format-my-access.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n MY_ACCESS_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\nimport { formatMyAccessResult } from \"./format-my-access.js\";\n\n/** Eve tool slug — must match the agent tool filename `get_my_access.ts`. */\nexport const MY_ACCESS_TOOL_NAME = \"get_my_access\";\n\nexport const myAccessInputSchema = z.object({});\n\nfunction tenantCapabilities(isAdmin: boolean) {\n return {\n manageSettings: isAdmin,\n manageBilling: isAdmin,\n manageTeam: isAdmin,\n manageIntegrations: isAdmin,\n };\n}\n\nexport function defineMyAccessTool() {\n return defineTool({\n description:\n \"Check the authenticated user's tenant role, group memberships, and admin capabilities in RobotRock. \" +\n \"Results render as an access card in chat — do not restate role, groups, or capabilities in your reply.\",\n inputSchema: myAccessInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent access details.\"\n );\n }\n\n return formatMyAccessResult({\n tenantSlug: caller.tenantSlug,\n tenantRole: caller.role,\n isTenantAdmin: caller.isAdmin,\n groupSlugs: caller.groups,\n capabilities: tenantCapabilities(caller.isAdmin),\n email: caller.email,\n });\n },\n });\n}\n\nexport const myAccessTool = defineMyAccessTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n chatId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const chatId = readStringAttribute(caller.attributes, \"chatId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n ...(chatId ? { chatId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n","import type {\n ToolDisplayEnvelope,\n ToolDisplayMetadata,\n UiSchema,\n} from \"@robotrock/core/schemas\";\n\nexport type ToolDisplayResultOptions = {\n widget?: string;\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n};\n\n/**\n * Wrap tool execute output with optional dashboard display hints.\n * Agents return plain JSON by default; use this when schema-driven widgets help.\n */\nexport function toolDisplayResult<T extends Record<string, unknown>>(\n data: T,\n options?: ToolDisplayResultOptions\n): ToolDisplayEnvelope<T> {\n const display: ToolDisplayMetadata | undefined =\n options?.widget || options?.title || options?.description\n ? {\n ...(options.widget ? { widget: options.widget } : {}),\n ...(options.title ? { title: options.title } : {}),\n ...(options.description ? { description: options.description } : {}),\n }\n : undefined;\n\n return {\n ...(display ? { display } : {}),\n data,\n ...(options?.ui ? { ui: options.ui } : {}),\n };\n}\n","import type { UiSchema } from \"@robotrock/core/schemas\";\nimport { toolDisplayResult } from \"./tool-result-display.js\";\nimport { withReplyGuidance } from \"./tool-reply-guidance.js\";\n\nexport type FormatToolObjectResultOptions = {\n title?: string;\n description?: string;\n ui?: Record<string, UiSchema>;\n replyGuidance: string;\n};\n\nexport type FormatToolListResultOptions = {\n title: string;\n itemUi?: UiSchema;\n /** Composite widget for each row (uses list layout instead of table). */\n itemWidget?: string;\n replyGuidance: string;\n};\n\n/** Wrap a flat object for schema-driven chat rendering. */\nexport function formatToolObjectResult(\n data: Record<string, unknown>,\n options: FormatToolObjectResultOptions\n) {\n return withReplyGuidance(\n toolDisplayResult(data, {\n title: options.title,\n description: options.description,\n ui: options.ui,\n }),\n options.replyGuidance\n );\n}\n\n/** Wrap an array field as a list widget inside a display envelope. */\nexport function formatToolListResult(\n listKey: string,\n items: Record<string, unknown>[],\n options: FormatToolListResultOptions\n) {\n const listFieldUi = options.itemWidget\n ? {\n \"ui:widget\": \"list\" as const,\n items: {\n \"ui:widget\": options.itemWidget,\n ...(options.itemUi ?? {}),\n },\n }\n : {\n \"ui:widget\": \"table\" as const,\n ...(options.itemUi ? { items: options.itemUi } : {}),\n };\n\n return formatToolObjectResult(\n { [listKey]: items },\n {\n title: options.title,\n ui: {\n [listKey]: listFieldUi,\n },\n replyGuidance: options.replyGuidance,\n }\n );\n}\n\nexport function isAgentAdminErrorResult(\n result: unknown\n): result is { ok: false; message: string } {\n return (\n typeof result === \"object\" &&\n result !== null &&\n (result as { ok?: unknown }).ok === false &&\n typeof (result as { message?: unknown }).message === \"string\"\n );\n}\n","import { formatToolObjectResult } from \"../../tool-display-format.js\";\nimport { MY_ACCESS_REPLY_GUIDANCE } from \"../../tool-reply-guidance.js\";\n\ntype MyAccessResultInput = {\n tenantSlug: string;\n tenantRole?: string;\n isTenantAdmin: boolean;\n groupSlugs: string[];\n capabilities: {\n manageSettings: boolean;\n manageBilling: boolean;\n manageTeam: boolean;\n manageIntegrations: boolean;\n };\n email?: string;\n};\n\nconst ACCESS_FIELD_UI = {\n workspace: { \"ui:title\": \"Workspace\" },\n role: { \"ui:title\": \"Role\" },\n tenantAdmin: { \"ui:title\": \"Tenant admin\", \"ui:widget\": \"boolean\" },\n groups: { \"ui:title\": \"Groups\" },\n manageSettings: { \"ui:title\": \"Manage settings\", \"ui:widget\": \"boolean\" },\n manageBilling: { \"ui:title\": \"Manage billing\", \"ui:widget\": \"boolean\" },\n manageTeam: { \"ui:title\": \"Manage team\", \"ui:widget\": \"boolean\" },\n manageIntegrations: {\n \"ui:title\": \"Manage integrations\",\n \"ui:widget\": \"boolean\",\n },\n email: { \"ui:title\": \"Email\" },\n} as const;\n\nexport function formatMyAccessResult(input: MyAccessResultInput) {\n return formatToolObjectResult(\n {\n access: {\n workspace: input.tenantSlug,\n role: input.tenantRole ?? \"member\",\n tenantAdmin: input.isTenantAdmin,\n groups: input.groupSlugs.join(\", \") || \"—\",\n manageSettings: input.capabilities.manageSettings,\n manageBilling: input.capabilities.manageBilling,\n manageTeam: input.capabilities.manageTeam,\n manageIntegrations: input.capabilities.manageIntegrations,\n ...(input.email ? { email: input.email } : {}),\n },\n },\n {\n ui: {\n access: {\n \"ui:widget\": \"access\",\n \"ui:title\": \"Your access\",\n items: ACCESS_FIELD_UI,\n },\n },\n replyGuidance: MY_ACCESS_REPLY_GUIDANCE,\n }\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as eve_tools from 'eve/tools';
|
|
2
|
-
import { T as TenantRole } from '../../../tenant-
|
|
2
|
+
import { T as TenantRole } from '../../../tenant-DmN0MMyC.js';
|
|
3
3
|
import { T as ToolResultWithReplyGuidance } from '../../../tool-reply-guidance-C3qrT1In.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import 'eve/context';
|
|
@@ -47,6 +47,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
47
47
|
}
|
|
48
48
|
const workosUserId = readStringAttribute(caller.attributes, "workosUserId");
|
|
49
49
|
const connectionId = readStringAttribute(caller.attributes, "connectionId");
|
|
50
|
+
const chatId = readStringAttribute(caller.attributes, "chatId");
|
|
50
51
|
const groups = readStringArrayAttribute(caller.attributes, "groups");
|
|
51
52
|
return {
|
|
52
53
|
userId: caller.principalId,
|
|
@@ -54,6 +55,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
54
55
|
name: name ?? email.split("@")[0] ?? email,
|
|
55
56
|
tenantSlug,
|
|
56
57
|
...connectionId ? { connectionId } : {},
|
|
58
|
+
...chatId ? { chatId } : {},
|
|
57
59
|
role,
|
|
58
60
|
isAdmin: role === "admin",
|
|
59
61
|
groups,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/eve/tools/identity/whoami.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n WHOAMI_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\n\nexport const WHOAMI_TOOL_NAME = \"whoami\";\n\nexport const whoamiInputSchema = z.object({});\n\nexport function defineWhoamiTool() {\n return defineTool({\n description:\n \"Return the authenticated RobotRock user, tenant, role, and group memberships for the current chat session. \" +\n \"The dashboard renders a profile card — do not repeat name, email, workspace, role, or groups in your reply; only add context not shown in the card.\",\n inputSchema: whoamiInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent identity details.\"\n );\n }\n\n return withReplyGuidance(\n {\n authenticated: true as const,\n userId: caller.userId,\n name: caller.name,\n email: caller.email,\n tenantSlug: caller.tenantSlug,\n role: caller.role,\n isAdmin: caller.isAdmin,\n groups: caller.groups.map((slug) => ({ slug })),\n ...(caller.workosUserId ? { workosUserId: caller.workosUserId } : {}),\n sessionId: ctx.session.id,\n },\n WHOAMI_REPLY_GUIDANCE\n );\n },\n });\n}\n\nexport const whoamiTool = defineWhoamiTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/eve/tools/identity/whoami.ts","../../../../src/eve/agent/attributes.ts","../../../../src/eve/agent/tenant.ts","../../../../src/eve/tool-reply-guidance.ts"],"sourcesContent":["import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\nimport { tryResolveTenantCaller } from \"../../agent/tenant.js\";\nimport {\n WHOAMI_REPLY_GUIDANCE,\n withReplyGuidance,\n} from \"../../tool-reply-guidance.js\";\n\nexport const WHOAMI_TOOL_NAME = \"whoami\";\n\nexport const whoamiInputSchema = z.object({});\n\nexport function defineWhoamiTool() {\n return defineTool({\n description:\n \"Return the authenticated RobotRock user, tenant, role, and group memberships for the current chat session. \" +\n \"The dashboard renders a profile card — do not repeat name, email, workspace, role, or groups in your reply; only add context not shown in the card.\",\n inputSchema: whoamiInputSchema,\n async execute(_input, ctx) {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n return withReplyGuidance(\n {\n authenticated: false as const,\n message:\n \"No RobotRock user is attached to this session. Dashboard chat supplies user context via the Eve proxy.\",\n },\n \"Explain that no user is attached to this session. Do not invent identity details.\"\n );\n }\n\n return withReplyGuidance(\n {\n authenticated: true as const,\n userId: caller.userId,\n name: caller.name,\n email: caller.email,\n tenantSlug: caller.tenantSlug,\n role: caller.role,\n isAdmin: caller.isAdmin,\n groups: caller.groups.map((slug) => ({ slug })),\n ...(caller.workosUserId ? { workosUserId: caller.workosUserId } : {}),\n sessionId: ctx.session.id,\n },\n WHOAMI_REPLY_GUIDANCE\n );\n },\n });\n}\n\nexport const whoamiTool = defineWhoamiTool();\n","export function readStringAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string | undefined {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && typeof value[0] === \"string\" && value[0].length > 0) {\n return value[0];\n }\n return undefined;\n}\n\nexport function readStringArrayAttribute(\n attributes: Readonly<Record<string, string | readonly string[]>> | undefined,\n key: string\n): string[] {\n const value = attributes?.[key];\n if (typeof value === \"string\" && value.length > 0) {\n return [value];\n }\n if (Array.isArray(value)) {\n return value.filter(\n (entry): entry is string => typeof entry === \"string\" && entry.length > 0\n );\n }\n return [];\n}\n\nexport function parseTenantRole(\n value: string | undefined\n): \"admin\" | \"member\" | null {\n if (value === \"admin\" || value === \"member\") {\n return value;\n }\n return null;\n}\n","import type { SessionContext } from \"eve/context\";\nimport {\n parseTenantRole,\n readStringArrayAttribute,\n readStringAttribute,\n} from \"./attributes.js\";\n\nexport type TenantRole = \"admin\" | \"member\";\n\nexport type TenantCaller = {\n userId: string;\n email: string;\n name: string;\n tenantSlug: string;\n connectionId?: string;\n chatId?: string;\n role: TenantRole;\n isAdmin: boolean;\n groups: string[];\n workosUserId?: string;\n};\n\n/** Resolve the authenticated RobotRock dashboard user for the current session. */\nexport function tryResolveTenantCaller(ctx: SessionContext): TenantCaller | null {\n const caller = ctx.session.auth.initiator ?? ctx.session.auth.current;\n if (caller?.principalType !== \"user\") {\n return null;\n }\n\n const email = readStringAttribute(caller.attributes, \"email\");\n const name = readStringAttribute(caller.attributes, \"name\");\n const tenantSlug =\n readStringAttribute(caller.attributes, \"tenantSlug\") ??\n readStringAttribute(caller.attributes, \"tenantId\");\n const role = parseTenantRole(readStringAttribute(caller.attributes, \"role\"));\n if (!email || !tenantSlug || !caller.principalId || !role) {\n return null;\n }\n\n const workosUserId = readStringAttribute(caller.attributes, \"workosUserId\");\n const connectionId = readStringAttribute(caller.attributes, \"connectionId\");\n const chatId = readStringAttribute(caller.attributes, \"chatId\");\n const groups = readStringArrayAttribute(caller.attributes, \"groups\");\n\n return {\n userId: caller.principalId,\n email,\n name: name ?? email.split(\"@\")[0] ?? email,\n tenantSlug,\n ...(connectionId ? { connectionId } : {}),\n ...(chatId ? { chatId } : {}),\n role,\n isAdmin: role === \"admin\",\n groups,\n ...(workosUserId ? { workosUserId } : {}),\n };\n}\n\n/** Like {@link tryResolveTenantCaller} but throws when the session has no user. */\nexport function requireTenantCaller(ctx: SessionContext): TenantCaller {\n const caller = tryResolveTenantCaller(ctx);\n if (!caller) {\n throw new Error(\"An authenticated RobotRock tenant user is required.\");\n }\n return caller;\n}\n\nexport function isAdminCaller(ctx: SessionContext): boolean {\n return tryResolveTenantCaller(ctx)?.isAdmin === true;\n}\n\n/** Require an authenticated tenant admin for the current session. */\nexport function requireAdminCaller(ctx: SessionContext): TenantCaller {\n const caller = requireTenantCaller(ctx);\n if (!caller.isAdmin) {\n throw new Error(\"Tenant admin access is required.\");\n }\n return caller;\n}\n","/** Field name agents read; dashboard widgets must ignore when rendering. */\nexport const TOOL_REPLY_GUIDANCE_FIELD = \"replyGuidance\" as const;\n\nexport const WHOAMI_REPLY_GUIDANCE =\n \"The dashboard shows a profile card with name, email, workspace, role, and groups. Do not restate those in your reply. Only add context not in the card (e.g. permissions, refund approval rules, missing group membership).\";\n\nexport const MY_ACCESS_REPLY_GUIDANCE =\n \"Access details may render in chat UI. Do not restate role, groups, or admin capabilities if already visible. Only explain what the user can or cannot do for their question (e.g. finance group required for refunds ≥ $10,000). When access.tenantAdmin is false and the user needs manageTeam, manageGroups, or query-tasks capabilities, explain briefly then call ask_question to offer sending an admin request to tenant admins — do not tell them to find an admin manually without offering delegation first.\";\n\nexport const CREATE_INBOX_TASK_REPLY_GUIDANCE =\n \"A task card shows title, assignee, and inbox status in chat. Do not restate task ID, assignee, or delegation details from the card. Only mention next steps or answer the user's question.\";\n\nexport const SEARCH_PRODUCTS_REPLY_GUIDANCE =\n \"Product results render as a list card in chat. Do not restate product names, prices, or SKUs. Only add context not in the list (e.g. no matches, recommendations).\";\n\nexport const MANAGE_TEAM_MEMBERS_LIST_REPLY_GUIDANCE =\n \"Team members render as a list card in chat. Do not restate names, emails, or roles from the list. Only add context not shown (e.g. duplicates, next admin actions).\";\n\nexport const MANAGE_TEAM_MEMBERS_DETAIL_REPLY_GUIDANCE =\n \"Team member details render in chat UI. Do not restate name, email, or role from the card. Only add next steps or policy context.\";\n\nexport const MANAGE_GROUPS_LIST_REPLY_GUIDANCE =\n \"Groups render as a list card in chat. Do not restate group names, slugs, or descriptions from the list. Use slug (not internal id) for get, update, delete, and membership actions. Only add context not shown.\";\n\nexport const MANAGE_GROUPS_DETAIL_REPLY_GUIDANCE =\n \"Group details render in chat UI. Do not restate fields already visible in the card. Only add next steps.\";\n\nexport const QUERY_TASKS_LIST_REPLY_GUIDANCE =\n \"Tasks render as a list card in chat. Do not restate task names, ids, or statuses from the list. Only add context not shown (e.g. filters applied, recommended next action).\";\n\nexport const QUERY_TASKS_DETAIL_REPLY_GUIDANCE =\n \"Task details render in chat UI. Do not restate fields already visible in the card. Only add analysis or next steps.\";\n\nexport const ASSIGN_TASKS_REPLY_GUIDANCE =\n \"Reassignment results render in chat UI. Do not restate task ids or assignee emails from the card. Only mention failures, partial success, or next steps.\";\n\nexport const REFUND_CHARGE_REPLY_GUIDANCE =\n \"Refund details render in chat UI. Do not restate charge id, amount, or status from the card. Only add policy context or next steps.\";\n\nexport const DEPLOY_RELEASE_REPLY_GUIDANCE =\n \"Deploy details render in chat UI. Do not restate service, version, or environment from the card. Only add rollout context or next steps.\";\n\nexport const GET_WEATHER_REPLY_GUIDANCE =\n \"Weather details render in chat UI. Do not restate city, condition, or temperature from the card. Only add conversational context if needed.\";\n\nexport const ADMIN_MUTATION_REPLY_GUIDANCE =\n \"The action result renders in chat UI. Do not restate fields already visible in the card. Confirm outcome briefly or add next steps only.\";\n\nexport type ToolResultWithReplyGuidance<T extends Record<string, unknown>> = T & {\n replyGuidance: string;\n};\n\n/** Attach model-facing narration hints to a tool execute result. */\nexport function withReplyGuidance<T extends Record<string, unknown>>(\n result: T,\n guidance: string\n): ToolResultWithReplyGuidance<T> {\n return {\n ...result,\n [TOOL_REPLY_GUIDANCE_FIELD]: guidance,\n };\n}\n\n/** Remove agent-only fields before showing raw JSON in the dashboard. */\nexport function stripAgentOnlyToolFields(output: unknown): unknown {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n return output;\n }\n\n const record = { ...(output as Record<string, unknown>) };\n delete record[TOOL_REPLY_GUIDANCE_FIELD];\n return record;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as eve_tools from 'eve/tools';
|
|
2
2
|
import { T as ToolResultWithReplyGuidance } from '../../../tool-reply-guidance-C3qrT1In.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { a as TenantCaller } from '../../../tenant-
|
|
4
|
+
import { a as TenantCaller } from '../../../tenant-DmN0MMyC.js';
|
|
5
5
|
import 'eve/context';
|
|
6
6
|
|
|
7
7
|
/** Eve tool slug — must match the agent tool filename `create_robotrock_task.ts`. */
|
|
@@ -868,11 +868,42 @@ function resolveRobotRockAuthConfig(overrides) {
|
|
|
868
868
|
return { kind: "apiKey", apiKey };
|
|
869
869
|
}
|
|
870
870
|
|
|
871
|
+
// src/chat-correlation.ts
|
|
872
|
+
var ROBOTROCK_CHAT_ID_HEADER = "x-robotrock-chat-id";
|
|
873
|
+
var ROBOTROCK_EVE_SESSION_ID_HEADER = "x-robotrock-eve-session-id";
|
|
874
|
+
function resolveChatCorrelationFromEnv() {
|
|
875
|
+
const chatId = process.env.ROBOTROCK_CHAT_ID?.trim();
|
|
876
|
+
const eveSessionId = process.env.ROBOTROCK_EVE_SESSION_ID?.trim();
|
|
877
|
+
if (!chatId && !eveSessionId) {
|
|
878
|
+
return void 0;
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
...chatId ? { chatId } : {},
|
|
882
|
+
...eveSessionId ? { eveSessionId } : {}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function buildChatCorrelationHeaders(correlation) {
|
|
886
|
+
if (!correlation) {
|
|
887
|
+
return {};
|
|
888
|
+
}
|
|
889
|
+
const headers = {};
|
|
890
|
+
const chatId = correlation.chatId?.trim();
|
|
891
|
+
const eveSessionId = correlation.eveSessionId?.trim();
|
|
892
|
+
if (chatId) {
|
|
893
|
+
headers[ROBOTROCK_CHAT_ID_HEADER] = chatId;
|
|
894
|
+
}
|
|
895
|
+
if (eveSessionId) {
|
|
896
|
+
headers[ROBOTROCK_EVE_SESSION_ID_HEADER] = eveSessionId;
|
|
897
|
+
}
|
|
898
|
+
return headers;
|
|
899
|
+
}
|
|
900
|
+
|
|
871
901
|
// src/chats.ts
|
|
872
902
|
function createChatsApi(config) {
|
|
873
903
|
const headers = () => ({
|
|
874
904
|
"Content-Type": "application/json",
|
|
875
|
-
...buildRobotRockAuthHeaders(config.auth)
|
|
905
|
+
...buildRobotRockAuthHeaders(config.auth),
|
|
906
|
+
...buildChatCorrelationHeaders(config.chatCorrelation)
|
|
876
907
|
});
|
|
877
908
|
return {
|
|
878
909
|
async create(input) {
|
|
@@ -1082,6 +1113,7 @@ function serializeValidUntil(value) {
|
|
|
1082
1113
|
}
|
|
1083
1114
|
var RobotRock = class {
|
|
1084
1115
|
auth;
|
|
1116
|
+
chatCorrelation;
|
|
1085
1117
|
baseUrl;
|
|
1086
1118
|
app;
|
|
1087
1119
|
agentVersion;
|
|
@@ -1109,6 +1141,7 @@ var RobotRock = class {
|
|
|
1109
1141
|
...apiKey ? { apiKey } : {},
|
|
1110
1142
|
...agentService ? { agentService } : {}
|
|
1111
1143
|
});
|
|
1144
|
+
this.chatCorrelation = config.chatCorrelation;
|
|
1112
1145
|
this.baseUrl = config.baseUrl ?? getRobotRockApiBaseUrl();
|
|
1113
1146
|
this.app = config.app;
|
|
1114
1147
|
this.agentVersion = config.version ?? resolveAgentVersionFromEnv();
|
|
@@ -1126,13 +1159,15 @@ var RobotRock = class {
|
|
|
1126
1159
|
this.chats = createChatsApi({
|
|
1127
1160
|
baseUrl: this.baseUrl,
|
|
1128
1161
|
auth: this.auth,
|
|
1129
|
-
app: this.app
|
|
1162
|
+
app: this.app,
|
|
1163
|
+
chatCorrelation: this.chatCorrelation
|
|
1130
1164
|
});
|
|
1131
1165
|
}
|
|
1132
1166
|
authHeaders(extra) {
|
|
1133
1167
|
return {
|
|
1134
1168
|
"Content-Type": "application/json",
|
|
1135
1169
|
...buildRobotRockAuthHeaders(this.auth),
|
|
1170
|
+
...buildChatCorrelationHeaders(this.chatCorrelation),
|
|
1136
1171
|
...extra
|
|
1137
1172
|
};
|
|
1138
1173
|
}
|
|
@@ -1403,28 +1438,6 @@ function normalizeSendToHumanInput(task, clientDefaults) {
|
|
|
1403
1438
|
};
|
|
1404
1439
|
}
|
|
1405
1440
|
|
|
1406
|
-
// src/env.ts
|
|
1407
|
-
function resolveRobotRockConfig(overrides) {
|
|
1408
|
-
const agentService = overrides?.agentService;
|
|
1409
|
-
const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
|
|
1410
|
-
if (agentService && apiKey) {
|
|
1411
|
-
throw new Error(
|
|
1412
|
-
"RobotRock client cannot configure both apiKey and agentService."
|
|
1413
|
-
);
|
|
1414
|
-
}
|
|
1415
|
-
const baseUrl = overrides?.baseUrl ?? getRobotRockApiBaseUrl();
|
|
1416
|
-
const app = overrides?.app ?? process.env.ROBOTROCK_APP;
|
|
1417
|
-
if (agentService) {
|
|
1418
|
-
return app ? { agentService, baseUrl, app } : { agentService, baseUrl };
|
|
1419
|
-
}
|
|
1420
|
-
if (!apiKey) {
|
|
1421
|
-
throw new Error(
|
|
1422
|
-
"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass agentService when creating the client."
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1441
|
// src/eve/agent/attributes.ts
|
|
1429
1442
|
function readStringAttribute(attributes, key) {
|
|
1430
1443
|
const value = attributes?.[key];
|
|
@@ -1470,6 +1483,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
1470
1483
|
}
|
|
1471
1484
|
const workosUserId = readStringAttribute(caller.attributes, "workosUserId");
|
|
1472
1485
|
const connectionId = readStringAttribute(caller.attributes, "connectionId");
|
|
1486
|
+
const chatId = readStringAttribute(caller.attributes, "chatId");
|
|
1473
1487
|
const groups = readStringArrayAttribute(caller.attributes, "groups");
|
|
1474
1488
|
return {
|
|
1475
1489
|
userId: caller.principalId,
|
|
@@ -1477,6 +1491,7 @@ function tryResolveTenantCaller(ctx) {
|
|
|
1477
1491
|
name: name ?? email.split("@")[0] ?? email,
|
|
1478
1492
|
tenantSlug,
|
|
1479
1493
|
...connectionId ? { connectionId } : {},
|
|
1494
|
+
...chatId ? { chatId } : {},
|
|
1480
1495
|
role,
|
|
1481
1496
|
isAdmin: role === "admin",
|
|
1482
1497
|
groups,
|
|
@@ -1493,27 +1508,37 @@ function requireTenantCaller(ctx) {
|
|
|
1493
1508
|
|
|
1494
1509
|
// src/eve/agent/client-from-session.ts
|
|
1495
1510
|
var warnedMissingAuth = false;
|
|
1511
|
+
function resolveBoundChatCorrelation(ctx) {
|
|
1512
|
+
const caller = tryResolveTenantCaller(ctx);
|
|
1513
|
+
return {
|
|
1514
|
+
eveSessionId: ctx.session.id,
|
|
1515
|
+
...caller?.chatId ? { chatId: caller.chatId } : {},
|
|
1516
|
+
...resolveChatCorrelationFromEnv()
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1496
1519
|
function tryCreateBoundRobotRockClient(ctx) {
|
|
1497
1520
|
const caller = tryResolveTenantCaller(ctx);
|
|
1498
1521
|
const serviceToken = process.env.ROBOTROCK_AGENT_SERVICE_TOKEN?.trim();
|
|
1522
|
+
const chatCorrelation = resolveBoundChatCorrelation(ctx);
|
|
1499
1523
|
if (caller?.connectionId && serviceToken) {
|
|
1500
1524
|
return createClient({
|
|
1501
1525
|
baseUrl: process.env.ROBOTROCK_BASE_URL?.trim(),
|
|
1526
|
+
chatCorrelation,
|
|
1502
1527
|
agentService: {
|
|
1503
1528
|
token: serviceToken,
|
|
1504
1529
|
tenantSlug: caller.tenantSlug,
|
|
1505
|
-
connectionId: caller.connectionId
|
|
1530
|
+
connectionId: caller.connectionId,
|
|
1531
|
+
actingUserId: caller.userId
|
|
1506
1532
|
}
|
|
1507
1533
|
});
|
|
1508
1534
|
}
|
|
1509
1535
|
const apiKey = process.env.ROBOTROCK_API_KEY?.trim();
|
|
1510
1536
|
if (apiKey) {
|
|
1511
|
-
return createClient(
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
);
|
|
1537
|
+
return createClient({
|
|
1538
|
+
apiKey,
|
|
1539
|
+
baseUrl: process.env.ROBOTROCK_BASE_URL?.trim(),
|
|
1540
|
+
chatCorrelation
|
|
1541
|
+
});
|
|
1517
1542
|
}
|
|
1518
1543
|
if (!warnedMissingAuth) {
|
|
1519
1544
|
warnedMissingAuth = true;
|