@ironflow/browser 0.19.3 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -6
- package/dist/agents/index.d.ts +34 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +24 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/invoke.d.ts +16 -0
- package/dist/agents/invoke.d.ts.map +1 -0
- package/dist/agents/invoke.js +222 -0
- package/dist/agents/invoke.js.map +1 -0
- package/dist/agents/readMemory.d.ts +18 -0
- package/dist/agents/readMemory.d.ts.map +1 -0
- package/dist/agents/readMemory.js +87 -0
- package/dist/agents/readMemory.js.map +1 -0
- package/dist/agents/subscribe.d.ts +23 -0
- package/dist/agents/subscribe.d.ts.map +1 -0
- package/dist/agents/subscribe.js +117 -0
- package/dist/agents/subscribe.js.map +1 -0
- package/dist/agents/types.d.ts +156 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +7 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +37 -14
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/subscription.d.ts +6 -0
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +8 -0
- package/dist/subscription.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ This is a **private npm package**. This README is the sole reference for coding
|
|
|
12
12
|
- [Events and Subscriptions](#events-and-subscriptions)
|
|
13
13
|
- [Emitting Events](#emitting-events)
|
|
14
14
|
- [Workflow Operations](#workflow-operations)
|
|
15
|
+
- [Agents (`ironflow.agents.*`)](#agents-ironflowagents)
|
|
15
16
|
- [Entity Streams (Event Sourcing)](#entity-streams-event-sourcing)
|
|
16
17
|
- [Projections](#projections)
|
|
17
18
|
- [KV Store](#kv-store)
|
|
@@ -442,6 +443,101 @@ console.log(output.output);
|
|
|
442
443
|
console.log(output.patched); // Whether output was injected
|
|
443
444
|
```
|
|
444
445
|
|
|
446
|
+
## Agents (`ironflow.agents.*`)
|
|
447
|
+
|
|
448
|
+
Browser helpers for `agent()` functions. Mirror the `@ironflow/node/agent` shape so the same agent runs in browser-driven UIs and server workers without divergence.
|
|
449
|
+
|
|
450
|
+
Spec: `src/agents/spec.md`. Issue #625.
|
|
451
|
+
|
|
452
|
+
### `agents.invoke(name, payload, opts?)`
|
|
453
|
+
|
|
454
|
+
Fire-and-wait. Triggers the agent, subscribes to its run events, resolves on the terminal `system.run.{runId}.completed` event.
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
import { ironflow } from '@ironflow/browser';
|
|
458
|
+
|
|
459
|
+
const result = await ironflow.agents.invoke<{ category: string }>(
|
|
460
|
+
'doc-processor',
|
|
461
|
+
{ docId: 'doc-1', imageUrl: 'https://example.com/x.png' },
|
|
462
|
+
{
|
|
463
|
+
timeoutMs: 60_000, // default 30s
|
|
464
|
+
idempotencyKey: 'click-abc', // server-side dedup
|
|
465
|
+
signal: ac.signal, // AbortController
|
|
466
|
+
replay: 1000, // default; covers the race window
|
|
467
|
+
onRunStarted: (runId) => {}, // optional: surfaces runId before terminal
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
console.log(result.runId, result.output, result.durationMs);
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Errors:
|
|
474
|
+
|
|
475
|
+
| Throws | When |
|
|
476
|
+
|---|---|
|
|
477
|
+
| `ValidationError` | empty/oversized `name` |
|
|
478
|
+
| `AbortError` (DOMException) | `signal` aborts; SDK calls `cancelRun(runId)` server-side |
|
|
479
|
+
| `AgentInvokeTimeoutError` | local `timeoutMs` elapsed; SDK calls `cancelRun(runId)` |
|
|
480
|
+
| `NoRunCreatedError` | server returned empty `runIds` |
|
|
481
|
+
| `RunFailedError` | `system.run.{runId}.failed` |
|
|
482
|
+
| `RunCancelledError` | `system.run.{runId}.cancelled` |
|
|
483
|
+
|
|
484
|
+
### `agents.subscribe(runId, callbacks)`
|
|
485
|
+
|
|
486
|
+
Typed wrapper over the broader `subscribe(pattern)` API. Dispatches by topic.
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
const sub = await ironflow.agents.subscribe(runId, {
|
|
490
|
+
onProgress: (e) => console.log('progress', e.topic, e.status),
|
|
491
|
+
onStep: (e) => console.log('step', e.stepId, e.type),
|
|
492
|
+
onComplete: (r) => console.log('done', r.output),
|
|
493
|
+
onFailed: (err) => console.warn('failed', err.message),
|
|
494
|
+
onCancelled: () => console.warn('cancelled'),
|
|
495
|
+
onError: (err) => console.error('transport', err),
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Unsubscribe is idempotent.
|
|
499
|
+
sub.unsubscribe();
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### `agents.readMemory(projection, opts?)`
|
|
503
|
+
|
|
504
|
+
Typed read of an agent memory projection. Optional read-your-writes via `minSeq` from a prior `streams.append`.
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
interface DocMemory {
|
|
508
|
+
docs: Record<string, { status: 'ocr' | 'classified' | 'published'; category?: string }>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Read current state — eventual consistency.
|
|
512
|
+
const mem = await ironflow.agents.readMemory<DocMemory>('doc-processor-memory');
|
|
513
|
+
console.log(mem.state.docs, mem.version);
|
|
514
|
+
|
|
515
|
+
// Read-your-writes: pass the seq returned by a prior append so the
|
|
516
|
+
// projection has caught up before the read.
|
|
517
|
+
const { sequence } = await ironflow.streams.append('agent-memory:doc-1', {
|
|
518
|
+
name: 'DocProcessed',
|
|
519
|
+
data: { docId: 'doc-1', status: 'classified' },
|
|
520
|
+
});
|
|
521
|
+
const fresh = await ironflow.agents.readMemory<DocMemory>('doc-processor-memory', {
|
|
522
|
+
minSeq: sequence,
|
|
523
|
+
timeoutMs: 5_000,
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Throws `MemoryCatchupTimeoutError` if the projection cannot catch up to `minSeq` within `timeoutMs`. Throws `AbortError` on caller cancellation.
|
|
528
|
+
|
|
529
|
+
### React example
|
|
530
|
+
|
|
531
|
+
A complete browser-driven demo lives at `examples/agents/doc-processor-agent/web/`. It exercises `agents.invoke` + `agents.subscribe` against the doc-processor agent's crash-resume flow, and `agents.readMemory` to render per-doc state.
|
|
532
|
+
|
|
533
|
+
### Server compatibility
|
|
534
|
+
|
|
535
|
+
Requires Ironflow server with `waitForProjectionCatchup` (#473) and the unified Trigger path. Any server built from `main` after #608 (Lane D) supports the full surface.
|
|
536
|
+
|
|
537
|
+
### Stuck or hanging?
|
|
538
|
+
|
|
539
|
+
See `docs/runbooks/runbook-browser-agent-stuck.md` for triage.
|
|
540
|
+
|
|
445
541
|
## Entity Streams (Event Sourcing)
|
|
446
542
|
|
|
447
543
|
Entity streams store domain events per entity with optimistic concurrency control.
|
|
@@ -540,11 +636,15 @@ const result = await ironflow.getProjection<{ totalOrders: number }>('order-stat
|
|
|
540
636
|
|
|
541
637
|
console.log(result.name); // 'order-stats'
|
|
542
638
|
console.log(result.state); // { totalOrders: 42 }
|
|
543
|
-
console.log(result.partition);
|
|
639
|
+
console.log(result.partition); // '__global__' or partition key
|
|
544
640
|
console.log(result.lastEventId); // Last processed event ID
|
|
545
|
-
console.log(result.lastEventTime); // Date
|
|
641
|
+
console.log(result.lastEventTime); // Date | undefined (undefined before first event)
|
|
642
|
+
console.log(result.lastEventSeq); // Last processed sequence number
|
|
546
643
|
console.log(result.version); // Projection version
|
|
547
644
|
console.log(result.mode); // 'managed' | 'external'
|
|
645
|
+
console.log(result.status); // 'active' | 'rebuilding' | 'paused' | 'error'
|
|
646
|
+
console.log(result.errorMessage); // Error string when status is 'error', else undefined
|
|
647
|
+
console.log(result.updatedAt); // Date
|
|
548
648
|
|
|
549
649
|
// Get partitioned projection state
|
|
550
650
|
const result = await ironflow.getProjection('order-stats', {
|
|
@@ -736,16 +836,18 @@ await config.delete('app-settings');
|
|
|
736
836
|
// all reach the subscriber. Payload includes `revision`; drop events whose
|
|
737
837
|
// revision is lower than the last one you applied to guard against rare
|
|
738
838
|
// out-of-order deliveries under retry.
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
839
|
+
const watcher = await config.watch('app-settings', {
|
|
840
|
+
onUpdate: (event) => {
|
|
841
|
+
// event: ConfigWatchEvent ({ type: "config_update", name, data, revision, updatedAt })
|
|
842
|
+
console.log('Config updated:', event.data, event.revision);
|
|
742
843
|
},
|
|
743
844
|
onError: (error) => {
|
|
744
845
|
console.error('Watch error:', error);
|
|
745
846
|
},
|
|
746
847
|
});
|
|
747
848
|
|
|
748
|
-
|
|
849
|
+
// `watcher` is a Subscription — call unsubscribe() to stop watching.
|
|
850
|
+
watcher.unsubscribe();
|
|
749
851
|
```
|
|
750
852
|
|
|
751
853
|
When the tab is backgrounded long enough that the browser silently kills the
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents` namespace.
|
|
3
|
+
*
|
|
4
|
+
* Browser-facing helpers for `agent()` functions: fire-and-wait `invoke`,
|
|
5
|
+
* typed `subscribe` for run/step events, and `readMemory` for typed
|
|
6
|
+
* projection state reads.
|
|
7
|
+
*
|
|
8
|
+
* Spec: ./spec.md
|
|
9
|
+
*/
|
|
10
|
+
import type { Subscription } from "@ironflow/core";
|
|
11
|
+
import type { AgentClientLike, AgentInvokeOptions, AgentInvokeResult, AgentMemoryResult, AgentReadMemoryOptions, AgentSubscribeCallbacks } from "./types.js";
|
|
12
|
+
export type { AgentClientLike, AgentInvokeOptions, AgentInvokeResult, AgentMemoryResult, AgentProgressEvent, AgentReadMemoryOptions, AgentStepEvent, AgentSubscribeCallbacks, } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* The shape exposed as `ironflow.agents`.
|
|
15
|
+
*/
|
|
16
|
+
export interface AgentSubscribeRuntimeOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Number of historical events to replay on attach. Default: 1000.
|
|
19
|
+
* Covers events emitted between `agents.invoke()` returning a runId
|
|
20
|
+
* via `onRunStarted` and this subscribe attaching.
|
|
21
|
+
*/
|
|
22
|
+
replay?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface AgentsNamespace {
|
|
25
|
+
invoke<TOutput = unknown>(name: string, payload: unknown, opts?: AgentInvokeOptions): Promise<AgentInvokeResult<TOutput>>;
|
|
26
|
+
subscribe(runId: string, callbacks: AgentSubscribeCallbacks, opts?: AgentSubscribeRuntimeOptions): Promise<Subscription>;
|
|
27
|
+
readMemory<TState = unknown>(projection: string, opts?: AgentReadMemoryOptions): Promise<AgentMemoryResult<TState>>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the `agents` namespace bound to a specific client. Called by
|
|
31
|
+
* the IronflowClient constructor.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createAgentsNamespace(client: AgentClientLike): AgentsNamespace;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,cAAc,EACd,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,OAAO,GAAG,OAAO,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IAEvC,SAAS,CACP,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,uBAAuB,EAClC,IAAI,CAAC,EAAE,4BAA4B,GAClC,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB,UAAU,CAAC,MAAM,GAAG,OAAO,EACzB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,sBAAsB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,GACtB,eAAe,CAOjB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents` namespace.
|
|
3
|
+
*
|
|
4
|
+
* Browser-facing helpers for `agent()` functions: fire-and-wait `invoke`,
|
|
5
|
+
* typed `subscribe` for run/step events, and `readMemory` for typed
|
|
6
|
+
* projection state reads.
|
|
7
|
+
*
|
|
8
|
+
* Spec: ./spec.md
|
|
9
|
+
*/
|
|
10
|
+
import { invoke as agentInvoke } from "./invoke.js";
|
|
11
|
+
import { readMemory as agentReadMemory } from "./readMemory.js";
|
|
12
|
+
import { subscribe as agentSubscribe } from "./subscribe.js";
|
|
13
|
+
/**
|
|
14
|
+
* Build the `agents` namespace bound to a specific client. Called by
|
|
15
|
+
* the IronflowClient constructor.
|
|
16
|
+
*/
|
|
17
|
+
export function createAgentsNamespace(client) {
|
|
18
|
+
return {
|
|
19
|
+
invoke: (name, payload, opts) => agentInvoke(client, name, payload, opts),
|
|
20
|
+
subscribe: (runId, callbacks, opts) => agentSubscribe(client, runId, callbacks, opts),
|
|
21
|
+
readMemory: (projection, opts) => agentReadMemory(client, projection, opts),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoD7D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAuB;IAEvB,OAAO;QACL,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;QACzE,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CACpC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC;QAChD,UAAU,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC;KAC5E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents.invoke()` — fire-and-wait against an agent function.
|
|
3
|
+
*
|
|
4
|
+
* Composes the existing async `client.invoke()` + `client.subscribe()` to
|
|
5
|
+
* deliver a single Promise<{runId, output, durationMs}>.
|
|
6
|
+
*
|
|
7
|
+
* Race window between Trigger return and subscribe attach is covered by
|
|
8
|
+
* `subscribe({replay})` — see ./spec.md.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentClientLike, AgentInvokeOptions, AgentInvokeResult } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Fire-and-wait: trigger the named agent function, subscribe to its run
|
|
13
|
+
* events, and resolve when a terminal event arrives.
|
|
14
|
+
*/
|
|
15
|
+
export declare function invoke<TOutput = unknown>(client: AgentClientLike, name: string, payload: unknown, opts?: AgentInvokeOptions): Promise<AgentInvokeResult<TOutput>>;
|
|
16
|
+
//# sourceMappingURL=invoke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAsEpB;;;GAGG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAC5C,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAiKrC"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents.invoke()` — fire-and-wait against an agent function.
|
|
3
|
+
*
|
|
4
|
+
* Composes the existing async `client.invoke()` + `client.subscribe()` to
|
|
5
|
+
* deliver a single Promise<{runId, output, durationMs}>.
|
|
6
|
+
*
|
|
7
|
+
* Race window between Trigger return and subscribe attach is covered by
|
|
8
|
+
* `subscribe({replay})` — see ./spec.md.
|
|
9
|
+
*/
|
|
10
|
+
import { AgentInvokeTimeoutError, NoRunCreatedError, RunCancelledError, RunFailedError, ValidationError, } from "@ironflow/core";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
12
|
+
// Match the server-side `ReplayMaxEvents` default (1000). Replay-from-zero
|
|
13
|
+
// against a per-run subject is bounded by the run's own emission rate, so
|
|
14
|
+
// 1000 covers ~500 steps (each emits `created`+`completed`) plus run
|
|
15
|
+
// lifecycle events. Step-heavier agents should raise both this option and
|
|
16
|
+
// the server's `ReplayMaxEvents` config.
|
|
17
|
+
const DEFAULT_REPLAY = 1000;
|
|
18
|
+
const MAX_NAME_LENGTH = 256;
|
|
19
|
+
// Defense-in-depth: even though the server is the source of `runId`, we
|
|
20
|
+
// guard against a misbehaving server or mock returning NATS metacharacters
|
|
21
|
+
// (`*`, `>`, `.`) that would widen the subscribe pattern.
|
|
22
|
+
const RUN_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
23
|
+
/**
|
|
24
|
+
* Validate the function name argument. Server validates the rest; we just
|
|
25
|
+
* catch the obviously broken cases client-side to fail fast.
|
|
26
|
+
*/
|
|
27
|
+
function validateName(name) {
|
|
28
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
29
|
+
throw new ValidationError("agents.invoke: name must be a non-empty string");
|
|
30
|
+
}
|
|
31
|
+
if (name.length > MAX_NAME_LENGTH) {
|
|
32
|
+
throw new ValidationError(`agents.invoke: name exceeds ${MAX_NAME_LENGTH} chars`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract terminal-event verdict from a topic suffix. Returns null for
|
|
37
|
+
* non-terminal events.
|
|
38
|
+
*
|
|
39
|
+
* Topics: `system.run.{runId}.{event}` where event ∈
|
|
40
|
+
* { created, updated, resumed, completed, failed, cancelled }.
|
|
41
|
+
* Step events live under `.step.{stepId}.{type}`.
|
|
42
|
+
*/
|
|
43
|
+
function classifyTerminal(topic) {
|
|
44
|
+
if (topic.includes(".step."))
|
|
45
|
+
return null;
|
|
46
|
+
if (topic.endsWith(".completed"))
|
|
47
|
+
return "completed";
|
|
48
|
+
if (topic.endsWith(".failed"))
|
|
49
|
+
return "failed";
|
|
50
|
+
if (topic.endsWith(".cancelled"))
|
|
51
|
+
return "cancelled";
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function extractError(data) {
|
|
55
|
+
if (data && typeof data === "object" && "error" in data) {
|
|
56
|
+
const err = data.error;
|
|
57
|
+
if (typeof err === "string") {
|
|
58
|
+
return { message: err };
|
|
59
|
+
}
|
|
60
|
+
if (err && typeof err === "object") {
|
|
61
|
+
return {
|
|
62
|
+
message: err.message ?? "Run failed",
|
|
63
|
+
code: err.code,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { message: "Run failed" };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Fire-and-wait: trigger the named agent function, subscribe to its run
|
|
71
|
+
* events, and resolve when a terminal event arrives.
|
|
72
|
+
*/
|
|
73
|
+
export async function invoke(client, name, payload, opts = {}) {
|
|
74
|
+
validateName(name);
|
|
75
|
+
// Pre-flight abort: throw before any network I/O.
|
|
76
|
+
if (opts.signal?.aborted) {
|
|
77
|
+
throw new DOMException("Aborted", "AbortError");
|
|
78
|
+
}
|
|
79
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
const replay = opts.replay ?? DEFAULT_REPLAY;
|
|
81
|
+
const startedAt = Date.now();
|
|
82
|
+
// 1) Trigger the agent. Server returns runId.
|
|
83
|
+
const triggerResult = await client.invoke(name, {
|
|
84
|
+
data: payload,
|
|
85
|
+
idempotencyKey: opts.idempotencyKey,
|
|
86
|
+
});
|
|
87
|
+
const runId = triggerResult.runIds?.[0];
|
|
88
|
+
if (!runId) {
|
|
89
|
+
throw new NoRunCreatedError(name);
|
|
90
|
+
}
|
|
91
|
+
// Defense-in-depth: server is the source of truth, but reject obviously
|
|
92
|
+
// malformed runIds before interpolating them into a NATS subject.
|
|
93
|
+
if (!RUN_ID_PATTERN.test(runId)) {
|
|
94
|
+
throw new ValidationError(`agents.invoke: server returned invalid runId "${runId}" (must match ${RUN_ID_PATTERN})`);
|
|
95
|
+
}
|
|
96
|
+
// Single deferred. resolve/reject from subscribe callback, timeout, or abort.
|
|
97
|
+
let settled = false;
|
|
98
|
+
let resolveOuter = () => { };
|
|
99
|
+
let rejectOuter = () => { };
|
|
100
|
+
const settle = (fn) => {
|
|
101
|
+
if (settled)
|
|
102
|
+
return;
|
|
103
|
+
settled = true;
|
|
104
|
+
fn();
|
|
105
|
+
};
|
|
106
|
+
const outer = new Promise((res, rej) => {
|
|
107
|
+
resolveOuter = res;
|
|
108
|
+
rejectOuter = rej;
|
|
109
|
+
});
|
|
110
|
+
// Attach a no-op handler eagerly so a fast settle() (e.g., synchronous
|
|
111
|
+
// timer or pre-subscribe abort) does not surface as an unhandled
|
|
112
|
+
// rejection before the awaiter below attaches. The original `outer`
|
|
113
|
+
// promise is unchanged and will still reject through `await outer`.
|
|
114
|
+
outer.catch(() => {
|
|
115
|
+
/* observed; real consumer is `await outer` below */
|
|
116
|
+
});
|
|
117
|
+
// Arm timeout + abort BEFORE running the user-provided onRunStarted hook
|
|
118
|
+
// so a hanging hook cannot bypass `timeoutMs` / `signal`.
|
|
119
|
+
const timeoutHandle = setTimeout(() => {
|
|
120
|
+
settle(() => rejectOuter(new AgentInvokeTimeoutError(runId, timeoutMs)));
|
|
121
|
+
}, timeoutMs);
|
|
122
|
+
const abortHandler = () => {
|
|
123
|
+
settle(() => rejectOuter(new DOMException("Aborted", "AbortError")));
|
|
124
|
+
};
|
|
125
|
+
opts.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
126
|
+
// Surface runId immediately so callers can attach a separate progress
|
|
127
|
+
// subscription via agents.subscribe(runId) without waiting for terminal.
|
|
128
|
+
// We await onRunStarted so the caller's async hook (e.g., attaching a
|
|
129
|
+
// watcher) completes before the SDK starts dispatching terminal events,
|
|
130
|
+
// preventing late events from leaking into a subsequent run's UI. The
|
|
131
|
+
// hook is bounded by the timeout/abort armed above.
|
|
132
|
+
if (opts.onRunStarted && !settled) {
|
|
133
|
+
try {
|
|
134
|
+
await opts.onRunStarted(runId);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
/* swallow — caller's bug is not ours */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Subscribe and wire dispatch. Race the attach against the deferred so
|
|
141
|
+
// a hung subscribe call cannot wedge the invoke past `timeoutMs`/abort.
|
|
142
|
+
let unsubscribe;
|
|
143
|
+
const subscribePromise = client
|
|
144
|
+
.subscribe(`system.run.${runId}.>`, {
|
|
145
|
+
replay,
|
|
146
|
+
onEvent: (event) => {
|
|
147
|
+
if (settled)
|
|
148
|
+
return;
|
|
149
|
+
const verdict = classifyTerminal(event.topic);
|
|
150
|
+
if (!verdict)
|
|
151
|
+
return;
|
|
152
|
+
if (verdict === "completed") {
|
|
153
|
+
const output = event.data?.output;
|
|
154
|
+
settle(() => resolveOuter({
|
|
155
|
+
runId,
|
|
156
|
+
output,
|
|
157
|
+
durationMs: Date.now() - startedAt,
|
|
158
|
+
}));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (verdict === "failed") {
|
|
162
|
+
const err = extractError(event.data);
|
|
163
|
+
settle(() => rejectOuter(new RunFailedError(runId, event.data, err.message)));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
settle(() => rejectOuter(new RunCancelledError(runId)));
|
|
167
|
+
},
|
|
168
|
+
onError: (subErr) => {
|
|
169
|
+
settle(() => rejectOuter(new Error(`agents.invoke subscription error: ${subErr.message}`)));
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
.then((sub) => {
|
|
173
|
+
unsubscribe = () => {
|
|
174
|
+
try {
|
|
175
|
+
sub.unsubscribe();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* idempotent */
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Race already settled before subscribe attached: clean up now.
|
|
182
|
+
if (settled)
|
|
183
|
+
unsubscribe();
|
|
184
|
+
})
|
|
185
|
+
.catch((err) => {
|
|
186
|
+
settle(() => rejectOuter(err));
|
|
187
|
+
});
|
|
188
|
+
// Don't block on subscribePromise; the outer race continues to fire on
|
|
189
|
+
// timeout/abort even if the attach never resolves.
|
|
190
|
+
void subscribePromise;
|
|
191
|
+
try {
|
|
192
|
+
return await outer;
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
// Property check on `name` instead of `instanceof DOMException` for
|
|
196
|
+
// cross-runtime compatibility (some test runners and older Node
|
|
197
|
+
// builds don't share a DOMException prototype).
|
|
198
|
+
const isAbort = typeof err === "object" &&
|
|
199
|
+
err !== null &&
|
|
200
|
+
"name" in err &&
|
|
201
|
+
err.name === "AbortError";
|
|
202
|
+
if (err instanceof AgentInvokeTimeoutError || isAbort) {
|
|
203
|
+
// Fire-and-forget: do NOT await the cancel. A hanging cancelRun
|
|
204
|
+
// would block propagation of the original timeout/abort error to
|
|
205
|
+
// the caller. The unhandled-rejection guard preserves error
|
|
206
|
+
// observability without blocking.
|
|
207
|
+
void client
|
|
208
|
+
.cancelRun(runId, "agents.invoke aborted")
|
|
209
|
+
.catch(() => {
|
|
210
|
+
/* swallow — best-effort */
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
clearTimeout(timeoutHandle);
|
|
217
|
+
opts.signal?.removeEventListener("abort", abortHandler);
|
|
218
|
+
if (unsubscribe)
|
|
219
|
+
unsubscribe();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=invoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoke.js","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents.readMemory()` — typed read of an agent memory
|
|
3
|
+
* projection with optional read-your-writes catchup.
|
|
4
|
+
*
|
|
5
|
+
* Composes `client.waitForProjectionCatchup` (when `opts.minSeq` is set)
|
|
6
|
+
* + `client.getProjection`. See ./spec.md.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentClientLike, AgentMemoryResult, AgentReadMemoryOptions } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Read materialized projection state for an agent memory.
|
|
11
|
+
*
|
|
12
|
+
* When `opts.minSeq` is provided, waits for the projection to catch up
|
|
13
|
+
* before reading. This delivers read-your-writes semantics for callers
|
|
14
|
+
* that just appended an event (use the seq returned from
|
|
15
|
+
* `streams.append`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function readMemory<TState = unknown>(client: AgentClientLike, projection: string, opts?: AgentReadMemoryOptions): Promise<AgentMemoryResult<TState>>;
|
|
18
|
+
//# sourceMappingURL=readMemory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readMemory.d.ts","sourceRoot":"","sources":["../../src/agents/readMemory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAsDpB;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,MAAM,GAAG,OAAO,EAC/C,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CA4CpC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents.readMemory()` — typed read of an agent memory
|
|
3
|
+
* projection with optional read-your-writes catchup.
|
|
4
|
+
*
|
|
5
|
+
* Composes `client.waitForProjectionCatchup` (when `opts.minSeq` is set)
|
|
6
|
+
* + `client.getProjection`. See ./spec.md.
|
|
7
|
+
*/
|
|
8
|
+
import { MemoryCatchupTimeoutError, ValidationError, } from "@ironflow/core";
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
10
|
+
const MAX_NAME_LENGTH = 256;
|
|
11
|
+
function validateProjection(name) {
|
|
12
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
13
|
+
throw new ValidationError("agents.readMemory: projection must be a non-empty string");
|
|
14
|
+
}
|
|
15
|
+
if (name.length > MAX_NAME_LENGTH) {
|
|
16
|
+
throw new ValidationError(`agents.readMemory: projection exceeds ${MAX_NAME_LENGTH} chars`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function throwIfAborted(signal) {
|
|
20
|
+
if (signal?.aborted) {
|
|
21
|
+
throw new DOMException("Aborted", "AbortError");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Race a Promise against a caller-supplied AbortSignal. Resolves with
|
|
26
|
+
* the original promise unless the signal aborts first, in which case
|
|
27
|
+
* an AbortError is thrown. The original promise is left to settle in
|
|
28
|
+
* the background — its result is discarded.
|
|
29
|
+
*/
|
|
30
|
+
function raceAbort(p, signal) {
|
|
31
|
+
if (!signal)
|
|
32
|
+
return p;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
36
|
+
};
|
|
37
|
+
if (signal.aborted) {
|
|
38
|
+
onAbort();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
42
|
+
p.then((v) => {
|
|
43
|
+
signal.removeEventListener("abort", onAbort);
|
|
44
|
+
resolve(v);
|
|
45
|
+
}, (err) => {
|
|
46
|
+
signal.removeEventListener("abort", onAbort);
|
|
47
|
+
reject(err);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read materialized projection state for an agent memory.
|
|
53
|
+
*
|
|
54
|
+
* When `opts.minSeq` is provided, waits for the projection to catch up
|
|
55
|
+
* before reading. This delivers read-your-writes semantics for callers
|
|
56
|
+
* that just appended an event (use the seq returned from
|
|
57
|
+
* `streams.append`).
|
|
58
|
+
*/
|
|
59
|
+
export async function readMemory(client, projection, opts = {}) {
|
|
60
|
+
validateProjection(projection);
|
|
61
|
+
throwIfAborted(opts.signal);
|
|
62
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
63
|
+
// Treat `minSeq=0` / `0n` as "no catchup required" per the spec —
|
|
64
|
+
// saves a no-op round trip when callers default the param.
|
|
65
|
+
const minSeqProvided = opts.minSeq !== undefined && opts.minSeq !== 0 && opts.minSeq !== 0n;
|
|
66
|
+
if (minSeqProvided) {
|
|
67
|
+
const waitResult = await raceAbort(client.waitForProjectionCatchup(projection, {
|
|
68
|
+
minSeq: opts.minSeq,
|
|
69
|
+
timeoutMs,
|
|
70
|
+
partition: opts.partition,
|
|
71
|
+
}), opts.signal);
|
|
72
|
+
if (waitResult.timedOut) {
|
|
73
|
+
throw new MemoryCatchupTimeoutError(projection, BigInt(opts.minSeq), timeoutMs);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
throwIfAborted(opts.signal);
|
|
77
|
+
const stateResult = await raceAbort(client.getProjection(projection, {
|
|
78
|
+
partition: opts.partition,
|
|
79
|
+
}), opts.signal);
|
|
80
|
+
return {
|
|
81
|
+
state: stateResult.state,
|
|
82
|
+
version: stateResult.version,
|
|
83
|
+
lastEventId: stateResult.lastEventId || undefined,
|
|
84
|
+
caughtUp: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=readMemory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readMemory.js","sourceRoot":"","sources":["../../src/agents/readMemory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ironflow.agents.subscribe()` — typed wrapper over `client.subscribe()`
|
|
3
|
+
* for an agent run's event stream.
|
|
4
|
+
*
|
|
5
|
+
* Spec: see ./spec.md
|
|
6
|
+
*/
|
|
7
|
+
import { type Subscription } from "@ironflow/core";
|
|
8
|
+
import type { AgentClientLike, AgentSubscribeCallbacks } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Promise-returning wrapper. Returns a Subscription whose `unsubscribe()`
|
|
11
|
+
* is idempotent.
|
|
12
|
+
*
|
|
13
|
+
* Topic dispatch:
|
|
14
|
+
* system.run.{runId}.completed → onComplete
|
|
15
|
+
* system.run.{runId}.failed → onFailed
|
|
16
|
+
* system.run.{runId}.cancelled → onCancelled
|
|
17
|
+
* system.run.{runId}.{created|updated|resumed} → onProgress
|
|
18
|
+
* system.run.{runId}.step.{stepId}.{type} → onStep
|
|
19
|
+
*/
|
|
20
|
+
export declare function subscribe(client: AgentClientLike, runId: string, callbacks: AgentSubscribeCallbacks, opts?: {
|
|
21
|
+
replay?: number;
|
|
22
|
+
}): Promise<Subscription>;
|
|
23
|
+
//# sourceMappingURL=subscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/agents/subscribe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EACV,eAAe,EAGf,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAiCpB;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,uBAAuB,EAClC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC,YAAY,CAAC,CAiCvB"}
|