@ironflow/browser 0.33.1 → 0.35.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 +36 -19
- package/dist/agents/index.d.ts +3 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/invoke.d.ts +19 -6
- package/dist/agents/invoke.d.ts.map +1 -1
- package/dist/agents/invoke.js +50 -181
- package/dist/agents/invoke.js.map +1 -1
- package/dist/agents/types.d.ts +24 -14
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/client.d.ts +53 -14
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +113 -66
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/offline.d.ts +10 -0
- package/dist/offline.d.ts.map +1 -1
- package/dist/offline.js +11 -1
- package/dist/offline.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -369,10 +369,16 @@ console.log(published.eventId, published.sequence);
|
|
|
369
369
|
### Emit and Wait, Batch Emit
|
|
370
370
|
|
|
371
371
|
```typescript
|
|
372
|
-
// Wait for the
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
372
|
+
// Wait for EVERY run the event triggers. Returns EmitSyncResult[] -- an event
|
|
373
|
+
// that matches nothing returns []. Run outcomes are never thrown: inspect
|
|
374
|
+
// status, error and waitTimedOut per element. A wait timeout leaves the run active.
|
|
375
|
+
const results = await ironflow.emitSync('order.placed', { orderId: '123' }, {
|
|
376
|
+
timeout: 30000,
|
|
377
|
+
idempotencyKey: 'order-123-placed',
|
|
378
|
+
});
|
|
379
|
+
for (const r of results) {
|
|
380
|
+
console.log(r.runId, r.functionId, r.status, r.output, r.waitTimedOut);
|
|
381
|
+
}
|
|
376
382
|
|
|
377
383
|
// One round trip, one EmitResult per event, in order
|
|
378
384
|
const results = await ironflow.triggerBatch([
|
|
@@ -558,18 +564,28 @@ which mode you are in; results report `queued: false, pending: false`.
|
|
|
558
564
|
|
|
559
565
|
### Invoke a Workflow Function
|
|
560
566
|
|
|
567
|
+
`invoke()` is keyed by **function ID** and blocks for that one run's result over
|
|
568
|
+
`InvokeFunctionSync`. Because there is exactly one run, it throws
|
|
569
|
+
`RunFailedError`, `RunCancelledError` and `RunWaitTimeoutError` on the outcome --
|
|
570
|
+
the deliberate counterpart to `emitSync()`, which never does (ADR 0067).
|
|
571
|
+
|
|
561
572
|
```typescript
|
|
562
573
|
import { ironflow } from '@ironflow/browser';
|
|
563
574
|
|
|
564
|
-
// Invoke with typed input
|
|
575
|
+
// Invoke one function by ID, with typed input
|
|
565
576
|
const result = await ironflow.invoke<{ orderId: string }>('process-order', {
|
|
566
577
|
data: { orderId: '123' },
|
|
578
|
+
timeout: 30000, // server-side wait budget, not a transport deadline
|
|
579
|
+
idempotencyKey: 'order-123',
|
|
567
580
|
});
|
|
568
581
|
|
|
569
|
-
console.log(result.
|
|
570
|
-
console.log(result.eventId); // Event ID that triggered the run
|
|
582
|
+
console.log(result.runId, result.status, result.output, result.durationMs);
|
|
571
583
|
```
|
|
572
584
|
|
|
585
|
+
Aborting the request cancels the run server-side: `InvokeFunctionSync` ties the
|
|
586
|
+
run's lifetime to the request context. To start a function *by event* without
|
|
587
|
+
waiting, use `emit()`, which returns `{ runIds, eventId }`.
|
|
588
|
+
|
|
573
589
|
### Get Run Status
|
|
574
590
|
|
|
575
591
|
```typescript
|
|
@@ -707,7 +723,7 @@ Spec: `src/agents/spec.md`. Issue #625.
|
|
|
707
723
|
|
|
708
724
|
### `agents.invoke(name, payload, opts?)`
|
|
709
725
|
|
|
710
|
-
Fire-and-wait.
|
|
726
|
+
Fire-and-wait. One `InvokeFunctionSync` call: the server creates exactly one run, waits for it, and returns its outcome. There is no subscribe and no replay window (ADR 0067).
|
|
711
727
|
|
|
712
728
|
```typescript
|
|
713
729
|
import { ironflow } from '@ironflow/browser';
|
|
@@ -718,9 +734,11 @@ const result = await ironflow.agents.invoke<{ category: string }>(
|
|
|
718
734
|
{
|
|
719
735
|
timeoutMs: 60_000, // default 30s
|
|
720
736
|
idempotencyKey: 'click-abc', // server-side dedup
|
|
721
|
-
signal: ac.signal, // AbortController
|
|
722
|
-
replay:
|
|
723
|
-
onRunStarted:
|
|
737
|
+
signal: ac.signal, // AbortController; the server cancels the run
|
|
738
|
+
// replay: DEPRECATED, unread -- there is no subscription to replay
|
|
739
|
+
// onRunStarted: DEPRECATED -- now fires AFTER the run settles, not before
|
|
740
|
+
// the wait. Use agents.subscribe(runId, { replay }) for
|
|
741
|
+
// live progress.
|
|
724
742
|
}
|
|
725
743
|
);
|
|
726
744
|
console.log(result.runId, result.output, result.durationMs);
|
|
@@ -731,11 +749,10 @@ Errors:
|
|
|
731
749
|
| Throws | When |
|
|
732
750
|
|---|---|
|
|
733
751
|
| `ValidationError` | empty/oversized `name` |
|
|
734
|
-
| `AbortError` (DOMException) | `signal` aborts; SDK
|
|
735
|
-
| `AgentInvokeTimeoutError` |
|
|
736
|
-
| `
|
|
737
|
-
| `
|
|
738
|
-
| `RunCancelledError` | `system.run.{runId}.cancelled` |
|
|
752
|
+
| `AbortError` (DOMException) | `signal` aborts; the request dies and the **server** cancels the run -- the SDK issues no `cancelRun` |
|
|
753
|
+
| `AgentInvokeTimeoutError` | `timeoutMs` elapsed. An expired budget deliberately leaves the run alive, so here the SDK does best-effort `cancelRun(runId)` |
|
|
754
|
+
| `RunFailedError` | the run failed |
|
|
755
|
+
| `RunCancelledError` | the run was cancelled |
|
|
739
756
|
|
|
740
757
|
### `agents.subscribe(runId, callbacks, opts?)`
|
|
741
758
|
|
|
@@ -1612,10 +1629,10 @@ import {
|
|
|
1612
1629
|
TimeoutError, // Request timeouts
|
|
1613
1630
|
ValidationError, // Invalid response or input validation
|
|
1614
1631
|
NotConfiguredError, // Client used before configure() was called
|
|
1632
|
+
RunWaitTimeoutError, // invoke() wait expired; durable run continues
|
|
1615
1633
|
RunFailedError, // agents.invoke: terminal run failure
|
|
1616
1634
|
RunCancelledError, // agents.invoke: terminal run cancellation
|
|
1617
1635
|
AgentInvokeTimeoutError, // agents.invoke: local timeoutMs elapsed
|
|
1618
|
-
NoRunCreatedError, // agents.invoke: server returned no runIds
|
|
1619
1636
|
QueueFullError, // offline queue at 500 writes or 5 MB
|
|
1620
1637
|
} from '@ironflow/browser';
|
|
1621
1638
|
```
|
|
@@ -1653,9 +1670,9 @@ Common error codes returned by the client:
|
|
|
1653
1670
|
|------|-------------|
|
|
1654
1671
|
| `HTTP_4xx` / `HTTP_5xx` | HTTP status-based errors |
|
|
1655
1672
|
| `TIMEOUT` | Request exceeded the configured timeout |
|
|
1673
|
+
| `RUN_WAIT_TIMEOUT` | `invoke()` stopped waiting; the durable run continues. `emitSync()` sets `waitTimedOut` per result instead of throwing |
|
|
1656
1674
|
| `REQUEST_FAILED` | Network or fetch failure |
|
|
1657
1675
|
| `PATCH_FAILED` | Step patch operation failed |
|
|
1658
|
-
| `RESUME_FAILED` | Run resume operation failed |
|
|
1659
1676
|
| `NOT_CONFIGURED` | Client used before `configure()` |
|
|
1660
1677
|
|
|
1661
1678
|
## Browser Compatibility
|
|
@@ -1688,7 +1705,7 @@ The package re-exports the following types from `@ironflow/core` for convenience
|
|
|
1688
1705
|
|
|
1689
1706
|
**Pub/Sub types:** `PublishOptions`, `PublishResult`
|
|
1690
1707
|
|
|
1691
|
-
**Invoke/Trigger types:** `InvokeResult`, `TriggerResult` (deprecated alias), `
|
|
1708
|
+
**Invoke/Trigger types:** `InvokeResult`, `TriggerResult` (deprecated alias), `EmitSyncResult`, `InvokeSyncOptions`, `InvokeSyncResult`
|
|
1692
1709
|
|
|
1693
1710
|
**Subscription types:** `SubscribeOptions`, `Subscription`, `AckableSubscription`, `SubscriptionEvent`, `SubscriptionErrorInfo`, `SubscriptionCallbacks`, `ConnectionState`, `AckHandle`
|
|
1694
1711
|
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export type { AgentClientLike, AgentInvokeOptions, AgentInvokeResult, AgentMemor
|
|
|
16
16
|
export interface AgentSubscribeRuntimeOptions {
|
|
17
17
|
/**
|
|
18
18
|
* Number of historical events to replay on attach. Default: 1000.
|
|
19
|
-
* Covers events emitted
|
|
20
|
-
*
|
|
19
|
+
* Covers events emitted before this subscribe attached — including the
|
|
20
|
+
* whole run when the runId came from a settled `agents.invoke()`, which
|
|
21
|
+
* waits on one synchronous call and surfaces the id only at the end.
|
|
21
22
|
*/
|
|
22
23
|
replay?: number;
|
|
23
24
|
}
|
|
@@ -1 +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
|
|
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;;;;;OAKG;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"}
|
package/dist/agents/index.js.map
CHANGED
|
@@ -1 +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;
|
|
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;AAqD7D;;;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"}
|
package/dist/agents/invoke.d.ts
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ironflow.agents.invoke()` — fire-and-wait against an agent function.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* One `InvokeFunctionSync` call. The server keys on the function id, creates
|
|
5
|
+
* exactly one run, waits for it, and returns its outcome — so there is no
|
|
6
|
+
* Trigger→subscribe→race compose left to get wrong, and no window between the
|
|
7
|
+
* two for an event to slip through.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Two behaviours are load-bearing and must not be "tidied":
|
|
10
|
+
*
|
|
11
|
+
* 1. `timeoutMs` is a wait budget, not a transport deadline. The client sends
|
|
12
|
+
* it as `timeout_ms` and keeps the HTTP deadline longer. Wiring it to a
|
|
13
|
+
* fetch abort would cancel the run on every timeout (see 2) and make the
|
|
14
|
+
* "still running" outcome unobservable.
|
|
15
|
+
* 2. Aborting the HTTP request cancels the run server-side (ADR 0067 / Q19),
|
|
16
|
+
* which is why the abort path no longer calls `cancelRun`. The timeout path
|
|
17
|
+
* still does: an expired budget deliberately leaves the run alive, and an
|
|
18
|
+
* agent run left alive is an agent run still spending money.
|
|
9
19
|
*/
|
|
10
20
|
import type { AgentClientLike, AgentInvokeOptions, AgentInvokeResult } from "./types.js";
|
|
11
21
|
/**
|
|
12
|
-
* Fire-and-wait:
|
|
13
|
-
*
|
|
22
|
+
* Fire-and-wait: invoke the named agent function and resolve with its output.
|
|
23
|
+
*
|
|
24
|
+
* Throws `RunFailedError` / `RunCancelledError` from the client for a failed
|
|
25
|
+
* or cancelled run, `AgentInvokeTimeoutError` when `timeoutMs` expires, and a
|
|
26
|
+
* DOMException `AbortError` when `opts.signal` fires.
|
|
14
27
|
*/
|
|
15
28
|
export declare function invoke<TOutput = unknown>(client: AgentClientLike, name: string, payload: unknown, opts?: AgentInvokeOptions): Promise<AgentInvokeResult<TOutput>>;
|
|
16
29
|
//# sourceMappingURL=invoke.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAoBpB;;;;;;GAMG;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,CAmDrC"}
|
package/dist/agents/invoke.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ironflow.agents.invoke()` — fire-and-wait against an agent function.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* One `InvokeFunctionSync` call. The server keys on the function id, creates
|
|
5
|
+
* exactly one run, waits for it, and returns its outcome — so there is no
|
|
6
|
+
* Trigger→subscribe→race compose left to get wrong, and no window between the
|
|
7
|
+
* two for an event to slip through.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Two behaviours are load-bearing and must not be "tidied":
|
|
10
|
+
*
|
|
11
|
+
* 1. `timeoutMs` is a wait budget, not a transport deadline. The client sends
|
|
12
|
+
* it as `timeout_ms` and keeps the HTTP deadline longer. Wiring it to a
|
|
13
|
+
* fetch abort would cancel the run on every timeout (see 2) and make the
|
|
14
|
+
* "still running" outcome unobservable.
|
|
15
|
+
* 2. Aborting the HTTP request cancels the run server-side (ADR 0067 / Q19),
|
|
16
|
+
* which is why the abort path no longer calls `cancelRun`. The timeout path
|
|
17
|
+
* still does: an expired budget deliberately leaves the run alive, and an
|
|
18
|
+
* agent run left alive is an agent run still spending money.
|
|
9
19
|
*/
|
|
10
|
-
import { AgentInvokeTimeoutError,
|
|
20
|
+
import { AgentInvokeTimeoutError, RunWaitTimeoutError, ValidationError, } from "@ironflow/core";
|
|
11
21
|
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
22
|
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
23
|
/**
|
|
24
24
|
* Validate the function name argument. Server validates the rest; we just
|
|
25
25
|
* catch the obviously broken cases client-side to fail fast.
|
|
@@ -33,42 +33,11 @@ function validateName(name) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* non-terminal events.
|
|
36
|
+
* Fire-and-wait: invoke the named agent function and resolve with its output.
|
|
38
37
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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.
|
|
38
|
+
* Throws `RunFailedError` / `RunCancelledError` from the client for a failed
|
|
39
|
+
* or cancelled run, `AgentInvokeTimeoutError` when `timeoutMs` expires, and a
|
|
40
|
+
* DOMException `AbortError` when `opts.signal` fires.
|
|
72
41
|
*/
|
|
73
42
|
export async function invoke(client, name, payload, opts = {}) {
|
|
74
43
|
validateName(name);
|
|
@@ -77,146 +46,46 @@ export async function invoke(client, name, payload, opts = {}) {
|
|
|
77
46
|
throw new DOMException("Aborted", "AbortError");
|
|
78
47
|
}
|
|
79
48
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
-
const replay = opts.replay ?? DEFAULT_REPLAY;
|
|
81
49
|
const startedAt = Date.now();
|
|
82
|
-
|
|
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;
|
|
50
|
+
let result;
|
|
191
51
|
try {
|
|
192
|
-
|
|
52
|
+
result = await client.invoke(name, {
|
|
53
|
+
data: payload,
|
|
54
|
+
timeout: timeoutMs,
|
|
55
|
+
idempotencyKey: opts.idempotencyKey,
|
|
56
|
+
signal: opts.signal,
|
|
57
|
+
});
|
|
193
58
|
}
|
|
194
59
|
catch (err) {
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
err.
|
|
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(() => {
|
|
60
|
+
// The wait budget expired with the run still active. Translate to the
|
|
61
|
+
// agents-level error the public contract promises, and best-effort cancel:
|
|
62
|
+
// unlike an abort, an expired budget does NOT cancel the run server-side,
|
|
63
|
+
// so skipping this would leave a zombie agent running.
|
|
64
|
+
if (err instanceof RunWaitTimeoutError) {
|
|
65
|
+
// Fire-and-forget: a hanging cancelRun must not delay the timeout error.
|
|
66
|
+
void client.cancelRun(err.runId, "agents.invoke timed out").catch(() => {
|
|
210
67
|
/* swallow — best-effort */
|
|
211
68
|
});
|
|
69
|
+
throw new AgentInvokeTimeoutError(err.runId, timeoutMs);
|
|
212
70
|
}
|
|
213
71
|
throw err;
|
|
214
72
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
73
|
+
// Surface the runId to the caller's hook. It settles after the run rather
|
|
74
|
+
// than before the wait now — see the deprecation note on the option.
|
|
75
|
+
if (opts.onRunStarted) {
|
|
76
|
+
try {
|
|
77
|
+
await opts.onRunStarted(result.runId);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* swallow — caller's bug is not ours */
|
|
81
|
+
}
|
|
220
82
|
}
|
|
83
|
+
return {
|
|
84
|
+
runId: result.runId,
|
|
85
|
+
output: result.output,
|
|
86
|
+
// Client wall-clock, unchanged from the compose version. `result.durationMs`
|
|
87
|
+
// measures the run alone and would silently redefine this field.
|
|
88
|
+
durationMs: Date.now() - startedAt,
|
|
89
|
+
};
|
|
221
90
|
}
|
|
222
91
|
//# sourceMappingURL=invoke.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.js","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"invoke.js","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAQxB,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,eAAe,CAAC,gDAAgD,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QAClC,MAAM,IAAI,eAAe,CACvB,+BAA+B,eAAe,QAAQ,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,MAAuB,EACvB,IAAY,EACZ,OAAgB,EAChB,OAA2B,EAAE;IAE7B,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnB,kDAAkD;IAClD,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,SAAS;YAClB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sEAAsE;QACtE,2EAA2E;QAC3E,0EAA0E;QAC1E,uDAAuD;QACvD,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;YACvC,yEAAyE;YACzE,KAAK,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrE,2BAA2B;YAC7B,CAAC,CAAC,CAAC;YACH,MAAM,IAAI,uBAAuB,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,0EAA0E;IAC1E,qEAAqE;IACrE,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAA6B;QAC5C,6EAA6E;QAC7E,iEAAiE;QACjE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;KACnC,CAAC;AACJ,CAAC"}
|
package/dist/agents/types.d.ts
CHANGED
|
@@ -3,20 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Spec: see ./spec.md
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { InvokeSyncResult, ProjectionStateResult, Run, Subscription, SubscriptionCallbacks, SubscriptionErrorInfo, WaitResult } from "@ironflow/core";
|
|
7
7
|
/**
|
|
8
8
|
* Options for `ironflow.agents.invoke()`.
|
|
9
9
|
*/
|
|
10
10
|
export interface AgentInvokeOptions {
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
12
|
+
* Wait budget in milliseconds, sent to the server as `timeout_ms`. When it
|
|
13
|
+
* expires the server returns with the run still alive; the SDK throws
|
|
14
|
+
* `AgentInvokeTimeoutError` and best-effort calls `cancelRun(runId)`.
|
|
15
|
+
* Default: 30000.
|
|
15
16
|
*/
|
|
16
17
|
timeoutMs?: number;
|
|
17
18
|
/**
|
|
18
|
-
* Caller cancellation.
|
|
19
|
-
*
|
|
19
|
+
* Caller cancellation. Aborts the HTTP request, which the server reads as an
|
|
20
|
+
* abandoned caller and cancels the run for us (ADR 0067 / Q19); the call
|
|
21
|
+
* then throws an `AbortError` (DOMException). No client-side `cancelRun` is
|
|
22
|
+
* issued on this path — the server already did it.
|
|
20
23
|
*/
|
|
21
24
|
signal?: AbortSignal;
|
|
22
25
|
/**
|
|
@@ -25,16 +28,21 @@ export interface AgentInvokeOptions {
|
|
|
25
28
|
*/
|
|
26
29
|
idempotencyKey?: string;
|
|
27
30
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
31
|
+
* @deprecated Unread. `invoke()` no longer subscribes to run events — it
|
|
32
|
+
* waits on a single `InvokeFunctionSync` response — so there is no replay
|
|
33
|
+
* budget to size. Pass `replay` to `agents.subscribe(runId, ...)` instead.
|
|
30
34
|
*/
|
|
31
35
|
replay?: number;
|
|
32
36
|
/**
|
|
33
|
-
* Called once with the runId
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
37
|
+
* Called once with the runId. May return a Promise; the SDK awaits it
|
|
38
|
+
* before resolving.
|
|
39
|
+
*
|
|
40
|
+
* @deprecated Fires AFTER the run has settled, not before the wait. The
|
|
41
|
+
* synchronous RPC returns the runId and the terminal outcome in the same
|
|
42
|
+
* response, so there is no earlier moment at which the id exists on the
|
|
43
|
+
* client. Attaching a live progress subscription from this hook no longer
|
|
44
|
+
* works; use `agents.subscribe(runId, { replay })` after the call, or drive
|
|
45
|
+
* progress from a subscription you open before invoking.
|
|
38
46
|
*/
|
|
39
47
|
onRunStarted?: (runId: string) => void | Promise<void>;
|
|
40
48
|
}
|
|
@@ -135,8 +143,10 @@ export interface AgentMemoryResult<TState = unknown> {
|
|
|
135
143
|
export interface AgentClientLike {
|
|
136
144
|
invoke<T = unknown>(functionId: string, options: {
|
|
137
145
|
data: T;
|
|
146
|
+
timeout?: number;
|
|
138
147
|
idempotencyKey?: string;
|
|
139
|
-
|
|
148
|
+
signal?: AbortSignal;
|
|
149
|
+
}): Promise<InvokeSyncResult>;
|
|
140
150
|
subscribe<T = unknown>(pattern: string | string[], callbacks: SubscriptionCallbacks<T> & {
|
|
141
151
|
replay?: number;
|
|
142
152
|
includeMetadata?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
|