@d4y/agent-runtime-nuxt 0.1.7 → 0.1.8

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 (33) hide show
  1. package/dist/module.d.mts +15 -1
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +9 -2
  4. package/dist/runtime/composables/useAgentRuntime.d.ts +118 -1
  5. package/dist/runtime/composables/useAgentRuntime.js +220 -44
  6. package/dist/runtime/server/api/app.get.d.ts +3 -0
  7. package/dist/runtime/server/api/app.get.js +5 -3
  8. package/dist/runtime/server/api/conversations/[id]/abort.post.js +1 -1
  9. package/dist/runtime/server/api/conversations/[id]/env.post.js +1 -1
  10. package/dist/runtime/server/api/conversations/[id]/files/preview/[...path].get.js +1 -1
  11. package/dist/runtime/server/api/conversations/[id]/files/raw/[...path].get.js +1 -1
  12. package/dist/runtime/server/api/conversations/[id]/files.get.js +1 -1
  13. package/dist/runtime/server/api/conversations/[id]/files.post.d.ts +2 -0
  14. package/dist/runtime/server/api/conversations/[id]/files.post.js +34 -0
  15. package/dist/runtime/server/api/conversations/[id]/history.get.js +1 -1
  16. package/dist/runtime/server/api/conversations/[id]/messages.post.js +1 -1
  17. package/dist/runtime/server/api/conversations/[id]/runs/[runId].get.d.ts +2 -0
  18. package/dist/runtime/server/api/conversations/[id]/runs/[runId].get.js +19 -0
  19. package/dist/runtime/server/api/conversations/[id]/runs.get.d.ts +2 -0
  20. package/dist/runtime/server/api/conversations/[id]/runs.get.js +17 -0
  21. package/dist/runtime/server/api/conversations/[id]/stream.get.js +1 -1
  22. package/dist/runtime/server/api/conversations/[id]/workflow.get.d.ts +2 -0
  23. package/dist/runtime/server/api/conversations/[id]/workflow.get.js +17 -0
  24. package/dist/runtime/server/api/conversations/[id]/workflows.post.d.ts +2 -0
  25. package/dist/runtime/server/api/conversations/[id]/workflows.post.js +27 -0
  26. package/dist/runtime/server/api/conversations/[id].delete.js +1 -1
  27. package/dist/runtime/server/api/conversations.post.js +9 -2
  28. package/dist/runtime/server/utils/agent-runtime.d.ts +2 -7
  29. package/dist/runtime/server/utils/agent-runtime.js +11 -3
  30. package/dist/runtime/shared.d.ts +6 -0
  31. package/dist/runtime/shared.js +8 -4
  32. package/dist/types.d.mts +1 -1
  33. package/package.json +13 -13
package/dist/module.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- export { AppAuth, AppEnvField, AppInfo, ChatStatus, FileEntry, SendOptions, UIMessage, UiAction, UseAgentRuntime } from '../dist/runtime/composables/useAgentRuntime.js';
2
+ export { AppAuth, AppEnvField, AppInfo, AppStartField, AppStartSpec, ChatStatus, FileEntry, RunKind, RunRecord, RunStatus, SendOptions, UIMessage, UiAction, UploadFilesOptions, UseAgentRuntime, UseAgentRuntimeOptions, WorkflowStartOptions } from '../dist/runtime/composables/useAgentRuntime.js';
3
3
  export { AgentRuntimeMarkdownRenderOptions } from '../dist/runtime/composables/useAgentRuntimeMarkdown.js';
4
4
  export { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind, AgentRuntimeResolvedAsset } from '../dist/runtime/utils/files.js';
5
5
  export { createAgentRuntimeRequestHeaders, createScopeFingerprint, normalizeAgentRuntimeBaseUrl, resolveAgentRuntimeConfig } from '../dist/runtime/shared.js';
