@meistrari/chat-nuxt 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +57 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +109 -2
  4. package/dist/runtime/components/chat/message-bubble.vue +2 -1
  5. package/dist/runtime/composables/useChat.js +2 -1
  6. package/dist/runtime/composables/useChatApi.js +2 -1
  7. package/dist/runtime/composables/useConversations.js +2 -1
  8. package/dist/runtime/composables/useGitHubSkills.js +2 -1
  9. package/dist/runtime/composables/useWorkspaceExternalSkills.js +2 -1
  10. package/dist/runtime/composables/useWorkspaceMembers.js +2 -1
  11. package/dist/runtime/composables/useWorkspaceSettings.js +2 -1
  12. package/dist/runtime/composables/useWorkspaces.js +2 -1
  13. package/dist/runtime/embed/components/ChatEmbed.vue +2 -1
  14. package/dist/runtime/embed/components/ChatEmbedInner.vue +2 -1
  15. package/dist/runtime/internal/agent-environment-resolver.stub.d.ts +2 -0
  16. package/dist/runtime/internal/agent-environment-resolver.stub.js +1 -0
  17. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +6 -2
  18. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +6 -2
  19. package/dist/runtime/server/utils/agent-api.js +4 -3
  20. package/dist/runtime/server/utils/agent-environment.d.ts +8 -0
  21. package/dist/runtime/server/utils/agent-environment.js +34 -0
  22. package/dist/runtime/server/utils/auth-token.d.ts +2 -0
  23. package/dist/runtime/server/utils/auth-token.js +21 -0
  24. package/dist/runtime/server/utils/conversation-agent-turn.d.ts +6 -1
  25. package/dist/runtime/server/utils/conversation-agent-turn.js +27 -8
  26. package/dist/runtime/server/utils/tela-api.js +2 -1
  27. package/dist/runtime/server/utils/workspace.js +3 -1
  28. package/dist/runtime/types/chat-agent-environment-resolver.d.ts +4 -0
  29. package/dist/runtime/types/chat-auth.d.ts +35 -0
  30. package/package.json +1 -1
package/README.md CHANGED
@@ -103,6 +103,15 @@ These never appear in module options — set them via `.env`, Infisical, etc. Wh
103
103
 
104
104
  Set `chatNuxt.validateConfig` to `'warn'` to log a warning when keys for an enabled feature are missing, or `'error'` to fail fast at module setup. The check is off by default since some deployments inject runtime config after build.
105
105
 
106
+ ### Auth modes
107
+
108
+ `@meistrari/chat-nuxt` can run with either auth mode exposed by `@meistrari/auth-nuxt`:
109
+
110
+ - Application auth (`telaAuth.application.enabled: true`) delegates chat identity and workspace state to `useTelaApplicationAuth()`.
111
+ - First-party auth delegates chat identity to `useTelaSession()` and workspace state to `useTelaOrganization()`.
112
+
113
+ The module chooses the bridge during Nuxt setup. Host apps should keep registering `@meistrari/auth-nuxt` before `@meistrari/chat-nuxt`.
114
+
106
115
  ### Local mode (no database)
107
116
 
