@digitornai/sdk 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.
@@ -0,0 +1,181 @@
1
+ const MAX_EVENTS = 500;
2
+ function messageText(m) {
3
+ if (!m)
4
+ return "";
5
+ if (typeof m.content === "string" && m.content)
6
+ return m.content;
7
+ if (Array.isArray(m.parts)) {
8
+ return m.parts.map((p) => p.text ?? p.content ?? "").join("");
9
+ }
10
+ return "";
11
+ }
12
+ function ensureStreamingAssistant(messages) {
13
+ const last = messages[messages.length - 1];
14
+ if (last && last.role === "assistant" && last.streaming) {
15
+ return { messages, tail: last };
16
+ }
17
+ const fresh = {
18
+ role: "assistant",
19
+ content: "",
20
+ streaming: true,
21
+ toolCalls: [],
22
+ ts: Date.now(),
23
+ };
24
+ return { messages: [...messages, fresh], tail: fresh };
25
+ }
26
+ function replaceTail(messages, next) {
27
+ return [...messages.slice(0, -1), next];
28
+ }
29
+ function applyEvent(state, env) {
30
+ const lastSeq = env.seq > state.lastSeq ? env.seq : state.lastSeq;
31
+ const events = [...state.events, env].slice(-MAX_EVENTS);
32
+ const base = { ...state, lastSeq, events };
33
+ switch (env.type) {
34
+ case "user_message": {
35
+ const cid = env.correlation_id;
36
+ if (cid && state.messages.some((m) => m.correlationId === cid)) {
37
+ return base;
38
+ }
39
+ const msg = {
40
+ role: "user",
41
+ content: messageText(env.message),
42
+ correlationId: cid,
43
+ ts: Date.now(),
44
+ };
45
+ return { ...base, messages: [...state.messages, msg] };
46
+ }
47
+ case "turn_started":
48
+ case "message_started":
49
+ return { ...base, turnActive: true, status: "streaming", error: null };
50
+ case "assistant_delta": {
51
+ const { messages, tail } = ensureStreamingAssistant(state.messages);
52
+ const next = { ...tail, content: tail.content + messageText(env.message) };
53
+ return {
54
+ ...base,
55
+ status: "streaming",
56
+ messages: replaceTail(messages, next),
57
+ };
58
+ }
59
+ case "assistant_reasoning_delta": {
60
+ const { messages, tail } = ensureStreamingAssistant(state.messages);
61
+ const delta = env.message?.reasoning ?? messageText(env.message);
62
+ const next = { ...tail, reasoning: (tail.reasoning ?? "") + delta };
63
+ return {
64
+ ...base,
65
+ status: "thinking",
66
+ messages: replaceTail(messages, next),
67
+ };
68
+ }
69
+ case "assistant_message":
70
+ case "message_done": {
71
+ const { messages, tail } = ensureStreamingAssistant(state.messages);
72
+ const full = messageText(env.message);
73
+ const next = {
74
+ ...tail,
75
+ content: full || tail.content,
76
+ reasoning: env.message?.reasoning ?? tail.reasoning,
77
+ streaming: false,
78
+ };
79
+ return { ...base, messages: replaceTail(messages, next) };
80
+ }
81
+ case "tool_call": {
82
+ if (!env.tool)
83
+ return base;
84
+ const { messages, tail } = ensureStreamingAssistant(state.messages);
85
+ const call = {
86
+ callId: env.tool.call_id,
87
+ name: env.tool.name,
88
+ args: env.tool.arguments,
89
+ status: env.tool.status ?? "running",
90
+ };
91
+ const next = { ...tail, toolCalls: [...(tail.toolCalls ?? []), call] };
92
+ return {
93
+ ...base,
94
+ status: "tool_use",
95
+ messages: replaceTail(messages, next),
96
+ };
97
+ }
98
+ case "tool_result": {
99
+ if (!env.tool)
100
+ return base;
101
+ const messages = state.messages.map((m) => {
102
+ if (m.role !== "assistant" || !m.toolCalls)
103
+ return m;
104
+ return {
105
+ ...m,
106
+ toolCalls: m.toolCalls.map((c) => c.callId === env.tool.call_id
107
+ ? { ...c, status: env.tool.status ?? "done", result: env.tool.text }
108
+ : c),
109
+ };
110
+ });
111
+ return { ...base, messages };
112
+ }
113
+ case "approval_request": {
114
+ if (!env.approval)
115
+ return base;
116
+ const a = env.approval;
117
+ if (state.approvals.some((x) => x.id === a.id))
118
+ return base;
119
+ return {
120
+ ...base,
121
+ approvals: [
122
+ ...state.approvals,
123
+ { id: a.id, kind: a.kind, payload: a.payload, status: a.status },
124
+ ],
125
+ };
126
+ }
127
+ case "approval_granted":
128
+ case "approval_denied": {
129
+ const id = env.approval?.id;
130
+ if (!id)
131
+ return base;
132
+ return {
133
+ ...base,
134
+ approvals: state.approvals.filter((x) => x.id !== id),
135
+ };
136
+ }
137
+ case "turn_ended": {
138
+ const last = state.messages[state.messages.length - 1];
139
+ const messages = last && last.role === "assistant" && last.streaming
140
+ ? replaceTail(state.messages, { ...last, streaming: false })
141
+ : state.messages;
142
+ return { ...base, turnActive: false, status: "idle", messages };
143
+ }
144
+ case "error":
145
+ return {
146
+ ...base,
147
+ status: "error",
148
+ turnActive: false,
149
+ error: env.error?.message ?? "Unknown error",
150
+ };
151
+ default:
152
+ return base;
153
+ }
154
+ }
155
+ export function reduce(state, action) {
156
+ switch (action.type) {
157
+ case "connected":
158
+ return { ...state, connected: true, error: null };
159
+ case "disconnected":
160
+ return { ...state, connected: false };
161
+ case "local_user":
162
+ return {
163
+ ...state,
164
+ turnActive: true,
165
+ status: "streaming",
166
+ messages: [
167
+ ...state.messages,
168
+ {
169
+ role: "user",
170
+ content: action.content,
171
+ correlationId: action.correlationId,
172
+ ts: Date.now(),
173
+ },
174
+ ],
175
+ };
176
+ case "event":
177
+ return applyEvent(state, action.env);
178
+ default:
179
+ return state;
180
+ }
181
+ }
@@ -0,0 +1,33 @@
1
+ /** A review command the host relays to the app (from the chat, or the app's own
2
+ * chrome). `accept`/`reject` act on one proposal by `id`; the `*-all` variants
3
+ * act on every pending one. */
4
+ export interface ReviewCommand {
5
+ /** `*-all`: every pending change. `next`/`prev`: move to and focus the
6
+ * neighbouring change (one-by-one review). `accept-current`/`reject-current`:
7
+ * resolve the change currently focused/at the cursor. `accept`/`reject`: a
8
+ * specific change by `id`. */
9
+ action: "accept-all" | "reject-all" | "next" | "prev" | "accept-current" | "reject-current" | "accept" | "reject";
10
+ /** Proposal id for `accept`/`reject`; ignored by the others. */
11
+ id?: string;
12
+ }
13
+ /** State an app publishes so the host can surface a review affordance ("12
14
+ * suggestions — Accept all / Review"). */
15
+ export interface ProposalsState {
16
+ /** Pending proposals awaiting the person's decision. 0 clears the affordance. */
17
+ count: number;
18
+ /** Optional label for the current review context ("12 suggestions",
19
+ * "3 changes in this section"). The host may show it verbatim. */
20
+ label?: string;
21
+ }
22
+ /**
23
+ * Report the current pending-proposals state to the host. Call it whenever the
24
+ * count changes (a suggestion lands, one is accepted/rejected). `count: 0`
25
+ * removes the chat's review affordance. Vanilla — safe from any editor.
26
+ */
27
+ export declare function reportProposals(state: ProposalsState): void;
28
+ /**
29
+ * Subscribe to review commands the host relays (Accept all / Reject all / accept
30
+ * one / reject one). The app applies them in its own engine (e.g. SuperDoc
31
+ * acceptAll). Returns an unsubscribe function. Vanilla — usable outside React.
32
+ */
33
+ export declare function onReviewCommand(handler: (cmd: ReviewCommand) => void): () => void;
@@ -0,0 +1,59 @@
1
+ // The Digitorn "review" primitive — the generic channel for "the assistant
2
+ // PROPOSES changes the person accepts or rejects", reusable by ANY app (a docx
3
+ // editor's tracked changes, a spreadsheet's proposed cells, a slide edit, a
4
+ // code diff). The SDK knows nothing app-specific: an app REPORTS how many
5
+ // proposals are pending, and RECEIVES accept/reject commands the host relays
6
+ // (e.g. from an "Accept all" button in the chat). How proposals are produced
7
+ // and rendered is entirely the app's business.
8
+ //
9
+ // Pairs with Digitorn's native mode system: an app exposes an "Auto" and a
10
+ // "Suggestions" mode (app.yaml), whose per-mode system_prompt tells the agent
11
+ // whether to edit directly or as tracked changes. This channel is only the
12
+ // review surface on top of that.
13
+ //
14
+ // No React import at module scope, so a vanilla (non-React) editor can import
15
+ // `reportProposals` / `onReviewCommand` directly.
16
+ /**
17
+ * Report the current pending-proposals state to the host. Call it whenever the
18
+ * count changes (a suggestion lands, one is accepted/rejected). `count: 0`
19
+ * removes the chat's review affordance. Vanilla — safe from any editor.
20
+ */
21
+ export function reportProposals(state) {
22
+ if (typeof window === "undefined")
23
+ return;
24
+ const count = Math.max(0, Math.floor(Number(state.count) || 0));
25
+ try {
26
+ window.parent?.postMessage({
27
+ type: "digi:proposals-state",
28
+ count,
29
+ label: typeof state.label === "string" ? state.label.slice(0, 80) : undefined,
30
+ }, "*");
31
+ }
32
+ catch {
33
+ /* cross-origin parent may reject — nothing to fall back to */
34
+ }
35
+ }
36
+ /**
37
+ * Subscribe to review commands the host relays (Accept all / Reject all / accept
38
+ * one / reject one). The app applies them in its own engine (e.g. SuperDoc
39
+ * acceptAll). Returns an unsubscribe function. Vanilla — usable outside React.
40
+ */
41
+ export function onReviewCommand(handler) {
42
+ if (typeof window === "undefined")
43
+ return () => { };
44
+ const listener = (ev) => {
45
+ const d = ev.data;
46
+ if (!d || typeof d !== "object" || d.type !== "digi:review-command")
47
+ return;
48
+ const action = d.action;
49
+ const valid = [
50
+ "accept-all", "reject-all", "next", "prev",
51
+ "accept-current", "reject-current", "accept", "reject",
52
+ ];
53
+ if (!valid.includes(action))
54
+ return;
55
+ handler({ action, id: typeof d.id === "string" ? d.id : undefined });
56
+ };
57
+ window.addEventListener("message", listener);
58
+ return () => window.removeEventListener("message", listener);
59
+ }
@@ -0,0 +1,24 @@
1
+ export interface SharedDocOptions {
2
+ /** Debounce (ms) before writing a local change back to the workdir.
3
+ * Default 200. High-frequency editors (Excalidraw, canvases) benefit
4
+ * from a slightly higher value. */
5
+ writeDebounceMs?: number;
6
+ }
7
+ /**
8
+ * `useSharedDoc` - convenience for the common "one JSON file" case: a workdir
9
+ * document synced two-way with the agent. It is just sugar over `useWorkspace`
10
+ * + `useWatch`.
11
+ *
12
+ * - The agent edits the file → `useWatch` fires → we re-read → re-render.
13
+ * - `setDoc(next)` writes it back (debounced) so the agent sees user edits.
14
+ *
15
+ * Echo from your own writes is suppressed so you never loop. Apps whose state
16
+ * is NOT a single JSON (a folder, many files, binary…) use `useWatch` +
17
+ * `useWorkspace` directly instead.
18
+ *
19
+ * @example Excalidraw - the agent draws in real time
20
+ * ```tsx
21
+ * const [scene, setScene] = useSharedDoc("scene.excalidraw", { elements: [] });
22
+ * ```
23
+ */
24
+ export declare function useSharedDoc<T = unknown>(path: string, initial?: T, options?: SharedDocOptions): [T | undefined, (next: T) => void];
package/dist/shared.js ADDED
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ import { useWorkspace, useWatch } from "./workspace.js";
3
+ /**
4
+ * `useSharedDoc` - convenience for the common "one JSON file" case: a workdir
5
+ * document synced two-way with the agent. It is just sugar over `useWorkspace`
6
+ * + `useWatch`.
7
+ *
8
+ * - The agent edits the file → `useWatch` fires → we re-read → re-render.
9
+ * - `setDoc(next)` writes it back (debounced) so the agent sees user edits.
10
+ *
11
+ * Echo from your own writes is suppressed so you never loop. Apps whose state
12
+ * is NOT a single JSON (a folder, many files, binary…) use `useWatch` +
13
+ * `useWorkspace` directly instead.
14
+ *
15
+ * @example Excalidraw - the agent draws in real time
16
+ * ```tsx
17
+ * const [scene, setScene] = useSharedDoc("scene.excalidraw", { elements: [] });
18
+ * ```
19
+ */
20
+ export function useSharedDoc(path, initial, options = {}) {
21
+ const { readFile, writeFile } = useWorkspace();
22
+ const [value, setValue] = React.useState(initial);
23
+ // The exact bytes we last wrote - a change event echoing our own write is
24
+ // ignored so a write→event→read→setState loop never fires.
25
+ const lastWritten = React.useRef(null);
26
+ const read = React.useCallback(async () => {
27
+ const text = await readFile(path);
28
+ if (text === undefined)
29
+ return; // missing / unreadable - keep current
30
+ if (text === lastWritten.current)
31
+ return; // our own echo
32
+ try {
33
+ setValue(text ? JSON.parse(text) : undefined);
34
+ }
35
+ catch {
36
+ /* invalid JSON mid-write - keep current, next event retries */
37
+ }
38
+ }, [readFile, path]);
39
+ React.useEffect(() => {
40
+ void read();
41
+ }, [read]);
42
+ useWatch(path, () => void read());
43
+ const timer = React.useRef(null);
44
+ const setDoc = React.useCallback((next) => {
45
+ setValue(next);
46
+ const content = JSON.stringify(next);
47
+ if (timer.current)
48
+ clearTimeout(timer.current);
49
+ timer.current = setTimeout(() => {
50
+ lastWritten.current = content;
51
+ void writeFile(path, content);
52
+ }, options.writeDebounceMs ?? 200);
53
+ }, [writeFile, path, options.writeDebounceMs]);
54
+ return [value, setDoc];
55
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * A button (or a small dropdown, via ``items``) the app adds to the host's
3
+ * top bar - the same bar Digitorn renders its own Code/Preview tabs and the
4
+ * GitHub/Publish buttons into. Only shown while THIS custom view is the
5
+ * active tab; switching away removes it automatically.
6
+ *
7
+ * Only ``label``/``icon`` cross the postMessage boundary - the host owns
8
+ * the actual chrome. ``onClick`` stays in your own code and never leaves
9
+ * the iframe; the host just tells you which id was clicked.
10
+ */
11
+ /**
12
+ * Bounded, themed style knobs for one button - not raw CSS. Every value is
13
+ * an enum the host maps to its own design tokens, so a view can stand its
14
+ * button out (a prominent "Deploy" CTA, a compact icon-only refresh) while
15
+ * staying visually native to Digitorn's chrome - and without a "data only"
16
+ * postMessage payload turning into arbitrary styling/markup control over
17
+ * the host's own page.
18
+ */
19
+ export interface TopBarActionStyle {
20
+ /** Button height/padding/text size. Default `"md"`. */
21
+ size?: "sm" | "md" | "lg";
22
+ /** Text weight. Default `"medium"`. */
23
+ weight?: "normal" | "medium" | "semibold" | "bold";
24
+ /** Corner rounding. Default `"rounded"`. */
25
+ shape?: "square" | "rounded" | "pill";
26
+ /** `"ghost"` (default, blends into the bar), `"outline"` (bordered), or
27
+ * `"solid"` (filled - reach for this on the one action that should read
28
+ * as the primary CTA, e.g. a "Deploy" button). */
29
+ variant?: "ghost" | "outline" | "solid";
30
+ /** `"default"` (neutral) or `"accent"` (Digitorn's brand color). */
31
+ tone?: "default" | "accent";
32
+ }
33
+ export interface TopBarAction {
34
+ id: string;
35
+ label: string;
36
+ /** A lucide icon name, e.g. "git-branch". Falls back to a generic icon. */
37
+ icon?: string;
38
+ onClick?: () => void;
39
+ /**
40
+ * Opens this URL in a new tab instead of relaying a click - `onClick` is
41
+ * ignored when this is set. Popups a host-relayed `onClick` opens with
42
+ * `window.open` get blocked: the click happens in the host's real DOM,
43
+ * but `window.open` would run later inside the iframe on a postMessage,
44
+ * which browsers don't count as the same user gesture. The host opens
45
+ * `href` itself, synchronously, in the click it actually received.
46
+ */
47
+ href?: string;
48
+ style?: TopBarActionStyle;
49
+ /** Turns this entry into a small dropdown instead of a plain button. */
50
+ items?: {
51
+ id: string;
52
+ label: string;
53
+ icon?: string;
54
+ onClick?: () => void;
55
+ href?: string;
56
+ style?: TopBarActionStyle;
57
+ }[];
58
+ }
59
+ /**
60
+ * Register this view's top-bar actions with the host. Call with an empty
61
+ * array (or unmount) to clear them - e.g. because the current screen inside
62
+ * your app doesn't have any right now.
63
+ *
64
+ * ```tsx
65
+ * useTopBarActions([
66
+ * { id: "clone", label: "Clone", icon: "git-branch", onClick: () => setCloneOpen(true) },
67
+ * { id: "more", label: "More", icon: "more-horizontal", items: [
68
+ * { id: "reset", label: "Reset", onClick: () => reset() },
69
+ * { id: "docs", label: "Docs", onClick: () => switchView("docs") },
70
+ * ]},
71
+ * ]);
72
+ * ```
73
+ */
74
+ export declare function useTopBarActions(actions: TopBarAction[]): void;
75
+ /** Asks the host to switch the workspace to another view - a built-in one
76
+ * ("code" | "preview" | "changes" | "activity" | "documents" | "project")
77
+ * or another `custom_views` id. */
78
+ export declare function switchView(viewId: string): void;
package/dist/topbar.js ADDED
@@ -0,0 +1,89 @@
1
+ import * as React from "react";
2
+ function flatten(actions) {
3
+ const map = new Map();
4
+ for (const a of actions) {
5
+ if (a.onClick && !a.href)
6
+ map.set(a.id, a.onClick);
7
+ for (const s of a.items ?? []) {
8
+ if (s.onClick && !s.href)
9
+ map.set(s.id, s.onClick);
10
+ }
11
+ }
12
+ return map;
13
+ }
14
+ /**
15
+ * Register this view's top-bar actions with the host. Call with an empty
16
+ * array (or unmount) to clear them - e.g. because the current screen inside
17
+ * your app doesn't have any right now.
18
+ *
19
+ * ```tsx
20
+ * useTopBarActions([
21
+ * { id: "clone", label: "Clone", icon: "git-branch", onClick: () => setCloneOpen(true) },
22
+ * { id: "more", label: "More", icon: "more-horizontal", items: [
23
+ * { id: "reset", label: "Reset", onClick: () => reset() },
24
+ * { id: "docs", label: "Docs", onClick: () => switchView("docs") },
25
+ * ]},
26
+ * ]);
27
+ * ```
28
+ */
29
+ export function useTopBarActions(actions) {
30
+ const handlersRef = React.useRef(new Map());
31
+ handlersRef.current = flatten(actions);
32
+ // Full replace, never a diff - simplest contract for both sides. Only
33
+ // label/icon/id cross the wire; `onClick` stays local (see `flatten`).
34
+ const wireItems = React.useMemo(() => actions.map((a) => ({
35
+ id: a.id,
36
+ label: a.label,
37
+ icon: a.icon,
38
+ href: a.href,
39
+ style: a.style,
40
+ items: a.items?.map((s) => ({ id: s.id, label: s.label, icon: s.icon, href: s.href, style: s.style })),
41
+ })),
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- content, not identity
43
+ [JSON.stringify(actions)]);
44
+ React.useEffect(() => {
45
+ if (typeof window === "undefined")
46
+ return;
47
+ try {
48
+ window.parent?.postMessage({ type: "digi:topbar-register", items: wireItems }, "*");
49
+ }
50
+ catch {
51
+ /* cross-origin parent may reject - nothing to fall back to here */
52
+ }
53
+ }, [wireItems]);
54
+ React.useEffect(() => {
55
+ if (typeof window === "undefined")
56
+ return;
57
+ const onMessage = (e) => {
58
+ const d = e.data;
59
+ if (!d || d.type !== "digi:topbar-click" || typeof d.id !== "string")
60
+ return;
61
+ handlersRef.current.get(d.id)?.();
62
+ };
63
+ window.addEventListener("message", onMessage);
64
+ return () => window.removeEventListener("message", onMessage);
65
+ }, []);
66
+ React.useEffect(() => {
67
+ return () => {
68
+ try {
69
+ window.parent?.postMessage({ type: "digi:topbar-register", items: [] }, "*");
70
+ }
71
+ catch {
72
+ /* best-effort - the host also clears on iframe unmount */
73
+ }
74
+ };
75
+ }, []);
76
+ }
77
+ /** Asks the host to switch the workspace to another view - a built-in one
78
+ * ("code" | "preview" | "changes" | "activity" | "documents" | "project")
79
+ * or another `custom_views` id. */
80
+ export function switchView(viewId) {
81
+ if (typeof window === "undefined")
82
+ return;
83
+ try {
84
+ window.parent?.postMessage({ type: "digi:switch-view", viewId }, "*");
85
+ }
86
+ catch {
87
+ /* cross-origin parent may reject */
88
+ }
89
+ }
@@ -0,0 +1,94 @@
1
+ export type SessionInfo = {
2
+ baseUrl: string;
3
+ appId: string;
4
+ sessionId: string;
5
+ /** JWT - used only outside the embedded preview. */
6
+ token?: string;
7
+ /** Per-session preview token (`?t=`) carried by the embedded-preview URL.
8
+ * When present, the SDK reads/writes via the public `preview/files` routes
9
+ * instead of the JWT-only `workspace/files` routes - no BFF, no cookie. */
10
+ previewToken?: string;
11
+ };
12
+ export type MessagePart = {
13
+ type?: string;
14
+ text?: string;
15
+ content?: string;
16
+ };
17
+ export type MessagePayload = {
18
+ role: string;
19
+ content?: string;
20
+ reasoning?: string;
21
+ parts?: MessagePart[];
22
+ };
23
+ export type ToolPayload = {
24
+ call_id: string;
25
+ name: string;
26
+ arguments?: Record<string, unknown>;
27
+ status?: string;
28
+ text?: string;
29
+ };
30
+ export type ApprovalPayload = {
31
+ id: string;
32
+ kind: string;
33
+ payload?: Record<string, unknown>;
34
+ status?: string;
35
+ reason?: string;
36
+ };
37
+ export type TurnPayload = {
38
+ turn_id: string;
39
+ phase?: string;
40
+ status?: string;
41
+ reason?: string;
42
+ };
43
+ export type ErrorPayload = {
44
+ code?: string;
45
+ message: string;
46
+ category?: string;
47
+ };
48
+ export type Envelope = {
49
+ seq: number;
50
+ type: string;
51
+ ts: number;
52
+ session_id: string;
53
+ correlation_id?: string;
54
+ message?: MessagePayload;
55
+ tool?: ToolPayload;
56
+ approval?: ApprovalPayload;
57
+ turn?: TurnPayload;
58
+ error?: ErrorPayload;
59
+ [key: string]: unknown;
60
+ };
61
+ export type AgentStatus = "idle" | "thinking" | "tool_use" | "streaming" | "error";
62
+ export type ToolCall = {
63
+ callId: string;
64
+ name: string;
65
+ args?: Record<string, unknown>;
66
+ status?: string;
67
+ result?: string;
68
+ };
69
+ export type ChatMessage = {
70
+ role: "user" | "assistant" | "system";
71
+ content: string;
72
+ reasoning?: string;
73
+ streaming?: boolean;
74
+ toolCalls?: ToolCall[];
75
+ correlationId?: string;
76
+ ts: number;
77
+ };
78
+ export type Approval = {
79
+ id: string;
80
+ kind: string;
81
+ payload?: Record<string, unknown>;
82
+ status?: string;
83
+ };
84
+ export type State = {
85
+ connected: boolean;
86
+ status: AgentStatus;
87
+ messages: ChatMessage[];
88
+ turnActive: boolean;
89
+ approvals: Approval[];
90
+ events: Envelope[];
91
+ lastSeq: number;
92
+ error: string | null;
93
+ };
94
+ export declare const initialState: State;
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ export const initialState = {
2
+ connected: false,
3
+ status: "idle",
4
+ messages: [],
5
+ turnActive: false,
6
+ approvals: [],
7
+ events: [],
8
+ lastSeq: 0,
9
+ error: null,
10
+ };