@@ -35,6 +35,15 @@ interface ModuleOptions {
35
35
  * Falls back to `process.env.AGENT_RUNTIME_APP_ID` then `omnisearch`.
36
36
  */
37
37
  appId?: string;
38
+ /**
39
+ * Additional server-side app bindings. This lets one Nuxt app render chats
40
+ * for multiple agent-runtime apps without exposing app keys to the browser.
41
+ */
42
+ apps?: Record<string, {
43
+ baseUrl?: string;
44
+ appKey?: string;
45
+ appId?: string;
46
+ }>;
38
47
  /**
39
48
  * URL prefix for the auto-registered Nitro proxy routes. Change it if you
40
49
  * need to disambiguate from another module on the same `/api/...` namespace.
@@ -55,6 +64,11 @@ interface PrivateAgentRuntimeConfig {
55
64
  baseUrl: string;
56
65
  appKey: string;
57
66
  appId: string;
67
+ apps?: Record<string, {
68
+ baseUrl?: string;
69
+ appKey?: string;
70
+ appId?: string;
71
+ }>;
58
72
  }
59
73
  declare module '@nuxt/schema' {
60
74
  interface RuntimeConfig {
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.13.0"
6
6
  },
7
- "version": "0.1.7",
7
+ "version": "0.1.8",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -20,10 +20,12 @@ const module$1 = defineNuxtModule({
20
20
  const appKey = userOptions.appKey ?? process.env.AGENT_RUNTIME_APP_KEY ?? "";
21
21
  const appId = userOptions.appId ?? process.env.AGENT_RUNTIME_APP_ID ?? DEFAULTS.appId;
22
22
  const apiPrefix = (userOptions.apiPrefix ?? DEFAULTS.apiPrefix).replace(/\/+$/, "");
23
+ const apps = userOptions.apps ?? {};
23
24
  nuxt.options.runtimeConfig.agentRuntime = defu(nuxt.options.runtimeConfig.agentRuntime, {
24
25
  baseUrl,
25
26
  appKey,
26
- appId
27
+ appId,
28
+ apps
27
29
  });
28
30
  nuxt.options.runtimeConfig.public.agentRuntime = defu(nuxt.options.runtimeConfig.public.agentRuntime, {
29
31
  apiPrefix,
@@ -34,7 +36,7 @@ const module$1 = defineNuxtModule({
34
36
  filename: "types/agent-runtime.d.ts",
35
37
  getContents: () => [
36
38
  `// Auto-generated by @d4y/agent-runtime-nuxt`,
37
- `export type { UseAgentRuntime, AppInfo, AppAuth, AppEnvField, FileEntry, UiAction, SendOptions, ChatStatus } from '${resolver.resolve("./runtime/composables/useAgentRuntime")}'`,
39
+ `export type { UseAgentRuntime, UseAgentRuntimeOptions, AppInfo, AppStartSpec, AppStartField, AppAuth, AppEnvField, FileEntry, UiAction, RunKind, RunStatus, RunRecord, SendOptions, WorkflowStartOptions, UploadFilesOptions, ChatStatus } from '${resolver.resolve("./runtime/composables/useAgentRuntime")}'`,
38
40
  `export type { AgentRuntimeMarkdownRenderOptions } from '${resolver.resolve("./runtime/composables/useAgentRuntimeMarkdown")}'`,
39
41
  `export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind, AgentRuntimeResolvedAsset } from '${resolver.resolve("./runtime/utils/files")}'`,
40
42
  ""
@@ -51,9 +53,14 @@ const module$1 = defineNuxtModule({
51
53
  route("/conversations/:id/history", "conversations/[id]/history.get", "get");
52
54
  route("/conversations/:id/stream", "conversations/[id]/stream.get", "get");
53
55
  route("/conversations/:id/messages", "conversations/[id]/messages.post", "post");
56
+ route("/conversations/:id/workflows", "conversations/[id]/workflows.post", "post");
54
57
  route("/conversations/:id/abort", "conversations/[id]/abort.post", "post");
55
58
  route("/conversations/:id/env", "conversations/[id]/env.post", "post");
59
+ route("/conversations/:id/workflow", "conversations/[id]/workflow.get", "get");
60
+ route("/conversations/:id/runs", "conversations/[id]/runs.get", "get");
61
+ route("/conversations/:id/runs/:runId", "conversations/[id]/runs/[runId].get", "get");
56
62
  route("/conversations/:id/files", "conversations/[id]/files.get", "get");
63
+ route("/conversations/:id/files", "conversations/[id]/files.post", "post");
57
64
  route("/conversations/:id/files/preview/**", "conversations/[id]/files/preview/[...path].get", "get");
58
65
  route("/conversations/:id/files/raw/**", "conversations/[id]/files/raw/[...path].get", "get");
59
66
  }
@@ -16,12 +16,47 @@ export interface AppEnvField {
16
16
  required: boolean;
17
17
  secret: boolean;
18
18
  description?: string;
19
+ default?: string;
20
+ }
21
+ export interface AppStartField {
22
+ type?: 'string' | 'number' | 'boolean' | 'array';
23
+ items?: 'string' | 'number' | 'boolean';
24
+ title?: string;
25
+ description?: string;
26
+ placeholder?: string;
27
+ enum?: string[];
28
+ options?: Array<{
29
+ label: string;
30
+ value: string;
31
+ description?: string;
32
+ disabled?: boolean;
33
+ disabledReason?: string;
34
+ }>;
35
+ default?: string | number | boolean | Array<string | number | boolean>;
36
+ }
37
+ export interface AppStartSpec {
38
+ mode: 'interactive' | 'workflow';
39
+ label?: string;
40
+ description?: string;
41
+ inputSchema?: {
42
+ type?: 'object';
43
+ required?: string[];
44
+ properties?: Record<string, AppStartField>;
45
+ };
46
+ workflow?: {
47
+ name?: string;
48
+ initialPrompt?: string;
49
+ runner?: 'autonomous' | 'supervisor';
50
+ maxIterations?: number;
51
+ };
19
52
  }
20
53
  /** Manifest summary for the active app, as returned by `${apiPrefix}/app`. */
21
54
  export interface AppInfo {
22
55
  appId: string;
23
56
  name: string;
24
57
  envSchema: Record<string, AppEnvField>;
58
+ start?: AppStartSpec;
59
+ workflows?: Record<string, unknown>;
25
60
  }
26
61
  /**
27
62
  * Generic auth state. Keys are app-defined (whatever the active app declares
@@ -43,6 +78,38 @@ export interface ContextUsage {
43
78
  contextWindow: number;
44
79
  percent: number | null;
45
80
  }
81
+ export type RunKind = 'workflow' | 'subagent';
82
+ export type RunStatus = 'queued' | 'running' | 'completed' | 'failed' | 'aborted';
83
+ export interface RunRecord {
84
+ schemaVersion: 1;
85
+ runId: string;
86
+ conversationId: string;
87
+ appId: string;
88
+ kind: RunKind;
89
+ status: RunStatus;
90
+ createdAt: string;
91
+ updatedAt: string;
92
+ startedAt?: string;
93
+ finishedAt?: string;
94
+ workflow?: string;
95
+ runner?: string;
96
+ iteration?: number;
97
+ maxIterations?: number;
98
+ agent?: string;
99
+ step?: string;
100
+ task?: string;
101
+ mode?: string;
102
+ parentRunId?: string;
103
+ parentToolCallId?: string;
104
+ parentToolName?: string;
105
+ edgeKind?: string;
106
+ edgeLabel?: string;
107
+ graphOrder?: number;
108
+ result?: unknown;
109
+ reason?: string;
110
+ error?: string;
111
+ sessionFile?: string;
112
+ }
46
113
  export interface CompactionLogEntry {
47
114
  phase: 'start' | 'end';
48
115
  at: number;
@@ -96,11 +163,50 @@ export interface SendOptions {
96
163
  }
97
164
  /** Options for `start`. */
98
165
  export interface StartOptions {
166
+ /** Conversation mode. `workflow` starts an autonomous run from `initialPrompt` or `workflow` input. */
167
+ chatType?: 'interactive' | 'workflow';
168
+ /** Initial autonomous workflow prompt. Used when `chatType` is `workflow`. */
169
+ initialPrompt?: string;
170
+ /** Optional named workflow input for autonomous conversations. */
171
+ workflow?: {
172
+ name?: string;
173
+ input?: unknown;
174
+ runner?: 'autonomous' | 'supervisor';
175
+ maxIterations?: number;
176
+ };
177
+ /** Optional previous conversation to use as context for a follow-up chat. */
178
+ parentConversationId?: string;
179
+ /** How much previous-conversation context to inject. */
180
+ parentContextMode?: 'summary+artifacts' | 'full-history' | 'artifacts-only';
99
181
  /** Optional language override for the newly created conversation. */
100
182
  language?: string;
101
183
  /** Optional default provider request options for the conversation. */
102
184
  requestOptions?: RequestOptions;
103
185
  }
186
+ export interface WorkflowStartOptions {
187
+ workflow: {
188
+ name?: string;
189
+ input?: unknown;
190
+ runner?: 'autonomous' | 'supervisor';
191
+ maxIterations?: number;
192
+ };
193
+ initialPrompt?: string;
194
+ /** Optional language override for the workflow turn. */
195
+ language?: string;
196
+ /** Optional provider request overrides for the workflow turn. */
197
+ requestOptions?: RequestOptions;
198
+ model?: unknown;
199
+ }
200
+ export interface UploadFilesOptions {
201
+ targetDir?: string;
202
+ }
203
+ type MaybeRef<T> = T | Ref<T> | ComputedRef<T>;
204
+ export interface UseAgentRuntimeOptions {
205
+ /** Override the proxy prefix. Admin pages can point this at an admin-backed proxy. */
206
+ apiPrefix?: MaybeRef<string | null | undefined>;
207
+ /** Optional active app id. When it changes, the composable loads that app and starts fresh chat state. */
208
+ appId?: MaybeRef<string | null | undefined>;
209
+ }
104
210
  /** Return shape of {@link useAgentRuntime}. All members are reactive. */
105
211
  export interface UseAgentRuntime {
106
212
  /** Active conversation id, or `null` until `start()` (or the first `send()`) succeeds. */
@@ -127,12 +233,22 @@ export interface UseAgentRuntime {
127
233
  authReady: ComputedRef<boolean>;
128
234
  /** Workspace files for the current conversation. Refreshed on every stream `end` event. */
129
235
  files: Ref<FileEntry[]>;
236
+ /** Persisted workflow/subagent runs for the current conversation. */
237
+ runs: Ref<RunRecord[]>;
130
238
  /** Build the proxied download URL for a workspace-relative path. */
131
239
  fileUrl: (relPath: string) => string;
132
240
  /** Build the proxied preview URL for a workspace-relative path. */
133
241
  filePreviewUrl: (relPath: string) => string;
134
242
  /** Manually re-fetch the workspace file listing. */
135
243
  refreshFiles: () => Promise<void>;
244
+ /** Manually re-fetch persisted run records. */
245
+ refreshRuns: () => Promise<void>;
246
+ /** Open an existing conversation, load its history, and attach to the live event stream. */
247
+ open: (conversationId: string) => Promise<void>;
248
+ /** Upload user files into this conversation workspace. */
249
+ uploadFiles: (files: File | File[] | FileList, options?: UploadFilesOptions) => Promise<FileEntry[]>;
250
+ /** Start a named workflow inside the active conversation. */
251
+ startWorkflow: (options: WorkflowStartOptions) => Promise<void>;
136
252
  /** Persist a new auth map; if a conversation is open, push the change to its env. */
137
253
  saveAuth: (next: AppAuth) => Promise<void>;
138
254
  /** Open a fresh conversation with the current auth. Returns the new id. */
@@ -169,4 +285,5 @@ export interface UseAgentRuntime {
169
285
  * </template>
170
286
  * ```
171
287
  */
172
- export declare const useAgentRuntime: () => UseAgentRuntime;
288
+ export declare const useAgentRuntime: (options?: UseAgentRuntimeOptions) => UseAgentRuntime;
289
+ export {};
@@ -1,4 +1,4 @@
1
- import { computed, onMounted, ref, shallowRef } from "vue";
1
+ import { computed, onMounted, ref, shallowRef, unref, watch } from "vue";
2
2
  import { useRuntimeConfig } from "nuxt/app";
3
3
  const newId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `id-${Date.now()}-${Math.random().toString(36).slice(2)}`;
4
4
  const authStorageKey = (appId) => `agent-runtime.app-auth.${appId}`;
@@ -35,9 +35,21 @@ const computeAuthReady = (auth, app) => {
35
35
  return required.every((k) => (auth.values[k] ?? "").trim().length > 0);
36
36
  };
37
37
  const encodeRelPath = (relPath) => relPath.split("/").map(encodeURIComponent).join("/");
38
- export const useAgentRuntime = () => {
38
+ export const useAgentRuntime = (options = {}) => {
39
39
  const cfg = useRuntimeConfig().public.agentRuntime;
40
- const apiPrefix = (cfg?.apiPrefix ?? "/api/agent-runtime").replace(/\/+$/, "");
40
+ const apiPrefix = computed(() => {
41
+ const raw = options.apiPrefix === void 0 ? cfg?.apiPrefix : unref(options.apiPrefix);
42
+ return `${raw || "/api/agent-runtime"}`.replace(/\/+$/, "");
43
+ });
44
+ const selectedAppId = computed(() => {
45
+ const raw = options.appId === void 0 ? cfg?.appId : unref(options.appId);
46
+ return `${raw ?? ""}`.trim();
47
+ });
48
+ const withAppQuery = (url) => {
49
+ const appId = selectedAppId.value;
50
+ if (!appId) return url;
51
+ return `${url}${url.includes("?") ? "&" : "?"}appId=${encodeURIComponent(appId)}`;
52
+ };
41
53
  const conversationId = ref(null);
42
54
  const messages = shallowRef([]);
43
55
  const status = ref("idle");
@@ -49,25 +61,69 @@ export const useAgentRuntime = () => {
49
61
  const app = ref(null);
50
62
  const auth = ref(emptyAuth());
51
63
  const authReady = computed(() => computeAuthReady(auth.value, app.value));
52
- onMounted(async () => {
64
+ let abortController = null;
65
+ let messageAbortController = null;
66
+ let currentAssistantId = null;
67
+ const clearLocalConversation = () => {
68
+ abortController?.abort();
69
+ abortController = null;
70
+ messageAbortController?.abort();
71
+ messageAbortController = null;
72
+ conversationId.value = null;
73
+ currentAssistantId = null;
74
+ replaceMessages([]);
75
+ uiActions.value = [];
76
+ contextUsage.value = null;
77
+ compactionInProgress.value = false;
78
+ compactionLog.value = [];
79
+ files.value = [];
80
+ runs.value = [];
81
+ status.value = "idle";
82
+ error.value = null;
83
+ };
84
+ const appUrl = () => {
85
+ const appId = selectedAppId.value;
86
+ const suffix = appId ? `?appId=${encodeURIComponent(appId)}` : "";
87
+ return `${apiPrefix.value}/app${suffix}`;
88
+ };
89
+ let appLoadSeq = 0;
90
+ const loadApp = async () => {
91
+ const seq = ++appLoadSeq;
92
+ if (options.appId !== void 0 && !selectedAppId.value) {
93
+ app.value = null;
94
+ auth.value = emptyAuth();
95
+ clearLocalConversation();
96
+ return;
97
+ }
53
98
  try {
54
- const info = await $fetch(`${apiPrefix}/app`);
99
+ const info = await $fetch(appUrl());
100
+ if (seq !== appLoadSeq) return;
55
101
  app.value = info;
56
102
  auth.value = loadAuth(info.appId);
57
103
  } catch (err) {
104
+ if (seq !== appLoadSeq) return;
58
105
  console.warn("[agent-runtime] failed to load app manifest", err);
106
+ app.value = null;
107
+ auth.value = emptyAuth();
59
108
  }
109
+ };
110
+ onMounted(loadApp);
111
+ watch(selectedAppId, async (next, previous) => {
112
+ if (next === previous) return;
113
+ clearLocalConversation();
114
+ await loadApp();
60
115
  });
61
116
  const files = ref([]);
117
+ const runs = ref([]);
62
118
  const fileUrl = (relPath) => {
63
119
  const cid = conversationId.value;
64
120
  if (!cid) return "";
65
- return `${apiPrefix}/conversations/${cid}/files/raw/${encodeRelPath(relPath)}`;
121
+ return withAppQuery(`${apiPrefix.value}/conversations/${cid}/files/raw/${encodeRelPath(relPath)}`);
66
122
  };
67
123
  const filePreviewUrl = (relPath) => {
68
124
  const cid = conversationId.value;
69
125
  if (!cid) return "";
70
- return `${apiPrefix}/conversations/${cid}/files/preview/${encodeRelPath(relPath)}`;
126
+ return withAppQuery(`${apiPrefix.value}/conversations/${cid}/files/preview/${encodeRelPath(relPath)}`);
71
127
  };
72
128
  const refreshFiles = async () => {
73
129
  const cid = conversationId.value;
@@ -76,14 +132,29 @@ export const useAgentRuntime = () => {
76
132
  return;
77
133
  }
78
134
  try {
79
- const res = await $fetch(`${apiPrefix}/conversations/${cid}/files`);
135
+ const res = await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${cid}/files`));
80
136
  files.value = res.items;
81
137
  } catch (err) {
82
138
  console.warn("[agent-runtime] file list failed", err);
83
139
  }
84
140
  };