108
117
  When `databaseUrl` is empty, chat-nuxt runs an embedded [pglite](https://pglite.dev) instance — Postgres compiled to WASM, in-process, file-backed at `<rootDir>/.data/chat-nuxt.db`. Migrations are applied automatically on first boot. Tables live under a dedicated `chat` Postgres schema so the database can be safely shared with the host app's own Drizzle setup.
@@ -256,8 +265,17 @@ runtimeConfig: {
256
265
  ```vue
257
266
  <script setup lang="ts">
258
267
  const { posthogPublicKey, posthogHost, posthogDefaults } = useRuntimeConfig().public
268
+
269
+ // Application auth hosts:
259
270
  const { user, activeOrganization } = useTelaApplicationAuth()
260
271
 
272
+ // First-party auth hosts can use this instead:
273
+ // const session = useTelaSession()
274
+ // const organization = useTelaOrganization()
275
+ // const user = session.user
276
+ // const activeOrganization = organization.activeOrganization
277
+ // await organization.getActiveOrganization()
278
+
261
279
  onMounted(async () => {
262
280
  if (!posthogPublicKey || !posthogHost) return
263
281
  await initPosthog(posthogPublicKey, posthogHost, posthogDefaults)
@@ -335,6 +353,45 @@ export default async function resolveWorkspaceSettings(
335
353
  }
336
354
  ```
337
355
 
356
+ ## Optional: Agent API Environment Resolver
357
+
358
+ When default chat mode starts a new Agent API session, `chat-nuxt` can attach `environmentVariables` to the Agent API create payload. Built-in workspace credentials still come from the `credentials` feature. Host apps can add server-only variables by creating `server/chat/resolve-agent-environment.ts`:
359
+
360
+ ```typescript
361
+ import type { H3Event } from 'h3'
362
+
363
+ export default async function resolveAgentEnvironment(
364
+ event: H3Event,
365
+ context: {
366
+ user: { id: string; email: string }
367
+ workspaceId: string
368
+ telaAgentId: string | null
369
+ conversationId: string
370
+ skills: readonly string[]
371
+ },
372
+ ) {
373
+ if (!context.skills.includes('github:meistrari/backoffice/skills/backoffice-admin')) {
374
+ return null
375
+ }
376
+
377
+ return {
378
+ BACKOFFICE_ADMIN_TOOL_URL: 'https://backoffice.example.com/api/admin-chat/tool',
379
+ BACKOFFICE_ADMIN_INVOCATION_GRANT: await mintInvocationGrant({
380
+ userId: context.user.id,
381
+ workspaceId: context.workspaceId,
382
+ conversationId: context.conversationId,
383
+ skills: context.skills,
384
+ }),
385
+ }
386
+ }
387
+ ```
388
+
389
+ The resolver runs on the server only. It is called for new default Agent API sessions, before `POST /v2/chat`. Continued sessions do not receive new environment variables.
390
+
391
+ If the resolver returns the same environment variable name as a workspace credential, the resolver value wins.
392
+
393
+ `chat-nuxt` only transports these variables to Agent API. A skill that calls a host backend must implement its own callback contract with the host backend. In short, callback authorization belongs to the skill and host backend, not `chat-nuxt`. The host backend remains responsible for validating invocation grants, allowed tools, schemas, replay protection, confirmation for writes, and audit logging.
394
+
338
395
  ## `<ChatConfigurationModal>`
339
396
 
340
397
  A reusable settings modal (system prompt, context files, Tela workstations, Tela skills, external skills, credentials) that any host app can open from any button. By default, the modal uses `useWorkspaceSettings()` to fetch and persist settings. If the host passes the `settings` prop, the host owns workspace settings state and persistence.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "1.5.1",
4
+ "version": "1.6.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,7 +1,33 @@
1
1
  import { existsSync, readFileSync, mkdirSync, symlinkSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { join, dirname } from 'node:path';
4
- import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
4
+ import { defineNuxtModule, createResolver, addTemplate, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
5
+
6
+ const agentEnvironmentResolverCandidates = [
7
+ "server/chat/resolve-agent-environment.ts",
8
+ "server/chat/resolve-agent-environment.mts",
9
+ "server/chat/resolve-agent-environment.mjs",
10
+ "server/chat/resolve-agent-environment.js"
11
+ ];
12
+ const stubExtensions$1 = [".ts", ".mjs", ".js"];
13
+ function resolveAgentEnvironmentResolverEntry(srcDir, fallbackResolverBase) {
14
+ for (const relativePath of agentEnvironmentResolverCandidates) {
15
+ const candidate = join(srcDir, relativePath);
16
+ if (existsSync(candidate)) {
17
+ return candidate;
18
+ }
19
+ }
20
+ for (const ext of stubExtensions$1) {
21
+ const candidate = fallbackResolverBase + ext;
22
+ if (existsSync(candidate)) {
23
+ return candidate;
24
+ }
25
+ }
26
+ const attempted = stubExtensions$1.map((ext) => fallbackResolverBase + ext).join(", ");
27
+ throw new Error(
28
+ `[@meistrari/chat-nuxt] Unable to resolve agent-environment stub. Tried: ${attempted}. This indicates a packaging bug - please report it.`
29
+ );
30
+ }
5
31
 
6
32
  const localRequire = createRequire(import.meta.url);
7
33
  const runtimeAliasPackages = [
@@ -153,6 +179,74 @@ const DEFAULT_FEATURES = {
153
179
  generateTitle: false,
154
180
  credentials: false
155
181
  };
182
+ function resolveChatAuthMode(runtimeConfig) {
183
+ const publicConfig = runtimeConfig.public;
184
+ const telaAuth = publicConfig?.telaAuth;
185
+ return telaAuth?.application?.enabled ? "application" : "first-party";
186
+ }
187
+ function buildChatAuthTemplate(mode) {
188
+ if (mode === "application") {
189
+ return `
190
+ import { useTelaApplicationAuth } from '#imports'
191
+
192
+ export function useChatAuth() {
193
+ const auth = useTelaApplicationAuth()
194
+
195
+ return {
196
+ user: auth.user,
197
+ activeOrganization: auth.activeOrganization,
198
+ getToken: auth.getToken,
199
+ getAvailableOrganizations: auth.getAvailableOrganizations,
200
+ switchOrganization: auth.switchOrganization,
201
+ }
202
+ }
203
+ `.trimStart();
204
+ }
205
+ return `
206
+ import { useState, useTelaOrganization, useTelaSession, watch } from '#imports'
207
+
208
+ export function useChatAuth() {
209
+ const session = useTelaSession()
210
+ const organization = useTelaOrganization()
211
+ const isHydratingActiveOrganization = useState('chat-nuxt:auth:is-hydrating-active-organization', () => false)
212
+ const hasHydratedActiveOrganization = useState('chat-nuxt:auth:has-hydrated-active-organization', () => false)
213
+
214
+ if (import.meta.client) {
215
+ watch(
216
+ () => session.user.value,
217
+ async (user) => {
218
+ if (!user) {
219
+ hasHydratedActiveOrganization.value = false
220
+ return
221
+ }
222
+
223
+ if (organization.activeOrganization.value || hasHydratedActiveOrganization.value || isHydratingActiveOrganization.value) {
224
+ return
225
+ }
226
+
227
+ isHydratingActiveOrganization.value = true
228
+ try {
229
+ await organization.getActiveOrganization()
230
+ hasHydratedActiveOrganization.value = true
231
+ }
232
+ finally {
233
+ isHydratingActiveOrganization.value = false
234
+ }
235
+ },
236
+ { immediate: true },
237
+ )
238
+ }
239
+
240
+ return {
241
+ user: session.user,
242
+ activeOrganization: organization.activeOrganization,
243
+ getToken: session.getToken,
244
+ getAvailableOrganizations: organization.listOrganizations,
245
+ switchOrganization: organization.setActiveOrganization,
246
+ }
247
+ }
248
+ `.trimStart();
249
+ }
156
250
  const ALWAYS_REQUIRED_KEYS = [
157
251
  "telaApiUrl",
158
252
  "agentApiUrl",
@@ -225,7 +319,7 @@ const module$1 = defineNuxtModule({
225
319
  const hasAuthModule = (nuxt.options.modules ?? []).some(isAuthModuleEntry);
226
320
  if (!hasAuthModule) {
227
321
  throw new Error(
228
- '[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so useTelaApplicationAuth() and event.context.auth are available.'
322
+ '[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so chat auth composables and event.context.auth are available.'
229
323
  );
230
324
  }
231
325
  ensureIconifyPackages(nuxt.options.rootDir);
@@ -237,14 +331,25 @@ const module$1 = defineNuxtModule({
237
331
  nuxt.options.srcDir,
238
332
  join(runtimeDir, "internal/workspace-settings-resolver.stub")
239
333
  );
334
+ const agentEnvironmentResolverEntry = resolveAgentEnvironmentResolverEntry(
335
+ nuxt.options.srcDir,
336
+ join(runtimeDir, "internal/agent-environment-resolver.stub")
337
+ );
240
338
  const telaBuildPath = resolveTelaBuildLayer();
241
339
  const runtimeAliases = resolveRuntimeAliases(telaBuildPath);
340
+ const chatAuthMode = resolveChatAuthMode(nuxt.options.runtimeConfig);
341
+ const chatAuthTemplate = addTemplate({
342
+ filename: "chat-nuxt/auth.mjs",
343
+ getContents: () => buildChatAuthTemplate(chatAuthMode)
344
+ });
242
345
  if (!Array.isArray(nuxt.options.extends)) {
243
346
  nuxt.options.extends = nuxt.options.extends ? [nuxt.options.extends] : [];
244
347
  }
245
348
  nuxt.options.extends.unshift(telaBuildPath);
246
349
  nuxt.options.alias = {
247
350
  ...nuxt.options.alias,
351
+ "#chat-auth": chatAuthTemplate.dst,
352
+ "#chat-agent-environment-resolver": agentEnvironmentResolverEntry,
248
353
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry,
249
354
  "#chat-runtime": runtimeDir,
250
355
  "@iconify-json/ph": resolvePackageRoot("@iconify-json/ph")
@@ -349,6 +454,8 @@ export default globalThis.ELK;
349
454
  nitroConfig.scanDirs.push(serverDir);
350
455
  nitroConfig.alias = {
351
456
  ...nitroConfig.alias,
457
+ "#chat-auth": chatAuthTemplate.dst,
458
+ "#chat-agent-environment-resolver": agentEnvironmentResolverEntry,
352
459
  "#chat-runtime": runtimeDir,
353
460
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
354
461
  };
@@ -1,11 +1,12 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { parseContentWithFiles } from "../../utils/file";
3
4
  import ChatVaultLink from "./vault-link.vue";
4
5
  const props = defineProps({
5
6
  message: { type: Object, required: true }
6
7
  });
7
8
  const emit = defineEmits(["retry"]);
8
- const { user } = useTelaApplicationAuth();
9
+ const { user } = useChatAuth();
9
10
  const { getMemberByEmail } = useWorkspaceMembers();
10
11
  const embedConfig = useEmbedConfig();
11
12
  const mergedCustomComponents = computed(() => ({
@@ -1,4 +1,5 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { exportConversationToMarkdown } from "../utils/export-conversation.js";
3
4
  import { resolveChatStateScope } from "../utils/tela-chat.js";
4
5
  import { useConversationFileActions } from "./useConversationFiles.js";
@@ -150,7 +151,7 @@ function parseReasoningFromPolling(reasoning) {
150
151
  export function useChat() {
151
152
  const chatApi = useChatApi();
152
153
  const embedConfig = useEmbedConfig();
153
- const { user: authUser, activeOrganization } = useTelaApplicationAuth();
154
+ const { user: authUser, activeOrganization } = useChatAuth();
154
155
  const { addFilesToConversation } = useConversationFileActions();
155
156
  const isTelaAgentMode = computed(() => !!embedConfig?.telaAgentId.value);
156
157
  const chatScope = computed(() => resolveChatStateScope(
@@ -1,6 +1,7 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useChatApi() {
2
3
  const embedConfig = useEmbedConfig();
3
- const { activeOrganization } = useTelaApplicationAuth();
4
+ const { activeOrganization } = useChatAuth();
4
5
  function buildHeaders(headers) {
5
6
  const mergedHeaders = {
6
7
  ...headers ?? {}
@@ -1,4 +1,5 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { resolveChatStateScope } from "../utils/tela-chat.js";
3
4
  function sortConversationsByUpdatedAt(conversations) {
4
5
  return [...conversations].sort(
@@ -26,7 +27,7 @@ export function mergeFetchedConversations(fetchedConversations, localConversatio
26
27
  export function useConversations() {
27
28
  const chatApi = useChatApi();
28
29
  const embedConfig = useEmbedConfig();
29
- const { activeOrganization } = useTelaApplicationAuth();
30
+ const { activeOrganization } = useChatAuth();
30
31
  const conversationBuckets = useState("conversations", () => ({}));
31
32
  const loadingBuckets = useState("conversations-loading", () => ({}));
32
33
  const errorBuckets = useState("conversations-error", () => ({}));
@@ -1,7 +1,8 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  export function useGitHubSkills() {
3
4
  const config = useRuntimeConfig();
4
- const { activeOrganization } = useTelaApplicationAuth();
5
+ const { activeOrganization } = useChatAuth();
5
6
  const { settings, updateSettings } = useWorkspaceSettings();
6
7
  const connection = useState("github-skills-connection", () => ({
7
8
  connected: false,
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaceExternalSkills(currentExternalSkills, searchQuery) {
2
- const { user } = useTelaApplicationAuth();
3
+ const { user } = useChatAuth();
3
4
  const { connection: skillsConnection, fetchConnection: fetchGitHubConnection, connect: connectGitHub } = useGitHubSkills();
4
5
  const pendingSkills = ref([]);
5
6
  const removedRefs = ref([]);
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaceMembers() {
2
- const { activeOrganization } = useTelaApplicationAuth();
3
+ const { activeOrganization } = useChatAuth();
3
4
  const members = computed(() => activeOrganization.value?.members ?? []);
4
5
  const membersMap = computed(() => {
5
6
  const map = /* @__PURE__ */ new Map();
@@ -1,4 +1,5 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  function isNullableArray(value) {
3
4
  return value === null || Array.isArray(value);
4
5
  }
@@ -19,7 +20,7 @@ function assertWorkspaceSettingsResponse(value) {
19
20
  export function useWorkspaceSettings() {
20
21
  const chatApi = useChatApi();
21
22
  const embedConfig = useEmbedConfig();
22
- const { activeOrganization } = useTelaApplicationAuth();
23
+ const { activeOrganization } = useChatAuth();
23
24
  const workspaceScope = computed(() => embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim() || "default");
24
25
  const settingsBuckets = useState("workspace-settings", () => ({}));
25
26
  const loadingBuckets = useState("workspace-settings-loading", () => ({}));
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaces() {
2
- const { getAvailableOrganizations } = useTelaApplicationAuth();
3
+ const { getAvailableOrganizations } = useChatAuth();
3
4
  const { data: workspaces, status, refresh: refreshWorkspaces } = useAsyncData(
4
5
  "workspaces",
5
6
  () => getAvailableOrganizations(),
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { provideEmbedConfig, useEmbedConfig } from "../../composables/useEmbedConfig";
3
4
  import { provideChatAction } from "../../composables/useChatAction";
4
5
  import { normalizeTelaAgentId } from "../../utils/tela-chat";
@@ -19,7 +20,7 @@ const props = defineProps({
19
20
  });
20
21
  const emit = defineEmits(["update:conversationId", "action"]);
21
22
  const parentEmbedConfig = useEmbedConfig();
22
- const { activeOrganization } = useTelaApplicationAuth();
23
+ const { activeOrganization } = useChatAuth();
23
24
  const workspaceId = computed(() => activeOrganization.value?.id?.trim() ?? "");
24
25
  const normalizedTelaAgentId = computed(() => normalizeTelaAgentId(props.telaAgentId));
25
26
  const ignoredPropsWarningKey = ref(null);
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { exportConversationToMarkdown } from "../../utils/export-conversation";
3
4
  import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
4
5
  import {
@@ -16,7 +17,7 @@ const props = defineProps({
16
17
  const emit = defineEmits(["update:conversationId"]);
17
18
  const chatApi = useChatApi();
18
19
  const embedConfig = useEmbedConfig();
19
- const { user: authUser } = useTelaApplicationAuth();
20
+ const { user: authUser } = useChatAuth();
20
21
  const { getMember } = useWorkspaceMembers();
21
22
  const statusToast = useStatusToast();
22
23
  const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
@@ -0,0 +1,2 @@
1
+ declare const _default: null;
2
+ export default _default;
@@ -0,0 +1 @@
1
+ export default null;
@@ -90,8 +90,12 @@ export default defineEventHandler(async (event) => {
90
90
  const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
91
91
  event,
92
92
  context,
93
- { conversationId, messageId },
94
- "Failed to resolve workspace credentials on retry, proceeding without env vars"
93
+ {
94
+ conversationId,
95
+ skills: defaultAgentTurn.createPayload.skills ?? [],
96
+ logContext: { conversationId, messageId },
97
+ credentialsFailureMessage: "Failed to resolve workspace credentials on retry, proceeding without env vars"
98
+ }
95
99
  );
96
100
  const result = await createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables));
97
101
  if (result.success) {
@@ -115,8 +115,12 @@ export default defineEventHandler(async (event) => {
115
115
  const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
116
116
  event,
117
117
  context,
118
- { conversationId },
119
- "Failed to resolve workspace credentials, proceeding without env vars"
118
+ {
119
+ conversationId,
120
+ skills: defaultAgentTurn.createPayload.skills ?? [],
121
+ logContext: { conversationId },
122
+ credentialsFailureMessage: "Failed to resolve workspace credentials, proceeding without env vars"
123
+ }
120
124
  );
121
125
  createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables)).then(async (result) => {
122
126
  if (result.success) {
@@ -2,11 +2,12 @@ import { createError } from "h3";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { agentApiQueryResponseSchema, agentApiSessionSchema } from "#chat-runtime/types/agent";
4
4
  import { logger } from "#chat-runtime/server/utils/logger";
5
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
5
6
  const getConfig = () => useRuntimeConfig();
6
7
  export function getAgentDataToken(event) {
7
- const cookieToken = event.context.auth?.token;
8
- if (cookieToken) {
9
- return cookieToken;
8
+ const token = resolveAuthToken(event);
9
+ if (token) {
10
+ return token;
10
11
  }
11
12
  throw createError({
12
13
  statusCode: 401,
@@ -0,0 +1,8 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
3
+ export type AgentEnvironmentResolverContext = ChatRuntimeContext & {
4
+ conversationId: string;
5
+ skills: readonly string[];
6
+ };
7
+ export type AgentEnvironmentVariables = Record<string, string>;
8
+ export declare function resolveHostAgentEnvironmentVariables(event: H3Event, context: AgentEnvironmentResolverContext, logContext: Record<string, unknown>): Promise<AgentEnvironmentVariables>;
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import registeredAgentEnvironmentResolver from "#chat-agent-environment-resolver";
3
+ import { logger } from "#chat-runtime/server/utils/logger";
4
+ const agentEnvironmentVariablesSchema = z.record(z.string(), z.string()).nullable().optional();
5
+ function getRegisteredAgentEnvironmentResolver() {
6
+ const resolver = registeredAgentEnvironmentResolver;
7
+ return typeof resolver === "function" ? resolver : null;
8
+ }
9
+ function parseAgentEnvironmentVariables(value, logContext) {
10
+ const parsed = agentEnvironmentVariablesSchema.safeParse(value);
11
+ if (!parsed.success) {
12
+ logger.warn({
13
+ ...logContext,
14
+ issues: parsed.error.issues
15
+ }, "Agent environment resolver returned an invalid payload");
16
+ return {};
17
+ }
18
+ return parsed.data ?? {};
19
+ }
20
+ export async function resolveHostAgentEnvironmentVariables(event, context, logContext) {
21
+ const resolver = getRegisteredAgentEnvironmentResolver();
22
+ if (!resolver) {
23
+ return {};
24
+ }
25
+ try {
26
+ return parseAgentEnvironmentVariables(await resolver(event, context), logContext);
27
+ } catch (error) {
28
+ logger.warn({
29
+ ...logContext,
30
+ error
31
+ }, "Agent environment resolver failed, proceeding without host env vars");
32
+ return {};
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ import { type H3Event } from 'h3';
2
+ export declare function resolveAuthToken(event: H3Event): string | undefined;
@@ -0,0 +1,21 @@
1
+ import { getCookie } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ const DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME = "tela-jwt";
4
+ function resolveFirstPartyJwtCookieName(event) {
5
+ const config = useRuntimeConfig(event);
6
+ return config.public?.telaAuth?.jwtCookieName || DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME;
7
+ }
8
+ export function resolveAuthToken(event) {
9
+ const auth = event.context.auth;
10
+ if (auth?.token) {
11
+ return auth.token;
12
+ }
13
+ if (!auth?.user || !auth.workspace) {
14
+ return void 0;
15
+ }
16
+ const token = getCookie(event, resolveFirstPartyJwtCookieName(event));
17
+ if (token) {
18
+ auth.token = token;
19
+ }
20
+ return token;
21
+ }
@@ -34,6 +34,11 @@ export declare function prepareDefaultAgentTurn(event: H3Event, db: DbInstance,
34
34
  requestedModel?: string;
35
35
  workspaceSettingsSnapshot?: unknown;
36
36
  }): Promise<PreparedDefaultAgentTurn>;
37
- export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, logContext: Record<string, unknown>, failureMessage: string): Promise<Record<string, string>>;
37
+ export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, options: {
38
+ conversationId: string;
39
+ skills?: readonly string[];
40
+ logContext: Record<string, unknown>;
41
+ credentialsFailureMessage: string;
42
+ }): Promise<Record<string, string>>;
38
43
  export declare function withEnvironmentVariables(payload: AgentApiChatPayload, environmentVariables: Record<string, string>): AgentApiChatPayload;
39
44
  export {};
@@ -2,6 +2,7 @@ import { and, eq, isNull } from "drizzle-orm";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { schema } from "#chat-runtime/server/db";
4
4
  import { buildMessageWithContext, renderSystemPrompt, truncateTitle } from "#chat-runtime/server/utils/chat-message-builder";
5
+ import { resolveHostAgentEnvironmentVariables } from "#chat-runtime/server/utils/agent-environment";
5
6
  import {
6
7
  getConversationAppliedSettings,
7
8
  getWorkspaceSettingsUpdatedAt,
@@ -150,15 +151,33 @@ export async function prepareDefaultAgentTurn(event, db, context, options) {
150
151
  }
151
152
  };
152
153
  }
153
- export async function resolveDefaultAgentEnvironmentVariables(event, context, logContext, failureMessage) {
154
- if (!resolveChatNuxtFeatures(useRuntimeConfig()).credentials)
155
- return {};
156
- try {
157
- return await resolveWorkspaceCredentials(event, context.workspaceId);
158
- } catch (error) {
159
- logger.warn({ ...logContext, error }, failureMessage);
160
- return {};
154
+ export async function resolveDefaultAgentEnvironmentVariables(event, context, options) {
155
+ let credentialVariables = {};
156
+ const logContext = {
157
+ workspaceId: context.workspaceId,
158
+ conversationId: options.conversationId,
159
+ ...options.logContext
160
+ };
161
+ if (resolveChatNuxtFeatures(useRuntimeConfig()).credentials) {
162
+ try {
163
+ credentialVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
164
+ } catch (error) {
165
+ logger.warn({ ...logContext, error }, options.credentialsFailureMessage);
166
+ }
161
167
  }
168
+ const hostVariables = await resolveHostAgentEnvironmentVariables(
169
+ event,
170
+ {
171
+ ...context,
172
+ conversationId: options.conversationId,
173
+ skills: options.skills ?? []
174
+ },
175
+ logContext
176
+ );
177
+ return {
178
+ ...credentialVariables,
179
+ ...hostVariables
180
+ };
162
181
  }
163
182
  export function withEnvironmentVariables(payload, environmentVariables) {
164
183
  if (Object.keys(environmentVariables).length === 0)
@@ -1,9 +1,10 @@
1
1
  import { createError } from "h3";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { logger } from "#chat-runtime/server/utils/logger";
4
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
4
5
  export async function telaFetch(event, path, options) {
5
6
  const config = useRuntimeConfig();
6
- const token = event.context.auth?.token;
7
+ const token = resolveAuthToken(event);
7
8
  if (!token) {
8
9
  throw createError({ statusCode: 401, statusMessage: "No auth token" });
9
10
  }
@@ -1,6 +1,7 @@
1
1
  import { useRuntimeConfig } from "nitropack/runtime";
2
2
  import { eq } from "drizzle-orm";
3
3
  import { schema } from "#chat-runtime/server/db";
4
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
4
5
  import { telaFetch } from "#chat-runtime/server/utils/tela-api";
5
6
  export async function getWorkspaceSettings(db, workspaceId) {
6
7
  const [settings] = await db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, workspaceId));
@@ -8,6 +9,7 @@ export async function getWorkspaceSettings(db, workspaceId) {
8
9
  }
9
10
  export async function resolveWorkspaceCredentials(event, workspaceId) {
10
11
  const config = useRuntimeConfig();
12
+ const token = resolveAuthToken(event);
11
13
  return await telaFetch(
12
14
  event,
13
15
  `/workspaces/${workspaceId}/credentials/resolve`,
@@ -15,7 +17,7 @@ export async function resolveWorkspaceCredentials(event, workspaceId) {
15
17
  headers: {
16
18
  "X-Service-Name": "chat-app",
17
19
  "X-Service-Key": config.chatAppServiceKey,
18
- "x-data-token": event.context.auth?.token ?? ""
20
+ "x-data-token": token ?? ""
19
21
  }
20
22
  }
21
23
  ) ?? {};
@@ -0,0 +1,4 @@
1
+ declare module '#chat-agent-environment-resolver' {
2
+ const resolver: unknown
3
+ export default resolver
4
+ }
@@ -0,0 +1,35 @@
1
+ import type { Ref } from 'vue'
2
+
3
+ type ChatAuthUser = {
4
+ id: string
5
+ email?: string | null
6
+ name?: string | null
7
+ image?: string | null
8
+ }
9
+
10
+ type ChatAuthMemberUser = {
11
+ name: string
12
+ email: string
13
+ image?: string
14
+ }
15
+
16
+ type ChatAuthMember = {
17
+ id: string
18
+ userId: string
19
+ user: ChatAuthMemberUser
20
+ }
21
+
22
+ type ChatAuthOrganization = {
23
+ id: string
24
+ members?: ChatAuthMember[]
25
+ }
26
+
27
+ declare module '#chat-auth' {
28
+ export function useChatAuth(): {
29
+ user: Ref<ChatAuthUser | null>
30
+ activeOrganization: Ref<ChatAuthOrganization | null>
31
+ getToken: () => Promise<string | null | undefined>
32
+ getAvailableOrganizations: () => Promise<ChatAuthOrganization[]>
33
+ switchOrganization: (organizationId: string) => Promise<void>
34
+ }
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {