The @openharness/vue package provides the same functionality as the React package, using Vue 3 composables and Nuxt-compatible components.
Setup
npm install @openharness/vue
Wrap your app with the OpenHarnessProvider component:
<script setup lang="ts">
import { OpenHarnessProvider } from "@openharness/vue";
</script>
<template>
<OpenHarnessProvider>
<Chat />
</OpenHarnessProvider>
</template>
useOpenHarness
Creates a chat session connected to your API endpoint. Returns an AI SDK 5 Chat instance with reactive properties (messages, status, sendMessage, stop, etc.), typed with OHUIMessage:
<script setup lang="ts">
import { ref } from "vue";
import { useOpenHarness } from "@openharness/vue";
const chat = useOpenHarness({ endpoint: "/api/chat" });
const input = ref("");
function send() {
const text = input.value.trim();
if (!text) return;
input.value = "";
chat.sendMessage({ text });
}
</script>
<template>
<div>
<div v-for="msg in chat.messages" :key="msg.id">
<template v-for="(part, i) in msg.parts" :key="i">
<span v-if="part.type === 'text'">{{ part.text }}</span>
</template>
</div>
<form @submit.prevent="send">
<input v-model="input" placeholder="Type a message..." />
<button type="submit">Send</button>
</form>
</div>
</template>
Resuming Streams
Pass a stable id and resume: true to reconnect to an active server-side stream when the component mounts:
<script setup lang="ts">
import { useOpenHarness } from "@openharness/vue";
const chat = useOpenHarness({
endpoint: "/api/tasks/task-1/chat",
id: "task-1",
resume: true,
prepareReconnectToStreamRequest: ({ id }) => ({
api: `/api/tasks/${id}/chat/stream`,
credentials: "include",
}),
});
</script>
By default, reconnect attempts use GET ${endpoint}/${id}/stream. The server must own the active run and return the active stream or 204 when no stream is running.
AI SDK stream resumption is not compatible with treating browser request abort as cancellation. If a user needs to stop a background run, expose a separate server-side cancel action.
useSubagentStatus
Returns a computed ref deriving reactive state from data-oh:subagent.* events:
<script setup lang="ts">
import { useSubagentStatus } from "@openharness/vue";
const subagent = useSubagentStatus();
</script>
<template>
<div v-if="subagent.hasActiveSubagents">
<div v-for="agent in subagent.activeSubagents" :key="agent.path.join('/')">
{{ agent.name }}: {{ agent.task }}
</div>
</div>
</template>
useSessionStatus
Returns a computed ref tracking turn index, compaction state, and retry info:
<script setup lang="ts">
import { useSessionStatus } from "@openharness/vue";
const session = useSessionStatus();
</script>
<template>
<div>Turn: {{ session.turnIndex }}</div>
</template>
Full Example
<script setup lang="ts">
import { ref } from "vue";
import {
useOpenHarness,
useSubagentStatus,
useSessionStatus,
} from "@openharness/vue";
const chat = useOpenHarness({ endpoint: "/api/chat" });
const subagent = useSubagentStatus();
const session = useSessionStatus();
const input = ref("");
function send() {
const text = input.value.trim();
if (!text) return;
input.value = "";
chat.sendMessage({ text });
}
</script>
<template>
<OpenHarnessProvider>
<div>
<div v-for="msg in chat.messages" :key="msg.id">
<template v-for="(part, i) in msg.parts" :key="i">
<span v-if="part.type === 'text'">{{ part.text }}</span>
</template>
</div>
<form @submit.prevent="send">
<input v-model="input" placeholder="Type a message..." />
<button type="submit">Send</button>
</form>
</div>
</OpenHarnessProvider>
</template>
The OpenHarnessProvider is a renderless wrapper component that provides shared subagent, session, and sandbox state via Vue’s provide/inject.