flowneer 0.2.0 → 0.3.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.
@@ -3,5 +3,5 @@ export { CircuitBreakerOptions, withCircuitBreaker, withCycles, withFallback, wi
3
3
  export { AuditEntry, AuditLogStore, CheckpointStore, VersionedCheckpointEntry, VersionedCheckpointStore, withAuditLog, withCheckpoint, withReplay, withVersionedCheckpoint } from './persistence/index.js';
4
4
  export { RateLimitOptions, withCostTracker, withRateLimit, withTokenBudget } from './llm/index.js';
5
5
  export { withAtomicUpdates, withDryRun, withMocks, withStepLimit } from './dev/index.js';
6
- export { peekChannel, receiveFrom, sendTo, withChannels } from './messaging/index.js';
6
+ export { StreamSubscriber, emit, peekChannel, receiveFrom, sendTo, withChannels, withStream } from './messaging/index.js';
7
7
  import '../index.js';
@@ -77,10 +77,16 @@ var withInterrupts = {
77
77
  var withFallback = {
78
78
  withFallback(fn) {
79
79
  this._setHooks({
80
- wrapStep: async (_meta, next, shared, params) => {
80
+ wrapStep: async (meta, next, shared, params) => {
81
81
  try {
82
82
  await next();
83
- } catch {
83
+ } catch (e) {
84
+ shared.__fallbackError = {
85
+ stepIndex: meta.index,
86
+ stepType: meta.type,
87
+ message: e instanceof Error ? e.message : String(e),
88
+ stack: e instanceof Error ? e.stack : void 0
89
+ };
84
90
  await fn(shared, params);
85
91
  }
86
92
  }
@@ -126,15 +132,20 @@ var withCircuitBreaker = {
126
132
  var withTimeout = {
127
133
  withTimeout(ms) {
128
134
  this._setHooks({
129
- wrapStep: (meta, next) => Promise.race([
130
- next(),
131
- new Promise(
132
- (_, reject) => setTimeout(
133
- () => reject(new Error(`step ${meta.index} timed out after ${ms}ms`)),
134
- ms
135
+ wrapStep: (meta, next) => {
136
+ let handle;
137
+ return Promise.race([
138
+ next().finally(() => clearTimeout(handle)),
139
+ new Promise(
140
+ (_, reject) => handle = setTimeout(
141
+ () => reject(
142
+ new Error(`step ${meta.index} timed out after ${ms}ms`)
143
+ ),
144
+ ms
145
+ )
135
146
  )
136
- )
137
- ])
147
+ ]);
148
+ }
138
149
  });
139
150
  return this;
140
151
  }
@@ -415,7 +426,23 @@ var withChannels = {
415
426
  return this;
416
427
  }
417
428
  };
429
+
430
+ // plugins/messaging/withStream.ts
431
+ var withStream = {
432
+ withStream(subscriber) {
433
+ this._setHooks({
434
+ beforeFlow: (shared) => {
435
+ shared.__stream = subscriber;
436
+ }
437
+ });
438
+ return this;
439
+ }
440
+ };
441
+ function emit(shared, chunk) {
442
+ shared.__stream?.(chunk);
443
+ }
418
444
  export {
445
+ emit,
419
446
  peekChannel,
420
447
  receiveFrom,
421
448
  sendTo,
@@ -434,6 +461,7 @@ export {
434
461
  withRateLimit,
435
462
  withReplay,
436
463
  withStepLimit,
464
+ withStream,
437
465
  withTimeout,
438
466
  withTiming,
439
467
  withTokenBudget,
@@ -17,4 +17,40 @@ declare module "../../Flowneer" {
17
17
  }
18
18
  declare const withChannels: FlowneerPlugin;
19
19
 
20
- export { peekChannel, receiveFrom, sendTo, withChannels };
20
+ /** Callback invoked each time a step calls `emit()`. */
21
+ type StreamSubscriber<T = unknown> = (chunk: T) => void;
22
+ declare module "../../Flowneer" {
23
+ interface FlowBuilder<S, P> {
24
+ /**
25
+ * Registers a streaming subscriber for this flow.
26
+ * Steps call `emit(shared, chunk)` to push data to the subscriber in real-time.
27
+ * The subscriber is stored on `shared.__stream` before the first step runs
28
+ * so it is automatically inherited by sub-flows (loop, batch, etc.).
29
+ *
30
+ * @example
31
+ * const flow = new FlowBuilder<MyState>()
32
+ * .withStream((chunk) => console.log("[stream]", chunk))
33
+ * // ...
34
+ *
35
+ * // Inside a step:
36
+ * emit(s, { type: "draft", content: s.draft });
37
+ */
38
+ withStream<T = unknown>(subscriber: StreamSubscriber<T>): this;
39
+ }
40
+ }
41
+ declare const withStream: FlowneerPlugin;
42
+ /**
43
+ * Push a chunk to the subscriber registered via `.withStream()`.
44
+ * Safe to call unconditionally — silently no-ops when no subscriber is registered.
45
+ *
46
+ * @example
47
+ * async function refineDraft(s: MyState) {
48
+ * // ... produce s.draft ...
49
+ * emit(s, { type: "draft", round: s.round, content: s.draft });
50
+ * }
51
+ */
52
+ declare function emit<T = unknown>(shared: {
53
+ __stream?: StreamSubscriber<T>;
54
+ }, chunk: T): void;
55
+
56
+ export { type StreamSubscriber, emit, peekChannel, receiveFrom, sendTo, withChannels, withStream };
@@ -32,9 +32,26 @@ var withChannels = {
32
32
  return this;
33
33
  }
34
34
  };
35
+
36
+ // plugins/messaging/withStream.ts
37
+ var withStream = {
38
+ withStream(subscriber) {
39
+ this._setHooks({
40
+ beforeFlow: (shared) => {
41
+ shared.__stream = subscriber;
42
+ }
43
+ });
44
+ return this;
45
+ }
46
+ };
47
+ function emit(shared, chunk) {
48
+ shared.__stream?.(chunk);
49
+ }
35
50
  export {
51
+ emit,
36
52
  peekChannel,
37
53
  receiveFrom,
38
54
  sendTo,
39
- withChannels
55
+ withChannels,
56
+ withStream
40
57
  };
@@ -2,10 +2,16 @@
2
2
  var withFallback = {
3
3
  withFallback(fn) {
4
4
  this._setHooks({
5
- wrapStep: async (_meta, next, shared, params) => {
5
+ wrapStep: async (meta, next, shared, params) => {
6
6
  try {
7
7
  await next();
8
- } catch {
8
+ } catch (e) {
9
+ shared.__fallbackError = {
10
+ stepIndex: meta.index,
11
+ stepType: meta.type,
12
+ message: e instanceof Error ? e.message : String(e),
13
+ stack: e instanceof Error ? e.stack : void 0
14
+ };
9
15
  await fn(shared, params);
10
16
  }
11
17
  }
@@ -51,15 +57,20 @@ var withCircuitBreaker = {
51
57
  var withTimeout = {
52
58
  withTimeout(ms) {
53
59
  this._setHooks({
54
- wrapStep: (meta, next) => Promise.race([
55
- next(),
56
- new Promise(
57
- (_, reject) => setTimeout(
58
- () => reject(new Error(`step ${meta.index} timed out after ${ms}ms`)),
59
- ms
60
+ wrapStep: (meta, next) => {
61
+ let handle;
62
+ return Promise.race([
63
+ next().finally(() => clearTimeout(handle)),
64
+ new Promise(
65
+ (_, reject) => handle = setTimeout(
66
+ () => reject(
67
+ new Error(`step ${meta.index} timed out after ${ms}ms`)
68
+ ),
69
+ ms
70
+ )
60
71
  )
61
- )
62
- ])
72
+ ]);
73
+ }
63
74
  });
64
75
  return this;
65
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowneer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Zero-dependency fluent flow builder for AI agents",
5
5
  "license": "MIT",
6
6
  "type": "module",