@aevr/agents-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @aevr/agents-react
2
+
3
+ Headless React hooks, state, and SSE transport for `@aevr/agents` conversation hosts. **No components, no styles, no Tailwind dependency** — compose the state with the shadcn chat suite (`Message`, `MessageScroller`, `Marker`, `Attachment`) or any other UI.
4
+
5
+ ## What ships
6
+
7
+ - `ConversationsClient` — framework-neutral fetch client for the host `/conversations` API (list/create/get/sendMessage/rename/end + SSE URL/auth helpers).
8
+ - `subscribeConversationEvents` / `readSseStream` — fetch-based SSE with named events, `Last-Event-ID` resume, and bounded auto-reconnect (EventSource cannot send `Authorization` headers).
9
+ - `applyConversationEvent` / `mergeThreadMessages` — pure timeline reducer folding canonical events (`message_delta`, `message_complete`, `tool_call`, `tool_result`, `file_ref`, `approval_required`, `usage`, `done`, `error`) and persisted thread history into one ordered item list.
10
+ - Hooks: `useConversations`, `useConversationEvents` (SSE + resume + polling fallback), `useSendMessage`, `useConversationCost`.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ const client = useMemo(() => new ConversationsClient({ baseUrl, getToken }), [baseUrl]);
16
+ const { conversations, create } = useConversations(client, projectId);
17
+ const { timeline, status, detail } = useConversationEvents(client, conversationId);
18
+ const { send, sending } = useSendMessage(client, conversationId);
19
+
20
+ // timeline.items: user_message | assistant_message | tool | approval | file_ref | error
21
+ // Recommended shadcn composition: user turns as scroll anchors, tool/approval
22
+ // items as Marker rows, file_ref items as Attachment rows.
23
+ ```
24
+
25
+ ## License
26
+
27
+ MIT
@@ -0,0 +1,32 @@
1
+ import type { ConversationDetail, ConversationMessage, ConversationSummary } from "./types.js";
2
+ export interface ConversationsClientOptions {
3
+ /** Host API base URL, e.g. https://dashboard.example.com */
4
+ readonly baseUrl: string;
5
+ /** Returns the bearer token for the current session. */
6
+ readonly getToken?: () => string | Promise<string | undefined> | undefined;
7
+ readonly fetchImpl?: typeof globalThis.fetch;
8
+ }
9
+ /** Minimal fetch client for the host /conversations API. Framework-neutral. */
10
+ export declare class ConversationsClient {
11
+ #private;
12
+ constructor(options: ConversationsClientOptions);
13
+ get baseUrl(): string;
14
+ authHeaders(): Promise<Record<string, string>>;
15
+ eventsUrl(conversationId: string, lastEventId?: string): string;
16
+ list(projectId: string): Promise<readonly ConversationSummary[]>;
17
+ create(input: {
18
+ projectId: string;
19
+ title?: string;
20
+ }): Promise<{
21
+ conversation: ConversationSummary;
22
+ threadId: string;
23
+ }>;
24
+ get(conversationId: string, limit?: number): Promise<ConversationDetail>;
25
+ sendMessage(conversationId: string, content: string): Promise<ConversationMessage>;
26
+ rename(conversationId: string, title: string): Promise<ConversationSummary>;
27
+ end(conversationId: string): Promise<{
28
+ id: string;
29
+ canceled: boolean;
30
+ }>;
31
+ }
32
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,0BAA0B;IACzC,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC9C;AAID,+EAA+E;AAC/E,qBAAa,mBAAmB;;gBAGlB,OAAO,EAAE,0BAA0B;IAI/C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAEK,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAKpD,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM;IAKzD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC;IAIhE,MAAM,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,mBAAmB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAItH,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAKxE,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIlF,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI3E,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAoB9E"}
package/dist/client.js ADDED
@@ -0,0 +1,54 @@
1
+ /** Minimal fetch client for the host /conversations API. Framework-neutral. */
2
+ export class ConversationsClient {
3
+ #options;
4
+ constructor(options) {
5
+ this.#options = options;
6
+ }
7
+ get baseUrl() {
8
+ return this.#options.baseUrl.replace(/\/$/, "");
9
+ }
10
+ async authHeaders() {
11
+ const token = await this.#options.getToken?.();
12
+ return token ? { Authorization: `Bearer ${token}` } : {};
13
+ }
14
+ eventsUrl(conversationId, lastEventId) {
15
+ const query = lastEventId ? `?lastEventId=${encodeURIComponent(lastEventId)}` : "";
16
+ return `${this.baseUrl}/conversations/${encodeURIComponent(conversationId)}/events${query}`;
17
+ }
18
+ async list(projectId) {
19
+ return this.#request(`/conversations?projectId=${encodeURIComponent(projectId)}`);
20
+ }
21
+ async create(input) {
22
+ return this.#request("/conversations", { method: "POST", body: input });
23
+ }
24
+ async get(conversationId, limit) {
25
+ const query = limit ? `?limit=${limit}` : "";
26
+ return this.#request(`/conversations/${encodeURIComponent(conversationId)}${query}`);
27
+ }
28
+ async sendMessage(conversationId, content) {
29
+ return this.#request(`/conversations/${encodeURIComponent(conversationId)}/messages`, { method: "POST", body: { content } });
30
+ }
31
+ async rename(conversationId, title) {
32
+ return this.#request(`/conversations/${encodeURIComponent(conversationId)}`, { method: "PATCH", body: { title } });
33
+ }
34
+ async end(conversationId) {
35
+ return this.#request(`/conversations/${encodeURIComponent(conversationId)}`, { method: "DELETE" });
36
+ }
37
+ async #request(path, init = {}) {
38
+ const fetchImpl = this.#options.fetchImpl ?? globalThis.fetch;
39
+ const response = await fetchImpl(`${this.baseUrl}${path}`, {
40
+ method: init.method ?? "GET",
41
+ headers: {
42
+ ...(init.body !== undefined ? { "Content-Type": "application/json" } : {}),
43
+ ...(await this.authHeaders()),
44
+ },
45
+ body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
46
+ });
47
+ const payload = await response.json().catch(() => null);
48
+ if (!response.ok || !payload?.success) {
49
+ throw new Error(payload?.error ?? `Conversations request failed with status ${response.status}`);
50
+ }
51
+ return payload.data;
52
+ }
53
+ }
54
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ConversationsClient } from "./client.js";
2
+ import { type ConversationTimeline } from "./timeline.js";
3
+ import type { ConversationDetail, ConversationSummary } from "./types.js";
4
+ export interface UseConversationsResult {
5
+ readonly conversations: readonly ConversationSummary[];
6
+ readonly loading: boolean;
7
+ readonly error: string | null;
8
+ readonly refresh: () => Promise<void>;
9
+ readonly create: (title?: string) => Promise<ConversationSummary>;
10
+ readonly rename: (conversationId: string, title: string) => Promise<void>;
11
+ readonly end: (conversationId: string) => Promise<void>;
12
+ }
13
+ export declare function useConversations(client: ConversationsClient, projectId: string | undefined): UseConversationsResult;
14
+ export type ConversationConnectionStatus = "connecting" | "streaming" | "polling" | "closed" | "error";
15
+ export interface UseConversationEventsResult {
16
+ readonly timeline: ConversationTimeline;
17
+ readonly status: ConversationConnectionStatus;
18
+ readonly detail: ConversationDetail | null;
19
+ readonly refresh: () => Promise<void>;
20
+ }
21
+ /**
22
+ * Live conversation state: loads persisted history, subscribes to the SSE
23
+ * stream with Last-Event-ID resume, and falls back to polling the thread when
24
+ * the stream cannot be established.
25
+ */
26
+ export declare function useConversationEvents(client: ConversationsClient, conversationId: string | undefined): UseConversationEventsResult;
27
+ export interface UseSendMessageResult {
28
+ readonly send: (content: string) => Promise<void>;
29
+ readonly sending: boolean;
30
+ readonly error: string | null;
31
+ }
32
+ export declare function useSendMessage(client: ConversationsClient, conversationId: string | undefined, onSent?: () => void): UseSendMessageResult;
33
+ /** Running conversation cost derived from usage events, with a detail fallback. */
34
+ export declare function useConversationCost(timeline: ConversationTimeline, detail: ConversationDetail | null): number;
35
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAA+D,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACvH,OAAO,KAAK,EAAE,kBAAkB,EAA6B,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAErG,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAClE,QAAQ,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,QAAQ,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,sBAAsB,CAsCnH;AAED,MAAM,MAAM,4BAA4B,GAAG,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvG,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,4BAA4B,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,2BAA2B,CA8DlI;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,oBAAoB,CAoBzI;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,oBAAoB,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAK7G"}
package/dist/hooks.js ADDED
@@ -0,0 +1,132 @@
1
+ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
2
+ import { subscribeConversationEvents } from "./sse.js";
3
+ import { EMPTY_TIMELINE, applyConversationEvent, mergeThreadMessages } from "./timeline.js";
4
+ export function useConversations(client, projectId) {
5
+ const [conversations, setConversations] = useState([]);
6
+ const [loading, setLoading] = useState(false);
7
+ const [error, setError] = useState(null);
8
+ const refresh = useCallback(async () => {
9
+ if (!projectId)
10
+ return;
11
+ setLoading(true);
12
+ try {
13
+ setConversations(await client.list(projectId));
14
+ setError(null);
15
+ }
16
+ catch (cause) {
17
+ setError(cause instanceof Error ? cause.message : String(cause));
18
+ }
19
+ finally {
20
+ setLoading(false);
21
+ }
22
+ }, [client, projectId]);
23
+ useEffect(() => { void refresh(); }, [refresh]);
24
+ const create = useCallback(async (title) => {
25
+ if (!projectId)
26
+ throw new Error("projectId is required to create a conversation.");
27
+ const created = await client.create({ projectId, title });
28
+ await refresh();
29
+ return created.conversation;
30
+ }, [client, projectId, refresh]);
31
+ const rename = useCallback(async (conversationId, title) => {
32
+ await client.rename(conversationId, title);
33
+ await refresh();
34
+ }, [client, refresh]);
35
+ const end = useCallback(async (conversationId) => {
36
+ await client.end(conversationId);
37
+ await refresh();
38
+ }, [client, refresh]);
39
+ return { conversations, loading, error, refresh, create, rename, end };
40
+ }
41
+ /**
42
+ * Live conversation state: loads persisted history, subscribes to the SSE
43
+ * stream with Last-Event-ID resume, and falls back to polling the thread when
44
+ * the stream cannot be established.
45
+ */
46
+ export function useConversationEvents(client, conversationId) {
47
+ const [timeline, dispatch] = useReducer((state, action) => {
48
+ if (action.type === "reset")
49
+ return EMPTY_TIMELINE;
50
+ if (action.type === "history") {
51
+ return mergeThreadMessages(state, action.detail.messages.items, { agentSubjectId: action.detail.conversation.assignedTo });
52
+ }
53
+ return applyConversationEvent(state, action.envelope);
54
+ }, EMPTY_TIMELINE);
55
+ const [status, setStatus] = useState("connecting");
56
+ const [detail, setDetail] = useState(null);
57
+ const lastEventIdRef = useRef(undefined);
58
+ lastEventIdRef.current = timeline.lastEventId;
59
+ const refresh = useCallback(async () => {
60
+ if (!conversationId)
61
+ return;
62
+ const loaded = await client.get(conversationId, 200);
63
+ setDetail(loaded);
64
+ dispatch({ type: "history", detail: loaded });
65
+ }, [client, conversationId]);
66
+ useEffect(() => {
67
+ if (!conversationId)
68
+ return;
69
+ dispatch({ type: "reset" });
70
+ setDetail(null);
71
+ setStatus("connecting");
72
+ let pollTimer = null;
73
+ let disposed = false;
74
+ void refresh().catch(() => undefined);
75
+ let unsubscribe = null;
76
+ void (async () => {
77
+ const headers = await client.authHeaders();
78
+ if (disposed)
79
+ return;
80
+ unsubscribe = subscribeConversationEvents({
81
+ url: client.eventsUrl(conversationId, lastEventIdRef.current),
82
+ headers,
83
+ lastEventId: lastEventIdRef.current,
84
+ onOpen: () => setStatus("streaming"),
85
+ onEvent: (envelope) => {
86
+ dispatch({ type: "event", envelope });
87
+ if (envelope.event === "done")
88
+ void refresh().catch(() => undefined);
89
+ },
90
+ onError: () => {
91
+ setStatus("polling");
92
+ // SSE unavailable — poll the persisted thread instead.
93
+ pollTimer ??= setInterval(() => { void refresh().catch(() => undefined); }, 3_000);
94
+ },
95
+ });
96
+ })();
97
+ return () => {
98
+ disposed = true;
99
+ unsubscribe?.();
100
+ if (pollTimer)
101
+ clearInterval(pollTimer);
102
+ };
103
+ }, [client, conversationId, refresh]);
104
+ return { timeline, status, detail, refresh };
105
+ }
106
+ export function useSendMessage(client, conversationId, onSent) {
107
+ const [sending, setSending] = useState(false);
108
+ const [error, setError] = useState(null);
109
+ const send = useCallback(async (content) => {
110
+ if (!conversationId || !content.trim())
111
+ return;
112
+ setSending(true);
113
+ try {
114
+ await client.sendMessage(conversationId, content);
115
+ setError(null);
116
+ onSent?.();
117
+ }
118
+ catch (cause) {
119
+ setError(cause instanceof Error ? cause.message : String(cause));
120
+ throw cause;
121
+ }
122
+ finally {
123
+ setSending(false);
124
+ }
125
+ }, [client, conversationId, onSent]);
126
+ return { send, sending, error };
127
+ }
128
+ /** Running conversation cost derived from usage events, with a detail fallback. */
129
+ export function useConversationCost(timeline, detail) {
130
+ return useMemo(() => Math.max(timeline.cumulativeCostUsd, detail?.conversation.usage?.projectedCostUsd ?? 0), [timeline.cumulativeCostUsd, detail]);
131
+ }
132
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from "./types.js";
2
+ export * from "./sse.js";
3
+ export * from "./timeline.js";
4
+ export * from "./client.js";
5
+ export * from "./hooks.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./types.js";
2
+ export * from "./sse.js";
3
+ export * from "./timeline.js";
4
+ export * from "./client.js";
5
+ export * from "./hooks.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/dist/sse.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { ConversationEventEnvelope } from "./types.js";
2
+ /** Parses a text/event-stream body into named event envelopes (data JSON-decoded when possible). */
3
+ export declare function readSseStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<ConversationEventEnvelope>;
4
+ export declare function parseSseBlock(block: string): ConversationEventEnvelope | null;
5
+ export interface SubscribeConversationEventsOptions {
6
+ readonly url: string;
7
+ readonly headers?: Readonly<Record<string, string>>;
8
+ readonly lastEventId?: string;
9
+ readonly fetchImpl?: typeof globalThis.fetch;
10
+ readonly onEvent: (envelope: ConversationEventEnvelope) => void;
11
+ readonly onError?: (error: unknown) => void;
12
+ readonly onOpen?: () => void;
13
+ /** Reconnect backoff in ms; reconnects resume from the last seen event id. Default 2000. */
14
+ readonly retryDelayMs?: number;
15
+ /** Maximum consecutive failed connection attempts before giving up. Default 5. */
16
+ readonly maxRetries?: number;
17
+ }
18
+ /**
19
+ * Fetch-based SSE subscription (EventSource cannot send Authorization headers).
20
+ * Auto-reconnects with Last-Event-ID resume. Returns an unsubscribe function.
21
+ */
22
+ export declare function subscribeConversationEvents(options: SubscribeConversationEventsOptions): () => void;
23
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,oGAAoG;AACpG,wBAAuB,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC,yBAAyB,CAAC,CAqBlH;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAmB7E;AAED,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,kCAAkC,GAAG,MAAM,IAAI,CAsCnG"}
package/dist/sse.js ADDED
@@ -0,0 +1,103 @@
1
+ /** Parses a text/event-stream body into named event envelopes (data JSON-decoded when possible). */
2
+ export async function* readSseStream(stream) {
3
+ const reader = stream.getReader();
4
+ const decoder = new TextDecoder();
5
+ let buffer = "";
6
+ try {
7
+ while (true) {
8
+ const { done, value } = await reader.read();
9
+ if (done)
10
+ break;
11
+ buffer += decoder.decode(value, { stream: true });
12
+ let boundary = buffer.indexOf("\n\n");
13
+ while (boundary >= 0) {
14
+ const block = buffer.slice(0, boundary);
15
+ buffer = buffer.slice(boundary + 2);
16
+ const envelope = parseSseBlock(block);
17
+ if (envelope)
18
+ yield envelope;
19
+ boundary = buffer.indexOf("\n\n");
20
+ }
21
+ }
22
+ }
23
+ finally {
24
+ reader.releaseLock();
25
+ }
26
+ }
27
+ export function parseSseBlock(block) {
28
+ let event = "message";
29
+ let id;
30
+ const dataLines = [];
31
+ for (const line of block.split("\n")) {
32
+ if (line.startsWith(":"))
33
+ continue; // comment / heartbeat
34
+ const separator = line.indexOf(":");
35
+ if (separator < 0)
36
+ continue;
37
+ const field = line.slice(0, separator);
38
+ const value = line.slice(separator + 1).replace(/^ /, "");
39
+ if (field === "event")
40
+ event = value;
41
+ else if (field === "id")
42
+ id = value;
43
+ else if (field === "data")
44
+ dataLines.push(value);
45
+ }
46
+ if (dataLines.length === 0)
47
+ return null;
48
+ const raw = dataLines.join("\n");
49
+ let data = raw;
50
+ try {
51
+ data = JSON.parse(raw);
52
+ }
53
+ catch { /* keep raw string */ }
54
+ return { event, id, data };
55
+ }
56
+ /**
57
+ * Fetch-based SSE subscription (EventSource cannot send Authorization headers).
58
+ * Auto-reconnects with Last-Event-ID resume. Returns an unsubscribe function.
59
+ */
60
+ export function subscribeConversationEvents(options) {
61
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
62
+ const controller = new AbortController();
63
+ let lastEventId = options.lastEventId;
64
+ let failures = 0;
65
+ void (async () => {
66
+ while (!controller.signal.aborted) {
67
+ try {
68
+ const response = await fetchImpl(options.url, {
69
+ headers: {
70
+ Accept: "text/event-stream",
71
+ ...options.headers,
72
+ ...(lastEventId ? { "Last-Event-ID": lastEventId } : {}),
73
+ },
74
+ signal: controller.signal,
75
+ });
76
+ if (!response.ok || !response.body)
77
+ throw new Error(`SSE connection failed with status ${response.status}`);
78
+ options.onOpen?.();
79
+ failures = 0;
80
+ for await (const envelope of readSseStream(response.body)) {
81
+ if (controller.signal.aborted)
82
+ return;
83
+ if (envelope.id)
84
+ lastEventId = envelope.id;
85
+ options.onEvent(envelope);
86
+ }
87
+ // Server closed the stream (terminal conversation state) — stop reconnecting.
88
+ return;
89
+ }
90
+ catch (error) {
91
+ if (controller.signal.aborted)
92
+ return;
93
+ failures += 1;
94
+ options.onError?.(error);
95
+ if (failures >= (options.maxRetries ?? 5))
96
+ return;
97
+ await new Promise((resolve) => setTimeout(resolve, options.retryDelayMs ?? 2_000));
98
+ }
99
+ }
100
+ })();
101
+ return () => controller.abort();
102
+ }
103
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { ConversationEventEnvelope, ConversationMessage, DoneEvent, FileRef } from "./types.js";
2
+ export type TimelineItem = {
3
+ readonly kind: "user_message";
4
+ readonly id: string;
5
+ readonly content: string;
6
+ readonly createdAt?: string;
7
+ } | {
8
+ readonly kind: "assistant_message";
9
+ readonly id: string;
10
+ readonly content: string;
11
+ readonly streaming: boolean;
12
+ readonly createdAt?: string;
13
+ } | {
14
+ readonly kind: "tool";
15
+ readonly id: string;
16
+ readonly callId: string;
17
+ readonly tool: string;
18
+ readonly args?: unknown;
19
+ readonly status: "running" | "ok" | "failed";
20
+ readonly summary?: string;
21
+ readonly truncated?: boolean;
22
+ readonly durationMs?: number;
23
+ readonly startedAt?: string;
24
+ } | {
25
+ readonly kind: "approval";
26
+ readonly id: string;
27
+ readonly approvalId: string;
28
+ readonly tool?: string;
29
+ readonly reason?: string;
30
+ } | {
31
+ readonly kind: "file_ref";
32
+ readonly id: string;
33
+ readonly ref: FileRef;
34
+ } | {
35
+ readonly kind: "error";
36
+ readonly id: string;
37
+ readonly code: string;
38
+ readonly message: string;
39
+ readonly retryable?: boolean;
40
+ };
41
+ export interface ConversationTimeline {
42
+ readonly items: readonly TimelineItem[];
43
+ readonly lastEventId?: string;
44
+ readonly cumulativeCostUsd: number;
45
+ readonly streaming: boolean;
46
+ readonly lastStopReason?: DoneEvent["stopReason"];
47
+ }
48
+ export declare const EMPTY_TIMELINE: ConversationTimeline;
49
+ /** Pure reducer: folds one canonical SSE event envelope into the timeline. */
50
+ export declare function applyConversationEvent(state: ConversationTimeline, envelope: ConversationEventEnvelope): ConversationTimeline;
51
+ /**
52
+ * Folds persisted thread messages (polling fallback / initial history) into the
53
+ * timeline, deduplicating by message id. Messages from the agent subject render
54
+ * as assistant messages; everything else as user messages.
55
+ */
56
+ export declare function mergeThreadMessages(state: ConversationTimeline, messages: readonly ConversationMessage[], options: {
57
+ readonly agentSubjectId?: string;
58
+ }): ConversationTimeline;
59
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,yBAAyB,EACzB,mBAAmB,EACnB,SAAS,EAET,OAAO,EAMR,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/I;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACzR;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjI;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnI,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;CACnD;AAED,eAAO,MAAM,cAAc,EAAE,oBAA2F,CAAC;AAUzH,8EAA8E;AAC9E,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,yBAAyB,GAAG,oBAAoB,CA0D7H;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,EAAE,SAAS,mBAAmB,EAAE,EACxC,OAAO,EAAE;IAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,oBAAoB,CAatB"}
@@ -0,0 +1,92 @@
1
+ export const EMPTY_TIMELINE = Object.freeze({ items: [], cumulativeCostUsd: 0, streaming: false });
2
+ function upsert(items, id, next) {
3
+ const index = items.findIndex((item) => item.id === id);
4
+ if (index < 0)
5
+ return [...items, next];
6
+ const copy = [...items];
7
+ copy[index] = next;
8
+ return copy;
9
+ }
10
+ /** Pure reducer: folds one canonical SSE event envelope into the timeline. */
11
+ export function applyConversationEvent(state, envelope) {
12
+ const lastEventId = envelope.id ?? state.lastEventId;
13
+ switch (envelope.event) {
14
+ case "message_delta": {
15
+ const data = envelope.data;
16
+ const existing = state.items.find((item) => item.id === data.messageId);
17
+ const content = existing?.kind === "assistant_message" ? existing.content + data.delta : data.delta;
18
+ return { ...state, lastEventId, streaming: true, items: upsert(state.items, data.messageId, { kind: "assistant_message", id: data.messageId, content, streaming: true }) };
19
+ }
20
+ case "message_complete": {
21
+ const data = envelope.data;
22
+ if (!data.content)
23
+ return { ...state, lastEventId };
24
+ return { ...state, lastEventId, items: upsert(state.items, data.messageId, { kind: "assistant_message", id: data.messageId, content: data.content, streaming: false }) };
25
+ }
26
+ case "tool_call": {
27
+ const data = envelope.data;
28
+ const id = `tool:${data.callId}`;
29
+ return { ...state, lastEventId, streaming: true, items: upsert(state.items, id, { kind: "tool", id, callId: data.callId, tool: data.tool, args: data.argsRedacted, status: "running", startedAt: data.startedAt }) };
30
+ }
31
+ case "tool_result": {
32
+ const data = envelope.data;
33
+ const id = `tool:${data.callId}`;
34
+ const existing = state.items.find((item) => item.id === id);
35
+ const base = existing?.kind === "tool" ? existing : { kind: "tool", id, callId: data.callId, tool: "unknown", status: "running" };
36
+ return { ...state, lastEventId, items: upsert(state.items, id, { ...base, status: data.ok ? "ok" : "failed", summary: data.summary, truncated: data.truncated, durationMs: data.durationMs }) };
37
+ }
38
+ case "file_ref": {
39
+ const data = envelope.data;
40
+ const id = `file:${data.kind}:${data.id}:${envelope.id ?? state.items.length}`;
41
+ return { ...state, lastEventId, items: [...state.items, { kind: "file_ref", id, ref: data }] };
42
+ }
43
+ case "approval_required": {
44
+ const data = envelope.data;
45
+ const id = `approval:${data.approvalId}`;
46
+ return { ...state, lastEventId, items: upsert(state.items, id, { kind: "approval", id, approvalId: data.approvalId, tool: data.tool, reason: data.reason }) };
47
+ }
48
+ case "usage": {
49
+ const data = envelope.data;
50
+ return { ...state, lastEventId, cumulativeCostUsd: Math.max(state.cumulativeCostUsd, data.cumulativeCostUsd) };
51
+ }
52
+ case "done": {
53
+ const data = envelope.data;
54
+ return {
55
+ ...state,
56
+ lastEventId,
57
+ streaming: false,
58
+ lastStopReason: data.stopReason,
59
+ items: state.items.map((item) => item.kind === "assistant_message" && item.streaming ? { ...item, streaming: false } : item),
60
+ };
61
+ }
62
+ case "error": {
63
+ const data = envelope.data;
64
+ const id = `error:${envelope.id ?? state.items.length}`;
65
+ return { ...state, lastEventId, streaming: false, items: [...state.items, { kind: "error", id, code: data.code, message: data.message, retryable: data.retryable }] };
66
+ }
67
+ default:
68
+ return { ...state, lastEventId };
69
+ }
70
+ }
71
+ /**
72
+ * Folds persisted thread messages (polling fallback / initial history) into the
73
+ * timeline, deduplicating by message id. Messages from the agent subject render
74
+ * as assistant messages; everything else as user messages.
75
+ */
76
+ export function mergeThreadMessages(state, messages, options) {
77
+ let items = state.items;
78
+ const assistantContents = new Set(items.filter((item) => item.kind === "assistant_message").map((item) => item.content));
79
+ for (const message of messages) {
80
+ if (items.some((item) => item.id === message.id))
81
+ continue;
82
+ const isAgent = options.agentSubjectId !== undefined && message.senderId === options.agentSubjectId;
83
+ // SSE-delivered assistant text has run-scoped ids; skip persisted duplicates by content.
84
+ if (isAgent && assistantContents.has(message.content))
85
+ continue;
86
+ items = [...items, isAgent
87
+ ? { kind: "assistant_message", id: message.id, content: message.content, streaming: false, createdAt: message.createdAt }
88
+ : { kind: "user_message", id: message.id, content: message.content, createdAt: message.createdAt }];
89
+ }
90
+ return items === state.items ? state : { ...state, items };
91
+ }
92
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AA8BA,MAAM,CAAC,MAAM,cAAc,GAAyB,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzH,SAAS,MAAM,CAAC,KAA8B,EAAE,EAAU,EAAE,IAAkB;IAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CAAC,KAA2B,EAAE,QAAmC;IACrG,MAAM,WAAW,GAAG,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,WAAW,CAAC;IACrD,QAAQ,QAAQ,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAyB,CAAC;YAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC;YACxE,MAAM,OAAO,GAAG,QAAQ,EAAE,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACpG,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC7K,CAAC;QACD,KAAK,kBAAkB,CAAC,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA4B,CAAC;YACnD,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC;YACpD,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC3K,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAqB,CAAC;YAC5C,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;QACvN,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAuB,CAAC;YAC9C,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAkB,EAAE,CAAC;YACpJ,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;QAClM,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAe,CAAC;YACtC,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC/E,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjG,CAAC;QACD,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA6B,CAAC;YACpD,MAAM,EAAE,GAAG,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YACzC,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QAChK,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAkB,CAAC;YACzC,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACjH,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAiB,CAAC;YACxC,OAAO;gBACL,GAAG,KAAK;gBACR,WAAW;gBACX,SAAS,EAAE,KAAK;gBAChB,cAAc,EAAE,IAAI,CAAC,UAAU;gBAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAC7H,CAAC;QACJ,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAkB,CAAC;YACzC,MAAM,EAAE,GAAG,SAAS,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACxD,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;QACxK,CAAC;QACD;YACE,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAA2B,EAC3B,QAAwC,EACxC,OAA6C;IAE7C,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACxB,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAE,IAA4B,CAAC,OAAO,CAAC,CAAC,CAAC;IAClJ,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC;YAAE,SAAS;QAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,cAAc,CAAC;QACpG,yFAAyF;QACzF,IAAI,OAAO,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QAChE,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE,OAAO;gBACxB,CAAC,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;gBACzH,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Canonical conversation SSE event contract (host: GET /conversations/:id/events).
3
+ * Mirrors the Phase 33 spec table — named events with JSON payloads.
4
+ */
5
+ export interface FileRef {
6
+ readonly kind: string;
7
+ readonly id: string;
8
+ readonly folder?: string;
9
+ readonly action: "created" | "updated" | "referenced" | "deleted";
10
+ readonly label?: string;
11
+ }
12
+ export interface MessageDeltaEvent {
13
+ readonly messageId: string;
14
+ readonly delta: string;
15
+ }
16
+ export interface MessageCompleteEvent {
17
+ readonly messageId: string;
18
+ readonly content: string;
19
+ }
20
+ export interface ToolCallEvent {
21
+ readonly callId: string;
22
+ readonly tool: string;
23
+ readonly argsRedacted?: unknown;
24
+ readonly startedAt?: string;
25
+ }
26
+ export interface ToolResultEvent {
27
+ readonly callId: string;
28
+ readonly ok: boolean;
29
+ readonly summary?: string;
30
+ readonly truncated?: boolean;
31
+ readonly durationMs?: number;
32
+ readonly fileRefs?: readonly FileRef[];
33
+ }
34
+ export interface ApprovalRequiredEvent {
35
+ readonly approvalId: string;
36
+ readonly tool?: string;
37
+ readonly argsRedacted?: unknown;
38
+ readonly reason?: string;
39
+ }
40
+ export interface UsageEvent {
41
+ readonly inputTokens: number;
42
+ readonly outputTokens: number;
43
+ readonly estimatedCostUsd: number;
44
+ readonly cumulativeCostUsd: number;
45
+ }
46
+ export interface DoneEvent {
47
+ readonly taskRunId: string | null;
48
+ readonly stopReason: "completed" | "awaiting_approval" | "max_iterations" | "cancelled" | "error";
49
+ }
50
+ export interface ErrorEvent {
51
+ readonly code: string;
52
+ readonly message: string;
53
+ readonly retryable?: boolean;
54
+ }
55
+ export type ConversationEvent = {
56
+ readonly event: "message_delta";
57
+ readonly data: MessageDeltaEvent;
58
+ } | {
59
+ readonly event: "message_complete";
60
+ readonly data: MessageCompleteEvent;
61
+ } | {
62
+ readonly event: "tool_call";
63
+ readonly data: ToolCallEvent;
64
+ } | {
65
+ readonly event: "tool_result";
66
+ readonly data: ToolResultEvent;
67
+ } | {
68
+ readonly event: "file_ref";
69
+ readonly data: FileRef;
70
+ } | {
71
+ readonly event: "approval_required";
72
+ readonly data: ApprovalRequiredEvent;
73
+ } | {
74
+ readonly event: "usage";
75
+ readonly data: UsageEvent;
76
+ } | {
77
+ readonly event: "done";
78
+ readonly data: DoneEvent;
79
+ } | {
80
+ readonly event: "error";
81
+ readonly data: ErrorEvent;
82
+ };
83
+ export interface ConversationEventEnvelope {
84
+ readonly event: string;
85
+ readonly id?: string;
86
+ readonly data: unknown;
87
+ }
88
+ /** Conversation summary as returned by the host list/detail endpoints. */
89
+ export interface ConversationSummary {
90
+ readonly id: string;
91
+ readonly projectId: string;
92
+ readonly title: string;
93
+ /** "idle" while the agent awaits the next user message; otherwise the task status. */
94
+ readonly status: string;
95
+ readonly taskStatus: string;
96
+ readonly blockedReason?: string;
97
+ readonly assignedTo?: string;
98
+ readonly latestRunId?: string;
99
+ readonly usage?: Readonly<Record<string, number>>;
100
+ readonly createdAt: string;
101
+ readonly updatedAt: string;
102
+ readonly threadId?: string | null;
103
+ readonly lastMessage?: ConversationMessage | null;
104
+ }
105
+ export interface ConversationMessage {
106
+ readonly id: string;
107
+ readonly threadId: string;
108
+ readonly senderId: string;
109
+ readonly recipientIds?: readonly string[];
110
+ readonly kind: string;
111
+ readonly content: string;
112
+ readonly createdAt: string;
113
+ }
114
+ export interface ConversationDetail {
115
+ readonly conversation: ConversationSummary;
116
+ readonly threadId: string | null;
117
+ readonly messages: {
118
+ readonly items: readonly ConversationMessage[];
119
+ };
120
+ }
121
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Canonical conversation SSE event contract (host: GET /conversations/:id/events).
3
+ * Mirrors the Phase 33 spec table — named events with JSON payloads.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@aevr/agents-react",
3
+ "version": "0.1.0",
4
+ "description": "Headless React hooks, state, and SSE transport for @aevr/agents conversations. No components, no styles — compose with shadcn or any UI.",
5
+ "keywords": [
6
+ "agents",
7
+ "ai",
8
+ "chat",
9
+ "conversations",
10
+ "headless",
11
+ "hooks",
12
+ "react",
13
+ "sse",
14
+ "typescript"
15
+ ],
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist/**/*.js",
28
+ "dist/**/*.js.map",
29
+ "dist/**/*.d.ts",
30
+ "dist/**/*.d.ts.map",
31
+ "README.md",
32
+ "CHANGELOG.md"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsc -p .",
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "tsx --test src/*.test.ts",
38
+ "prepublishOnly": "npm test && npm run typecheck && npm run build",
39
+ "release": "standard-version --tag-prefix agents-react@v",
40
+ "release:patch": "standard-version --release-as patch --tag-prefix agents-react@v",
41
+ "release:minor": "standard-version --release-as minor --tag-prefix agents-react@v",
42
+ "release:major": "standard-version --release-as major --tag-prefix agents-react@v"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/miracleonyenma/proxmox-management.git",
50
+ "directory": "packages/agents-react"
51
+ },
52
+ "homepage": "https://github.com/miracleonyenma/proxmox-management/tree/main/packages/agents-react#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/miracleonyenma/proxmox-management/issues"
55
+ },
56
+ "license": "MIT",
57
+ "engines": {
58
+ "node": ">=22.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=18.0.0"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "22.20.1",
65
+ "@types/react": "^19.0.0",
66
+ "react": "^19.0.0",
67
+ "standard-version": "^9.5.0",
68
+ "tsx": "^4.20.6",
69
+ "typescript": "^5.7.2"
70
+ }
71
+ }