@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.
- package/README.md +27 -4
- package/dist/module.d.mts +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -1
- package/dist/runtime/components/AgentRuntimeArtifactDialog.d.vue.ts +20 -0
- package/dist/runtime/components/AgentRuntimeArtifactDialog.vue +90 -0
- package/dist/runtime/components/AgentRuntimeArtifactDialog.vue.d.ts +20 -0
- package/dist/runtime/components/AgentRuntimeArtifactPreview.d.vue.ts +0 -2
- package/dist/runtime/components/AgentRuntimeArtifactPreview.vue +19 -64
- package/dist/runtime/components/AgentRuntimeArtifactPreview.vue.d.ts +0 -2
- package/dist/runtime/components/AgentRuntimeMarkdown.d.vue.ts +19 -0
- package/dist/runtime/components/AgentRuntimeMarkdown.vue +77 -0
- package/dist/runtime/components/AgentRuntimeMarkdown.vue.d.ts +19 -0
- package/dist/runtime/composables/useAgentRuntime.d.ts +42 -0
- package/dist/runtime/composables/useAgentRuntime.js +77 -4
- package/dist/runtime/composables/useAgentRuntimeMarkdown.d.ts +2 -7
- package/dist/runtime/composables/useAgentRuntimeMarkdown.js +94 -56
- package/dist/runtime/frontend.d.ts +4 -2
- package/dist/runtime/frontend.js +8 -2
- package/dist/runtime/server/api/conversations/[id]/files/preview/[...path].get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id]/files/preview/[...path].get.js +42 -0
- package/dist/runtime/server/api/conversations/[id]/files.get.d.ts +1 -0
- package/dist/runtime/server/api/conversations/[id]/messages.post.js +6 -1
- package/dist/runtime/server/api/conversations.post.js +7 -1
- package/dist/runtime/utils/files.d.ts +21 -3
- package/dist/runtime/utils/files.js +63 -18
- package/dist/types.d.mts +3 -1
- 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
|
|
164
|
-
|
|
165
|
-
|
|
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
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
|
|
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 === '
|
|
30
|
+
v-else-if="kind === 'html' && resolvedSrc"
|
|
80
31
|
:src="resolvedSrc"
|
|
81
|
-
:title="file.name ?? file.relPath ?? '
|
|
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
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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(
|
|
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 :
|
|
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: {
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
|
22
|
-
if (
|
|
23
|
-
token.attrSet("href",
|
|
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
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return `<
|
|
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
|
|
110
|
+
return renderWorkspaceLink(md, asset, alt || asset.label);
|
|
50
111
|
};
|
|
51
|
-
md.core.ruler.after("inline", "
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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,
|
|
7
|
+
export { canPreviewAgentRuntimeFileInline, isAgentRuntimeBlockedMimeType, isAgentRuntimeBlockedPath, isAgentRuntimeHtmlMimeType, isAgentRuntimeHtmlPath, getAgentRuntimeFilePreviewKind, isAgentRuntimeImageMimeType, isAgentRuntimeImagePath, isAgentRuntimePdfMimeType, isAgentRuntimePdfPath, resolveAgentRuntimeFileAsset, resolveAgentRuntimeWorkspaceUri, resolveAgentRuntimeWorkspaceAsset, toAgentRuntimeWorkspaceRelativePath, } from './utils/files.js';
|
package/dist/runtime/frontend.js
CHANGED
|
@@ -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
|
-
|
|
11
|
-
isAgentRuntimeTextPath,
|
|
16
|
+
resolveAgentRuntimeFileAsset,
|
|
12
17
|
resolveAgentRuntimeWorkspaceUri,
|
|
18
|
+
resolveAgentRuntimeWorkspaceAsset,
|
|
13
19
|
toAgentRuntimeWorkspaceRelativePath
|
|
14
20
|
} from "./utils/files.js";
|
|
@@ -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
|
+
});
|
|
@@ -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({
|
|
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({
|
|
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
|
|
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
|
|
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)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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)
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
38
|
-
return "
|
|
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)
|
|
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.
|
|
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",
|