85
- let abortController = null;
86
- let currentAssistantId = null;
141
+ const refreshRuns = async () => {
142
+ const cid = conversationId.value;
143
+ if (!cid) {
144
+ runs.value = [];
145
+ return;
146
+ }
147
+ try {
148
+ const res = await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${cid}/runs`));
149
+ runs.value = res.runs;
150
+ } catch (err) {
151
+ console.warn("[agent-runtime] run list failed", err);
152
+ }
153
+ };
154
+ const upsertRun = (run) => {
155
+ const next = [run, ...runs.value.filter((item) => item.runId !== run.runId)];
156
+ runs.value = next.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
157
+ };
87
158
  const replaceMessages = (next) => {
88
159
  messages.value = next;
89
160
  };
@@ -114,13 +185,24 @@ export const useAgentRuntime = () => {
114
185
  });
115
186
  };
116
187
  const upsertToolPart = (toolName, toolCallId, mut) => {
188
+ let found = false;
189
+ const nextMessages = messages.value.map((message) => {
190
+ if (message.role !== "assistant") return message;
191
+ const parts = message.parts.slice();
192
+ const idx = parts.findIndex((p) => p.type === `tool-${toolName}` && p.toolCallId === toolCallId);
193
+ if (idx < 0) return message;
194
+ found = true;
195
+ parts[idx] = mut(parts[idx]);
196
+ return { ...message, parts };
197
+ });
198
+ if (found) {
199
+ replaceMessages(nextMessages);
200
+ return;
201
+ }
117
202
  ensureAssistant();
118
203
  updateAssistant((msg) => {
119
204
  const parts = msg.parts.slice();
120
- const idx = parts.findIndex((p) => p.type === `tool-${toolName}` && p.toolCallId === toolCallId);
121
- const next = mut(idx >= 0 ? parts[idx] : void 0);
122
- if (idx >= 0) parts[idx] = next;
123
- else parts.push(next);
205
+ parts.push(mut(void 0));
124
206
  return { ...msg, parts };
125
207
  });
126
208
  };
@@ -208,11 +290,21 @@ export const useAgentRuntime = () => {
208
290
  uiActions.value = [{ toolCallId: d.toolCallId, type: d.type, payload: d.payload, at: Date.now() }, ...uiActions.value].slice(0, 50);
209
291
  break;
210
292
  }
293
+ case "run_started":
294
+ case "run_updated":
295
+ case "run_completed":
296
+ case "run_failed":
297
+ case "run_aborted": {
298
+ const d = data;
299
+ if (d.run?.runId) upsertRun(d.run);
300
+ break;
301
+ }
211
302
  case "end":
212
303
  compactionInProgress.value = false;
213
304
  status.value = "ready";
214
305
  currentAssistantId = null;
215
306
  void refreshFiles();
307
+ void refreshRuns();
216
308
  break;
217
309
  case "error": {
218
310
  const d = data;
@@ -227,7 +319,7 @@ export const useAgentRuntime = () => {
227
319
  }
228
320
  };
229
321
  const consume = async (signal) => {
230
- const res = await fetch(`${apiPrefix}/conversations/${conversationId.value}/stream`, { signal });
322
+ const res = await fetch(withAppQuery(`${apiPrefix.value}/conversations/${conversationId.value}/stream`), { signal });
231
323
  if (!res.ok || !res.body) throw new Error(`stream failed: ${res.status}`);
232
324
  const reader = res.body.getReader();
233
325
  const decoder = new TextDecoder();
@@ -259,7 +351,17 @@ export const useAgentRuntime = () => {
259
351
  }
260
352
  }
261
353
  };
262
- const start = async (options = {}) => {
354
+ const openStream = () => {
355
+ abortController?.abort();
356
+ abortController = new AbortController();
357
+ const signal = abortController.signal;
358
+ consume(signal).catch((err) => {
359
+ if (signal.aborted) return;
360
+ error.value = err instanceof Error ? err : new Error(String(err));
361
+ status.value = "error";
362
+ });
363
+ };
364
+ const start = async (options2 = {}) => {
263
365
  if (!authReady.value) {
264
366
  const missing = app.value ? Object.entries(app.value.envSchema).filter(([k, f]) => f.required && !(auth.value.values[k] ?? "").trim()).map(([k]) => k) : [];
265
367
  throw new Error(
@@ -267,31 +369,79 @@ export const useAgentRuntime = () => {
267
369
  );
268
370
  }
269
371
  error.value = null;
270
- const res = await $fetch(`${apiPrefix}/conversations`, {
372
+ const res = await $fetch(`${apiPrefix.value}/conversations`, {
271
373
  method: "POST",
272
374
  body: {
273
375
  env: buildEnvFromAuth(auth.value),
274
- language: options.language,
275
- requestOptions: options.requestOptions
376
+ appId: (app.value?.appId ?? selectedAppId.value) || void 0,
377
+ chatType: options2.chatType,
378
+ initialPrompt: options2.initialPrompt,
379
+ workflow: options2.workflow,
380
+ parentConversationId: options2.parentConversationId,
381
+ parentContextMode: options2.parentContextMode,
382
+ language: options2.language,
383
+ requestOptions: options2.requestOptions
276
384
  }
277
385
  });
278
386
  conversationId.value = res.conversationId;
279
- abortController?.abort();
280
- abortController = new AbortController();
281
- const signal = abortController.signal;
282
- consume(signal).catch((err) => {
283
- if (signal.aborted) return;
284
- error.value = err instanceof Error ? err : new Error(String(err));
285
- status.value = "error";
286
- });
387
+ openStream();
287
388
  void refreshFiles();
389
+ void refreshRuns();
288
390
  return res.conversationId;
289
391
  };
392
+ const open = async (id) => {
393
+ const nextId = id.trim();
394
+ if (!nextId) return;
395
+ abortController?.abort();
396
+ abortController = null;
397
+ messageAbortController?.abort();
398
+ messageAbortController = null;
399
+ error.value = null;
400
+ status.value = "ready";
401
+ currentAssistantId = null;
402
+ conversationId.value = nextId;
403
+ uiActions.value = [];
404
+ contextUsage.value = null;
405
+ compactionInProgress.value = false;
406
+ compactionLog.value = [];
407
+ const history = await $fetch(
408
+ withAppQuery(`${apiPrefix.value}/conversations/${nextId}/history`)
409
+ );
410
+ replaceMessages(history.messages ?? []);
411
+ openStream();
412
+ await Promise.all([refreshFiles(), refreshRuns()]);
413
+ };
414
+ const uploadFiles = async (filesInput, options2 = {}) => {
415
+ const cid = conversationId.value;
416
+ if (!cid) throw new Error("Start or open a conversation before uploading files.");
417
+ const filesArray = typeof FileList !== "undefined" && filesInput instanceof FileList ? Array.from(filesInput) : Array.isArray(filesInput) ? filesInput : [filesInput];
418
+ const form = new FormData();
419
+ if (options2.targetDir) form.append("targetDir", options2.targetDir);
420
+ for (const file of filesArray) form.append("files", file, file.name);
421
+ const res = await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${cid}/files`), {
422
+ method: "POST",
423
+ body: form
424
+ });
425
+ await refreshFiles();
426
+ return res.items;
427
+ };
428
+ const startWorkflow = async (options2) => {
429
+ const cid = conversationId.value;
430
+ if (!cid) throw new Error("Start or open a conversation before running a workflow.");
431
+ error.value = null;
432
+ status.value = "submitted";
433
+ if (!abortController) openStream();
434
+ await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${cid}/workflows`), {
435
+ method: "POST",
436
+ body: options2
437
+ });
438
+ await refreshRuns();
439
+ };
290
440
  const saveAuth = async (next) => {
291
441
  auth.value = { values: { ...next.values } };
292
442
  if (app.value) persistAuth(app.value.appId, auth.value);
293
443
  if (conversationId.value) {
294
- await $fetch(`${apiPrefix}/conversations/${conversationId.value}/env`, {
444
+ await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${conversationId.value}/env`), {
295
445
  method: "POST",
296
446
  body: { env: buildEnvFromAuth(auth.value), merge: true }
297
447
  }).catch((err) => {
@@ -299,43 +449,63 @@ export const useAgentRuntime = () => {
299
449
  });
300
450
  }
