melony 0.2.9 → 0.3.1

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/dist/cli.js CHANGED
@@ -37,13 +37,15 @@ program.command("chat").description("Start an interactive chat session with a Me
37
37
  });
38
38
  let fullResponse = "";
39
39
  for await (const event of stream) {
40
- if (event.type === "assistant:text-delta") {
41
- const delta = event.data.delta;
40
+ if (event.type === "llm:text:delta" || event.type === "assistant:text:delta" || event.type === "assistant:text-delta") {
41
+ const data = event.data;
42
+ const delta = data.text ?? data.delta ?? "";
42
43
  process.stdout.write(delta);
43
44
  fullResponse += delta;
44
- } else if (event.type === "assistant:text") {
45
+ } else if (event.type === "llm:text" || event.type === "assistant:text") {
45
46
  if (!fullResponse) {
46
- process.stdout.write(event.data.content);
47
+ const data = event.data;
48
+ process.stdout.write(data.text ?? data.content ?? "");
47
49
  }
48
50
  } else if (event.type.startsWith("action:")) {
49
51
  console.log(`
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":"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","file":"cli.js","sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport * as readline from \"node:readline/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { MelonyClient } from \"./client\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst program = new Command();\n\nprogram\n .name(\"melony\")\n .description(\"Melony CLI - Fast, minimalist AI agent framework\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"chat\")\n .description(\"Start an interactive chat session with a Melony agent\")\n .option(\"-u, --url <url>\", \"Server URL\", process.env.MELONY_SERVER_URL ?? \"http://localhost:4001/api/chat\")\n .action(async (options) => {\n const client = new MelonyClient({\n url: options.url,\n });\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(\"------------------------------------------\");\n console.log(\"🍎 Melony Interactive CLI\");\n console.log(`Connected to: ${options.url}`);\n console.log(\"Type your message and press Enter. Type 'exit' to quit.\");\n console.log(\"------------------------------------------\");\n\n while (true) {\n const input = await rl.question(\"\\nYou: \");\n\n if (input.toLowerCase() === \"exit\" || input.toLowerCase() === \"quit\") {\n break;\n }\n\n if (!input.trim()) continue;\n\n process.stdout.write(\"Agent: \");\n\n try {\n const stream = client.send({\n type: \"user:text\",\n data: { content: input },\n } as any);\n\n let fullResponse = \"\";\n\n for await (const event of stream) {\n if (event.type === \"assistant:text-delta\") {\n const delta = (event.data as any).delta;\n process.stdout.write(delta);\n fullResponse += delta;\n } else if (event.type === \"assistant:text\") {\n if (!fullResponse) {\n process.stdout.write((event.data as any).content);\n }\n } else if (event.type.startsWith(\"action:\")) {\n console.log(`\\n[Action: ${JSON.stringify(event.data, null, 2)}]`);\n }\n }\n process.stdout.write(\"\\n\");\n } catch (error) {\n console.error(\"\\nError:\", error instanceof Error ? error.message : String(error));\n }\n }\n\n console.log(\"\\nGoodbye!\");\n rl.close();\n });\n\n// Default to chat if no command is provided\nif (!process.argv.slice(2).length) {\n process.argv.push(\"chat\");\n}\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":"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","file":"cli.js","sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport * as readline from \"node:readline/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { MelonyClient } from \"./client\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst program = new Command();\n\nprogram\n .name(\"melony\")\n .description(\"Melony CLI - Fast, minimalist AI agent framework\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"chat\")\n .description(\"Start an interactive chat session with a Melony agent\")\n .option(\"-u, --url <url>\", \"Server URL\", process.env.MELONY_SERVER_URL ?? \"http://localhost:4001/api/chat\")\n .action(async (options) => {\n const client = new MelonyClient({\n url: options.url,\n });\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(\"------------------------------------------\");\n console.log(\"🍎 Melony Interactive CLI\");\n console.log(`Connected to: ${options.url}`);\n console.log(\"Type your message and press Enter. Type 'exit' to quit.\");\n console.log(\"------------------------------------------\");\n\n while (true) {\n const input = await rl.question(\"\\nYou: \");\n\n if (input.toLowerCase() === \"exit\" || input.toLowerCase() === \"quit\") {\n break;\n }\n\n if (!input.trim()) continue;\n\n process.stdout.write(\"Agent: \");\n\n try {\n const stream = client.send({\n type: \"user:text\",\n data: { content: input },\n } as any);\n\n let fullResponse = \"\";\n\n for await (const event of stream) {\n if (\n event.type === \"llm:text:delta\" ||\n event.type === \"assistant:text:delta\" ||\n event.type === \"assistant:text-delta\"\n ) {\n const data = event.data as any;\n const delta = data.text ?? data.delta ?? \"\";\n process.stdout.write(delta);\n fullResponse += delta;\n } else if (event.type === \"llm:text\" || event.type === \"assistant:text\") {\n if (!fullResponse) {\n const data = event.data as any;\n process.stdout.write(data.text ?? data.content ?? \"\");\n }\n } else if (event.type.startsWith(\"action:\")) {\n console.log(`\\n[Action: ${JSON.stringify(event.data, null, 2)}]`);\n }\n }\n process.stdout.write(\"\\n\");\n } catch (error) {\n console.error(\"\\nError:\", error instanceof Error ? error.message : String(error));\n }\n }\n\n console.log(\"\\nGoodbye!\");\n rl.close();\n });\n\n// Default to chat if no command is provided\nif (!process.argv.slice(2).length) {\n process.argv.push(\"chat\");\n}\n\nprogram.parse();\n"]}
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as Event } from './generate-id-B7nRWyjz.js';
2
- export { g as generateId } from './generate-id-B7nRWyjz.js';
1
+ import { E as Event } from './generate-id-nIiN6qMt.js';
2
+ export { g as generateId } from './generate-id-nIiN6qMt.js';
3
3
 
4
4
  interface ClientState<TEvent extends Event = Event> {
5
5
  events: TEvent[];
@@ -4,8 +4,6 @@
4
4
  */
5
5
  declare class Runtime<TState = any, TEvent extends Event = Event> {
6
6
  readonly config: Config<TState, TEvent>;
7
- private queue;
8
- private isEmitting;
9
7
  constructor(config: Config<TState, TEvent>);
10
8
  /**
11
9
  * Process an incoming event through the runtime.
@@ -68,7 +66,7 @@ type Interceptor<TState = any, TEvent extends Event = Event> = (event: TEvent, c
68
66
  type EventHandler<TState = any, TEvent extends Event = Event> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void;
69
67
  interface Config<TState = any, TEvent extends Event = Event> {
70
68
  eventHandlers: Map<string, EventHandler<TState, TEvent>[]>;
71
- interceptors: Interceptor<TState, TEvent>[];
69
+ interceptors: Map<string, Interceptor<TState, TEvent>[]>;
72
70
  }
73
71
 
74
72
  declare const generateId: () => string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as Runtime, E as Event, C as Config, a as RuntimeContext, I as Interceptor } from './generate-id-B7nRWyjz.js';
2
- export { b as ErrorEvent, c as EventHandler, g as generateId } from './generate-id-B7nRWyjz.js';
1
+ import { R as Runtime, E as Event, C as Config, a as RuntimeContext, I as Interceptor } from './generate-id-nIiN6qMt.js';
2
+ export { b as ErrorEvent, c as EventHandler, g as generateId } from './generate-id-nIiN6qMt.js';
3
3
 
4
4
  declare const MelonyRuntime: typeof Runtime;
5
5
 
@@ -19,7 +19,7 @@ declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
19
19
  * Add an event handler for a specific event type. Supports method chaining.
20
20
  * The handler receives the narrowed event type based on the eventType string.
21
21
  */
22
- on<K extends TEvent["type"]>(eventType: K, handler: (event: Extract<TEvent, {
22
+ on<K extends TEvent["type"]>(eventType: K | "*", handler: (event: K extends "*" ? TEvent : Extract<TEvent, {
23
23
  type: K;
24
24
  }>, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void): this;
25
25
  /**
@@ -27,6 +27,9 @@ declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
27
27
  * Useful for logging, validation, or suspending for approval.
28
28
  */
29
29
  intercept(interceptor: Interceptor<TState, TEvent>): this;
30
+ intercept<K extends TEvent["type"]>(eventType: K, interceptor: (event: Extract<TEvent, {
31
+ type: K;
32
+ }>, context: RuntimeContext<TState, TEvent>) => Promise<TEvent | void> | TEvent | void): this;
30
33
  /**
31
34
  * Use a plugin to extend the builder.
32
35
  * This is ideal for modularizing common handlers.
package/dist/index.js CHANGED
@@ -7,8 +7,6 @@ function isEvent(val) {
7
7
  }
8
8
  var Runtime = class {
9
9
  constructor(config) {
10
- this.queue = [];
11
- this.isEmitting = false;
12
10
  this.config = config;
13
11
  }
14
12
  /**
@@ -50,15 +48,25 @@ var Runtime = class {
50
48
  */
51
49
  async *runEventHandlers(event, context) {
52
50
  let currentEvent = event;
53
- for (const interceptor of this.config.interceptors) {
51
+ const globalInterceptors = this.config.interceptors.get("*") || [];
52
+ for (const interceptor of globalInterceptors) {
54
53
  const result = await interceptor(currentEvent, context);
55
54
  if (result && typeof result === "object" && "type" in result) {
56
55
  currentEvent = result;
57
56
  }
58
57
  }
58
+ if (currentEvent.type !== "*") {
59
+ const specificInterceptors = this.config.interceptors.get(currentEvent.type) || [];
60
+ for (const interceptor of specificInterceptors) {
61
+ const result = await interceptor(currentEvent, context);
62
+ if (result && typeof result === "object" && "type" in result) {
63
+ currentEvent = result;
64
+ }
65
+ }
66
+ }
59
67
  const handlers = [
60
- ...this.config.eventHandlers.get(currentEvent.type) || [],
61
- ...this.config.eventHandlers.get("*") || []
68
+ ...this.config.eventHandlers.get("*") || [],
69
+ ...this.config.eventHandlers.get(currentEvent.type) || []
62
70
  ];
63
71
  yield* this.emit(currentEvent, context);
64
72
  for (const handler of handlers) {
@@ -74,17 +82,7 @@ var Runtime = class {
74
82
  * Internal helper to yield an event with metadata.
75
83
  */
76
84
  async *emit(event, context) {
77
- this.queue.push(event);
78
- if (this.isEmitting) return;
79
- this.isEmitting = true;
80
- try {
81
- while (this.queue.length > 0) {
82
- const current = this.queue.shift();
83
- yield current;
84
- }
85
- } finally {
86
- this.isEmitting = false;
87
- }
85
+ yield event;
88
86
  }
89
87
  };
90
88
 
@@ -123,7 +121,7 @@ var MelonyBuilder = class {
123
121
  constructor(initialConfig) {
124
122
  this.config = {
125
123
  eventHandlers: initialConfig?.eventHandlers ?? /* @__PURE__ */ new Map(),
126
- interceptors: initialConfig?.interceptors ?? []
124
+ interceptors: initialConfig?.interceptors ?? /* @__PURE__ */ new Map()
127
125
  };
128
126
  }
129
127
  /**
@@ -137,12 +135,21 @@ var MelonyBuilder = class {
137
135
  this.config.eventHandlers.get(eventType).push(handler);
138
136
  return this;
139
137
  }
140
- /**
141
- * Register an interceptor that runs before any handlers.
142
- * Useful for logging, validation, or suspending for approval.
143
- */
144
- intercept(interceptor) {
145
- this.config.interceptors.push(interceptor);
138
+ intercept(arg1, arg2) {
139
+ if (typeof arg1 === "string") {
140
+ const type = arg1;
141
+ const interceptor = arg2;
142
+ if (!this.config.interceptors.has(type)) {
143
+ this.config.interceptors.set(type, []);
144
+ }
145
+ this.config.interceptors.get(type).push(interceptor);
146
+ } else {
147
+ const interceptor = arg1;
148
+ if (!this.config.interceptors.has("*")) {
149
+ this.config.interceptors.set("*", []);
150
+ }
151
+ this.config.interceptors.get("*").push(interceptor);
152
+ }
146
153
  return this;
147
154
  }
148
155
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime.ts","../src/melony.ts","../src/utils/create-stream-response.ts","../src/builder.ts"],"names":["event"],"mappings":"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","file":"index.js","sourcesContent":["import {\n Event,\n RuntimeContext,\n Config,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\n/**\n * Helper to check if a value is a Melony Event.\n */\nfunction isEvent(val: any): val is Event {\n return val && typeof val === \"object\" && typeof val.type === \"string\";\n}\n\n/**\n * The Melony Runtime.\n * Fully unopinionated - processes events through handlers.\n */\nexport class Runtime<TState = any, TEvent extends Event = Event> {\n public readonly config: Config<TState, TEvent>;\n private queue: TEvent[] = [];\n private isEmitting = false;\n\n constructor(config: Config<TState, TEvent>) {\n this.config = config;\n }\n\n /**\n * Process an incoming event through the runtime.\n * All event processing is handled by user-defined event handlers.\n */\n public async *run(\n event: TEvent, \n options?: { state?: TState; runId?: string }\n ): AsyncGenerator<TEvent> {\n const runId = options?.runId ?? generateId();\n \n const context: RuntimeContext<TState, TEvent> = {\n state: (options?.state ?? {}) as TState,\n runtime: this,\n runId,\n suspend: (event?: TEvent) => {\n throw event || { type: \"run-suspended\", data: {} };\n },\n };\n\n try {\n // Process the incoming event through handlers\n yield* this.runEventHandlers(event, context);\n } catch (error) {\n let eventToEmit: TEvent | undefined;\n\n if (isEvent(error)) {\n eventToEmit = error as TEvent;\n } else {\n eventToEmit = {\n type: \"error\",\n data: {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n },\n } as TEvent;\n }\n\n if (eventToEmit) {\n yield* this.emit(eventToEmit, context);\n }\n }\n }\n\n\n /**\n * Run all event handlers that match the given event type.\n */\n private async *runEventHandlers(\n event: TEvent,\n context: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n let currentEvent = event;\n\n // Run all interceptors sequentially\n for (const interceptor of this.config.interceptors) {\n const result = await interceptor(currentEvent, context);\n \n // If interceptor returns a new event object, we use it for subsequent steps\n if (result && typeof result === \"object\" && \"type\" in result) {\n currentEvent = result as TEvent;\n }\n }\n\n const handlers = [\n ...(this.config.eventHandlers.get(currentEvent.type) || []),\n ...(this.config.eventHandlers.get(\"*\") || []),\n ];\n \n // First emit the event itself\n yield* this.emit(currentEvent, context);\n\n for (const handler of handlers) {\n const result = handler(currentEvent, context);\n if (result) {\n for await (const yieldedEvent of result) {\n // Recursively process yielded events through their handlers\n yield* this.runEventHandlers(yieldedEvent, context);\n }\n }\n }\n }\n\n /**\n * Internal helper to yield an event with metadata.\n */\n private async *emit(\n event: TEvent,\n context?: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n this.queue.push(event);\n\n if (this.isEmitting) return;\n\n this.isEmitting = true;\n try {\n while (this.queue.length > 0) {\n const current = this.queue.shift()!;\n yield current;\n }\n } finally {\n this.isEmitting = false;\n }\n }\n}\n","import { Runtime } from \"./runtime\";\n\nexport const MelonyRuntime = Runtime;","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","import {\n Event,\n EventHandler,\n Config,\n RuntimeContext,\n Interceptor,\n} from \"./types\";\nimport { Runtime } from \"./runtime\";\nimport { createStreamResponse } from \"./utils/create-stream-response\";\n\n/**\n * A Melony plugin is a function that receives the builder and extends it.\n * This allows for modularizing common handlers.\n */\nexport type MelonyPlugin<TState = any, TEvent extends Event = Event> = (\n builder: MelonyBuilder<TState, TEvent>\n) => void;\n\n/**\n * Fluent builder for creating Melony agents with excellent developer experience.\n * Provides method chaining for handlers and plugins with full TypeScript support.\n */\nexport class MelonyBuilder<\n TState = any,\n TEvent extends Event = Event\n> {\n private config: Config<TState, TEvent>;\n\n constructor(initialConfig?: Partial<Config<TState, TEvent>>) {\n this.config = {\n eventHandlers: initialConfig?.eventHandlers ?? new Map(),\n interceptors: initialConfig?.interceptors ?? [],\n };\n }\n\n /**\n * Add an event handler for a specific event type. Supports method chaining.\n * The handler receives the narrowed event type based on the eventType string.\n */\n on<K extends TEvent[\"type\"]>(\n eventType: K,\n handler: (\n event: Extract<TEvent, { type: K }>,\n context: RuntimeContext<TState, TEvent>\n ) => AsyncGenerator<TEvent, void, unknown> | void\n ): this {\n if (!this.config.eventHandlers.has(eventType)) {\n this.config.eventHandlers.set(eventType, []);\n }\n // Cast is safe because runtime only calls this handler for matching event types\n this.config.eventHandlers.get(eventType)!.push(handler as EventHandler<TState, TEvent>);\n return this;\n }\n\n /**\n * Register an interceptor that runs before any handlers.\n * Useful for logging, validation, or suspending for approval.\n */\n intercept(interceptor: Interceptor<TState, TEvent>): this {\n this.config.interceptors.push(interceptor);\n return this;\n }\n\n /**\n * Use a plugin to extend the builder.\n * This is ideal for modularizing common handlers.\n */\n use(plugin: MelonyPlugin<TState, TEvent>): this {\n plugin(this);\n return this;\n }\n\n /**\n * Build and return the Melony runtime instance.\n * This is the final method in the fluent chain.\n */\n build(): Runtime<TState, TEvent> {\n return new Runtime(this.config);\n }\n\n /**\n * Execute and stream the response for an event.\n * This is a convenience method that builds the runtime and calls run().\n */\n async streamResponse(\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ): Promise<Response> {\n const runtime = this.build();\n const generator = runtime.run(event, options);\n return createStreamResponse(generator);\n }\n\n /**\n * Execute the agent and return the data from the last event of a specific type as a JSON response.\n * Ideal for initialization or non-streaming requests where you only need the final UI state.\n */\n async jsonResponse(\n event: TEvent,\n options?: {\n state?: TState;\n runId?: string;\n }\n ): Promise<Response> {\n const events = [];\n const runtime = this.build();\n\n for await (const e of runtime.run(event, options)) {\n events.push(e);\n }\n\n return new Response(JSON.stringify({ events }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n /**\n * Get the current configuration (useful for debugging or serialization).\n */\n getConfig(): Config<TState, TEvent> {\n return { ...this.config };\n }\n}\n\n/**\n * Factory function to create a new Melony builder instance.\n * This is the entry point for the fluent API.\n */\nexport function melony<\n TState = any,\n TEvent extends Event = Event\n>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent> {\n return new MelonyBuilder<TState, TEvent>(initialConfig);\n}\n"]}
1
+ {"version":3,"sources":["../src/runtime.ts","../src/melony.ts","../src/utils/create-stream-response.ts","../src/builder.ts"],"names":["event"],"mappings":"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","file":"index.js","sourcesContent":["import {\n Event,\n RuntimeContext,\n Config,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\n/**\n * Helper to check if a value is a Melony Event.\n */\nfunction isEvent(val: any): val is Event {\n return val && typeof val === \"object\" && typeof val.type === \"string\";\n}\n\n/**\n * The Melony Runtime.\n * Fully unopinionated - processes events through handlers.\n */\nexport class Runtime<TState = any, TEvent extends Event = Event> {\n public readonly config: Config<TState, TEvent>;\n\n constructor(config: Config<TState, TEvent>) {\n this.config = config;\n }\n\n /**\n * Process an incoming event through the runtime.\n * All event processing is handled by user-defined event handlers.\n */\n public async *run(\n event: TEvent, \n options?: { state?: TState; runId?: string }\n ): AsyncGenerator<TEvent> {\n const runId = options?.runId ?? generateId();\n \n const context: RuntimeContext<TState, TEvent> = {\n state: (options?.state ?? {}) as TState,\n runtime: this,\n runId,\n suspend: (event?: TEvent) => {\n throw event || { type: \"run-suspended\", data: {} };\n },\n };\n\n try {\n // Process the incoming event through handlers\n yield* this.runEventHandlers(event, context);\n } catch (error) {\n let eventToEmit: TEvent | undefined;\n\n if (isEvent(error)) {\n eventToEmit = error as TEvent;\n } else {\n eventToEmit = {\n type: \"error\",\n data: {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n },\n } as TEvent;\n }\n\n if (eventToEmit) {\n yield* this.emit(eventToEmit, context);\n }\n }\n }\n\n\n /**\n * Run all event handlers that match the given event type.\n */\n private async *runEventHandlers(\n event: TEvent,\n context: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n let currentEvent = event;\n\n // 1. Run global interceptors first\n const globalInterceptors = this.config.interceptors.get(\"*\") || [];\n for (const interceptor of globalInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && \"type\" in result) {\n currentEvent = result as TEvent;\n }\n }\n\n // 2. Run specific interceptors for the (possibly new) event type\n // If currentEvent.type is \"*\", it's already been handled by the global interceptors\n if (currentEvent.type !== \"*\") {\n const specificInterceptors = this.config.interceptors.get(currentEvent.type) || [];\n for (const interceptor of specificInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && \"type\" in result) {\n currentEvent = result as TEvent;\n }\n }\n }\n\n const handlers = [\n ...(this.config.eventHandlers.get(\"*\") || []),\n ...(this.config.eventHandlers.get(currentEvent.type) || []),\n ];\n \n // First emit the event itself\n yield* this.emit(currentEvent, context);\n\n for (const handler of handlers) {\n const result = handler(currentEvent, context);\n if (result) {\n for await (const yieldedEvent of result) {\n // Recursively process yielded events through their handlers\n yield* this.runEventHandlers(yieldedEvent, context);\n }\n }\n }\n }\n\n /**\n * Internal helper to yield an event with metadata.\n */\n private async *emit(\n event: TEvent,\n context?: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n yield event;\n }\n}\n","import { Runtime } from \"./runtime\";\n\nexport const MelonyRuntime = Runtime;","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","import {\n Event,\n EventHandler,\n Config,\n RuntimeContext,\n Interceptor,\n} from \"./types\";\nimport { Runtime } from \"./runtime\";\nimport { createStreamResponse } from \"./utils/create-stream-response\";\n\n/**\n * A Melony plugin is a function that receives the builder and extends it.\n * This allows for modularizing common handlers.\n */\nexport type MelonyPlugin<TState = any, TEvent extends Event = Event> = (\n builder: MelonyBuilder<TState, TEvent>\n) => void;\n\n/**\n * Fluent builder for creating Melony agents with excellent developer experience.\n * Provides method chaining for handlers and plugins with full TypeScript support.\n */\nexport class MelonyBuilder<\n TState = any,\n TEvent extends Event = Event\n> {\n private config: Config<TState, TEvent>;\n\n constructor(initialConfig?: Partial<Config<TState, TEvent>>) {\n this.config = {\n eventHandlers: initialConfig?.eventHandlers ?? new Map(),\n interceptors: initialConfig?.interceptors ?? new Map(),\n };\n }\n\n /**\n * Add an event handler for a specific event type. Supports method chaining.\n * The handler receives the narrowed event type based on the eventType string.\n */\n on<K extends TEvent[\"type\"]>(\n eventType: K | \"*\",\n handler: (\n event: K extends \"*\" ? TEvent : Extract<TEvent, { type: K }>,\n context: RuntimeContext<TState, TEvent>\n ) => AsyncGenerator<TEvent, void, unknown> | void\n ): this {\n if (!this.config.eventHandlers.has(eventType)) {\n this.config.eventHandlers.set(eventType, []);\n }\n // Cast is safe because runtime only calls this handler for matching event types\n this.config.eventHandlers.get(eventType)!.push(handler as EventHandler<TState, TEvent>);\n return this;\n }\n\n /**\n * Register an interceptor that runs before any handlers.\n * Useful for logging, validation, or suspending for approval.\n */\n intercept(interceptor: Interceptor<TState, TEvent>): this;\n intercept<K extends TEvent[\"type\"]>(\n eventType: K,\n interceptor: (\n event: Extract<TEvent, { type: K }>,\n context: RuntimeContext<TState, TEvent>\n ) => Promise<TEvent | void> | TEvent | void\n ): this;\n intercept(\n arg1: string | Interceptor<TState, TEvent>,\n arg2?: any\n ): this {\n if (typeof arg1 === \"string\") {\n const type = arg1;\n const interceptor = arg2!;\n if (!this.config.interceptors.has(type)) {\n this.config.interceptors.set(type, []);\n }\n this.config.interceptors.get(type)!.push(interceptor as Interceptor<TState, TEvent>);\n } else {\n const interceptor = arg1;\n if (!this.config.interceptors.has(\"*\")) {\n this.config.interceptors.set(\"*\", []);\n }\n this.config.interceptors.get(\"*\")!.push(interceptor);\n }\n return this;\n }\n\n /**\n * Use a plugin to extend the builder.\n * This is ideal for modularizing common handlers.\n */\n use(plugin: MelonyPlugin<TState, TEvent>): this {\n plugin(this);\n return this;\n }\n\n /**\n * Build and return the Melony runtime instance.\n * This is the final method in the fluent chain.\n */\n build(): Runtime<TState, TEvent> {\n return new Runtime(this.config);\n }\n\n /**\n * Execute and stream the response for an event.\n * This is a convenience method that builds the runtime and calls run().\n */\n async streamResponse(\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ): Promise<Response> {\n const runtime = this.build();\n const generator = runtime.run(event, options);\n return createStreamResponse(generator);\n }\n\n /**\n * Execute the agent and return the data from the last event of a specific type as a JSON response.\n * Ideal for initialization or non-streaming requests where you only need the final UI state.\n */\n async jsonResponse(\n event: TEvent,\n options?: {\n state?: TState;\n runId?: string;\n }\n ): Promise<Response> {\n const events = [];\n const runtime = this.build();\n\n for await (const e of runtime.run(event, options)) {\n events.push(e);\n }\n\n return new Response(JSON.stringify({ events }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n /**\n * Get the current configuration (useful for debugging or serialization).\n */\n getConfig(): Config<TState, TEvent> {\n return { ...this.config };\n }\n}\n\n/**\n * Factory function to create a new Melony builder instance.\n * This is the entry point for the fluent API.\n */\nexport function melony<\n TState = any,\n TEvent extends Event = Event\n>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent> {\n return new MelonyBuilder<TState, TEvent>(initialConfig);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "melony",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,