melony 0.1.12

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,104 @@
1
+ # @melony/core
2
+
3
+ Melony Core is a small **event-streaming runtime** for AI agents with first-class **Server‑Driven UI (SDUI)**.
4
+
5
+ You write a `brain` (decides what to do) and `actions` (do work and stream events). Actions/brains can stream:
6
+
7
+ - **Text** (`{ type: "text" }` / `{ type: "text-delta" }`)
8
+ - **UI trees** (`event.ui = ui.card(...)`) that React (and other renderers) can display
9
+ - Any custom events you want (`{ type: "tool-result", data: ... }`)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @melony/core zod
15
+ ```
16
+
17
+ ## 60-second usage
18
+
19
+ ### 1) Define an agent runtime
20
+
21
+ ```ts
22
+ import { melony, action, ui } from "@melony/core";
23
+ import { z } from "zod";
24
+
25
+ export const assistant = melony({
26
+ actions: {
27
+ searchProducts: action({
28
+ name: "searchProducts",
29
+ description: "Search products by keyword",
30
+ paramsSchema: z.object({ query: z.string() }),
31
+ execute: async function* ({ query }) {
32
+ yield { type: "text", data: { content: `Searching for "${query}"...` } };
33
+
34
+ // SDUI: stream real UI to the frontend
35
+ yield {
36
+ type: "ui",
37
+ ui: ui.card({
38
+ title: "Results",
39
+ children: [
40
+ ui.list([
41
+ ui.listItem({ children: [ui.text("Product A — $10")] }),
42
+ ui.listItem({ children: [ui.text("Product B — $12")] }),
43
+ ]),
44
+ ],
45
+ }),
46
+ };
47
+ },
48
+ }),
49
+ },
50
+
51
+ // The brain receives events and returns the next action to run.
52
+ brain: async function* (event) {
53
+ if (event.type === "text") {
54
+ return { action: "searchProducts", params: { query: event.data?.content } };
55
+ }
56
+ },
57
+ });
58
+ ```
59
+
60
+ ### 2) Serve it (Hono adapter)
61
+
62
+ ```ts
63
+ import { Hono } from "hono";
64
+ import { handle } from "@melony/core/adapters/hono";
65
+ import { assistant } from "./assistant";
66
+
67
+ const app = new Hono();
68
+ app.post("/api/chat", handle(assistant));
69
+ ```
70
+
71
+ ### 3) Stream from the client
72
+
73
+ ```ts
74
+ import { MelonyClient, createHttpTransport } from "@melony/core/client";
75
+
76
+ const client = new MelonyClient(createHttpTransport("/api/chat"));
77
+
78
+ for await (const event of client.sendEvent({
79
+ type: "text",
80
+ role: "user",
81
+ data: { content: "running shoes" },
82
+ })) {
83
+ console.log("event:", event);
84
+ }
85
+ ```
86
+
87
+ ## Core concepts
88
+
89
+ - **Event**: the universal unit of streaming.
90
+ - **Action**: an async generator that yields events and (optionally) returns a `NextAction`.
91
+ - **Brain**: an async generator that decides the next action to run based on incoming events/results.
92
+ - **Plugins/Hooks**: intercept runs, actions, and events (great place for HITL, logging, persistence).
93
+
94
+ ## SDUI (Server‑Driven UI)
95
+
96
+ Melony ships a typed UI contract and builder:
97
+
98
+ - `ui.card(...)`, `ui.form(...)`, `ui.button(...)`, etc.
99
+ - Put the resulting `UINode` into `event.ui`
100
+ - `@melony/react` renders it automatically
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Event-centric Hono adapter for Melony.
3
+ */
4
+ declare const handle: (instance: {
5
+ run: any;
6
+ config: any;
7
+ }) => (c: any) => Promise<any>;
8
+
9
+ export { handle };
@@ -0,0 +1,23 @@
1
+ import { createStreamResponse } from '../chunk-CFG7FFEZ.js';
2
+
3
+ // src/adapters/hono.ts
4
+ var handle = (instance) => {
5
+ return async (c) => {
6
+ const body = await c.req.json();
7
+ const event = body.event;
8
+ if (!event) {
9
+ return c.json({ error: "Invalid request: event required" }, 400);
10
+ }
11
+ return createStreamResponse(
12
+ instance.run({
13
+ event,
14
+ runId: body.runId,
15
+ state: body.state
16
+ })
17
+ );
18
+ };
19
+ };
20
+
21
+ export { handle };
22
+ //# sourceMappingURL=hono.js.map
23
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/hono.ts"],"names":[],"mappings":";;;AAMO,IAAM,MAAA,GAAS,CACpB,QAAA,KACG;AACH,EAAA,OAAO,OAAO,CAAA,KAAW;AACvB,IAAA,MAAM,IAAA,GAAO,MAAM,CAAA,CAAE,GAAA,CAAI,IAAA,EAAK;AAC9B,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA;AAEnB,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,OAAO,EAAE,IAAA,CAAK,EAAE,KAAA,EAAO,iCAAA,IAAqC,GAAG,CAAA;AAAA,IACjE;AAEA,IAAA,OAAO,oBAAA;AAAA,MACL,SAAS,GAAA,CAAI;AAAA,QACX,KAAA;AAAA,QACA,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,OAAO,IAAA,CAAK;AAAA,OACb;AAAA,KACH;AAAA,EACF,CAAA;AACF","file":"hono.js","sourcesContent":["import { Event } from \"../types\";\nimport { createStreamResponse } from \"../utils/create-stream-response\";\n\n/**\n * Event-centric Hono adapter for Melony.\n */\nexport const handle = (\n instance: { run: any; config: any }\n) => {\n return async (c: any) => {\n const body = await c.req.json();\n const event = body.event as Event;\n\n if (!event) {\n return c.json({ error: \"Invalid request: event required\" }, 400);\n }\n\n return createStreamResponse(\n instance.run({\n event,\n runId: body.runId,\n state: body.state,\n })\n );\n };\n};\n"]}
@@ -0,0 +1,30 @@
1
+ // src/utils/create-stream-response.ts
2
+ function createStreamResponse(generator) {
3
+ const encoder = new TextEncoder();
4
+ const stream = new ReadableStream({
5
+ async start(controller) {
6
+ try {
7
+ for await (const message of generator) {
8
+ const chunk = `data: ${JSON.stringify(message)}
9
+
10
+ `;
11
+ controller.enqueue(encoder.encode(chunk));
12
+ }
13
+ controller.close();
14
+ } catch (error) {
15
+ controller.error(error);
16
+ }
17
+ }
18
+ });
19
+ return new Response(stream, {
20
+ headers: {
21
+ "Content-Type": "text/event-stream",
22
+ "Cache-Control": "no-cache",
23
+ Connection: "keep-alive"
24
+ }
25
+ });
26
+ }
27
+
28
+ export { createStreamResponse };
29
+ //# sourceMappingURL=chunk-CFG7FFEZ.js.map
30
+ //# sourceMappingURL=chunk-CFG7FFEZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/create-stream-response.ts"],"names":[],"mappings":";AAMO,SAAS,qBACd,SAAA,EACU;AACV,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,MAAA,GAAS,IAAI,cAAA,CAAe;AAAA,IAChC,MAAM,MAAM,UAAA,EAAY;AACtB,MAAA,IAAI;AACF,QAAA,WAAA,MAAiB,WAAW,SAAA,EAAW;AAErC,UAAA,MAAM,KAAA,GAAQ,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC;;AAAA,CAAA;AAC9C,UAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,QAC1C;AACA,QAAA,UAAA,CAAW,KAAA,EAAM;AAAA,MACnB,SAAS,KAAA,EAAO;AACd,QAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,GACD,CAAA;AAED,EAAA,OAAO,IAAI,SAAS,MAAA,EAAQ;AAAA,IAC1B,OAAA,EAAS;AAAA,MACP,cAAA,EAAgB,mBAAA;AAAA,MAChB,eAAA,EAAiB,UAAA;AAAA,MACjB,UAAA,EAAY;AAAA;AACd,GACD,CAAA;AACH","file":"chunk-CFG7FFEZ.js","sourcesContent":["import { Event } from \"../types\";\n\n/**\n * Convert an async generator of events to an HTTP streaming response\n * Exported for backward compatibility and standalone usage\n */\nexport function createStreamResponse(\n generator: AsyncGenerator<Event>\n): Response {\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n try {\n for await (const message of generator) {\n // Format as SSE: data: {...}\\n\\n\n const chunk = `data: ${JSON.stringify(message)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n controller.close();\n } catch (error) {\n controller.error(error);\n }\n },\n });\n\n return new Response(stream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n"]}
@@ -0,0 +1,8 @@
1
+ // src/utils/generate-id.ts
2
+ var generateId = () => {
3
+ return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(7);
4
+ };
5
+
6
+ export { generateId };
7
+ //# sourceMappingURL=chunk-WAI5H335.js.map
8
+ //# sourceMappingURL=chunk-WAI5H335.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/generate-id.ts"],"names":[],"mappings":";AAAO,IAAM,aAAa,MAAM;AAC9B,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,aAC3C,MAAA,CAAO,UAAA,EAAW,GAClB,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,UAAU,CAAC,CAAA;AAC5C","file":"chunk-WAI5H335.js","sourcesContent":["export const generateId = () => {\n return typeof crypto !== \"undefined\" && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).substring(7);\n};\n"]}
@@ -0,0 +1,40 @@
1
+ import { E as Event } from './generate-id-CUOAZU5w.js';
2
+ export { k as generateId } from './generate-id-CUOAZU5w.js';
3
+ import 'zod';
4
+
5
+ interface ClientState {
6
+ events: Event[];
7
+ isLoading: boolean;
8
+ error: Error | null;
9
+ }
10
+ declare class MelonyClient {
11
+ private transport;
12
+ private state;
13
+ private abortController;
14
+ private stateListeners;
15
+ constructor(transport: TransportFn, options?: {
16
+ initialEvents?: Event[];
17
+ });
18
+ subscribe(listener: (state: ClientState) => void): () => void;
19
+ getState(): {
20
+ events: Event[];
21
+ isLoading: boolean;
22
+ error: Error | null;
23
+ };
24
+ private setState;
25
+ sendEvent(event: Event, options?: {
26
+ runId?: string;
27
+ state?: Record<string, any>;
28
+ }): AsyncGenerator<Event>;
29
+ private handleIncomingEvent;
30
+ reset(events?: Event[]): void;
31
+ }
32
+ interface TransportRequest {
33
+ event: Event;
34
+ runId?: string;
35
+ state?: Record<string, any>;
36
+ }
37
+ type TransportFn = (request: TransportRequest, signal?: AbortSignal) => Promise<ReadableStream<Uint8Array>>;
38
+ declare function createHttpTransport(api: string): TransportFn;
39
+
40
+ export { type ClientState, Event, MelonyClient, type TransportFn, type TransportRequest, createHttpTransport };
package/dist/client.js ADDED
@@ -0,0 +1,117 @@
1
+ import { generateId } from './chunk-WAI5H335.js';
2
+ export { generateId } from './chunk-WAI5H335.js';
3
+
4
+ // src/client.ts
5
+ var MelonyClient = class {
6
+ constructor(transport, options) {
7
+ this.abortController = null;
8
+ this.stateListeners = /* @__PURE__ */ new Set();
9
+ this.transport = transport;
10
+ this.state = {
11
+ events: options?.initialEvents ?? [],
12
+ isLoading: false,
13
+ error: null
14
+ };
15
+ }
16
+ subscribe(listener) {
17
+ this.stateListeners.add(listener);
18
+ return () => {
19
+ this.stateListeners.delete(listener);
20
+ };
21
+ }
22
+ getState() {
23
+ return { ...this.state };
24
+ }
25
+ setState(updates) {
26
+ this.state = { ...this.state, ...updates };
27
+ this.stateListeners.forEach((l) => l(this.getState()));
28
+ }
29
+ async *sendEvent(event, options) {
30
+ if (this.abortController) this.abortController.abort();
31
+ this.abortController = new AbortController();
32
+ const runId = options?.runId ?? generateId();
33
+ const optimisticEvent = {
34
+ ...event,
35
+ runId,
36
+ role: event.role ?? "user",
37
+ timestamp: event.timestamp ?? Date.now()
38
+ };
39
+ this.setState({
40
+ isLoading: true,
41
+ error: null,
42
+ events: [...this.state.events, optimisticEvent]
43
+ });
44
+ try {
45
+ const stream = await this.transport(
46
+ { event: optimisticEvent, ...options, runId },
47
+ this.abortController.signal
48
+ );
49
+ const reader = stream.getReader();
50
+ const decoder = new TextDecoder();
51
+ let buffer = "";
52
+ while (true) {
53
+ const { done, value } = await reader.read();
54
+ if (done) break;
55
+ buffer += decoder.decode(value, { stream: true });
56
+ const lines = buffer.split("\n\n");
57
+ buffer = lines.pop() || "";
58
+ for (const line of lines) {
59
+ if (!line.startsWith("data: ")) continue;
60
+ try {
61
+ const incomingEvent = JSON.parse(line.slice(6));
62
+ this.handleIncomingEvent(incomingEvent);
63
+ yield incomingEvent;
64
+ } catch (e) {
65
+ console.error("Failed to parse event", e);
66
+ }
67
+ }
68
+ }
69
+ this.setState({ isLoading: false });
70
+ } catch (err) {
71
+ if (err instanceof Error && err.name === "AbortError") {
72
+ this.setState({ isLoading: false });
73
+ return;
74
+ }
75
+ const error = err instanceof Error ? err : new Error(String(err));
76
+ this.setState({ error, isLoading: false });
77
+ throw error;
78
+ }
79
+ }
80
+ handleIncomingEvent(event) {
81
+ const events = [...this.state.events];
82
+ const lastEvent = events[events.length - 1];
83
+ if (event.type === "text-delta" && lastEvent?.type === "text-delta" && event.runId === lastEvent.runId && event.data?.delta) {
84
+ events[events.length - 1] = {
85
+ ...lastEvent,
86
+ data: {
87
+ ...lastEvent.data,
88
+ delta: (lastEvent.data?.delta || "") + event.data.delta
89
+ }
90
+ };
91
+ } else {
92
+ events.push(event);
93
+ }
94
+ this.setState({ events });
95
+ }
96
+ reset(events = []) {
97
+ if (this.abortController) this.abortController.abort();
98
+ this.setState({ events, error: null, isLoading: false });
99
+ }
100
+ };
101
+ function createHttpTransport(api) {
102
+ return async (request, signal) => {
103
+ const response = await fetch(api, {
104
+ method: "POST",
105
+ headers: { "Content-Type": "application/json" },
106
+ body: JSON.stringify(request),
107
+ signal
108
+ });
109
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
110
+ if (!response.body) throw new Error("No response body");
111
+ return response.body;
112
+ };
113
+ }
114
+
115
+ export { MelonyClient, createHttpTransport };
116
+ //# sourceMappingURL=client.js.map
117
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["import { Event } from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\nexport type { Event };\nexport { generateId };\n\nexport interface ClientState {\n events: Event[];\n isLoading: boolean;\n error: Error | null;\n}\n\nexport class MelonyClient {\n private transport: TransportFn;\n private state: ClientState;\n private abortController: AbortController | null = null;\n private stateListeners: Set<(state: ClientState) => void> = new Set();\n\n constructor(transport: TransportFn, options?: { initialEvents?: Event[] }) {\n this.transport = transport;\n this.state = {\n events: options?.initialEvents ?? [],\n isLoading: false,\n error: null,\n };\n }\n\n subscribe(listener: (state: ClientState) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n getState() {\n return { ...this.state };\n }\n\n private setState(updates: Partial<ClientState>) {\n this.state = { ...this.state, ...updates };\n this.stateListeners.forEach((l) => l(this.getState()));\n }\n\n async *sendEvent(\n event: Event,\n options?: { runId?: string; state?: Record<string, any> }\n ): AsyncGenerator<Event> {\n if (this.abortController) this.abortController.abort();\n this.abortController = new AbortController();\n\n const runId = options?.runId ?? generateId();\n const optimisticEvent: Event = {\n ...event,\n runId,\n role: event.role ?? \"user\",\n timestamp: event.timestamp ?? Date.now(),\n };\n\n this.setState({\n isLoading: true,\n error: null,\n events: [...this.state.events, optimisticEvent],\n });\n\n try {\n const stream = await this.transport(\n { event: optimisticEvent, ...options, runId },\n this.abortController.signal\n );\n\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n try {\n const incomingEvent: Event = JSON.parse(line.slice(6));\n this.handleIncomingEvent(incomingEvent);\n yield incomingEvent;\n } catch (e) {\n console.error(\"Failed to parse event\", e);\n }\n }\n }\n this.setState({ isLoading: false });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n this.setState({ isLoading: false });\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n this.setState({ error, isLoading: false });\n throw error;\n }\n }\n\n private handleIncomingEvent(event: Event) {\n const events = [...this.state.events];\n\n // Contiguous text-delta merging for the same run\n const lastEvent = events[events.length - 1];\n if (\n event.type === \"text-delta\" &&\n lastEvent?.type === \"text-delta\" &&\n event.runId === lastEvent.runId &&\n event.data?.delta\n ) {\n events[events.length - 1] = {\n ...lastEvent,\n data: {\n ...lastEvent.data,\n delta: (lastEvent.data?.delta || \"\") + event.data.delta,\n },\n };\n } else {\n events.push(event);\n }\n\n this.setState({ events });\n }\n\n reset(events: Event[] = []) {\n if (this.abortController) this.abortController.abort();\n this.setState({ events, error: null, isLoading: false });\n }\n}\n\nexport interface TransportRequest {\n event: Event;\n runId?: string;\n state?: Record<string, any>;\n}\n\nexport type TransportFn = (\n request: TransportRequest,\n signal?: AbortSignal\n) => Promise<ReadableStream<Uint8Array>>;\n\nexport function createHttpTransport(api: string): TransportFn {\n return async (request: TransportRequest, signal?: AbortSignal) => {\n const response = await fetch(api, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n signal,\n });\n\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\n if (!response.body) throw new Error(\"No response body\");\n\n return response.body;\n };\n}\n"]}
@@ -0,0 +1,284 @@
1
+ import z from 'zod';
2
+
3
+ type UISize = "sm" | "md" | "lg";
4
+ type UIAlign = "start" | "center" | "end" | "stretch";
5
+ type UIJustify = "start" | "center" | "end" | "between" | "around";
6
+ type UIWrap = "nowrap" | "wrap" | "wrap-reverse";
7
+ type UIOrientation = "horizontal" | "vertical";
8
+ type UIColor = "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "background" | "foreground" | "muted" | "mutedForeground" | "border";
9
+ type UISpacing = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
10
+ /**
11
+ * UI Component Contracts
12
+ * This acts as the source of truth for the SDUI protocol.
13
+ */
14
+ interface UIContract {
15
+ card: {
16
+ title?: string;
17
+ subtitle?: string;
18
+ background?: string;
19
+ isLoading?: boolean;
20
+ };
21
+ row: {
22
+ align?: UIAlign;
23
+ justify?: UIJustify;
24
+ wrap?: UIWrap;
25
+ gap?: UISpacing;
26
+ };
27
+ col: {
28
+ align?: UIAlign;
29
+ justify?: UIJustify;
30
+ gap?: UISpacing;
31
+ width?: string | number;
32
+ height?: string | number;
33
+ padding?: UISpacing;
34
+ };
35
+ box: {
36
+ padding?: UISpacing;
37
+ margin?: string | number;
38
+ background?: string;
39
+ border?: boolean;
40
+ borderRadius?: UISpacing;
41
+ width?: string | number;
42
+ height?: string | number;
43
+ };
44
+ spacer: {
45
+ size?: UISpacing;
46
+ direction?: UIOrientation;
47
+ };
48
+ divider: {
49
+ orientation?: UIOrientation;
50
+ color?: UIColor;
51
+ };
52
+ text: {
53
+ value: string;
54
+ size?: UISpacing;
55
+ weight?: "normal" | "medium" | "semibold" | "bold";
56
+ color?: UIColor;
57
+ align?: UIAlign;
58
+ };
59
+ heading: {
60
+ value: string;
61
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
62
+ };
63
+ badge: {
64
+ label: string;
65
+ variant?: "primary" | "secondary" | "success" | "danger" | "warning";
66
+ size?: UISize;
67
+ };
68
+ image: {
69
+ src: string;
70
+ alt?: string;
71
+ size?: UISize;
72
+ };
73
+ icon: {
74
+ name: string;
75
+ size?: UISize;
76
+ color?: UIColor;
77
+ };
78
+ chart: {
79
+ data: Array<{
80
+ label: string;
81
+ value: number;
82
+ color?: string;
83
+ }>;
84
+ chartType?: "bar" | "line" | "area" | "pie";
85
+ title?: string;
86
+ };
87
+ list: {};
88
+ listItem: {
89
+ onClickAction?: Event;
90
+ gap?: UISpacing;
91
+ };
92
+ form: {
93
+ onSubmitAction?: Event;
94
+ };
95
+ input: {
96
+ name: string;
97
+ label?: string;
98
+ placeholder?: string;
99
+ defaultValue?: string;
100
+ inputType?: string;
101
+ onChangeAction?: Event;
102
+ };
103
+ textarea: {
104
+ name: string;
105
+ label?: string;
106
+ placeholder?: string;
107
+ defaultValue?: string;
108
+ rows?: number;
109
+ onChangeAction?: Event;
110
+ };
111
+ select: {
112
+ name: string;
113
+ label?: string;
114
+ options: Array<{
115
+ label: string;
116
+ value: string;
117
+ }>;
118
+ defaultValue?: string;
119
+ placeholder?: string;
120
+ onChangeAction?: Event;
121
+ };
122
+ checkbox: {
123
+ name: string;
124
+ label?: string;
125
+ checked?: boolean;
126
+ onChangeAction?: Event;
127
+ };
128
+ radioGroup: {
129
+ name: string;
130
+ options: Array<{
131
+ label: string;
132
+ value: string;
133
+ disabled?: boolean;
134
+ }>;
135
+ label?: string;
136
+ defaultValue?: string;
137
+ orientation?: UIOrientation;
138
+ onChangeAction?: Event;
139
+ };
140
+ label: {
141
+ value: string;
142
+ htmlFor?: string;
143
+ required?: boolean;
144
+ };
145
+ button: {
146
+ label: string;
147
+ variant?: "primary" | "secondary" | "success" | "danger" | "outline" | "ghost" | "link";
148
+ size?: UISize;
149
+ disabled?: boolean;
150
+ onClickAction?: Event;
151
+ };
152
+ }
153
+ type UINode<T extends keyof UIContract = keyof UIContract> = {
154
+ type: T;
155
+ props?: UIContract[T];
156
+ children?: UINode<any>[];
157
+ };
158
+ /**
159
+ * UI Builder for SDUI.
160
+ * Typed using the UIContract source of truth.
161
+ */
162
+ declare const ui: {
163
+ card: (props: UIContract["card"] & {
164
+ children?: UINode<any>[];
165
+ }) => UINode<"card">;
166
+ row: (props: UIContract["row"] & {
167
+ children?: UINode<any>[];
168
+ }) => UINode<"row">;
169
+ col: (props: UIContract["col"] & {
170
+ children?: UINode<any>[];
171
+ }) => UINode<"col">;
172
+ box: (props: UIContract["box"] & {
173
+ children?: UINode<any>[];
174
+ }) => UINode<"box">;
175
+ spacer: (props: UIContract["spacer"]) => UINode<"spacer">;
176
+ divider: (props: UIContract["divider"]) => UINode<"divider">;
177
+ text: (value: string, props?: Omit<UIContract["text"], "value">) => UINode<"text">;
178
+ heading: (value: string, level?: UIContract["heading"]["level"]) => UINode<"heading">;
179
+ badge: (label: string, variant?: UIContract["badge"]["variant"], size?: UISize) => UINode<"badge">;
180
+ image: (src: string, alt?: string, size?: UISize) => UINode<"image">;
181
+ icon: (name: string, size?: UISize, color?: UIColor) => UINode<"icon">;
182
+ chart: (props: UIContract["chart"]) => UINode<"chart">;
183
+ list: (children: UINode<any>[]) => UINode<"list">;
184
+ listItem: (props: UIContract["listItem"] & {
185
+ children: UINode<any>[];
186
+ }) => UINode<"listItem">;
187
+ form: (props: UIContract["form"] & {
188
+ children?: UINode<any>[];
189
+ }) => UINode<"form">;
190
+ input: (props: UIContract["input"]) => UINode<"input">;
191
+ textarea: (props: UIContract["textarea"]) => UINode<"textarea">;
192
+ select: (props: UIContract["select"]) => UINode<"select">;
193
+ checkbox: (props: UIContract["checkbox"]) => UINode<"checkbox">;
194
+ radioGroup: (props: UIContract["radioGroup"]) => UINode<"radioGroup">;
195
+ label: (value: string, props?: Omit<UIContract["label"], "value">) => UINode<"label">;
196
+ button: (props: UIContract["button"]) => UINode<"button">;
197
+ };
198
+ type Role = "user" | "assistant" | "system";
199
+ type Event = {
200
+ type: string;
201
+ data?: any;
202
+ ui?: UINode;
203
+ runId?: string;
204
+ timestamp?: number;
205
+ role?: Role;
206
+ };
207
+ interface Action<TParams extends z.ZodSchema = z.ZodObject<any>> {
208
+ name: string;
209
+ description?: string;
210
+ paramsSchema: TParams;
211
+ execute: (params: z.infer<TParams>, context: RuntimeContext) => AsyncGenerator<Event, NextAction | void, unknown>;
212
+ }
213
+ interface NextAction {
214
+ action?: string;
215
+ params?: any;
216
+ description?: string;
217
+ }
218
+ interface RuntimeContext<TState = any> {
219
+ state: TState;
220
+ runId: string;
221
+ stepCount: number;
222
+ isDone: boolean;
223
+ actions: Record<string, Action<any>>;
224
+ ui: typeof ui;
225
+ suspend: () => void;
226
+ }
227
+ /**
228
+ * Standardized Hook Result for consistent DX.
229
+ */
230
+ type HookResult = Promise<Event | void>;
231
+ interface Hooks {
232
+ /**
233
+ * Called when a run session begins.
234
+ * Can return an Event to be emitted, or a NextAction to jump-start the loop.
235
+ */
236
+ onBeforeRun?: (input: {
237
+ event: Event;
238
+ runId: string;
239
+ state: Record<string, any>;
240
+ }, context: RuntimeContext) => Promise<Event | NextAction | void>;
241
+ /**
242
+ * Called when a run session completes.
243
+ */
244
+ onAfterRun?: (context: RuntimeContext) => HookResult;
245
+ /**
246
+ * Called whenever an event is yielded by the runtime.
247
+ */
248
+ onEvent?: (event: Event, context: RuntimeContext) => HookResult;
249
+ /**
250
+ * Called before an action is executed.
251
+ * Return an event to intercept/suspend the action.
252
+ */
253
+ onBeforeAction?: (call: {
254
+ action: Action<any>;
255
+ params: any;
256
+ }, context: RuntimeContext) => HookResult;
257
+ /**
258
+ * Called after an action completes.
259
+ */
260
+ onAfterAction?: (result: {
261
+ action: Action<any>;
262
+ data: NextAction | void;
263
+ }, context: RuntimeContext) => HookResult;
264
+ }
265
+ /**
266
+ * A plugin is just a named set of hooks.
267
+ */
268
+ interface Plugin extends Hooks {
269
+ name: string;
270
+ }
271
+ interface Config {
272
+ actions: Record<string, Action<any>>;
273
+ /**
274
+ * The central brain for handling incoming events.
275
+ */
276
+ brain?: (event: Event, context: RuntimeContext) => AsyncGenerator<Event, NextAction | void, unknown>;
277
+ hooks?: Hooks;
278
+ plugins?: Plugin[];
279
+ safetyMaxSteps?: number;
280
+ }
281
+
282
+ declare const generateId: () => string;
283
+
284
+ export { type Action as A, type Config as C, type Event as E, type HookResult as H, type NextAction as N, type Plugin as P, type Role as R, type UISize as U, type UIAlign as a, type UIJustify as b, type UIWrap as c, type UIOrientation as d, type UIColor as e, type UISpacing as f, type UIContract as g, type UINode as h, type RuntimeContext as i, type Hooks as j, generateId as k, ui as u };
@@ -0,0 +1,47 @@
1
+ import { C as Config, E as Event, A as Action, P as Plugin } from './generate-id-CUOAZU5w.js';
2
+ export { H as HookResult, j as Hooks, N as NextAction, R as Role, i as RuntimeContext, a as UIAlign, e as UIColor, g as UIContract, b as UIJustify, h as UINode, d as UIOrientation, U as UISize, f as UISpacing, c as UIWrap, k as generateId, u as ui } from './generate-id-CUOAZU5w.js';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * The Slim Runtime.
7
+ * Single Responsibility: Orchestrate Event -> Action -> Event transitions.
8
+ */
9
+ declare class Runtime {
10
+ private config;
11
+ constructor(config: Config);
12
+ run(input: {
13
+ event: Event;
14
+ runId?: string;
15
+ state?: Record<string, any>;
16
+ }): AsyncGenerator<Event>;
17
+ private dispatchToBrain;
18
+ private executeAction;
19
+ /**
20
+ * Internal helper to yield an event and trigger the onEvent hook.
21
+ */
22
+ private emit;
23
+ }
24
+ declare const melony: (config: Config) => {
25
+ config: Config;
26
+ run: (input: {
27
+ event: Event;
28
+ runId?: string;
29
+ state?: Record<string, any>;
30
+ }) => AsyncGenerator<Event>;
31
+ };
32
+ /**
33
+ * Helper to define an action with full type inference.
34
+ */
35
+ declare const action: <T extends z.ZodSchema>(config: Action<T>) => Action<T>;
36
+ /**
37
+ * Helper to define a plugin.
38
+ */
39
+ declare const plugin: (config: Plugin) => Plugin;
40
+
41
+ /**
42
+ * Convert an async generator of events to an HTTP streaming response
43
+ * Exported for backward compatibility and standalone usage
44
+ */
45
+ declare function createStreamResponse(generator: AsyncGenerator<Event>): Response;
46
+
47
+ export { Action, Config, Event, Plugin, Runtime, action, createStreamResponse, melony, plugin };
package/dist/index.js ADDED
@@ -0,0 +1,314 @@
1
+ import { generateId } from './chunk-WAI5H335.js';
2
+ export { generateId } from './chunk-WAI5H335.js';
3
+ export { createStreamResponse } from './chunk-CFG7FFEZ.js';
4
+
5
+ // src/types.ts
6
+ var ui = {
7
+ card: (props) => {
8
+ const { children, ...rest } = props;
9
+ return { type: "card", props: rest, children };
10
+ },
11
+ row: (props) => {
12
+ const { children, ...rest } = props;
13
+ return { type: "row", props: rest, children };
14
+ },
15
+ col: (props) => {
16
+ const { children, ...rest } = props;
17
+ return { type: "col", props: rest, children };
18
+ },
19
+ box: (props) => {
20
+ const { children, ...rest } = props;
21
+ return { type: "box", props: rest, children };
22
+ },
23
+ spacer: (props) => ({
24
+ type: "spacer",
25
+ props
26
+ }),
27
+ divider: (props) => ({
28
+ type: "divider",
29
+ props
30
+ }),
31
+ text: (value, props) => ({
32
+ type: "text",
33
+ props: { ...props, value }
34
+ }),
35
+ heading: (value, level = 1) => ({
36
+ type: "heading",
37
+ props: { value, level }
38
+ }),
39
+ badge: (label, variant = "primary", size = "md") => ({
40
+ type: "badge",
41
+ props: { label, variant, size }
42
+ }),
43
+ image: (src, alt, size = "md") => ({
44
+ type: "image",
45
+ props: { src, alt, size }
46
+ }),
47
+ icon: (name, size = "md", color) => ({
48
+ type: "icon",
49
+ props: { name, size, color }
50
+ }),
51
+ chart: (props) => ({
52
+ type: "chart",
53
+ props
54
+ }),
55
+ list: (children) => ({
56
+ type: "list",
57
+ children
58
+ }),
59
+ listItem: (props) => {
60
+ const { children, ...rest } = props;
61
+ return { type: "listItem", props: rest, children };
62
+ },
63
+ form: (props) => {
64
+ const { children, ...rest } = props;
65
+ return { type: "form", props: rest, children };
66
+ },
67
+ input: (props) => ({
68
+ type: "input",
69
+ props
70
+ }),
71
+ textarea: (props) => ({
72
+ type: "textarea",
73
+ props
74
+ }),
75
+ select: (props) => ({
76
+ type: "select",
77
+ props
78
+ }),
79
+ checkbox: (props) => ({
80
+ type: "checkbox",
81
+ props
82
+ }),
83
+ radioGroup: (props) => ({
84
+ type: "radioGroup",
85
+ props
86
+ }),
87
+ label: (value, props) => ({
88
+ type: "label",
89
+ props: { ...props, value }
90
+ }),
91
+ button: (props) => ({
92
+ type: "button",
93
+ props
94
+ })
95
+ };
96
+
97
+ // src/runtime.ts
98
+ var Runtime = class {
99
+ constructor(config) {
100
+ this.config = config;
101
+ }
102
+ async *run(input) {
103
+ const runId = input.runId ?? generateId();
104
+ const context = {
105
+ state: input.state ?? {},
106
+ runId,
107
+ stepCount: 0,
108
+ isDone: false,
109
+ actions: this.config.actions,
110
+ ui,
111
+ suspend: () => {
112
+ context.isDone = true;
113
+ }
114
+ };
115
+ let nextAction = void 0;
116
+ for (const plugin2 of this.config.plugins || []) {
117
+ if (plugin2.onBeforeRun) {
118
+ const result = await plugin2.onBeforeRun(
119
+ { event: input.event, runId, state: context.state },
120
+ context
121
+ );
122
+ if (result) {
123
+ if ("type" in result) {
124
+ yield* this.emit(result, context);
125
+ } else {
126
+ nextAction = result;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ if (this.config.hooks?.onBeforeRun) {
132
+ const result = await this.config.hooks.onBeforeRun(
133
+ { event: input.event, runId, state: context.state },
134
+ context
135
+ );
136
+ if (result) {
137
+ if ("type" in result) {
138
+ yield* this.emit(result, context);
139
+ } else {
140
+ nextAction = result;
141
+ }
142
+ }
143
+ }
144
+ if (context.isDone) return;
145
+ yield* this.emit(
146
+ { type: "run-started", data: { inputEvent: input.event } },
147
+ context
148
+ );
149
+ if (!nextAction && this.config.brain) {
150
+ nextAction = yield* this.dispatchToBrain(input.event, context);
151
+ }
152
+ while (nextAction && !context.isDone) {
153
+ if (context.stepCount++ >= (this.config.safetyMaxSteps ?? 10)) {
154
+ yield* this.emit(
155
+ { type: "error", data: { message: "Max steps exceeded" } },
156
+ context
157
+ );
158
+ break;
159
+ }
160
+ const current = nextAction;
161
+ nextAction = void 0;
162
+ const actionName = current.action ?? Object.keys(this.config.actions)[0];
163
+ const action2 = this.config.actions[actionName];
164
+ if (!action2) {
165
+ yield* this.emit(
166
+ {
167
+ type: "error",
168
+ data: { message: `Action ${actionName} not found` }
169
+ },
170
+ context
171
+ );
172
+ break;
173
+ }
174
+ const result = yield* this.executeAction(action2, current.params, context);
175
+ if (this.config.brain) {
176
+ nextAction = yield* this.dispatchToBrain(
177
+ {
178
+ type: "action-result",
179
+ data: {
180
+ action: actionName,
181
+ params: current.params,
182
+ result
183
+ }
184
+ },
185
+ context
186
+ );
187
+ } else {
188
+ nextAction = result;
189
+ }
190
+ }
191
+ for (const plugin2 of this.config.plugins || []) {
192
+ if (plugin2.onAfterRun) {
193
+ const extra = await plugin2.onAfterRun(context);
194
+ if (extra) yield* this.emit(extra, context);
195
+ }
196
+ }
197
+ if (this.config.hooks?.onAfterRun) {
198
+ const extra = await this.config.hooks.onAfterRun(context);
199
+ if (extra) yield* this.emit(extra, context);
200
+ }
201
+ }
202
+ async *dispatchToBrain(event, context) {
203
+ const generator = this.config.brain(event, context);
204
+ while (true) {
205
+ const { value, done } = await generator.next();
206
+ if (done) return value;
207
+ yield* this.emit(value, context);
208
+ }
209
+ }
210
+ async *executeAction(action2, params, context) {
211
+ for (const plugin2 of this.config.plugins || []) {
212
+ if (plugin2.onBeforeAction) {
213
+ const hookResult = await plugin2.onBeforeAction(
214
+ { action: action2, params },
215
+ context
216
+ );
217
+ if (hookResult) {
218
+ yield* this.emit(hookResult, context);
219
+ if (context.isDone) return;
220
+ }
221
+ }
222
+ }
223
+ if (this.config.hooks?.onBeforeAction) {
224
+ const hookResult = await this.config.hooks.onBeforeAction(
225
+ { action: action2, params },
226
+ context
227
+ );
228
+ if (hookResult) {
229
+ yield* this.emit(hookResult, context);
230
+ if (context.isDone) return;
231
+ }
232
+ }
233
+ try {
234
+ const generator = action2.execute(params, context);
235
+ let result;
236
+ while (true) {
237
+ const { value, done } = await generator.next();
238
+ if (done) {
239
+ result = value;
240
+ break;
241
+ }
242
+ yield* this.emit(value, context);
243
+ if (context.isDone) return;
244
+ }
245
+ for (const plugin2 of this.config.plugins || []) {
246
+ if (plugin2.onAfterAction) {
247
+ const extra = await plugin2.onAfterAction(
248
+ { action: action2, data: result },
249
+ context
250
+ );
251
+ if (extra) yield* this.emit(extra, context);
252
+ }
253
+ }
254
+ if (this.config.hooks?.onAfterAction) {
255
+ const extra = await this.config.hooks.onAfterAction(
256
+ { action: action2, data: result },
257
+ context
258
+ );
259
+ if (extra) yield* this.emit(extra, context);
260
+ }
261
+ return result;
262
+ } catch (error) {
263
+ yield* this.emit(
264
+ {
265
+ type: "error",
266
+ data: {
267
+ action: action2.name,
268
+ error: error instanceof Error ? error.message : String(error)
269
+ }
270
+ },
271
+ context
272
+ );
273
+ }
274
+ }
275
+ /**
276
+ * Internal helper to yield an event and trigger the onEvent hook.
277
+ */
278
+ async *emit(event, context) {
279
+ const finalEvent = {
280
+ ...event,
281
+ runId: context.runId,
282
+ timestamp: event.timestamp ?? Date.now(),
283
+ role: event.role ?? "assistant"
284
+ };
285
+ yield finalEvent;
286
+ for (const plugin2 of this.config.plugins || []) {
287
+ if (plugin2.onEvent) {
288
+ const extra = await plugin2.onEvent(finalEvent, context);
289
+ if (extra) {
290
+ yield { ...extra, runId: context.runId, timestamp: Date.now() };
291
+ }
292
+ }
293
+ }
294
+ if (this.config.hooks?.onEvent) {
295
+ const extra = await this.config.hooks.onEvent(finalEvent, context);
296
+ if (extra) {
297
+ yield { ...extra, runId: context.runId, timestamp: Date.now() };
298
+ }
299
+ }
300
+ }
301
+ };
302
+ var melony = (config) => {
303
+ const runtime = new Runtime(config);
304
+ return {
305
+ config,
306
+ run: runtime.run.bind(runtime)
307
+ };
308
+ };
309
+ var action = (config) => config;
310
+ var plugin = (config) => config;
311
+
312
+ export { Runtime, action, melony, plugin, ui };
313
+ //# sourceMappingURL=index.js.map
314
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/runtime.ts"],"names":["plugin","action"],"mappings":"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","file":"index.js","sourcesContent":["import z from \"zod\";\n\n// ============================================\n// UI Protocol & Contracts\n// ============================================\n\nexport type UISize = \"sm\" | \"md\" | \"lg\";\nexport type UIAlign = \"start\" | \"center\" | \"end\" | \"stretch\";\nexport type UIJustify = \"start\" | \"center\" | \"end\" | \"between\" | \"around\";\nexport type UIWrap = \"nowrap\" | \"wrap\" | \"wrap-reverse\";\nexport type UIOrientation = \"horizontal\" | \"vertical\";\n\nexport type UIColor =\n | \"primary\"\n | \"secondary\"\n | \"success\"\n | \"danger\"\n | \"warning\"\n | \"info\"\n | \"background\"\n | \"foreground\"\n | \"muted\"\n | \"mutedForeground\"\n | \"border\";\n\nexport type UISpacing = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"xxl\";\n\n/**\n * UI Component Contracts\n * This acts as the source of truth for the SDUI protocol.\n */\nexport interface UIContract {\n card: {\n title?: string;\n subtitle?: string;\n background?: string;\n isLoading?: boolean;\n };\n row: {\n align?: UIAlign;\n justify?: UIJustify;\n wrap?: UIWrap;\n gap?: UISpacing;\n };\n col: {\n align?: UIAlign;\n justify?: UIJustify;\n gap?: UISpacing;\n width?: string | number;\n height?: string | number;\n padding?: UISpacing;\n };\n box: {\n padding?: UISpacing;\n margin?: string | number;\n background?: string;\n border?: boolean;\n borderRadius?: UISpacing;\n width?: string | number;\n height?: string | number;\n };\n spacer: {\n size?: UISpacing;\n direction?: UIOrientation;\n };\n divider: {\n orientation?: UIOrientation;\n color?: UIColor;\n };\n text: {\n value: string;\n size?: UISpacing;\n weight?: \"normal\" | \"medium\" | \"semibold\" | \"bold\";\n color?: UIColor;\n align?: UIAlign;\n };\n heading: {\n value: string;\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n };\n badge: {\n label: string;\n variant?: \"primary\" | \"secondary\" | \"success\" | \"danger\" | \"warning\";\n size?: UISize;\n };\n image: {\n src: string;\n alt?: string;\n size?: UISize;\n };\n icon: {\n name: string;\n size?: UISize;\n color?: UIColor;\n };\n chart: {\n data: Array<{ label: string; value: number; color?: string }>;\n chartType?: \"bar\" | \"line\" | \"area\" | \"pie\";\n title?: string;\n };\n list: {};\n listItem: {\n onClickAction?: Event;\n gap?: UISpacing;\n };\n form: {\n onSubmitAction?: Event;\n };\n input: {\n name: string;\n label?: string;\n placeholder?: string;\n defaultValue?: string;\n inputType?: string;\n onChangeAction?: Event;\n };\n textarea: {\n name: string;\n label?: string;\n placeholder?: string;\n defaultValue?: string;\n rows?: number;\n onChangeAction?: Event;\n };\n select: {\n name: string;\n label?: string;\n options: Array<{ label: string; value: string }>;\n defaultValue?: string;\n placeholder?: string;\n onChangeAction?: Event;\n };\n checkbox: {\n name: string;\n label?: string;\n checked?: boolean;\n onChangeAction?: Event;\n };\n radioGroup: {\n name: string;\n options: Array<{ label: string; value: string; disabled?: boolean }>;\n label?: string;\n defaultValue?: string;\n orientation?: UIOrientation;\n onChangeAction?: Event;\n };\n label: {\n value: string;\n htmlFor?: string;\n required?: boolean;\n };\n button: {\n label: string;\n variant?: \"primary\" | \"secondary\" | \"success\" | \"danger\" | \"outline\" | \"ghost\" | \"link\";\n size?: UISize;\n disabled?: boolean;\n onClickAction?: Event;\n };\n}\n\nexport type UINode<T extends keyof UIContract = keyof UIContract> = {\n type: T;\n props?: UIContract[T];\n children?: UINode<any>[];\n};\n\n/**\n * UI Builder for SDUI.\n * Typed using the UIContract source of truth.\n */\nexport const ui = {\n card: (props: UIContract[\"card\"] & { children?: UINode<any>[] }): UINode<\"card\"> => {\n const { children, ...rest } = props;\n return { type: \"card\", props: rest, children };\n },\n row: (props: UIContract[\"row\"] & { children?: UINode<any>[] }): UINode<\"row\"> => {\n const { children, ...rest } = props;\n return { type: \"row\", props: rest, children };\n },\n col: (props: UIContract[\"col\"] & { children?: UINode<any>[] }): UINode<\"col\"> => {\n const { children, ...rest } = props;\n return { type: \"col\", props: rest, children };\n },\n box: (props: UIContract[\"box\"] & { children?: UINode<any>[] }): UINode<\"box\"> => {\n const { children, ...rest } = props;\n return { type: \"box\", props: rest, children };\n },\n spacer: (props: UIContract[\"spacer\"]): UINode<\"spacer\"> => ({\n type: \"spacer\",\n props,\n }),\n divider: (props: UIContract[\"divider\"]): UINode<\"divider\"> => ({\n type: \"divider\",\n props,\n }),\n text: (\n value: string,\n props?: Omit<UIContract[\"text\"], \"value\">\n ): UINode<\"text\"> => ({\n type: \"text\",\n props: { ...props, value },\n }),\n heading: (\n value: string,\n level: UIContract[\"heading\"][\"level\"] = 1\n ): UINode<\"heading\"> => ({\n type: \"heading\",\n props: { value, level },\n }),\n badge: (\n label: string,\n variant: UIContract[\"badge\"][\"variant\"] = \"primary\",\n size: UISize = \"md\"\n ): UINode<\"badge\"> => ({\n type: \"badge\",\n props: { label, variant, size },\n }),\n image: (\n src: string,\n alt?: string,\n size: UISize = \"md\"\n ): UINode<\"image\"> => ({\n type: \"image\",\n props: { src, alt, size },\n }),\n icon: (\n name: string,\n size: UISize = \"md\",\n color?: UIColor\n ): UINode<\"icon\"> => ({\n type: \"icon\",\n props: { name, size, color },\n }),\n chart: (props: UIContract[\"chart\"]): UINode<\"chart\"> => ({\n type: \"chart\",\n props,\n }),\n list: (children: UINode<any>[]): UINode<\"list\"> => ({\n type: \"list\",\n children,\n }),\n listItem: (\n props: UIContract[\"listItem\"] & { children: UINode<any>[] }\n ): UINode<\"listItem\"> => {\n const { children, ...rest } = props;\n return { type: \"listItem\", props: rest, children };\n },\n form: (props: UIContract[\"form\"] & { children?: UINode<any>[] }): UINode<\"form\"> => {\n const { children, ...rest } = props;\n return { type: \"form\", props: rest, children };\n },\n input: (props: UIContract[\"input\"]): UINode<\"input\"> => ({\n type: \"input\",\n props,\n }),\n textarea: (props: UIContract[\"textarea\"]): UINode<\"textarea\"> => ({\n type: \"textarea\",\n props,\n }),\n select: (props: UIContract[\"select\"]): UINode<\"select\"> => ({\n type: \"select\",\n props,\n }),\n checkbox: (props: UIContract[\"checkbox\"]): UINode<\"checkbox\"> => ({\n type: \"checkbox\",\n props,\n }),\n radioGroup: (props: UIContract[\"radioGroup\"]): UINode<\"radioGroup\"> => ({\n type: \"radioGroup\",\n props,\n }),\n label: (\n value: string,\n props?: Omit<UIContract[\"label\"], \"value\">\n ): UINode<\"label\"> => ({\n type: \"label\",\n props: { ...props, value },\n }),\n button: (props: UIContract[\"button\"]): UINode<\"button\"> => ({\n type: \"button\",\n props,\n }),\n};\n\n// ============================================\n// Events\n// ============================================\n\nexport type Role = \"user\" | \"assistant\" | \"system\";\n\nexport type Event = {\n type: string;\n data?: any;\n ui?: UINode;\n runId?: string;\n timestamp?: number;\n role?: Role;\n};\n\n// ============================================\n// Runtime & Hooks\n// ============================================\n\nexport interface Action<TParams extends z.ZodSchema = z.ZodObject<any>> {\n name: string;\n description?: string;\n paramsSchema: TParams;\n execute: (\n params: z.infer<TParams>,\n context: RuntimeContext\n ) => AsyncGenerator<Event, NextAction | void, unknown>;\n}\n\nexport interface NextAction {\n action?: string;\n params?: any;\n description?: string;\n}\n\nexport interface RuntimeContext<TState = any> {\n state: TState;\n runId: string;\n stepCount: number;\n isDone: boolean;\n actions: Record<string, Action<any>>;\n ui: typeof ui;\n suspend: () => void;\n}\n\n/**\n * Standardized Hook Result for consistent DX.\n */\nexport type HookResult = Promise<Event | void>;\n\nexport interface Hooks {\n /**\n * Called when a run session begins.\n * Can return an Event to be emitted, or a NextAction to jump-start the loop.\n */\n onBeforeRun?: (\n input: { event: Event; runId: string; state: Record<string, any> },\n context: RuntimeContext\n ) => Promise<Event | NextAction | void>;\n\n /**\n * Called when a run session completes.\n */\n onAfterRun?: (context: RuntimeContext) => HookResult;\n\n /**\n * Called whenever an event is yielded by the runtime.\n */\n onEvent?: (event: Event, context: RuntimeContext) => HookResult;\n\n /**\n * Called before an action is executed.\n * Return an event to intercept/suspend the action.\n */\n onBeforeAction?: (\n call: { action: Action<any>; params: any },\n context: RuntimeContext\n ) => HookResult;\n\n /**\n * Called after an action completes.\n */\n onAfterAction?: (\n result: { action: Action<any>; data: NextAction | void },\n context: RuntimeContext\n ) => HookResult;\n}\n\n/**\n * A plugin is just a named set of hooks.\n */\nexport interface Plugin extends Hooks {\n name: string;\n}\n\nexport interface Config {\n actions: Record<string, Action<any>>;\n /**\n * The central brain for handling incoming events.\n */\n brain?: (\n event: Event,\n context: RuntimeContext\n ) => AsyncGenerator<Event, NextAction | void, unknown>;\n hooks?: Hooks;\n plugins?: Plugin[];\n safetyMaxSteps?: number;\n}\n","import {\n Action,\n Event,\n NextAction,\n RuntimeContext,\n Config,\n Plugin,\n ui,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\nimport { z } from \"zod\";\n\n/**\n * The Slim Runtime.\n * Single Responsibility: Orchestrate Event -> Action -> Event transitions.\n */\nexport class Runtime {\n private config: Config;\n\n constructor(config: Config) {\n this.config = config;\n }\n\n public async *run(input: {\n event: Event;\n runId?: string;\n state?: Record<string, any>;\n }): AsyncGenerator<Event> {\n const runId = input.runId ?? generateId();\n\n const context: RuntimeContext = {\n state: input.state ?? {},\n runId,\n stepCount: 0,\n isDone: false,\n actions: this.config.actions,\n ui,\n suspend: () => {\n context.isDone = true;\n },\n };\n\n let nextAction: NextAction | void = undefined;\n\n // 1. Trigger Plugins: onBeforeRun\n for (const plugin of this.config.plugins || []) {\n if (plugin.onBeforeRun) {\n const result = await plugin.onBeforeRun(\n { event: input.event, runId, state: context.state },\n context\n );\n if (result) {\n if (\"type\" in result) {\n yield* this.emit(result, context);\n } else {\n nextAction = result;\n }\n }\n }\n }\n\n // 2. Trigger Hook: onBeforeRun\n if (this.config.hooks?.onBeforeRun) {\n const result = await this.config.hooks.onBeforeRun(\n { event: input.event, runId, state: context.state },\n context\n );\n if (result) {\n if (\"type\" in result) {\n yield* this.emit(result, context);\n } else {\n nextAction = result;\n }\n }\n }\n\n if (context.isDone) return;\n\n yield* this.emit(\n { type: \"run-started\", data: { inputEvent: input.event } },\n context\n );\n\n // Initial dispatch of the incoming event to the agent's brain\n // Only if onBeforeRun didn't already provide a nextAction\n if (!nextAction && this.config.brain) {\n nextAction = yield* this.dispatchToBrain(input.event, context);\n }\n\n // Agentic loop\n while (nextAction && !context.isDone) {\n if (context.stepCount++ >= (this.config.safetyMaxSteps ?? 10)) {\n yield* this.emit(\n { type: \"error\", data: { message: \"Max steps exceeded\" } },\n context\n );\n break;\n }\n\n const current: NextAction = nextAction;\n nextAction = undefined; // Reset\n\n // 1. Resolve Action\n const actionName: string =\n current.action ?? Object.keys(this.config.actions)[0];\n const action: Action<any> = this.config.actions[actionName];\n\n if (!action) {\n yield* this.emit(\n {\n type: \"error\",\n data: { message: `Action ${actionName} not found` },\n },\n context\n );\n break;\n }\n\n // 2. Execute Action\n const result = yield* this.executeAction(action, current.params, context);\n\n // 3. Decide Next Step\n if (this.config.brain) {\n // If we have a brain, feed the result back to it to decide what to do next.\n // This keeps the brain in the loop for multi-step reasoning.\n nextAction = yield* this.dispatchToBrain(\n {\n type: \"action-result\",\n data: {\n action: actionName,\n params: current.params,\n result,\n },\n },\n context\n );\n } else {\n // Simple mode: follow the action's own suggestion for the next step.\n nextAction = result;\n }\n }\n\n // 1. Trigger Plugins: onAfterRun\n for (const plugin of this.config.plugins || []) {\n if (plugin.onAfterRun) {\n const extra = await plugin.onAfterRun(context);\n if (extra) yield* this.emit(extra, context);\n }\n }\n\n // 2. Trigger Hook: onAfterRun\n if (this.config.hooks?.onAfterRun) {\n const extra = await this.config.hooks.onAfterRun(context);\n if (extra) yield* this.emit(extra, context);\n }\n }\n\n private async *dispatchToBrain(\n event: Event,\n context: RuntimeContext\n ): AsyncGenerator<Event, NextAction | void> {\n const generator = this.config.brain!(event, context);\n while (true) {\n const { value, done } = await generator.next();\n if (done) return value as NextAction | void;\n yield* this.emit(value as Event, context);\n }\n }\n\n private async *executeAction(\n action: Action,\n params: any,\n context: RuntimeContext\n ): AsyncGenerator<Event, NextAction | void> {\n // 1. Trigger Plugins: onBeforeAction\n for (const plugin of this.config.plugins || []) {\n if (plugin.onBeforeAction) {\n const hookResult = await plugin.onBeforeAction(\n { action, params },\n context\n );\n if (hookResult) {\n yield* this.emit(hookResult, context);\n if (context.isDone) return;\n }\n }\n }\n\n // 2. Trigger Hook: onBeforeAction\n if (this.config.hooks?.onBeforeAction) {\n const hookResult = await this.config.hooks.onBeforeAction(\n { action, params },\n context\n );\n if (hookResult) {\n yield* this.emit(hookResult, context);\n if (context.isDone) return;\n }\n }\n\n try {\n const generator = action.execute(params, context);\n let result: NextAction | void;\n\n while (true) {\n const { value, done } = await generator.next();\n if (done) {\n result = value as NextAction | void;\n break;\n }\n yield* this.emit(value as Event, context);\n if (context.isDone) return;\n }\n\n // 3. Trigger Plugins: onAfterAction\n for (const plugin of this.config.plugins || []) {\n if (plugin.onAfterAction) {\n const extra = await plugin.onAfterAction(\n { action, data: result },\n context\n );\n if (extra) yield* this.emit(extra, context);\n }\n }\n\n // 4. Trigger Hook: onAfterAction\n if (this.config.hooks?.onAfterAction) {\n const extra = await this.config.hooks.onAfterAction(\n { action, data: result },\n context\n );\n if (extra) yield* this.emit(extra, context);\n }\n\n return result;\n } catch (error) {\n yield* this.emit(\n {\n type: \"error\",\n data: {\n action: action.name,\n error: error instanceof Error ? error.message : String(error),\n },\n },\n context\n );\n }\n }\n\n /**\n * Internal helper to yield an event and trigger the onEvent hook.\n */\n private async *emit(\n event: Event,\n context: RuntimeContext\n ): AsyncGenerator<Event> {\n const finalEvent = {\n ...event,\n runId: context.runId,\n timestamp: event.timestamp ?? Date.now(),\n role: event.role ?? \"assistant\",\n };\n\n // Yield the actual event first\n yield finalEvent;\n\n // 1. Trigger Plugins: onEvent\n for (const plugin of this.config.plugins || []) {\n if (plugin.onEvent) {\n const extra = await plugin.onEvent(finalEvent, context);\n if (extra) {\n yield { ...extra, runId: context.runId, timestamp: Date.now() };\n }\n }\n }\n\n // 2. Trigger Hook: onEvent for side-effects or extra events\n if (this.config.hooks?.onEvent) {\n const extra = await this.config.hooks.onEvent(finalEvent, context);\n if (extra) {\n // Yield extra event from hook, ensuring it has required metadata\n yield { ...extra, runId: context.runId, timestamp: Date.now() };\n }\n }\n }\n}\n\nexport const melony = (config: Config) => {\n const runtime = new Runtime(config);\n return {\n config,\n run: runtime.run.bind(runtime),\n };\n};\n\n/**\n * Helper to define an action with full type inference.\n */\nexport const action = <T extends z.ZodSchema>(\n config: Action<T>\n): Action<T> => config;\n\n/**\n * Helper to define a plugin.\n */\nexport const plugin = (config: Plugin): Plugin => config;\n"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "melony",
3
+ "version": "0.1.12",
4
+ "main": "dist/index.js",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "keywords": [
8
+ "ai",
9
+ "agents",
10
+ "framework",
11
+ "typescript",
12
+ "sdui"
13
+ ],
14
+ "author": "",
15
+ "license": "MIT",
16
+ "description": "The universal AI agent framework with built-in SDUI and HITL.",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./client": {
27
+ "types": "./dist/client.d.ts",
28
+ "import": "./dist/client.js",
29
+ "default": "./dist/client.js"
30
+ },
31
+ "./adapters/*": {
32
+ "types": "./dist/adapters/*.d.ts",
33
+ "import": "./dist/adapters/*.js",
34
+ "default": "./dist/adapters/*.js"
35
+ },
36
+ "./package.json": "./package.json"
37
+ },
38
+ "module": "dist/index.js",
39
+ "types": "dist/index.d.ts",
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "devDependencies": {
44
+ "@types/node": "^24.10.1",
45
+ "rimraf": "^5.0.10",
46
+ "tsup": "^8.5.0",
47
+ "typescript": "^5.9.2"
48
+ },
49
+ "dependencies": {
50
+ "zod": "^4.1.11",
51
+ "node-html-parser": "^7.0.1"
52
+ },
53
+ "scripts": {
54
+ "build": "tsup",
55
+ "clean": "rimraf dist",
56
+ "dev": "tsup --watch",
57
+ "typecheck": "tsc --noEmit"
58
+ }
59
+ }