301
451
  };
302
- const send = async (text, options = {}) => {
452
+ const send = async (text, options2 = {}) => {
303
453
  if (!text.trim()) return;
304
454
  if (!conversationId.value) {
305
455
  try {
306
- await start({ language: options.language });
456
+ await start({ language: options2.language });
307
457
  } catch (err) {
308
458
  error.value = err instanceof Error ? err : new Error(String(err));
309
459
  status.value = "error";
310
460
  return;
311
461
  }
462
+ } else if (!abortController) {
463
+ openStream();
312
464
  }
313
465
  error.value = null;
314
466
  status.value = "submitted";
315
467
  currentAssistantId = null;
468
+ messageAbortController?.abort();
469
+ const messageController = new AbortController();
470
+ messageAbortController = messageController;
316
471
  replaceMessages([
317
472
  ...messages.value,
318
473
  { id: newId(), role: "user", parts: [{ type: "text", text }] }
319
474
  ]);
320
- const wireContent = options.rewriteContent ? options.rewriteContent(text) : text;
321
- await $fetch(`${apiPrefix}/conversations/${conversationId.value}/messages`, {
322
- method: "POST",
323
- body: {
324
- content: wireContent,
325
- context: options.context,
326
- language: options.language,
327
- requestOptions: options.requestOptions
328
- }
329
- }).catch((err) => {
475
+ const wireContent = options2.rewriteContent ? options2.rewriteContent(text) : text;
476
+ try {
477
+ await $fetch(withAppQuery(`${apiPrefix.value}/conversations/${conversationId.value}/messages`), {
478
+ method: "POST",
479
+ signal: messageController.signal,
480
+ body: {
481
+ content: wireContent,
482
+ context: options2.context,
483
+ language: options2.language,
484
+ requestOptions: options2.requestOptions
485
+ }
486
+ });
487
+ } catch (err) {
488
+ if (messageController.signal.aborted) return;
330
489
  error.value = err instanceof Error ? err : new Error(String(err));
331
490
  status.value = "error";
332
- });
491
+ } finally {
492
+ if (messageAbortController === messageController) {
493
+ messageAbortController = null;
494
+ }
495
+ }
333
496
  };
334
497
  const abort = async () => {
335
- if (!conversationId.value) return;
336
- await $fetch(`${apiPrefix}/conversations/${conversationId.value}/abort`, { method: "POST" }).catch(() => {
337
- });
498
+ const cid = conversationId.value;
499
+ if (!cid) return;
500
+ abortController?.abort();
501
+ abortController = null;
502
+ messageAbortController?.abort();
503
+ messageAbortController = null;
504
+ compactionInProgress.value = false;
338
505
  status.value = "ready";
506
+ currentAssistantId = null;
507
+ void $fetch(withAppQuery(`${apiPrefix.value}/conversations/${cid}/abort`), { method: "POST" }).catch(() => {
508
+ });
339
509
  };
340
510
  const reset = async () => {
341
511
  abortController?.abort();
@@ -348,6 +518,7 @@ export const useAgentRuntime = () => {
348
518
  compactionInProgress.value = false;
349
519
  compactionLog.value = [];
350
520
  files.value = [];
521
+ runs.value = [];
351
522
  status.value = "idle";
352
523
  error.value = null;
353
524
  };
@@ -367,9 +538,14 @@ export const useAgentRuntime = () => {
367
538
  auth,
368
539
  authReady,
369
540
  files,
541
+ runs,
370
542
  fileUrl,
371
543
  filePreviewUrl,
372
544
  refreshFiles,
545
+ refreshRuns,
546
+ open,
547
+ uploadFiles,
548
+ startWorkflow,
373
549
  saveAuth,
374
550
  start,
375
551
  send,
@@ -2,6 +2,7 @@ interface AppEnvField {
2
2
  required?: boolean;
3
3
  secret?: boolean;
4
4
  description?: string;
5
+ default?: string;
5
6
  }
6
7
  /**
7
8
  * GET `${apiPrefix}/app` — returns the manifest of the configured app
@@ -11,5 +12,7 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
11
12
  appId: string;
12
13
  name: string;
13
14
  envSchema: Record<string, AppEnvField>;
15
+ start: unknown;
16
+ workflows: Record<string, unknown>;
14
17
  }>>;
15
18
  export default _default;
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler } from "h3";
2
2
  import { agentRuntime, agentRuntimeHeaders } from "../utils/agent-runtime.js";
3
- export default defineEventHandler(async () => {
4
- const cfg = agentRuntime();
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
5
  const response = await fetch(`${cfg.baseUrl}/v1/apps`, {
6
6
  headers: agentRuntimeHeaders(cfg)
7
7
  });
@@ -22,6 +22,8 @@ export default defineEventHandler(async () => {
22
22
  return {
23
23
  appId: app.appId,
24
24
  name: app.name,
25
- envSchema: app.envSchema ?? {}
25
+ envSchema: app.envSchema ?? {},
26
+ start: app.start,
27
+ workflows: app.workflows ?? {}
26
28
  };
27
29
  });
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam, setResponseStatus } from "h3";
2
2
  import { agentRuntime, agentRuntimeHeaders } from "../../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/abort`, {
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
2
  import { agentRuntime, agentRuntimeHeaders } from "../../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const body = await readBody(event).catch(() => ({}));
@@ -11,7 +11,7 @@ const PASS_THROUGH_HEADERS = [
11
11
  "x-content-type-options"
12
12
  ];
13
13
  export default defineEventHandler(async (event) => {
14
- const cfg = agentRuntime();
14
+ const cfg = agentRuntime(event);
15
15
  const id = getRouterParam(event, "id");
16
16
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
17
17
  const apiPrefix = (useRuntimeConfig().public.agentRuntime?.apiPrefix ?? "/api/agent-runtime").replace(/\/+$/, "");
@@ -4,7 +4,7 @@ import { createError, defineEventHandler, getRouterParam, sendStream, setHeader
4
4
  import { agentRuntime } from "../../../../../utils/agent-runtime.js";
5
5
  const PASS_THROUGH_HEADERS = ["content-type", "content-length", "content-disposition", "cache-control"];
6
6
  export default defineEventHandler(async (event) => {
7
- const cfg = agentRuntime();
7
+ const cfg = agentRuntime(event);
8
8
  const id = getRouterParam(event, "id");
9
9
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
10
10
  const apiPrefix = (useRuntimeConfig().public.agentRuntime?.apiPrefix ?? "/api/agent-runtime").replace(/\/+$/, "");
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam } from "h3";
2
2
  import { agentRuntime } from "../../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/files`, {
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,34 @@
1
+ import { createError, defineEventHandler, getRouterParam, readMultipartFormData } from "h3";
2
+ import { agentRuntime } from "../../../utils/agent-runtime.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
+ const id = getRouterParam(event, "id");
6
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
+ const parts = await readMultipartFormData(event);
8
+ if (!parts?.length) throw createError({ statusCode: 400, statusMessage: "expected multipart/form-data" });
9
+ const form = new FormData();
10
+ for (const part of parts) {
11
+ if (!part.name) continue;
12
+ if (part.filename) {
13
+ const bytes = new Uint8Array(part.data.length);
14
+ bytes.set(part.data);
15
+ form.append(part.name, new Blob([bytes], { type: part.type || "application/octet-stream" }), part.filename);
16
+ } else {
17
+ form.append(part.name, part.data.toString("utf8"));
18
+ }
19
+ }
20
+ const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/files`, {
21
+ method: "POST",
22
+ headers: {
23
+ "X-Agent-Runtime-App-Key": cfg.appKey
24
+ },
25
+ body: form
26
+ });
27
+ if (!response.ok) {
28
+ throw createError({
29
+ statusCode: response.status,
30
+ statusMessage: await response.text()
31
+ });
32
+ }
33
+ return await response.json();
34
+ });
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam } from "h3";
2
2
  import { agentRuntime } from "../../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/history`, {
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
2
  import { agentRuntime, agentRuntimeHeaders } from "../../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const body = await readBody(event);
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { createError, defineEventHandler, getRouterParam } from "h3";
2
+ import { agentRuntime } from "../../../../utils/agent-runtime.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
+ const id = getRouterParam(event, "id");
6
+ const runId = getRouterParam(event, "runId");
7
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
8
+ if (!runId) throw createError({ statusCode: 400, statusMessage: "missing run id" });
9
+ const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/runs/${runId}`, {
10
+ headers: { "X-Agent-Runtime-App-Key": cfg.appKey }
11
+ });
12
+ if (!response.ok) {
13
+ throw createError({
14
+ statusCode: response.status,
15
+ statusMessage: await response.text()
16
+ });
17
+ }
18
+ return await response.json();
19
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { createError, defineEventHandler, getRouterParam } from "h3";
2
+ import { agentRuntime } from "../../../utils/agent-runtime.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
+ const id = getRouterParam(event, "id");
6
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
+ const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/runs`, {
8
+ headers: { "X-Agent-Runtime-App-Key": cfg.appKey }
9
+ });
10
+ if (!response.ok) {
11
+ throw createError({
12
+ statusCode: response.status,
13
+ statusMessage: await response.text()
14
+ });
15
+ }
16
+ return await response.json();
17
+ });
@@ -2,7 +2,7 @@ import { Readable } from "node:stream";
2
2
  import { createError, defineEventHandler, getRouterParam, sendStream, setHeader } from "h3";
3
3
  import { agentRuntime } from "../../../utils/agent-runtime.js";
4
4
  export default defineEventHandler(async (event) => {
5
- const cfg = agentRuntime();
5
+ const cfg = agentRuntime(event);
6
6
  const id = getRouterParam(event, "id");
7
7
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
8
8
  const controller = new AbortController();
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { createError, defineEventHandler, getRouterParam } from "h3";
2
+ import { agentRuntime } from "../../../utils/agent-runtime.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
+ const id = getRouterParam(event, "id");
6
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
+ const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/workflow`, {
8
+ headers: { "X-Agent-Runtime-App-Key": cfg.appKey }
9
+ });
10
+ if (!response.ok) {
11
+ throw createError({
12
+ statusCode: response.status,
13
+ statusMessage: await response.text()
14
+ });
15
+ }
16
+ return await response.json();
17
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,27 @@
1
+ import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
+ import { agentRuntime, agentRuntimeHeaders } from "../../../utils/agent-runtime.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const cfg = agentRuntime(event);
5
+ const id = getRouterParam(event, "id");
6
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
+ const body = await readBody(event);
8
+ if (!body?.workflow) throw createError({ statusCode: 400, statusMessage: "missing workflow" });
9
+ const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/workflows`, {
10
+ method: "POST",
11
+ headers: agentRuntimeHeaders(cfg),
12
+ body: JSON.stringify({
13
+ workflow: body.workflow,
14
+ initialPrompt: body.initialPrompt,
15
+ requestOptions: body.requestOptions,
16
+ language: body.language,
17
+ model: body.model
18
+ })
19
+ });
20
+ if (!response.ok) {
21
+ throw createError({
22
+ statusCode: response.status,
23
+ statusMessage: await response.text()
24
+ });
25
+ }
26
+ return await response.json();
27
+ });
@@ -1,7 +1,7 @@
1
1
  import { createError, defineEventHandler, getRouterParam, setResponseStatus } from "h3";
2
2
  import { agentRuntime } from "../../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
4
+ const cfg = agentRuntime(event);
5
5
  const id = getRouterParam(event, "id");
6
6
  if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
7
7
  const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}`, {
@@ -1,8 +1,8 @@
1
1
  import { createError, defineEventHandler, readBody } from "h3";
2
2
  import { agentRuntime, agentRuntimeHeaders } from "../utils/agent-runtime.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const cfg = agentRuntime();
5
4
  const body = await readBody(event).catch(() => ({}));
5
+ const cfg = agentRuntime(event, body.appId);
6
6
  const response = await fetch(`${cfg.baseUrl}/v1/conversations`, {
7
7
  method: "POST",
8
8
  headers: agentRuntimeHeaders(cfg),
@@ -10,8 +10,15 @@ export default defineEventHandler(async (event) => {
10
10
  appId: cfg.appId,
11
11
  env: body.env ?? {},
12
12
  model: body.model,
13
+ chatType: body.chatType,
14
+ initialPrompt: body.initialPrompt,
15
+ workflow: body.workflow,
16
+ parentConversationId: body.parentConversationId,
17
+ parentContextMode: body.parentContextMode,
13
18
  requestOptions: body.requestOptions,
14
- language: body.language
19
+ language: body.language,
20
+ systemPromptAppend: body.systemPromptAppend,
21
+ metadata: body.metadata
15
22
  })
16
23
  });
17
24
  if (!response.ok) {
@@ -1,12 +1,7 @@
1
+ import { type H3Event } from 'h3';
1
2
  import { type AgentRuntimeResolvedConfig } from '../../shared.js';
2
3
  /** Resolved server-side configuration for talking to agent-runtime. */
3
4
  export type AgentRuntimeRuntime = AgentRuntimeResolvedConfig;
4
- /**
5
- * Read the agent-runtime section of `runtimeConfig`, validating that the app key
6
- * has been provided. Throws a typed Nitro 500 otherwise so the proxy routes
7
- * surface a clear error instead of silently forwarding an unauthenticated
8
- * request.
9
- */
10
- export declare const agentRuntime: () => AgentRuntimeRuntime;
5
+ export declare const agentRuntime: (event?: H3Event, appId?: string | null) => AgentRuntimeRuntime;
11
6
  /** Build the standard JSON request headers, including the app key. */
12
7
  export declare const agentRuntimeHeaders: (cfg: AgentRuntimeRuntime, extra?: Record<string, string>) => Record<string, string>;
@@ -1,9 +1,17 @@
1
1
  import { useRuntimeConfig } from "#imports";
2
- import { createError } from "h3";
2
+ import { createError, getQuery } from "h3";
3
3
  import { createAgentRuntimeRequestHeaders, resolveAgentRuntimeConfig } from "../../shared.js";
4
- export const agentRuntime = () => {
4
+ const queryAppId = (event) => {
5
+ if (!event) return void 0;
6
+ const raw = getQuery(event).appId;
7
+ if (Array.isArray(raw)) return raw[0] ? String(raw[0]) : void 0;
8
+ return raw ? String(raw) : void 0;
9
+ };
10
+ export const agentRuntime = (event, appId) => {
5
11
  try {
6
- return resolveAgentRuntimeConfig(useRuntimeConfig().agentRuntime);
12
+ return resolveAgentRuntimeConfig(useRuntimeConfig().agentRuntime, {
13
+ appId: appId || queryAppId(event)
14
+ });
7
15
  } catch (error) {
8
16
  throw createError({
9
17
  statusCode: 500,
@@ -2,6 +2,11 @@ export interface AgentRuntimeConfigInput {
2
2
  baseUrl?: string | null;
3
3
  appKey?: string | null;
4
4
  appId?: string | null;
5
+ apps?: Record<string, {
6
+ baseUrl?: string | null;
7
+ appKey?: string | null;
8
+ appId?: string | null;
9
+ }> | null;
5
10
  }
6
11
  export interface AgentRuntimeResolvedConfig {
7
12
  baseUrl: string;
@@ -11,6 +16,7 @@ export interface AgentRuntimeResolvedConfig {
11
16
  export declare const normalizeAgentRuntimeBaseUrl: (value: string | null | undefined) => string;
12
17
  export declare const createScopeFingerprint: (parts: Array<string | null | undefined>) => string;
13
18
  export declare const resolveAgentRuntimeConfig: (config: AgentRuntimeConfigInput | null | undefined, options?: {
19
+ appId?: string | null;
14
20
  defaultAppId?: string;
15
21
  }) => AgentRuntimeResolvedConfig;
16
22
  export declare const createAgentRuntimeRequestHeaders: (config: Pick<AgentRuntimeResolvedConfig, "appKey">, extra?: Record<string, string>) => Record<string, string>;
@@ -5,14 +5,18 @@ export const createScopeFingerprint = (parts) => {
5
5
  return createHash("sha256").update(normalized).digest("hex").slice(0, 12);
6
6
  };
7
7
  export const resolveAgentRuntimeConfig = (config, options) => {
8
- const appKey = String(config?.appKey || "").trim();
8
+ const requestedAppId = String(options?.appId || "").trim();
9
+ const defaultAppId = String(config?.appId || options?.defaultAppId || "omnisearch").trim();
10
+ const selectedAppId = requestedAppId || defaultAppId;
11
+ const appConfig = selectedAppId ? config?.apps?.[selectedAppId] : void 0;
12
+ const appKey = String(appConfig?.appKey || (!requestedAppId || selectedAppId === config?.appId ? config?.appKey : "") || "").trim();
9
13
  if (!appKey) {
10
- throw new Error("[agent-runtime] AGENT_RUNTIME_APP_KEY is not configured");
14
+ throw new Error(`[agent-runtime] AGENT_RUNTIME_APP_KEY is not configured for app "${selectedAppId}"`);
11
15
  }
12
16
  return {
13
- baseUrl: normalizeAgentRuntimeBaseUrl(config?.baseUrl),
17
+ baseUrl: normalizeAgentRuntimeBaseUrl(appConfig?.baseUrl || config?.baseUrl),
14
18
  appKey,
15
- appId: String(config?.appId || options?.defaultAppId || "omnisearch")
19
+ appId: String(appConfig?.appId || selectedAppId)
16
20
  };
17
21
  };
18
22
  export const createAgentRuntimeRequestHeaders = (config, extra = {}) => ({
package/dist/types.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { type AppAuth, type AppEnvField, type AppInfo, type ChatStatus, type FileEntry, type SendOptions, type UIMessage, type UiAction, type UseAgentRuntime } from '../dist/runtime/composables/useAgentRuntime.js'
1
+ export { type AppAuth, type AppEnvField, type AppInfo, type AppStartField, type AppStartSpec, type ChatStatus, type FileEntry, type RunKind, type RunRecord, type RunStatus, type SendOptions, type UIMessage, type UiAction, type UploadFilesOptions, type UseAgentRuntime, type UseAgentRuntimeOptions, type WorkflowStartOptions } from '../dist/runtime/composables/useAgentRuntime.js'
2
2
 
3
3
  export { type AgentRuntimeMarkdownRenderOptions } from '../dist/runtime/composables/useAgentRuntimeMarkdown.js'
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d4y/agent-runtime-nuxt",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Headless Nuxt module that connects a Nuxt app to an agent-runtime server. Ships server-side proxy routes (so your X-Agent-Runtime-App-Key never leaves the server) and a single composable, useAgentRuntime(), that exposes a typed chat client.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -57,20 +57,20 @@
57
57
  "typecheck": "tsc --noEmit -p tsconfig.json"
58
58
  },
59
59
  "dependencies": {
60
- "@nuxt/kit": "^4.4.2",
61
- "ai": "^6.0.168",
62
- "defu": "^6.1.4",
63
- "h3": "^1.15.11",
64
- "markdown-it": "^14.1.1"
60
+ "@nuxt/kit": "4.4.4",
61
+ "ai": "6.0.175",
62
+ "defu": "6.1.7",
63
+ "h3": "1.15.11",
64
+ "markdown-it": "14.1.1"
65
65
  },
66
66
  "devDependencies": {
67
- "@nuxt/module-builder": "^1.0.2",
68
- "@nuxt/schema": "^4.4.2",
69
- "@types/markdown-it": "^14.1.2",
70
- "nuxt": "^4.4.2",
71
- "typescript": "^6.0.2",
72
- "vue": "^3.5.32",
73
- "vue-tsc": "^3.2.7"
67
+ "@nuxt/module-builder": "1.0.2",
68
+ "@nuxt/schema": "4.4.4",
69
+ "@types/markdown-it": "14.1.2",
70
+ "nuxt": "4.4.4",
71
+ "typescript": "6.0.3",
72
+ "vue": "3.5.34",
73
+ "vue-tsc": "3.2.8"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "nuxt": "^3.13.0 || ^4.0.0"