@d4y/agent-runtime-nuxt 0.1.2 → 0.1.4

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 (28) hide show
  1. package/README.md +27 -4
  2. package/dist/module.d.mts +2 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +3 -1
  5. package/dist/runtime/components/AgentRuntimeArtifactDialog.d.vue.ts +20 -0
  6. package/dist/runtime/components/AgentRuntimeArtifactDialog.vue +90 -0
  7. package/dist/runtime/components/AgentRuntimeArtifactDialog.vue.d.ts +20 -0
  8. package/dist/runtime/components/AgentRuntimeArtifactPreview.d.vue.ts +0 -2
  9. package/dist/runtime/components/AgentRuntimeArtifactPreview.vue +19 -64
  10. package/dist/runtime/components/AgentRuntimeArtifactPreview.vue.d.ts +0 -2
  11. package/dist/runtime/components/AgentRuntimeMarkdown.d.vue.ts +19 -0
  12. package/dist/runtime/components/AgentRuntimeMarkdown.vue +77 -0
  13. package/dist/runtime/components/AgentRuntimeMarkdown.vue.d.ts +19 -0
  14. package/dist/runtime/composables/useAgentRuntime.d.ts +42 -0
  15. package/dist/runtime/composables/useAgentRuntime.js +77 -4
  16. package/dist/runtime/composables/useAgentRuntimeMarkdown.d.ts +2 -7
  17. package/dist/runtime/composables/useAgentRuntimeMarkdown.js +94 -56
  18. package/dist/runtime/frontend.d.ts +4 -2
  19. package/dist/runtime/frontend.js +8 -2
  20. package/dist/runtime/server/api/conversations/[id]/files/preview/[...path].get.d.ts +2 -0
  21. package/dist/runtime/server/api/conversations/[id]/files/preview/[...path].get.js +42 -0
  22. package/dist/runtime/server/api/conversations/[id]/files.get.d.ts +1 -0
  23. package/dist/runtime/server/api/conversations/[id]/messages.post.js +6 -1
  24. package/dist/runtime/server/api/conversations.post.js +7 -1
  25. package/dist/runtime/utils/files.d.ts +21 -3
  26. package/dist/runtime/utils/files.js +63 -18
  27. package/dist/types.d.mts +3 -1
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -108,7 +108,7 @@ The composable returns AI-SDK / Nuxt UI compatible `UIMessage` shapes, so
108
108
  | `/conversations/:id` | DELETE | Delete a persisted conversation. |
109
109
  | `/conversations/:id/history` | GET | Return the persisted conversation history. |
110
110
  | `/conversations/:id/stream` | GET | Pipes the upstream SSE stream verbatim. |
111
- | `/conversations/:id/messages` | POST | Append a user turn. Body: `{ content, context? }`. |
111
+ | `/conversations/:id/messages` | POST | Append a user turn. Body: `{ content, context?, requestOptions? }`. |
112
112
  | `/conversations/:id/abort` | POST | Cancel the in-flight agent run. |
113
113
  | `/conversations/:id/env` | POST | Patch workspace env. Body: `{ env, merge? }`. |
114
114
  | `/conversations/:id/files` | GET | List workspace files. |
@@ -160,9 +160,32 @@ workspace via `POST /conversations/:id/env`, which retriggers any matching
160
160
  #### `send(text, options?)`
161
161
 
162
162
  Lazily calls `start()` if no conversation exists. The displayed user bubble
163
- always shows `text`; pass `options.rewriteContent` to mutate the *forwarded*
164
- prompt without changing what the user sees. Useful for model-specific soft
165
- switches:
163
+ always shows `text`.
164
+
165
+ Use `requestOptions` for provider-side controls such as Qwen/vLLM
166
+ `chat_template_kwargs`:
167
+
168
+ ```ts
169
+ await chat.start({
170
+ requestOptions: {
171
+ temperature: 0.7,
172
+ topP: 0.8,
173
+ presencePenalty: 1.5
174
+ }
175
+ })
176
+
177
+ await chat.send(input.value, {
178
+ requestOptions: {
179
+ extraBody: {
180
+ top_k: 20,
181
+ chat_template_kwargs: { enable_thinking: false }
182
+ }
183
+ }
184
+ })
185
+ ```
186
+
187
+ `rewriteContent` is still available as a fallback when a model expects prompt
188
+ text toggles rather than provider payload fields:
166
189
 
167
190
  ```ts
168
191
  await chat.send(input.value, {
package/dist/module.d.mts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  export { AppAuth, AppEnvField, AppInfo, ChatStatus, FileEntry, SendOptions, UIMessage, UiAction, UseAgentRuntime } from '../dist/runtime/composables/useAgentRuntime.js';
3
3
  export { AgentRuntimeMarkdownRenderOptions } from '../dist/runtime/composables/useAgentRuntimeMarkdown.js';
4
- export { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from '../dist/runtime/utils/files.js';
4
+ export { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind, AgentRuntimeResolvedAsset } from '../dist/runtime/utils/files.js';
5
+ export { createAgentRuntimeRequestHeaders, createScopeFingerprint, normalizeAgentRuntimeBaseUrl, resolveAgentRuntimeConfig } from '../dist/runtime/shared.js';
5
6
 
6
7
  /**
7
8
  * Module options for `@d4y/agent-runtime-nuxt`.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.13.0"
6
6
  },
7
- "version": "0.1.2",
7
+ "version": "0.1.4",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { defineNuxtModule, createResolver, addImportsDir, addTypeTemplate, addServerHandler } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
+ export { createAgentRuntimeRequestHeaders, createScopeFingerprint, normalizeAgentRuntimeBaseUrl, resolveAgentRuntimeConfig } from '../dist/runtime/shared.js';
3
4
 
4
5
  const DEFAULTS = {
5
6
  baseUrl: "http://127.0.0.1:18791",
@@ -35,7 +36,7 @@ const module$1 = defineNuxtModule({
35
36
  `// Auto-generated by @d4y/agent-runtime-nuxt`,
36
37
  `export type { UseAgentRuntime, AppInfo, AppAuth, AppEnvField, FileEntry, UiAction, SendOptions, ChatStatus } from '${resolver.resolve("./runtime/composables/useAgentRuntime")}'`,
37
38
  `export type { AgentRuntimeMarkdownRenderOptions } from '${resolver.resolve("./runtime/composables/useAgentRuntimeMarkdown")}'`,
38
- `export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from '${resolver.resolve("./runtime/utils/files")}'`,
39
+ `export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind, AgentRuntimeResolvedAsset } from '${resolver.resolve("./runtime/utils/files")}'`,
39
40
  ""
40
41
  ].join("\n")
41
42
  });
@@ -53,6 +54,7 @@ const module$1 = defineNuxtModule({
53
54
  route("/conversations/:id/abort", "conversations/[id]/abort.post", "post");
54
55
  route("/conversations/:id/env", "conversations/[id]/env.post", "post");
55
56
  route("/conversations/:id/files", "conversations/[id]/files.get", "get");
57
+ route("/conversations/:id/files/preview/**", "conversations/[id]/files/preview/[...path].get", "get");
56
58
  route("/conversations/:id/files/raw/**", "conversations/[id]/files/raw/[...path].get", "get");
57
59
  }
58
60
  });
@@ -0,0 +1,20 @@
1
+ import { type AgentRuntimeFileLike } from '../utils/files.js';
2
+ type __VLS_Props = {
3
+ open: boolean;
4
+ file?: AgentRuntimeFileLike | null;
5
+ title?: string | null;
6
+ previewSrc?: string | null;
7
+ downloadSrc?: string | null;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:open": (open: boolean) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:open"?: ((open: boolean) => any) | undefined;
13
+ }>, {
14
+ file: AgentRuntimeFileLike | null;
15
+ title: string | null;
16
+ previewSrc: string | null;
17
+ downloadSrc: string | null;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,90 @@
1
+ <script setup>
2
+ import { computed, onBeforeUnmount, watch } from "vue";
3
+ import AgentRuntimeArtifactPreview from "./AgentRuntimeArtifactPreview.vue";
4
+ import { getAgentRuntimeFilePreviewKind } from "../utils/files";
5
+ const props = defineProps({
6
+ open: { type: Boolean, required: true },
7
+ file: { type: [Object, null], required: false, default: null },
8
+ title: { type: [String, null], required: false, default: null },
9
+ previewSrc: { type: [String, null], required: false, default: null },
10
+ downloadSrc: { type: [String, null], required: false, default: null }
11
+ });
12
+ const emit = defineEmits(["update:open"]);
13
+ const previewKind = computed(() => props.file ? getAgentRuntimeFilePreviewKind(props.file) : "download");
14
+ const isPreviewable = computed(() => previewKind.value === "image" || previewKind.value === "html" || previewKind.value === "pdf");
15
+ const dialogTitle = computed(() => props.title ?? props.file?.name ?? props.file?.relPath ?? "Artifact preview");
16
+ const close = () => emit("update:open", false);
17
+ const onBackdropClick = () => close();
18
+ const onKeydown = (event) => {
19
+ if (event.key === "Escape") {
20
+ close();
21
+ }
22
+ };
23
+ watch(() => props.open, (open) => {
24
+ if (!import.meta.client) {
25
+ return;
26
+ }
27
+ if (open) {
28
+ window.addEventListener("keydown", onKeydown);
29
+ return;
30
+ }
31
+ window.removeEventListener("keydown", onKeydown);
32
+ }, { immediate: true });
33
+ onBeforeUnmount(() => {
34
+ if (!import.meta.client) {
35
+ return;
36
+ }
37
+ window.removeEventListener("keydown", onKeydown);
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <Teleport to="body">
43
+ <Transition
44
+ enter-active-class="transition duration-200 ease-out"
45
+ enter-from-class="opacity-0"
46
+ enter-to-class="opacity-100"
47
+ leave-active-class="transition duration-150 ease-in"
48
+ leave-from-class="opacity-100"
49
+ leave-to-class="opacity-0">
50
+ <div
51
+ v-if="open"
52
+ class="agent-runtime-artifact-dialog"
53
+ aria-modal="true"
54
+ role="dialog"
55
+ :aria-label="dialogTitle"
56
+ @click="onBackdropClick">
57
+ <div class="agent-runtime-artifact-dialog__viewport">
58
+ <div
59
+ class="agent-runtime-artifact-dialog__surface"
60
+ :class="{ 'agent-runtime-artifact-dialog__surface--preview': isPreviewable }"
61
+ @click.stop>
62
+ <AgentRuntimeArtifactPreview
63
+ v-if="file"
64
+ :file="file"
65
+ :src="previewSrc" />
66
+
67
+ <div
68
+ v-if="downloadSrc && isPreviewable"
69
+ class="agent-runtime-artifact-dialog__actions">
70
+ <UButton
71
+ size="sm"
72
+ color="neutral"
73
+ variant="soft"
74
+ icon="material-symbols:download-rounded"
75
+ label="Download"
76
+ :href="downloadSrc"
77
+ external
78
+ :download="file?.name ?? file?.relPath ?? void 0"
79
+ target="_blank" />
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </Transition>
85
+ </Teleport>
86
+ </template>
87
+
88
+ <style scoped>
89
+ .agent-runtime-artifact-dialog{backdrop-filter:blur(4px);background:rgba(24,24,27,.46);inset:0;position:fixed;z-index:200}.agent-runtime-artifact-dialog__viewport{align-items:center;display:flex;justify-content:center;min-height:100vh;padding:1rem;width:100%}.agent-runtime-artifact-dialog__surface{background:transparent;max-width:none;overflow:visible;position:relative;width:min(96vw,72rem)}.agent-runtime-artifact-dialog__surface--preview{align-items:center;display:flex;justify-content:center}.agent-runtime-artifact-dialog__actions{bottom:1rem;left:1rem;position:absolute;z-index:1}
90
+ </style>
@@ -0,0 +1,20 @@
1
+ import { type AgentRuntimeFileLike } from '../utils/files.js';
2
+ type __VLS_Props = {
3
+ open: boolean;
4
+ file?: AgentRuntimeFileLike | null;
5
+ title?: string | null;
6
+ previewSrc?: string | null;
7
+ downloadSrc?: string | null;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:open": (open: boolean) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:open"?: ((open: boolean) => any) | undefined;
13
+ }>, {
14
+ file: AgentRuntimeFileLike | null;
15
+ title: string | null;
16
+ previewSrc: string | null;
17
+ downloadSrc: string | null;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -3,12 +3,10 @@ type __VLS_Props = {
3
3
  file: AgentRuntimeFileLike;
4
4
  src?: string | null;
5
5
  resolveSrc?: ((relPath: string) => string | null | undefined) | null;
6
- textMaxChars?: number;
7
6
  };
8
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
9
8
  src: string | null;
10
9
  resolveSrc: ((relPath: string) => string | null | undefined) | null;
11
- textMaxChars: number;
12
10
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
11
  declare const _default: typeof __VLS_export;
14
12
  export default _default;
@@ -1,11 +1,10 @@
1
1
  <script setup>
2
- import { computed, onBeforeUnmount, ref, watch } from "vue";
2
+ import { computed } from "vue";
3
3
  import { getAgentRuntimeFilePreviewKind } from "../utils/files";
4
4
  const props = defineProps({
5
5
  file: { type: Object, required: true },
6
6
  src: { type: [String, null], required: false, default: null },
7
- resolveSrc: { type: [Function, null], required: false, default: null },
8
- textMaxChars: { type: Number, required: false, default: 12e4 }
7
+ resolveSrc: { type: [Function, null], required: false, default: null }
9
8
  });
10
9
  const kind = computed(() => getAgentRuntimeFilePreviewKind(props.file));
11
10
  const resolvedSrc = computed(() => {
@@ -17,54 +16,6 @@ const resolvedSrc = computed(() => {
17
16
  }
18
17
  return props.resolveSrc(props.file.relPath) ?? null;
19
18
  });
20
- const textContent = ref("");
21
- const textError = ref(null);
22
- const textLoading = ref(false);
23
- let textAbortController = null;
24
- const abortTextLoad = () => {
25
- textAbortController?.abort();
26
- textAbortController = null;
27
- };
28
- const loadTextPreview = async () => {
29
- abortTextLoad();
30
- textContent.value = "";
31
- textError.value = null;
32
- if (kind.value !== "text" || !resolvedSrc.value) {
33
- textLoading.value = false;
34
- return;
35
- }
36
- const controller = new AbortController();
37
- textAbortController = controller;
38
- textLoading.value = true;
39
- try {
40
- const response = await fetch(resolvedSrc.value, {
41
- signal: controller.signal
42
- });
43
- if (!response.ok) {
44
- throw new Error(`Failed to load preview (${response.status})`);
45
- }
46
- const body = await response.text();
47
- textContent.value = body.length > props.textMaxChars ? `${body.slice(0, props.textMaxChars).trimEnd()}
48
-
49
- \u2026` : body;
50
- } catch (error) {
51
- if (controller.signal.aborted) {
52
- return;
53
- }
54
- textError.value = error instanceof Error ? error.message : "Failed to load preview";
55
- } finally {
56
- if (textAbortController === controller) {
57
- textAbortController = null;
58
- }
59
- textLoading.value = false;
60
- }
61
- };
62
- watch([kind, resolvedSrc], () => {
63
- void loadTextPreview();
64
- }, { immediate: true });
65
- onBeforeUnmount(() => {
66
- abortTextLoad();
67
- });
68
19
  </script>
69
20
 
70
21
  <template>
@@ -76,22 +27,20 @@ onBeforeUnmount(() => {
76
27
  class="agent-runtime-artifact-preview__image">
77
28
 
78
29
  <iframe
79
- v-else-if="kind === 'pdf' && resolvedSrc"
30
+ v-else-if="kind === 'html' && resolvedSrc"
80
31
  :src="resolvedSrc"
81
- :title="file.name ?? file.relPath ?? 'PDF preview'"
32
+ :title="file.name ?? file.relPath ?? 'Artifact preview'"
33
+ sandbox=""
34
+ referrerpolicy="no-referrer"
82
35
  class="agent-runtime-artifact-preview__frame" />
83
36
 
84
- <div v-else-if="kind === 'text'" class="agent-runtime-artifact-preview__text-shell">
85
- <div v-if="textLoading" class="agent-runtime-artifact-preview__note">
86
- Loading preview…
87
- </div>
88
- <div v-else-if="textError" class="agent-runtime-artifact-preview__note agent-runtime-artifact-preview__note--error">
89
- {{ textError }}
90
- </div>
91
- <pre v-else class="agent-runtime-artifact-preview__text">{{ textContent }}</pre>
92
- </div>
37
+ <iframe
38
+ v-else-if="kind === 'pdf' && resolvedSrc"
39
+ :src="resolvedSrc"
40
+ :title="file.name ?? file.relPath ?? 'Artifact preview'"
41
+ class="agent-runtime-artifact-preview__frame" />
93
42
 
94
- <div v-else class="agent-runtime-artifact-preview__fallback">
43
+ <div v-else-if="kind !== 'blocked'" class="agent-runtime-artifact-preview__fallback">
95
44
  <p class="agent-runtime-artifact-preview__note">
96
45
  Preview unavailable for this file type.
97
46
  </p>
@@ -104,9 +53,15 @@ onBeforeUnmount(() => {
104
53
  Open file
105
54
  </a>
106
55
  </div>
56
+
57
+ <div v-else class="agent-runtime-artifact-preview__fallback">
58
+ <p class="agent-runtime-artifact-preview__note">
59
+ This file type is not available in the user-facing UI.
60
+ </p>
61
+ </div>
107
62
  </div>
108
63
  </template>
109
64
 
110
65
  <style scoped>
111
- .agent-runtime-artifact-preview{display:block;min-height:12rem;width:100%}.agent-runtime-artifact-preview__frame,.agent-runtime-artifact-preview__image{background:rgba(15,23,42,.04);border:0;border-radius:.875rem;display:block;width:100%}.agent-runtime-artifact-preview__image{max-height:min(70vh,48rem);-o-object-fit:contain;object-fit:contain}.agent-runtime-artifact-preview__frame{min-height:min(78vh,52rem)}.agent-runtime-artifact-preview__fallback,.agent-runtime-artifact-preview__text-shell{background:rgba(15,23,42,.03);border:1px solid rgba(15,23,42,.1);border-radius:.875rem;min-height:18rem}.agent-runtime-artifact-preview__text{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.8rem;line-height:1.55;margin:0;max-height:min(72vh,52rem);overflow:auto;padding:1rem;white-space:pre-wrap;word-break:break-word}.agent-runtime-artifact-preview__note{color:rgba(15,23,42,.72);font-size:.875rem;padding:1rem}.agent-runtime-artifact-preview__note--error{color:#b91c1c}.agent-runtime-artifact-preview__link{color:inherit;display:inline-flex;margin:0 1rem 1rem;text-decoration:underline;text-underline-offset:.18em}
66
+ .agent-runtime-artifact-preview{display:block;min-height:12rem;width:100%}.agent-runtime-artifact-preview__frame,.agent-runtime-artifact-preview__image{background:rgba(15,23,42,.04);border:0;border-radius:.875rem;display:block;width:100%}.agent-runtime-artifact-preview__image{max-height:min(70vh,48rem);-o-object-fit:contain;object-fit:contain}.agent-runtime-artifact-preview__frame{min-height:min(68vh,48rem)}.agent-runtime-artifact-preview__fallback{background:rgba(15,23,42,.03);border:1px solid rgba(15,23,42,.1);border-radius:.875rem;min-height:18rem}.agent-runtime-artifact-preview__note{color:rgba(15,23,42,.72);font-size:.875rem;padding:1rem}.agent-runtime-artifact-preview__link{color:inherit;display:inline-flex;margin:0 1rem 1rem;text-decoration:underline;text-underline-offset:.18em}
112
67
  </style>
@@ -3,12 +3,10 @@ type __VLS_Props = {
3
3
  file: AgentRuntimeFileLike;
4
4
  src?: string | null;
5
5
  resolveSrc?: ((relPath: string) => string | null | undefined) | null;
6
- textMaxChars?: number;
7
6
  };
8
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
9
8
  src: string | null;
10
9
  resolveSrc: ((relPath: string) => string | null | undefined) | null;
11
- textMaxChars: number;
12
10
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
11
  declare const _default: typeof __VLS_export;
14
12
  export default _default;
@@ -0,0 +1,19 @@
1
+ type __VLS_Props = {
2
+ text: string;
3
+ class?: string | Record<string, boolean> | string[];
4
+ streaming?: boolean;
5
+ resolveWorkspacePath?: ((relPath: string) => string | null | undefined) | null;
6
+ resolveWorkspacePreviewPath?: ((relPath: string) => string | null | undefined) | null;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ renderedChange: (rendered: boolean) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ onRenderedChange?: ((rendered: boolean) => any) | undefined;
12
+ }>, {
13
+ class: string | Record<string, boolean> | string[];
14
+ streaming: boolean;
15
+ resolveWorkspacePath: ((relPath: string) => string | null | undefined) | null;
16
+ resolveWorkspacePreviewPath: ((relPath: string) => string | null | undefined) | null;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,77 @@
1
+ <script setup>
2
+ import { computed, nextTick, ref, watch } from "vue";
3
+ import { useAgentRuntimeMarkdown } from "../composables/useAgentRuntimeMarkdown";
4
+ import AgentRuntimeArtifactDialog from "./AgentRuntimeArtifactDialog.vue";
5
+ const emit = defineEmits(["renderedChange"]);
6
+ const props = defineProps({
7
+ text: { type: String, required: true },
8
+ class: { type: [String, Object, Array], required: false, default: "" },
9
+ streaming: { type: Boolean, required: false, default: false },
10
+ resolveWorkspacePath: { type: [Function, null], required: false, default: null },
11
+ resolveWorkspacePreviewPath: { type: [Function, null], required: false, default: null }
12
+ });
13
+ const { render } = useAgentRuntimeMarkdown({
14
+ resolveWorkspacePath: (relPath) => props.resolveWorkspacePath?.(relPath) ?? null,
15
+ resolveWorkspacePreviewPath: (relPath) => props.resolveWorkspacePreviewPath?.(relPath) ?? null
16
+ });
17
+ const html = computed(() => props.text ? render(props.text) : "");
18
+ const dialogOpen = ref(false);
19
+ const dialogFile = ref(null);
20
+ const dialogPreviewSrc = ref(null);
21
+ const dialogDownloadSrc = ref(null);
22
+ const rendered = computed(() => html.value.length > 0);
23
+ const onClick = (event) => {
24
+ const target = event.target instanceof HTMLElement ? event.target : null;
25
+ const trigger = target?.closest('[data-agent-runtime-preview="true"]');
26
+ if (!trigger) {
27
+ return;
28
+ }
29
+ const previewUrl = trigger.dataset.agentRuntimePreviewUrl;
30
+ const kind = trigger.dataset.agentRuntimePreviewKind;
31
+ const label = trigger.dataset.agentRuntimePreviewLabel;
32
+ if (!previewUrl || !kind) {
33
+ return;
34
+ }
35
+ event.preventDefault();
36
+ dialogFile.value = {
37
+ name: label ?? void 0,
38
+ relPath: null,
39
+ mimeType: kind === "image" ? "image/*" : kind === "pdf" ? "application/pdf" : "text/html",
40
+ kind
41
+ };
42
+ dialogPreviewSrc.value = previewUrl;
43
+ dialogDownloadSrc.value = trigger.dataset.agentRuntimeDownloadUrl ?? previewUrl;
44
+ dialogOpen.value = true;
45
+ };
46
+ watch(rendered, async (value) => {
47
+ if (!value) {
48
+ emit("renderedChange", false);
49
+ return;
50
+ }
51
+ await nextTick();
52
+ emit("renderedChange", true);
53
+ }, { immediate: true });
54
+ </script>
55
+
56
+ <template>
57
+ <div
58
+ v-if="rendered"
59
+ class="agent-runtime-markdown"
60
+ :class="[props.class, streaming ? 'agent-runtime-markdown--streaming' : '']"
61
+ @click="onClick"
62
+ v-html="
63
+ html
64
+ /* eslint-disable-line vue/no-v-html -- output is escaped by markdown-it; raw HTML disabled */
65
+ " />
66
+
67
+ <AgentRuntimeArtifactDialog
68
+ v-model:open="dialogOpen"
69
+ :file="dialogFile"
70
+ :title="dialogFile?.name ?? dialogFile?.relPath ?? null"
71
+ :preview-src="dialogPreviewSrc"
72
+ :download-src="dialogDownloadSrc" />
73
+ </template>
74
+
75
+ <style scoped>
76
+ .agent-runtime-markdown{line-height:1.65}.agent-runtime-markdown--streaming{animation:agent-runtime-markdown-pulse .16s ease-out}.agent-runtime-markdown :deep(.agent-runtime-md-image-link){cursor:zoom-in;display:inline-block}.agent-runtime-markdown :deep(.agent-runtime-md-image){border-radius:.95rem;display:block;max-height:20rem;max-width:min(100%,30rem);-o-object-fit:contain;object-fit:contain}.agent-runtime-markdown :deep(.agent-runtime-md-file-link){color:inherit;text-decoration:underline;text-underline-offset:.18em}.agent-runtime-markdown :deep(.agent-runtime-md-html-shell),.agent-runtime-markdown :deep(.agent-runtime-md-pdf-shell){background:color-mix(in srgb,var(--ui-bg-elevated,rgba(248,250,252,.9)) 82%,transparent);border:1px solid color-mix(in srgb,var(--ui-border,rgba(148,163,184,.28)) 80%,transparent);border-radius:1rem;display:block;margin-top:.75rem;overflow:hidden}.agent-runtime-markdown :deep(.agent-runtime-md-embed-toolbar){align-items:center;border-bottom:1px solid color-mix(in srgb,var(--ui-border,rgba(148,163,184,.28)) 70%,transparent);display:flex;flex-wrap:wrap;font-size:.75rem;gap:.75rem;justify-content:space-between;padding:.75rem .9rem}.agent-runtime-markdown :deep(.agent-runtime-md-embed-label){font-family:var(--font-mono,ui-monospace,monospace);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-runtime-markdown :deep(.agent-runtime-md-embed-actions){display:inline-flex;flex-wrap:wrap;gap:.5rem}.agent-runtime-markdown :deep(.agent-runtime-md-download-link),.agent-runtime-markdown :deep(.agent-runtime-md-preview-link){color:inherit;text-decoration:underline;text-underline-offset:.18em}.agent-runtime-markdown :deep(.agent-runtime-md-preview-link[data-agent-runtime-preview=true]){cursor:zoom-in}.agent-runtime-markdown :deep(.agent-runtime-md-html-frame),.agent-runtime-markdown :deep(.agent-runtime-md-pdf-frame){background:color-mix(in srgb,var(--ui-bg,hsla(0,0%,100%,.94)) 85%,transparent);border:0;display:block;height:22rem;width:100%}@keyframes agent-runtime-markdown-pulse{0%{opacity:.88;transform:translateY(1px)}to{opacity:1;transform:translateY(0)}}
77
+ </style>
@@ -0,0 +1,19 @@
1
+ type __VLS_Props = {
2
+ text: string;
3
+ class?: string | Record<string, boolean> | string[];
4
+ streaming?: boolean;
5
+ resolveWorkspacePath?: ((relPath: string) => string | null | undefined) | null;
6
+ resolveWorkspacePreviewPath?: ((relPath: string) => string | null | undefined) | null;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ renderedChange: (rendered: boolean) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ onRenderedChange?: ((rendered: boolean) => any) | undefined;
12
+ }>, {
13
+ class: string | Record<string, boolean> | string[];
14
+ streaming: boolean;
15
+ resolveWorkspacePath: ((relPath: string) => string | null | undefined) | null;
16
+ resolveWorkspacePreviewPath: ((relPath: string) => string | null | undefined) | null;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -38,6 +38,22 @@ export interface UiAction {
38
38
  payload: unknown;
39
39
  at: number;
40
40
  }
41
+ export interface ContextUsage {
42
+ tokens: number | null;
43
+ contextWindow: number;
44
+ percent: number | null;
45
+ }
46
+ export interface CompactionLogEntry {
47
+ phase: 'start' | 'end';
48
+ at: number;
49
+ reason?: string;
50
+ tokensBefore?: number;
51
+ tokensAfter?: number;
52
+ compacted?: boolean;
53
+ aborted?: boolean;
54
+ willRetry?: boolean;
55
+ error?: string;
56
+ }
41
57
  /** Workspace file entry, mirrors the backend response. */
42
58
  export interface FileEntry {
43
59
  name: string;
@@ -45,6 +61,20 @@ export interface FileEntry {
45
61
  size: number;
46
62
  mtime: string;
47
63
  mimeType: string;
64
+ kind: 'image' | 'html' | 'pdf' | 'download';
65
+ }
66
+ /**
67
+ * Generic provider request options forwarded to the runtime.
68
+ * `extraBody` is merged verbatim into the provider payload, which makes this
69
+ * suitable for model-specific flags such as Qwen's `chat_template_kwargs`.
70
+ */
71
+ export interface RequestOptions {
72
+ temperature?: number;
73
+ maxTokens?: number;
74
+ topP?: number;
75
+ presencePenalty?: number;
76
+ frequencyPenalty?: number;
77
+ extraBody?: Record<string, unknown>;
48
78
  }
49
79
  /** Per-turn options for `send`. */
50
80
  export interface SendOptions {
@@ -61,11 +91,15 @@ export interface SendOptions {
61
91
  rewriteContent?: (text: string) => string;
62
92
  /** Optional language override for this turn / conversation, e.g. `en` or `de-CH`. */
63
93
  language?: string;
94
+ /** Optional provider request overrides for this turn only. */
95
+ requestOptions?: RequestOptions;
64
96
  }
65
97
  /** Options for `start`. */
66
98
  export interface StartOptions {
67
99
  /** Optional language override for the newly created conversation. */
68
100
  language?: string;
101
+ /** Optional default provider request options for the conversation. */
102
+ requestOptions?: RequestOptions;
69
103
  }
70
104
  /** Return shape of {@link useAgentRuntime}. All members are reactive. */
71
105
  export interface UseAgentRuntime {
@@ -79,6 +113,12 @@ export interface UseAgentRuntime {
79
113
  error: Ref<Error | null>;
80
114
  /** Most recent UI-action events, newest first, capped at 50. */
81
115
  uiActions: Ref<UiAction[]>;
116
+ /** Latest context usage sample from the active PI session. */
117
+ contextUsage: Ref<ContextUsage | null>;
118
+ /** True iff a compaction run is currently in progress. */
119
+ compactionInProgress: Ref<boolean>;
120
+ /** Most recent compaction events, newest first. */
121
+ compactionLog: Ref<CompactionLogEntry[]>;
82
122
  /** Active app manifest. `null` until the first `onMounted` fetch resolves. */
83
123
  app: Ref<AppInfo | null>;
84
124
  /** Current auth values (persisted in localStorage, scoped per appId). */
@@ -89,6 +129,8 @@ export interface UseAgentRuntime {
89
129
  files: Ref<FileEntry[]>;
90
130
  /** Build the proxied download URL for a workspace-relative path. */
91
131
  fileUrl: (relPath: string) => string;
132
+ /** Build the proxied preview URL for a workspace-relative path. */
133
+ filePreviewUrl: (relPath: string) => string;
92
134
  /** Manually re-fetch the workspace file listing. */
93
135
  refreshFiles: () => Promise<void>;
94
136
  /** Persist a new auth map; if a conversation is open, push the change to its env. */
@@ -43,6 +43,9 @@ export const useAgentRuntime = () => {
43
43
  const status = ref("idle");
44
44
  const error = ref(null);
45
45
  const uiActions = ref([]);
46
+ const contextUsage = ref(null);
47
+ const compactionInProgress = ref(false);
48
+ const compactionLog = ref([]);
46
49
  const app = ref(null);
47
50
  const auth = ref(emptyAuth());
48
51
  const authReady = computed(() => computeAuthReady(auth.value, app.value));
@@ -61,6 +64,11 @@ export const useAgentRuntime = () => {
61
64
  if (!cid) return "";
62
65
  return `${apiPrefix}/conversations/${cid}/files/raw/${encodeRelPath(relPath)}`;
63
66
  };
67
+ const filePreviewUrl = (relPath) => {
68
+ const cid = conversationId.value;
69
+ if (!cid) return "";
70
+ return `${apiPrefix}/conversations/${cid}/files/preview/${encodeRelPath(relPath)}`;
71
+ };
64
72
  const refreshFiles = async () => {
65
73
  const cid = conversationId.value;
66
74
  if (!cid) {
@@ -116,6 +124,12 @@ export const useAgentRuntime = () => {
116
124
  return { ...msg, parts };
117
125
  });
118
126
  };
127
+ const normalizePercent = (value) => Math.max(0, Math.min(100, value > 1 ? value : value * 100));
128
+ const toNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
129
+ const toNullableNumber = (value) => value === null ? null : toNumber(value);
130
+ const pushCompactionLog = (entry) => {
131
+ compactionLog.value = [entry, ...compactionLog.value].slice(0, 20);
132
+ };
119
133
  const onSseEvent = (eventName, data) => {
120
134
  switch (eventName) {
121
135
  case "agent_start":
@@ -143,23 +157,66 @@ export const useAgentRuntime = () => {
143
157
  type: `tool-${d.toolName}`,
144
158
  toolCallId: d.toolCallId,
145
159
  state: d.isError ? "output-error" : "output-available",
146
- output: d.isError ? void 0 : "ok",
147
- errorText: d.isError ? "Tool returned an error" : void 0
160
+ output: d.isError ? void 0 : d.output,
161
+ errorText: d.isError ? d.errorText ?? "Tool returned an error" : void 0
148
162
  }));
149
163
  break;
150
164
  }
165
+ case "context_usage": {
166
+ const d = data;
167
+ const tokens = toNullableNumber(d.tokens);
168
+ const contextWindow = toNumber(d.contextWindow);
169
+ if (tokens === void 0 || contextWindow === void 0) break;
170
+ const fallbackPercent = typeof tokens === "number" && contextWindow > 0 ? tokens / contextWindow * 100 : null;
171
+ const percent = toNullableNumber(d.percent) ?? fallbackPercent;
172
+ if (percent === void 0) break;
173
+ contextUsage.value = {
174
+ tokens,
175
+ contextWindow,
176
+ percent: typeof percent === "number" ? normalizePercent(percent) : null
177
+ };
178
+ break;
179
+ }
180
+ case "compaction_start": {
181
+ const d = data;
182
+ compactionInProgress.value = true;
183
+ pushCompactionLog({
184
+ phase: "start",
185
+ at: Date.now(),
186
+ reason: typeof d.reason === "string" && d.reason.trim().length > 0 ? d.reason.trim() : void 0
187
+ });
188
+ break;
189
+ }
190
+ case "compaction_end": {
191
+ const d = data;
192
+ const result = d.result && typeof d.result === "object" ? d.result : null;
193
+ compactionInProgress.value = false;
194
+ pushCompactionLog({
195
+ phase: "end",
196
+ at: Date.now(),
197
+ reason: typeof d.reason === "string" && d.reason.trim().length > 0 ? d.reason.trim() : void 0,
198
+ compacted: typeof d.aborted === "boolean" ? !d.aborted && !!result : !!result,
199
+ aborted: typeof d.aborted === "boolean" ? d.aborted : void 0,
200
+ willRetry: typeof d.willRetry === "boolean" ? d.willRetry : void 0,
201
+ tokensBefore: toNumber(result?.tokensBefore),
202
+ error: typeof d.errorMessage === "string" && d.errorMessage.trim().length > 0 ? d.errorMessage : void 0
203
+ });
204
+ break;
205
+ }
151
206
  case "ui_action": {
152
207
  const d = data;
153
208
  uiActions.value = [{ toolCallId: d.toolCallId, type: d.type, payload: d.payload, at: Date.now() }, ...uiActions.value].slice(0, 50);
154
209
  break;
155
210
  }
156
211
  case "end":
212
+ compactionInProgress.value = false;
157
213
  status.value = "ready";
158
214
  currentAssistantId = null;
159
215
  void refreshFiles();
160
216
  break;
161
217
  case "error": {
162
218
  const d = data;
219
+ compactionInProgress.value = false;
163
220
  error.value = new Error(d?.message ?? "stream error");
164
221
  status.value = "error";
165
222
  currentAssistantId = null;
@@ -212,7 +269,11 @@ export const useAgentRuntime = () => {
212
269
  error.value = null;
213
270
  const res = await $fetch(`${apiPrefix}/conversations`, {
214
271
  method: "POST",
215
- body: { env: buildEnvFromAuth(auth.value), language: options.language }
272
+ body: {
273
+ env: buildEnvFromAuth(auth.value),
274
+ language: options.language,
275
+ requestOptions: options.requestOptions
276
+ }
216
277
  });
217
278
  conversationId.value = res.conversationId;
218
279
  abortController?.abort();
@@ -259,7 +320,12 @@ export const useAgentRuntime = () => {
259
320
  const wireContent = options.rewriteContent ? options.rewriteContent(text) : text;
260
321
  await $fetch(`${apiPrefix}/conversations/${conversationId.value}/messages`, {
261
322
  method: "POST",
262
- body: { content: wireContent, context: options.context, language: options.language }
323
+ body: {
324
+ content: wireContent,
325
+ context: options.context,
326
+ language: options.language,
327
+ requestOptions: options.requestOptions
328
+ }
263
329
  }).catch((err) => {
264
330
  error.value = err instanceof Error ? err : new Error(String(err));
265
331
  status.value = "error";
@@ -278,6 +344,9 @@ export const useAgentRuntime = () => {
278
344
  currentAssistantId = null;
279
345
  replaceMessages([]);
280
346
  uiActions.value = [];
347
+ contextUsage.value = null;
348
+ compactionInProgress.value = false;
349
+ compactionLog.value = [];
281
350
  files.value = [];
282
351
  status.value = "idle";
283
352
  error.value = null;
@@ -291,11 +360,15 @@ export const useAgentRuntime = () => {
291
360
  status,
292
361
  error,
293
362
  uiActions,
363
+ contextUsage,
364
+ compactionInProgress,
365
+ compactionLog,
294
366
  app,
295
367
  auth,
296
368
  authReady,
297
369
  files,
298
370
  fileUrl,
371
+ filePreviewUrl,
299
372
  refreshFiles,
300
373
  saveAuth,
301
374
  start,
@@ -1,11 +1,6 @@
1
1
  import MarkdownIt from 'markdown-it';
2
- export interface AgentRuntimeMarkdownRenderOptions {
3
- /**
4
- * Resolve a workspace-relative path (e.g. `outputs/foo.pdf`) to a fetchable
5
- * URL on the runtime backend. Returns null/empty if the path can't be
6
- * resolved, in which case the renderer falls back to the original text.
7
- */
8
- resolveWorkspacePath?: (relPath: string) => string | null | undefined;
2
+ import { type AgentRuntimeAssetResolverOptions } from '../utils/files.js';
3
+ export interface AgentRuntimeMarkdownRenderOptions extends AgentRuntimeAssetResolverOptions {
9
4
  }
10
5
  export declare const createAgentRuntimeMarkdownRenderer: (options?: AgentRuntimeMarkdownRenderOptions) => MarkdownIt;
11
6
  export declare const useAgentRuntimeMarkdown: (options?: AgentRuntimeMarkdownRenderOptions) => {
@@ -1,10 +1,70 @@
1
1
  import MarkdownIt from "markdown-it";
2
- import {
3
- isAgentRuntimeImagePath,
4
- resolveAgentRuntimeWorkspaceUri,
5
- toAgentRuntimeWorkspaceRelativePath
6
- } from "../utils/files.js";
7
- const BARE_WORKSPACE_PATH_RE = /(sandbox:[^\s)\]"'<>]+|\/workspace\/[^\s)\]"'<>]+)/g;
2
+ import { resolveAgentRuntimeWorkspaceAsset, resolveAgentRuntimeWorkspaceUri } from "../utils/files.js";
3
+ const escapeAttr = (md, value) => md.utils.escapeHtml(value);
4
+ const previewAttrs = (md, asset) => {
5
+ if (!asset.canPreview || !asset.previewUrl) {
6
+ return "";
7
+ }
8
+ const attrs = [
9
+ ["data-agent-runtime-preview", "true"],
10
+ ["data-agent-runtime-preview-kind", asset.kind],
11
+ ["data-agent-runtime-preview-label", asset.label],
12
+ ["data-agent-runtime-preview-url", asset.previewUrl]
13
+ ];
14
+ if (asset.canDownload && asset.rawUrl) {
15
+ attrs.push(["data-agent-runtime-download-url", asset.rawUrl]);
16
+ }
17
+ return attrs.map(([name, value]) => `${name}="${escapeAttr(md, value)}"`).join(" ");
18
+ };
19
+ const buildDownloadLinkMarkup = (md, asset, label) => {
20
+ if (!asset.canDownload || !asset.rawUrl) {
21
+ return escapeAttr(md, label);
22
+ }
23
+ return `<a href="${escapeAttr(md, asset.rawUrl)}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-file-link">${escapeAttr(md, label)}</a>`;
24
+ };
25
+ const buildImageMarkup = (md, asset, alt, title) => {
26
+ const previewUrl = asset.previewUrl ?? asset.rawUrl;
27
+ if (!previewUrl) {
28
+ return escapeAttr(md, alt || asset.label);
29
+ }
30
+ const safeAlt = escapeAttr(md, alt || asset.label);
31
+ const safePreviewUrl = escapeAttr(md, previewUrl);
32
+ const titleAttr = title ? ` title="${escapeAttr(md, title)}"` : "";
33
+ const attrs = previewAttrs(md, asset);
34
+ return `<a href="${safePreviewUrl}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-image-link" ${attrs}><img src="${safePreviewUrl}" alt="${safeAlt}" loading="lazy" class="agent-runtime-md-image"${titleAttr} /></a>`;
35
+ };
36
+ const buildPreviewShellMarkup = (md, asset, label) => {
37
+ const previewUrl = asset.previewUrl;
38
+ if (!previewUrl) {
39
+ return buildDownloadLinkMarkup(md, asset, label);
40
+ }
41
+ const safeLabel = escapeAttr(md, label);
42
+ const safePreviewUrl = escapeAttr(md, previewUrl);
43
+ const attrs = previewAttrs(md, asset);
44
+ const shellClass = asset.kind === "pdf" ? "agent-runtime-md-pdf-shell" : "agent-runtime-md-html-shell";
45
+ const frameClass = asset.kind === "pdf" ? "agent-runtime-md-pdf-frame" : "agent-runtime-md-html-frame";
46
+ const frameAttrs = asset.kind === "pdf" ? "" : ' sandbox="" referrerpolicy="no-referrer"';
47
+ const title = asset.kind === "pdf" ? `PDF preview: ${label}` : `HTML preview: ${label}`;
48
+ const downloadButton = asset.canDownload && asset.rawUrl ? `<a href="${escapeAttr(md, asset.rawUrl)}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-download-link">Download</a>` : "";
49
+ return `<span class="${shellClass}"><span class="agent-runtime-md-embed-toolbar"><span class="agent-runtime-md-embed-label">${safeLabel}</span><span class="agent-runtime-md-embed-actions"><a href="${safePreviewUrl}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-preview-link" ${attrs}>Preview</a>${downloadButton}</span></span><iframe src="${safePreviewUrl}" title="${escapeAttr(md, title)}" loading="lazy"${frameAttrs} class="${frameClass}"></iframe></span>`;
50
+ };
51
+ const toHtmlToken = (state, markup) => {
52
+ const token = new state.Token("html_inline", "", 0);
53
+ token.content = markup;
54
+ return token;
55
+ };
56
+ const renderWorkspaceLink = (md, asset, label) => {
57
+ if (asset.kind === "blocked") {
58
+ return escapeAttr(md, label);
59
+ }
60
+ if (asset.kind === "image") {
61
+ return buildImageMarkup(md, asset, label);
62
+ }
63
+ if (asset.kind === "html" || asset.kind === "pdf") {
64
+ return buildPreviewShellMarkup(md, asset, label);
65
+ }
66
+ return buildDownloadLinkMarkup(md, asset, label);
67
+ };
8
68
  export const createAgentRuntimeMarkdownRenderer = (options = {}) => {
9
69
  const md = new MarkdownIt({
10
70
  html: false,
@@ -12,15 +72,15 @@ export const createAgentRuntimeMarkdownRenderer = (options = {}) => {
12
72
  breaks: true,
13
73
  typographer: false
14
74
  });
15
- const resolve = (uri) => resolveAgentRuntimeWorkspaceUri(uri, options.resolveWorkspacePath);
75
+ const resolveAsset = (uri) => resolveAgentRuntimeWorkspaceAsset(uri, options);
16
76
  const defaultLinkOpen = md.renderer.rules.link_open ?? ((tokens, idx, opts, _env, self) => self.renderToken(tokens, idx, opts));
17
77
  md.renderer.rules.link_open = (tokens, idx, opts, env, self) => {
18
78
  const token = tokens[idx];
19
79
  if (token) {
20
80
  const href = token.attrGet("href") ?? "";
21
- const rewritten = resolve(href);
22
- if (rewritten) {
23
- token.attrSet("href", rewritten);
81
+ const resolved = resolveAgentRuntimeWorkspaceUri(href, options.resolveWorkspacePath);
82
+ if (resolved) {
83
+ token.attrSet("href", resolved);
24
84
  token.attrSet("target", "_blank");
25
85
  token.attrSet("rel", "noopener noreferrer");
26
86
  token.attrSet("class", "agent-runtime-md-file-link");
@@ -34,64 +94,42 @@ export const createAgentRuntimeMarkdownRenderer = (options = {}) => {
34
94
  md.renderer.rules.image = (tokens, idx) => {
35
95
  const token = tokens[idx];
36
96
  if (!token) return "";
37
- const rawSrc = token.attrGet("src") ?? "";
38
- const rewritten = resolve(rawSrc);
39
- const src = rewritten ?? rawSrc;
97
+ const src = token.attrGet("src") ?? "";
40
98
  const alt = token.content || "";
41
99
  const title = token.attrGet("title");
42
- const titleAttr = title ? ` title="${md.utils.escapeHtml(title)}"` : "";
43
- const safeAlt = md.utils.escapeHtml(alt);
44
- const safeSrc = md.utils.escapeHtml(src);
45
- const looksLikeImage = isAgentRuntimeImagePath(rawSrc) || isAgentRuntimeImagePath(src);
46
- if (!looksLikeImage && rewritten) {
47
- return `<a href="${safeSrc}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-file-link">${safeAlt || safeSrc}</a>`;
100
+ const asset = resolveAsset(src);
101
+ if (!asset) {
102
+ const safeSrc = escapeAttr(md, src);
103
+ const safeAlt = escapeAttr(md, alt);
104
+ const titleAttr = title ? ` title="${escapeAttr(md, title)}"` : "";
105
+ return `<img src="${safeSrc}" alt="${safeAlt}" loading="lazy" class="agent-runtime-md-image"${titleAttr} />`;
106
+ }
107
+ if (asset.kind === "image") {
108
+ return buildImageMarkup(md, asset, alt, title);
48
109
  }
49
- return `<a href="${safeSrc}" target="_blank" rel="noopener noreferrer" class="agent-runtime-md-image-link"><img src="${safeSrc}" alt="${safeAlt}" loading="lazy" class="agent-runtime-md-image"${titleAttr} /></a>`;
110
+ return renderWorkspaceLink(md, asset, alt || asset.label);
50
111
  };
51
- md.core.ruler.after("inline", "agent_runtime_workspace_autolink", (state) => {
112
+ md.core.ruler.after("inline", "agent_runtime_workspace_links", (state) => {
52
113
  for (const blockToken of state.tokens) {
53
114
  if (blockToken.type !== "inline" || !blockToken.children) continue;
54
115
  const out = [];
55
- for (const child of blockToken.children) {
56
- if (child.type !== "text") {
57
- out.push(child);
116
+ const children = blockToken.children;
117
+ for (let idx = 0; idx < children.length; idx++) {
118
+ const child = children[idx];
119
+ if (!child) {
58
120
  continue;
59
121
  }
60
- const text = child.content;
61
- BARE_WORKSPACE_PATH_RE.lastIndex = 0;
62
- if (!BARE_WORKSPACE_PATH_RE.test(text)) {
122
+ const next = children[idx + 1];
123
+ const nextNext = children[idx + 2];
124
+ const href = child.type === "link_open" && typeof child.attrGet === "function" ? child.attrGet("href") ?? "" : "";
125
+ const asset = href ? resolveAsset(href) : null;
126
+ const isWorkspaceLink = asset && child.type === "link_open" && next?.type === "text" && nextNext?.type === "link_close";
127
+ if (!isWorkspaceLink) {
63
128
  out.push(child);
64
129
  continue;
65
130
  }
66
- BARE_WORKSPACE_PATH_RE.lastIndex = 0;
67
- let lastIndex = 0;
68
- let match;
69
- while ((match = BARE_WORKSPACE_PATH_RE.exec(text)) !== null) {
70
- const uri = match[0];
71
- const url = resolve(uri);
72
- if (!url) continue;
73
- if (match.index > lastIndex) {
74
- const before = new state.Token("text", "", 0);
75
- before.content = text.slice(lastIndex, match.index);
76
- out.push(before);
77
- }
78
- const open = new state.Token("link_open", "a", 1);
79
- open.attrSet("href", url);
80
- open.attrSet("target", "_blank");
81
- open.attrSet("rel", "noopener noreferrer");
82
- open.attrSet("class", "agent-runtime-md-file-link");
83
- const inner = new state.Token("text", "", 0);
84
- const relPath = toAgentRuntimeWorkspaceRelativePath(uri) ?? uri;
85
- inner.content = relPath.split("/").pop() || relPath;
86
- const close = new state.Token("link_close", "a", -1);
87
- out.push(open, inner, close);
88
- lastIndex = match.index + uri.length;
89
- }
90
- if (lastIndex < text.length) {
91
- const tail = new state.Token("text", "", 0);
92
- tail.content = text.slice(lastIndex);
93
- out.push(tail);
94
- }
131
+ out.push(toHtmlToken(state, renderWorkspaceLink(md, asset, next.content || asset.label)));
132
+ idx += 2;
95
133
  }
96
134
  blockToken.children = out;
97
135
  }
@@ -1,5 +1,7 @@
1
1
  export type { AgentRuntimeMarkdownRenderOptions } from './composables/useAgentRuntimeMarkdown.js';
2
- export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from './utils/files.js';
2
+ export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind, AgentRuntimeResolvedAsset } from './utils/files.js';
3
+ export { default as AgentRuntimeArtifactDialog } from './components/AgentRuntimeArtifactDialog.vue.js';
3
4
  export { default as AgentRuntimeArtifactPreview } from './components/AgentRuntimeArtifactPreview.vue.js';
5
+ export { default as AgentRuntimeMarkdown } from './components/AgentRuntimeMarkdown.vue.js';
4
6
  export { createAgentRuntimeMarkdownRenderer, useAgentRuntimeMarkdown } from './composables/useAgentRuntimeMarkdown.js';
5
- export { canPreviewAgentRuntimeFileInline, getAgentRuntimeFilePreviewKind, isAgentRuntimeImageMimeType, isAgentRuntimeImagePath, isAgentRuntimePdfMimeType, isAgentRuntimePdfPath, isAgentRuntimeTextMimeType, isAgentRuntimeTextPath, resolveAgentRuntimeWorkspaceUri, toAgentRuntimeWorkspaceRelativePath, } from './utils/files.js';
7
+ export { canPreviewAgentRuntimeFileInline, isAgentRuntimeBlockedMimeType, isAgentRuntimeBlockedPath, isAgentRuntimeHtmlMimeType, isAgentRuntimeHtmlPath, getAgentRuntimeFilePreviewKind, isAgentRuntimeImageMimeType, isAgentRuntimeImagePath, isAgentRuntimePdfMimeType, isAgentRuntimePdfPath, resolveAgentRuntimeFileAsset, resolveAgentRuntimeWorkspaceUri, resolveAgentRuntimeWorkspaceAsset, toAgentRuntimeWorkspaceRelativePath, } from './utils/files.js';
@@ -1,14 +1,20 @@
1
+ export { default as AgentRuntimeArtifactDialog } from "./components/AgentRuntimeArtifactDialog.vue";
1
2
  export { default as AgentRuntimeArtifactPreview } from "./components/AgentRuntimeArtifactPreview.vue";
3
+ export { default as AgentRuntimeMarkdown } from "./components/AgentRuntimeMarkdown.vue";
2
4
  export { createAgentRuntimeMarkdownRenderer, useAgentRuntimeMarkdown } from "./composables/useAgentRuntimeMarkdown.js";
3
5
  export {
4
6
  canPreviewAgentRuntimeFileInline,
7
+ isAgentRuntimeBlockedMimeType,
8
+ isAgentRuntimeBlockedPath,
9
+ isAgentRuntimeHtmlMimeType,
10
+ isAgentRuntimeHtmlPath,
5
11
  getAgentRuntimeFilePreviewKind,
6
12
  isAgentRuntimeImageMimeType,
7
13
  isAgentRuntimeImagePath,
8
14
  isAgentRuntimePdfMimeType,
9
15
  isAgentRuntimePdfPath,
10
- isAgentRuntimeTextMimeType,
11
- isAgentRuntimeTextPath,
16
+ resolveAgentRuntimeFileAsset,
12
17
  resolveAgentRuntimeWorkspaceUri,
18
+ resolveAgentRuntimeWorkspaceAsset,
13
19
  toAgentRuntimeWorkspaceRelativePath
14
20
  } from "./utils/files.js";
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
+ export default _default;
@@ -0,0 +1,42 @@
1
+ import { Readable } from "node:stream";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { createError, defineEventHandler, getRouterParam, sendStream, setHeader } from "h3";
4
+ import { agentRuntime } from "../../../../../utils/agent-runtime.js";
5
+ const PASS_THROUGH_HEADERS = [
6
+ "content-type",
7
+ "content-length",
8
+ "content-disposition",
9
+ "cache-control",
10
+ "content-security-policy",
11
+ "x-content-type-options"
12
+ ];
13
+ export default defineEventHandler(async (event) => {
14
+ const cfg = agentRuntime();
15
+ const id = getRouterParam(event, "id");
16
+ if (!id) throw createError({ statusCode: 400, statusMessage: "missing conversation id" });
17
+ const apiPrefix = (useRuntimeConfig().public.agentRuntime?.apiPrefix ?? "/api/agent-runtime").replace(/\/+$/, "");
18
+ const marker = `${apiPrefix}/conversations/${id}/files/preview/`;
19
+ const idx = event.path.indexOf(marker);
20
+ const tail = idx >= 0 ? event.path.slice(idx + marker.length) : "";
21
+ if (!tail) throw createError({ statusCode: 400, statusMessage: "missing file path" });
22
+ const upstreamUrl = `${cfg.baseUrl}/v1/conversations/${id}/files/preview/${tail}`;
23
+ const controller = new AbortController();
24
+ event.node.req.on("close", () => controller.abort());
25
+ const upstream = await fetch(upstreamUrl, {
26
+ headers: { "X-Agent-Runtime-App-Key": cfg.appKey },
27
+ signal: controller.signal
28
+ });
29
+ if (!upstream.ok || !upstream.body) {
30
+ throw createError({
31
+ statusCode: upstream.status || 502,
32
+ statusMessage: `agent-runtime file preview failed: ${upstream.status} ${upstream.statusText}`
33
+ });
34
+ }
35
+ for (const name of PASS_THROUGH_HEADERS) {
36
+ const value = upstream.headers.get(name);
37
+ if (value) {
38
+ setHeader(event, name, value);
39
+ }
40
+ }
41
+ return sendStream(event, Readable.fromWeb(upstream.body));
42
+ });
@@ -5,6 +5,7 @@ export interface FileEntry {
5
5
  size: number;
6
6
  mtime: string;
7
7
  mimeType: string;
8
+ kind: 'image' | 'html' | 'pdf' | 'download';
8
9
  }
9
10
  /**
10
11
  * GET `${apiPrefix}/conversations/:id/files` — lists the workspace files
@@ -9,7 +9,12 @@ export default defineEventHandler(async (event) => {
9
9
  const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/messages`, {
10
10
  method: "POST",
11
11
  headers: agentRuntimeHeaders(cfg),
12
- body: JSON.stringify({ content: body.content, context: body.context, language: body.language })
12
+ body: JSON.stringify({
13
+ content: body.content,
14
+ context: body.context,
15
+ requestOptions: body.requestOptions,
16
+ language: body.language
17
+ })
13
18
  });
14
19
  if (!response.ok) {
15
20
  throw createError({
@@ -6,7 +6,13 @@ export default defineEventHandler(async (event) => {
6
6
  const response = await fetch(`${cfg.baseUrl}/v1/conversations`, {
7
7
  method: "POST",
8
8
  headers: agentRuntimeHeaders(cfg),
9
- body: JSON.stringify({ appId: cfg.appId, env: body.env ?? {}, model: body.model, language: body.language })
9
+ body: JSON.stringify({
10
+ appId: cfg.appId,
11
+ env: body.env ?? {},
12
+ model: body.model,
13
+ requestOptions: body.requestOptions,
14
+ language: body.language
15
+ })
10
16
  });
11
17
  if (!response.ok) {
12
18
  throw createError({
@@ -2,15 +2,33 @@ export interface AgentRuntimeFileLike {
2
2
  name?: string | null;
3
3
  relPath?: string | null;
4
4
  mimeType?: string | null;
5
+ kind?: AgentRuntimeFilePreviewKind | null;
6
+ }
7
+ export type AgentRuntimeFilePreviewKind = 'image' | 'html' | 'pdf' | 'download' | 'blocked';
8
+ export interface AgentRuntimeResolvedAsset extends AgentRuntimeFileLike {
9
+ kind: AgentRuntimeFilePreviewKind;
10
+ label: string;
11
+ relPath: string | null;
12
+ rawUrl: string | null;
13
+ previewUrl: string | null;
14
+ canPreview: boolean;
15
+ canDownload: boolean;
16
+ }
17
+ export interface AgentRuntimeAssetResolverOptions {
18
+ resolveWorkspacePath?: (relPath: string) => string | null | undefined;
19
+ resolveWorkspacePreviewPath?: (relPath: string) => string | null | undefined;
5
20
  }
6
- export type AgentRuntimeFilePreviewKind = 'image' | 'pdf' | 'text' | 'download';
7
21
  export declare const toAgentRuntimeWorkspaceRelativePath: (uri: string) => string | null;
8
22
  export declare const resolveAgentRuntimeWorkspaceUri: (uri: string, resolveWorkspacePath?: (relPath: string) => string | null | undefined) => string | null;
23
+ export declare const isAgentRuntimeBlockedMimeType: (mimeType: string | null | undefined) => boolean;
9
24
  export declare const isAgentRuntimeImageMimeType: (mimeType: string | null | undefined) => boolean;
25
+ export declare const isAgentRuntimeHtmlMimeType: (mimeType: string | null | undefined) => boolean;
10
26
  export declare const isAgentRuntimePdfMimeType: (mimeType: string | null | undefined) => boolean;
11
- export declare const isAgentRuntimeTextMimeType: (mimeType: string | null | undefined) => boolean;
12
27
  export declare const isAgentRuntimeImagePath: (value: string | null | undefined) => boolean;
28
+ export declare const isAgentRuntimeHtmlPath: (value: string | null | undefined) => boolean;
13
29
  export declare const isAgentRuntimePdfPath: (value: string | null | undefined) => boolean;
14
- export declare const isAgentRuntimeTextPath: (value: string | null | undefined) => boolean;
30
+ export declare const isAgentRuntimeBlockedPath: (value: string | null | undefined) => boolean;
15
31
  export declare const getAgentRuntimeFilePreviewKind: (file: AgentRuntimeFileLike) => AgentRuntimeFilePreviewKind;
16
32
  export declare const canPreviewAgentRuntimeFileInline: (file: AgentRuntimeFileLike) => boolean;
33
+ export declare const resolveAgentRuntimeFileAsset: (file: AgentRuntimeFileLike, options?: AgentRuntimeAssetResolverOptions) => AgentRuntimeResolvedAsset;
34
+ export declare const resolveAgentRuntimeWorkspaceAsset: (uri: string, options?: AgentRuntimeAssetResolverOptions) => AgentRuntimeResolvedAsset | null;
@@ -1,42 +1,87 @@
1
1
  const IMAGE_EXT_RE = /\.(?:png|jpe?g|gif|webp|svg|avif|bmp)(?:[?#].*)?$/i;
2
+ const HTML_EXT_RE = /\.html?(?:[?#].*)?$/i;
2
3
  const PDF_EXT_RE = /\.pdf(?:[?#].*)?$/i;
3
- const TEXT_EXT_RE = /\.(?:txt|md|json|csv|tsv|xml|ya?ml|log|html?|css|js|mjs|cjs|ts|tsx|jsx|vue|py|rb|sh|sql)(?:[?#].*)?$/i;
4
+ const DOWNLOAD_EXT_RE = /\.(?:txt|md|json|csv|tsv|xml|ya?ml|log)(?:[?#].*)?$/i;
5
+ const BLOCKED_EXT_RE = /\.(?:py|rb|pl|php|sh|bash|zsh|fish|ps1|bat|cmd|js|mjs|cjs|ts|tsx|jsx|vue)(?:[?#].*)?$/i;
6
+ const BLOCKED_MIME_RE = /^(?:application\/javascript|application\/typescript|application\/x-httpd-php|application\/x-python-code|application\/x-sh|text\/javascript|text\/x-python|text\/x-script\.python|text\/x-shellscript|text\/x-typescript)(?:$|;)/i;
7
+ const PREVIEWABLE_KINDS = /* @__PURE__ */ new Set(["image", "html", "pdf"]);
8
+ const FILE_KIND_ORDER = ["image", "html", "pdf", "download", "blocked"];
9
+ const basename = (value) => value.split("/").pop() || value;
10
+ const normalizeKind = (value) => typeof value === "string" && FILE_KIND_ORDER.includes(value) ? value : null;
4
11
  export const toAgentRuntimeWorkspaceRelativePath = (uri) => {
5
- if (!uri) return null;
6
- if (uri.startsWith("sandbox:")) return uri.slice("sandbox:".length).replace(/^\/+/, "");
7
- if (uri.startsWith("/workspace/")) return uri.slice("/workspace/".length);
8
- if (uri === "/workspace") return "";
9
- if (uri.startsWith("workspace/")) return uri.slice("workspace/".length);
10
- return null;
12
+ if (!uri.startsWith("sandbox:")) {
13
+ return null;
14
+ }
15
+ const relPath = uri.slice("sandbox:".length).replace(/^\/+/, "");
16
+ return relPath.length > 0 ? relPath : null;
11
17
  };
12
18
  export const resolveAgentRuntimeWorkspaceUri = (uri, resolveWorkspacePath) => {
13
19
  const relPath = toAgentRuntimeWorkspaceRelativePath(uri);
14
- if (relPath === null) return null;
15
- const resolved = resolveWorkspacePath?.(relPath);
16
- return resolved || null;
20
+ if (relPath === null) {
21
+ return null;
22
+ }
23
+ return resolveWorkspacePath?.(relPath) ?? null;
17
24
  };
25
+ export const isAgentRuntimeBlockedMimeType = (mimeType) => typeof mimeType === "string" && BLOCKED_MIME_RE.test(mimeType);
18
26
  export const isAgentRuntimeImageMimeType = (mimeType) => typeof mimeType === "string" && /^image\//i.test(mimeType);
27
+ export const isAgentRuntimeHtmlMimeType = (mimeType) => typeof mimeType === "string" && /^text\/html(?:$|;)/i.test(mimeType);
19
28
  export const isAgentRuntimePdfMimeType = (mimeType) => typeof mimeType === "string" && mimeType.toLowerCase() === "application/pdf";
20
- export const isAgentRuntimeTextMimeType = (mimeType) => {
29
+ export const isAgentRuntimeImagePath = (value) => typeof value === "string" && IMAGE_EXT_RE.test(value);
30
+ export const isAgentRuntimeHtmlPath = (value) => typeof value === "string" && HTML_EXT_RE.test(value);
31
+ export const isAgentRuntimePdfPath = (value) => typeof value === "string" && PDF_EXT_RE.test(value);
32
+ export const isAgentRuntimeBlockedPath = (value) => typeof value === "string" && BLOCKED_EXT_RE.test(value);
33
+ const isAgentRuntimeDownloadMimeType = (mimeType) => {
21
34
  if (typeof mimeType !== "string" || !mimeType) {
22
35
  return false;
23
36
  }
24
37
  const normalized = mimeType.toLowerCase();
25
- return normalized.startsWith("text/") || normalized === "application/json" || normalized === "application/ld+json" || normalized === "application/xml" || normalized === "application/yaml" || normalized === "application/x-yaml";
38
+ return normalized.startsWith("text/") && !isAgentRuntimeHtmlMimeType(normalized) && !isAgentRuntimeBlockedMimeType(normalized) || normalized === "application/json" || normalized === "application/ld+json" || normalized === "application/xml" || normalized === "application/yaml" || normalized === "application/x-yaml";
26
39
  };
27
- export const isAgentRuntimeImagePath = (value) => typeof value === "string" && IMAGE_EXT_RE.test(value);
28
- export const isAgentRuntimePdfPath = (value) => typeof value === "string" && PDF_EXT_RE.test(value);
29
- export const isAgentRuntimeTextPath = (value) => typeof value === "string" && TEXT_EXT_RE.test(value);
40
+ const isAgentRuntimeDownloadPath = (value) => typeof value === "string" && DOWNLOAD_EXT_RE.test(value);
30
41
  export const getAgentRuntimeFilePreviewKind = (file) => {
42
+ const explicitKind = normalizeKind(file.kind);
43
+ if (explicitKind) {
44
+ return explicitKind;
45
+ }
46
+ if (isAgentRuntimeBlockedMimeType(file.mimeType) || isAgentRuntimeBlockedPath(file.name) || isAgentRuntimeBlockedPath(file.relPath)) {
47
+ return "blocked";
48
+ }
31
49
  if (isAgentRuntimeImageMimeType(file.mimeType) || isAgentRuntimeImagePath(file.name) || isAgentRuntimeImagePath(file.relPath)) {
32
50
  return "image";
33
51
  }
52
+ if (isAgentRuntimeHtmlMimeType(file.mimeType) || isAgentRuntimeHtmlPath(file.name) || isAgentRuntimeHtmlPath(file.relPath)) {
53
+ return "html";
54
+ }
34
55
  if (isAgentRuntimePdfMimeType(file.mimeType) || isAgentRuntimePdfPath(file.name) || isAgentRuntimePdfPath(file.relPath)) {
35
56
  return "pdf";
36
57
  }
37
- if (isAgentRuntimeTextMimeType(file.mimeType) || isAgentRuntimeTextPath(file.name) || isAgentRuntimeTextPath(file.relPath)) {
38
- return "text";
58
+ if (isAgentRuntimeDownloadMimeType(file.mimeType) || isAgentRuntimeDownloadPath(file.name) || isAgentRuntimeDownloadPath(file.relPath)) {
59
+ return "download";
39
60
  }
40
61
  return "download";
41
62
  };
42
- export const canPreviewAgentRuntimeFileInline = (file) => getAgentRuntimeFilePreviewKind(file) !== "download";
63
+ export const canPreviewAgentRuntimeFileInline = (file) => PREVIEWABLE_KINDS.has(getAgentRuntimeFilePreviewKind(file));
64
+ export const resolveAgentRuntimeFileAsset = (file, options = {}) => {
65
+ const relPath = file.relPath ?? null;
66
+ const kind = getAgentRuntimeFilePreviewKind(file);
67
+ const label = file.name ?? (relPath ? basename(relPath) : "Attachment");
68
+ const rawUrl = relPath ? options.resolveWorkspacePath?.(relPath) || null : null;
69
+ const previewUrl = relPath && PREVIEWABLE_KINDS.has(kind) ? options.resolveWorkspacePreviewPath?.(relPath) || rawUrl : null;
70
+ return {
71
+ ...file,
72
+ kind,
73
+ label,
74
+ relPath,
75
+ rawUrl,
76
+ previewUrl,
77
+ canPreview: kind !== "blocked" && previewUrl !== null,
78
+ canDownload: kind !== "blocked" && rawUrl !== null
79
+ };
80
+ };
81
+ export const resolveAgentRuntimeWorkspaceAsset = (uri, options = {}) => {
82
+ const relPath = toAgentRuntimeWorkspaceRelativePath(uri);
83
+ if (relPath === null) {
84
+ return null;
85
+ }
86
+ return resolveAgentRuntimeFileAsset({ relPath, name: basename(relPath) }, options);
87
+ };
package/dist/types.d.mts CHANGED
@@ -2,7 +2,9 @@ export { type AppAuth, type AppEnvField, type AppInfo, type ChatStatus, type Fil
2
2
 
3
3
  export { type AgentRuntimeMarkdownRenderOptions } from '../dist/runtime/composables/useAgentRuntimeMarkdown.js'
4
4
 
5
- export { type AgentRuntimeFileLike, type AgentRuntimeFilePreviewKind } from '../dist/runtime/utils/files.js'
5
+ export { type AgentRuntimeFileLike, type AgentRuntimeFilePreviewKind, type AgentRuntimeResolvedAsset } from '../dist/runtime/utils/files.js'
6
+
7
+ export { type createAgentRuntimeRequestHeaders, type createScopeFingerprint, type normalizeAgentRuntimeBaseUrl, type resolveAgentRuntimeConfig } from '../dist/runtime/shared.js'
6
8
 
7
9
  export { default } from './module.mjs'
8
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d4y/agent-runtime-nuxt",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",