nexus-ai-pro 2.1.0 → 2.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.
- package/API_STABILITY.md +77 -0
- package/CHANGELOG.md +286 -1
- package/README.md +19 -9
- package/dist/agent/capabilities.d.ts +39 -0
- package/dist/agent/capabilities.js +36 -0
- package/dist/agent/create-agent.d.ts +61 -11
- package/dist/agent/create-agent.js +136 -22
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +1 -1
- package/dist/agent/middleware/approval.d.ts +26 -0
- package/dist/agent/middleware/approval.js +23 -0
- package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist/agent/middleware/context-editor.d.ts +52 -0
- package/dist/agent/middleware/context-editor.js +66 -0
- package/dist/agent/middleware/filesystem.d.ts +44 -0
- package/dist/agent/middleware/filesystem.js +83 -0
- package/dist/agent/middleware/index.d.ts +19 -0
- package/dist/agent/middleware/index.js +9 -0
- package/dist/agent/middleware/limits.d.ts +42 -0
- package/dist/agent/middleware/limits.js +52 -0
- package/dist/agent/middleware/models.d.ts +48 -0
- package/dist/agent/middleware/models.js +44 -0
- package/dist/agent/middleware/pii.d.ts +67 -0
- package/dist/agent/middleware/pii.js +173 -0
- package/dist/agent/middleware/retry.d.ts +66 -0
- package/dist/agent/middleware/retry.js +59 -0
- package/dist/agent/middleware/shared.d.ts +41 -0
- package/dist/agent/middleware/shared.js +73 -0
- package/dist/agent/middleware/tool-selector.d.ts +64 -0
- package/dist/agent/middleware/tool-selector.js +106 -0
- package/dist/agent/permissions.d.ts +113 -0
- package/dist/agent/permissions.js +180 -0
- package/dist/agent/sandbox.d.ts +151 -0
- package/dist/agent/sandbox.js +257 -0
- package/dist/agent/tool.d.ts +8 -3
- package/dist/agent/tool.js +14 -3
- package/dist/cache/memory-cache.d.ts +8 -0
- package/dist/cache/memory-cache.js +9 -0
- package/dist/cache/semantic-cache.d.ts +6 -3
- package/dist/cache/semantic-cache.js +5 -2
- package/dist/cli/args.js +3 -0
- package/dist/cli/db.d.ts +16 -4
- package/dist/cli/db.js +169 -22
- package/dist/cli/doctor.d.ts +17 -0
- package/dist/cli/doctor.js +53 -0
- package/dist/cli.js +12 -1
- package/dist/core/lifecycle.js +11 -6
- package/dist/core/nexus.d.ts +6 -0
- package/dist/core/nexus.js +103 -22
- package/dist/deep-agent/index.d.ts +109 -0
- package/dist/deep-agent/index.js +216 -0
- package/dist/doctor/index.d.ts +90 -0
- package/dist/doctor/index.js +237 -0
- package/dist/graph/functional.d.ts +89 -7
- package/dist/graph/functional.js +156 -36
- package/dist/graph/graph.d.ts +15 -5
- package/dist/graph/graph.js +95 -22
- package/dist/graph/lint.d.ts +4 -1
- package/dist/graph/lint.js +35 -0
- package/dist/images/evals.js +4 -2
- package/dist/operations/adapters.d.ts +52 -4
- package/dist/operations/adapters.js +194 -7
- package/dist/operations/runner.js +1 -0
- package/dist/operations/stats.js +8 -1
- package/dist/operations/store.d.ts +5 -1
- package/dist/operations/store.js +7 -0
- package/dist/ops/circuit-breaker.d.ts +52 -0
- package/dist/ops/circuit-breaker.js +4 -0
- package/dist/ops/circuit-store.d.ts +13 -2
- package/dist/ops/circuit-store.js +68 -0
- package/dist/ops/circuit-sync.d.ts +17 -0
- package/dist/ops/circuit-sync.js +86 -0
- package/dist/ops/rate-limiter.d.ts +2 -0
- package/dist/ops/rate-limiter.js +2 -0
- package/dist/postgres/circuits.d.ts +21 -4
- package/dist/postgres/circuits.js +65 -4
- package/dist/postgres/client.d.ts +0 -2
- package/dist/postgres/client.js +0 -4
- package/dist/postgres/evaluate.d.ts +16 -4
- package/dist/postgres/evaluate.js +17 -5
- package/dist/postgres/fulltext.d.ts +67 -0
- package/dist/postgres/fulltext.js +91 -0
- package/dist/postgres/index.d.ts +32 -11
- package/dist/postgres/index.js +65 -31
- package/dist/postgres/migrations.d.ts +60 -0
- package/dist/postgres/migrations.js +121 -0
- package/dist/postgres/operations.d.ts +14 -3
- package/dist/postgres/operations.js +45 -15
- package/dist/postgres/prompts.d.ts +11 -2
- package/dist/postgres/prompts.js +14 -3
- package/dist/postgres/rollups.d.ts +35 -0
- package/dist/postgres/rollups.js +129 -0
- package/dist/postgres/store.d.ts +11 -2
- package/dist/postgres/store.js +14 -3
- package/dist/postgres/traces.d.ts +13 -3
- package/dist/postgres/traces.js +25 -3
- package/dist/postgres/vectors.d.ts +11 -2
- package/dist/postgres/vectors.js +14 -3
- package/dist/rag/elasticsearch.d.ts +71 -0
- package/dist/rag/elasticsearch.js +130 -0
- package/dist/rag/pipeline.d.ts +140 -0
- package/dist/rag/pipeline.js +187 -0
- package/dist/rag/rerankers.d.ts +92 -0
- package/dist/rag/rerankers.js +97 -0
- package/dist/rag/retrievers.d.ts +12 -1
- package/dist/router/auto-router.d.ts +34 -7
- package/dist/router/auto-router.js +201 -68
- package/dist/server/assistant.d.ts +9 -1
- package/dist/server/assistant.js +10 -0
- package/dist/server/auth.d.ts +250 -0
- package/dist/server/auth.js +392 -0
- package/dist/server/errors.d.ts +12 -0
- package/dist/server/errors.js +7 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/runs.d.ts +24 -1
- package/dist/server/runs.js +90 -14
- package/dist/sqlite/index.d.ts +16 -3
- package/dist/sqlite/index.js +16 -3
- package/dist/sqlite/migrations.d.ts +26 -0
- package/dist/sqlite/migrations.js +63 -0
- package/dist/sqlite/operations.d.ts +14 -3
- package/dist/sqlite/operations.js +42 -15
- package/dist/sqlite/store.d.ts +9 -2
- package/dist/sqlite/store.js +13 -3
- package/dist/sqlite/vectors.d.ts +11 -2
- package/dist/sqlite/vectors.js +13 -2
- package/dist/store/memory.d.ts +10 -0
- package/dist/store/memory.js +10 -0
- package/dist/store/tenant.d.ts +12 -0
- package/dist/store/tenant.js +29 -0
- package/dist/tenancy/index.d.ts +113 -0
- package/dist/tenancy/index.js +233 -0
- package/dist/tracing/instrument.js +2 -0
- package/dist/types/config.d.ts +24 -2
- package/dist/types/graph.d.ts +49 -1
- package/dist/types/messages.d.ts +41 -2
- package/dist/types/operations.d.ts +6 -0
- package/dist/types/principal.d.ts +21 -0
- package/dist/types/principal.js +1 -0
- package/dist/types/response.d.ts +20 -0
- package/dist/types/server.d.ts +12 -10
- package/dist/utils/schema-migrations.d.ts +97 -0
- package/dist/utils/schema-migrations.js +91 -0
- package/dist/utils/signals.d.ts +17 -0
- package/dist/utils/signals.js +26 -0
- package/dist/utils/tenant.d.ts +25 -0
- package/dist/utils/tenant.js +30 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/capabilities.d.ts +39 -0
- package/dist-cjs/agent/capabilities.js +41 -0
- package/dist-cjs/agent/create-agent.d.ts +61 -11
- package/dist-cjs/agent/create-agent.js +136 -22
- package/dist-cjs/agent/index.d.ts +3 -2
- package/dist-cjs/agent/index.js +4 -4
- package/dist-cjs/agent/middleware/approval.d.ts +26 -0
- package/dist-cjs/agent/middleware/approval.js +26 -0
- package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
- package/dist-cjs/agent/middleware/context-editor.js +70 -0
- package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
- package/dist-cjs/agent/middleware/filesystem.js +86 -0
- package/dist-cjs/agent/middleware/index.d.ts +19 -0
- package/dist-cjs/agent/middleware/index.js +28 -0
- package/dist-cjs/agent/middleware/limits.d.ts +42 -0
- package/dist-cjs/agent/middleware/limits.js +57 -0
- package/dist-cjs/agent/middleware/models.d.ts +48 -0
- package/dist-cjs/agent/middleware/models.js +48 -0
- package/dist-cjs/agent/middleware/pii.d.ts +67 -0
- package/dist-cjs/agent/middleware/pii.js +178 -0
- package/dist-cjs/agent/middleware/retry.d.ts +66 -0
- package/dist-cjs/agent/middleware/retry.js +63 -0
- package/dist-cjs/agent/middleware/shared.d.ts +41 -0
- package/dist-cjs/agent/middleware/shared.js +83 -0
- package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
- package/dist-cjs/agent/middleware/tool-selector.js +109 -0
- package/dist-cjs/agent/permissions.d.ts +113 -0
- package/dist-cjs/agent/permissions.js +189 -0
- package/dist-cjs/agent/sandbox.d.ts +151 -0
- package/dist-cjs/agent/sandbox.js +297 -0
- package/dist-cjs/agent/tool.d.ts +8 -3
- package/dist-cjs/agent/tool.js +14 -3
- package/dist-cjs/cache/memory-cache.d.ts +8 -0
- package/dist-cjs/cache/memory-cache.js +9 -0
- package/dist-cjs/cache/semantic-cache.d.ts +6 -3
- package/dist-cjs/cache/semantic-cache.js +5 -2
- package/dist-cjs/cli/args.js +3 -0
- package/dist-cjs/cli/db.d.ts +16 -4
- package/dist-cjs/cli/db.js +200 -20
- package/dist-cjs/cli/doctor.d.ts +17 -0
- package/dist-cjs/cli/doctor.js +56 -0
- package/dist-cjs/core/lifecycle.js +11 -6
- package/dist-cjs/core/nexus.d.ts +6 -0
- package/dist-cjs/core/nexus.js +103 -22
- package/dist-cjs/deep-agent/index.d.ts +109 -0
- package/dist-cjs/deep-agent/index.js +219 -0
- package/dist-cjs/doctor/index.d.ts +90 -0
- package/dist-cjs/doctor/index.js +276 -0
- package/dist-cjs/graph/functional.d.ts +89 -7
- package/dist-cjs/graph/functional.js +155 -35
- package/dist-cjs/graph/graph.d.ts +15 -5
- package/dist-cjs/graph/graph.js +95 -22
- package/dist-cjs/graph/lint.d.ts +4 -1
- package/dist-cjs/graph/lint.js +35 -0
- package/dist-cjs/images/evals.js +4 -2
- package/dist-cjs/operations/adapters.d.ts +52 -4
- package/dist-cjs/operations/adapters.js +193 -6
- package/dist-cjs/operations/runner.js +1 -0
- package/dist-cjs/operations/stats.js +8 -1
- package/dist-cjs/operations/store.d.ts +5 -1
- package/dist-cjs/operations/store.js +7 -0
- package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
- package/dist-cjs/ops/circuit-breaker.js +4 -0
- package/dist-cjs/ops/circuit-store.d.ts +13 -2
- package/dist-cjs/ops/circuit-store.js +68 -0
- package/dist-cjs/ops/circuit-sync.d.ts +17 -0
- package/dist-cjs/ops/circuit-sync.js +87 -0
- package/dist-cjs/ops/rate-limiter.d.ts +2 -0
- package/dist-cjs/ops/rate-limiter.js +2 -0
- package/dist-cjs/postgres/circuits.d.ts +21 -4
- package/dist-cjs/postgres/circuits.js +98 -3
- package/dist-cjs/postgres/client.d.ts +0 -2
- package/dist-cjs/postgres/client.js +0 -5
- package/dist-cjs/postgres/evaluate.d.ts +16 -4
- package/dist-cjs/postgres/evaluate.js +50 -4
- package/dist-cjs/postgres/fulltext.d.ts +67 -0
- package/dist-cjs/postgres/fulltext.js +129 -0
- package/dist-cjs/postgres/index.d.ts +32 -11
- package/dist-cjs/postgres/index.js +69 -20
- package/dist-cjs/postgres/migrations.d.ts +60 -0
- package/dist-cjs/postgres/migrations.js +129 -0
- package/dist-cjs/postgres/operations.d.ts +14 -3
- package/dist-cjs/postgres/operations.js +78 -14
- package/dist-cjs/postgres/prompts.d.ts +11 -2
- package/dist-cjs/postgres/prompts.js +47 -2
- package/dist-cjs/postgres/rollups.d.ts +35 -0
- package/dist-cjs/postgres/rollups.js +168 -0
- package/dist-cjs/postgres/store.d.ts +11 -2
- package/dist-cjs/postgres/store.js +47 -2
- package/dist-cjs/postgres/traces.d.ts +13 -3
- package/dist-cjs/postgres/traces.js +58 -2
- package/dist-cjs/postgres/vectors.d.ts +11 -2
- package/dist-cjs/postgres/vectors.js +47 -2
- package/dist-cjs/rag/elasticsearch.d.ts +71 -0
- package/dist-cjs/rag/elasticsearch.js +135 -0
- package/dist-cjs/rag/pipeline.d.ts +140 -0
- package/dist-cjs/rag/pipeline.js +191 -0
- package/dist-cjs/rag/rerankers.d.ts +92 -0
- package/dist-cjs/rag/rerankers.js +107 -0
- package/dist-cjs/rag/retrievers.d.ts +12 -1
- package/dist-cjs/router/auto-router.d.ts +34 -7
- package/dist-cjs/router/auto-router.js +201 -68
- package/dist-cjs/server/assistant.d.ts +9 -1
- package/dist-cjs/server/assistant.js +10 -0
- package/dist-cjs/server/auth.d.ts +250 -0
- package/dist-cjs/server/auth.js +402 -0
- package/dist-cjs/server/errors.d.ts +12 -0
- package/dist-cjs/server/errors.js +9 -1
- package/dist-cjs/server/index.d.ts +1 -1
- package/dist-cjs/server/index.js +2 -1
- package/dist-cjs/server/runs.d.ts +24 -1
- package/dist-cjs/server/runs.js +89 -13
- package/dist-cjs/sqlite/index.d.ts +16 -3
- package/dist-cjs/sqlite/index.js +31 -10
- package/dist-cjs/sqlite/migrations.d.ts +26 -0
- package/dist-cjs/sqlite/migrations.js +71 -0
- package/dist-cjs/sqlite/operations.d.ts +14 -3
- package/dist-cjs/sqlite/operations.js +76 -15
- package/dist-cjs/sqlite/store.d.ts +9 -2
- package/dist-cjs/sqlite/store.js +47 -3
- package/dist-cjs/sqlite/vectors.d.ts +11 -2
- package/dist-cjs/sqlite/vectors.js +47 -2
- package/dist-cjs/store/memory.d.ts +10 -0
- package/dist-cjs/store/memory.js +10 -0
- package/dist-cjs/store/tenant.d.ts +12 -0
- package/dist-cjs/store/tenant.js +33 -0
- package/dist-cjs/tenancy/index.d.ts +113 -0
- package/dist-cjs/tenancy/index.js +250 -0
- package/dist-cjs/tracing/instrument.js +2 -0
- package/dist-cjs/types/config.d.ts +24 -2
- package/dist-cjs/types/graph.d.ts +49 -1
- package/dist-cjs/types/messages.d.ts +41 -2
- package/dist-cjs/types/operations.d.ts +6 -0
- package/dist-cjs/types/principal.d.ts +21 -0
- package/dist-cjs/types/principal.js +2 -0
- package/dist-cjs/types/response.d.ts +20 -0
- package/dist-cjs/types/server.d.ts +12 -10
- package/dist-cjs/utils/schema-migrations.d.ts +97 -0
- package/dist-cjs/utils/schema-migrations.js +102 -0
- package/dist-cjs/utils/signals.d.ts +17 -0
- package/dist-cjs/utils/signals.js +29 -0
- package/dist-cjs/utils/tenant.d.ts +25 -0
- package/dist-cjs/utils/tenant.js +37 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +6 -4
- package/package.json +188 -2
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type { OperationLifecycleLike } from '../types/lifecycle.js';
|
|
2
|
+
import type { Principal } from '../types/principal.js';
|
|
2
3
|
import { GraphError } from './errors.js';
|
|
3
|
-
import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy } from '../types/graph.js';
|
|
4
|
+
import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy, RunControlLike } from '../types/graph.js';
|
|
4
5
|
import type { Store } from '../types/store.js';
|
|
5
6
|
/** What a workflow checkpoint holds: the input, every completed step's result, and the output. */
|
|
6
7
|
export interface WorkflowState<I = unknown, O = unknown> {
|
|
7
8
|
/** The input the workflow was started with. */
|
|
8
9
|
input: I;
|
|
9
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Each completed step's result, keyed by step name — `name#2` for the second call with a name. A
|
|
12
|
+
* step whose `onError` supplied its result after its retries ran out also records `recovered`.
|
|
13
|
+
*/
|
|
10
14
|
steps: Record<string, {
|
|
11
15
|
value?: unknown;
|
|
16
|
+
recovered?: StepRecovery;
|
|
12
17
|
}>;
|
|
13
18
|
/** The workflow's return value, once it has completed. */
|
|
14
19
|
output?: O;
|
|
@@ -23,21 +28,70 @@ export interface StepContext {
|
|
|
23
28
|
readonly attempt: number;
|
|
24
29
|
/** Aborted when the run is cancelled or the step exceeds its timeout. */
|
|
25
30
|
readonly signal: AbortSignal;
|
|
31
|
+
/**
|
|
32
|
+
* Shows the step is still making progress, which refreshes its idle timeout. For long work that
|
|
33
|
+
* sends nothing else, such as a large upload or a slow tool call.
|
|
34
|
+
*/
|
|
35
|
+
heartbeat(): void;
|
|
36
|
+
}
|
|
37
|
+
/** Limits on one attempt of a step: its whole run, and the time it may go without progress. */
|
|
38
|
+
export interface StepTimeout {
|
|
39
|
+
/** Fails the attempt when it runs longer than this, in milliseconds. */
|
|
40
|
+
runMs?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Fails the attempt when this long passes without `context.heartbeat()`, in milliseconds, which
|
|
43
|
+
* tells a hung step from a slow one that is working.
|
|
44
|
+
*/
|
|
45
|
+
idleMs?: number;
|
|
46
|
+
}
|
|
47
|
+
/** What a step's `onError` receives once its retries have run out. */
|
|
48
|
+
export interface StepFailure {
|
|
49
|
+
/** The step's name. */
|
|
50
|
+
name: string;
|
|
51
|
+
/** The key its result is recorded under. */
|
|
52
|
+
key: string;
|
|
53
|
+
/** Attempts made. */
|
|
54
|
+
attempts: number;
|
|
55
|
+
/** The last attempt's error. */
|
|
56
|
+
error: unknown;
|
|
57
|
+
}
|
|
58
|
+
/** How a step's result was recovered, as recorded in the workflow state. */
|
|
59
|
+
export interface StepRecovery {
|
|
60
|
+
/** The error that exhausted the step's retries. */
|
|
61
|
+
error: {
|
|
62
|
+
name: string;
|
|
63
|
+
message: string;
|
|
64
|
+
};
|
|
65
|
+
/** Attempts made before `onError` decided. */
|
|
66
|
+
attempts: number;
|
|
26
67
|
}
|
|
27
68
|
/** How one step runs. */
|
|
28
|
-
export interface StepOptions {
|
|
69
|
+
export interface StepOptions<T = unknown> {
|
|
29
70
|
/** Retries a failing attempt, overriding the workflow's default policy. */
|
|
30
71
|
retry?: RetryPolicy;
|
|
31
72
|
/** Aborts the step's signal and fails the attempt when it runs longer than this, in milliseconds. */
|
|
32
73
|
timeoutMs?: number;
|
|
74
|
+
/** Run and idle limits on each attempt. `runMs` is `timeoutMs` by another name; `timeoutMs` wins when both are set. */
|
|
75
|
+
timeout?: StepTimeout;
|
|
76
|
+
/**
|
|
77
|
+
* Decides what happens once the step's retries run out. What it returns becomes the step's
|
|
78
|
+
* result, recorded with `recovered`, so a resumed run does not run the step again — a refund after
|
|
79
|
+
* a charge that kept failing, say. Throwing fails the step with that error. A cancelled run, a
|
|
80
|
+
* question, or a drain is never passed to it.
|
|
81
|
+
*/
|
|
82
|
+
onError?: (failure: StepFailure) => T | Promise<T>;
|
|
33
83
|
}
|
|
34
84
|
/** What a workflow function receives besides its input. */
|
|
35
85
|
export interface WorkflowContext {
|
|
36
86
|
/** The thread this run belongs to. */
|
|
37
87
|
readonly threadId: string;
|
|
88
|
+
/** The tenant the run is for, when it has one. */
|
|
89
|
+
readonly tenantId?: string;
|
|
90
|
+
/** Who the run is for, when it was started with a principal: the caller the server authenticated. */
|
|
91
|
+
readonly principal?: Readonly<Principal>;
|
|
38
92
|
/** Aborted when the run is cancelled. */
|
|
39
93
|
readonly signal: AbortSignal;
|
|
40
|
-
/** Long-term memory, when the workflow was created with a store. */
|
|
94
|
+
/** Long-term memory, when the workflow was created with a store. In a tenant's run, the tenant's view of it. */
|
|
41
95
|
readonly store?: Store;
|
|
42
96
|
/**
|
|
43
97
|
* Runs a piece of work once per thread and records its result.
|
|
@@ -46,7 +100,7 @@ export interface WorkflowContext {
|
|
|
46
100
|
* running. Name steps so the same call has the same name every time; a name used more than once
|
|
47
101
|
* is numbered in call order. The result must survive JSON serialization.
|
|
48
102
|
*/
|
|
49
|
-
step<T>(name: string, run: (context: StepContext) => Promise<T> | T, options?: StepOptions): Promise<T>;
|
|
103
|
+
step<T>(name: string, run: (context: StepContext) => Promise<T> | T, options?: StepOptions<T>): Promise<T>;
|
|
50
104
|
/**
|
|
51
105
|
* Suspends the workflow until a person answers.
|
|
52
106
|
*
|
|
@@ -73,6 +127,11 @@ export interface WorkflowOptions {
|
|
|
73
127
|
store?: Store;
|
|
74
128
|
/** Default retry policy for every step. A step's own policy wins. Defaults to one attempt. */
|
|
75
129
|
retry?: RetryPolicy;
|
|
130
|
+
/**
|
|
131
|
+
* Defaults for every step: its retries, its run and idle timeouts, and its `onError`. A step's own
|
|
132
|
+
* options win, field by field.
|
|
133
|
+
*/
|
|
134
|
+
stepDefaults?: Pick<StepOptions, 'retry' | 'timeout' | 'onError'>;
|
|
76
135
|
/** Steps that may run at once, for a workflow that starts many with `Promise.all`. Defaults to 16. */
|
|
77
136
|
maxConcurrency?: number;
|
|
78
137
|
/**
|
|
@@ -93,6 +152,25 @@ export interface WorkflowRunOptions {
|
|
|
93
152
|
metadata?: Record<string, unknown>;
|
|
94
153
|
/** Receives each step starting, retrying, and finishing, each checkpoint, and custom events. */
|
|
95
154
|
onEvent?: (event: GraphEvent) => void;
|
|
155
|
+
/**
|
|
156
|
+
* Lets the caller stop the run between steps: pass a `RunControl` and call `drain()`. Steps in
|
|
157
|
+
* flight finish and are recorded, no new step starts, and the run ends with `GraphDrainedError`;
|
|
158
|
+
* `continue()` carries it on, on this worker or another. The agent server drains a workflow it
|
|
159
|
+
* serves this way.
|
|
160
|
+
*/
|
|
161
|
+
control?: RunControlLike;
|
|
162
|
+
/**
|
|
163
|
+
* The tenant the run is for. Recorded on every checkpoint as `metadata.tenantId`, read as
|
|
164
|
+
* `context.tenantId`, and scoping `context.store`; another tenant's thread is not found. A run
|
|
165
|
+
* without one carries on the thread's.
|
|
166
|
+
*/
|
|
167
|
+
tenantId?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Who the run is for, read as `context.principal`. Its `tenantId` is the run's tenant when
|
|
170
|
+
* `tenantId` is not given, and a run whose two tenants differ is refused; its `userId` is recorded
|
|
171
|
+
* on every checkpoint as `metadata.userId`.
|
|
172
|
+
*/
|
|
173
|
+
principal?: Principal;
|
|
96
174
|
}
|
|
97
175
|
/** One event of a streamed workflow run. */
|
|
98
176
|
export type WorkflowEvent<O = unknown> = {
|
|
@@ -152,17 +230,21 @@ export interface WorkflowResult<O = unknown> {
|
|
|
152
230
|
/** Every pending question. */
|
|
153
231
|
interrupts?: PendingInterrupt[];
|
|
154
232
|
}
|
|
155
|
-
/** Raised when a step outlives its
|
|
233
|
+
/** Raised when a step outlives its run timeout, or goes longer than its idle timeout without progress. */
|
|
156
234
|
export declare class WorkflowStepTimeoutError extends GraphError {
|
|
157
235
|
/** The step. */
|
|
158
236
|
readonly stepName: string;
|
|
159
237
|
/** The limit it exceeded, in milliseconds. */
|
|
160
238
|
readonly timeoutMs: number;
|
|
239
|
+
/** Which limit: `run` for the whole attempt, `idle` for the time without progress. */
|
|
240
|
+
readonly kind: 'run' | 'idle';
|
|
161
241
|
constructor(
|
|
162
242
|
/** The step. */
|
|
163
243
|
stepName: string,
|
|
164
244
|
/** The limit it exceeded, in milliseconds. */
|
|
165
|
-
timeoutMs: number
|
|
245
|
+
timeoutMs: number,
|
|
246
|
+
/** Which limit: `run` for the whole attempt, `idle` for the time without progress. */
|
|
247
|
+
kind?: 'run' | 'idle');
|
|
166
248
|
}
|
|
167
249
|
/**
|
|
168
250
|
* A durable workflow: a function whose steps are checkpointed, so it survives interrupts, restarts,
|
package/dist/graph/functional.js
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { MemoryGraphCheckpointer } from './checkpointer.js';
|
|
2
2
|
import { withLifecycle } from '../lifecycle/run.js';
|
|
3
3
|
import { migrateCheckpoint, toCheckpoint } from './checkpoint-migration.js';
|
|
4
|
-
import {
|
|
4
|
+
import { tenantStore } from '../store/tenant.js';
|
|
5
|
+
import { withPrincipal } from '../utils/tenant.js';
|
|
6
|
+
import { GraphDrainedError, GraphError, GraphNotInterruptedError, GraphThreadNotFoundError } from './errors.js';
|
|
5
7
|
export class WorkflowStepTimeoutError extends GraphError {
|
|
6
8
|
stepName;
|
|
7
9
|
timeoutMs;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
kind;
|
|
11
|
+
constructor(stepName, timeoutMs, kind = 'run') {
|
|
12
|
+
super(kind === 'idle'
|
|
13
|
+
? `Step "${stepName}" made no progress for ${timeoutMs} ms`
|
|
14
|
+
: `Step "${stepName}" exceeded its ${timeoutMs} ms timeout`, 'WORKFLOW_STEP_TIMEOUT');
|
|
10
15
|
this.stepName = stepName;
|
|
11
16
|
this.timeoutMs = timeoutMs;
|
|
17
|
+
this.kind = kind;
|
|
12
18
|
this.name = 'WorkflowStepTimeoutError';
|
|
13
19
|
}
|
|
14
20
|
}
|
|
21
|
+
class WorkflowDraining extends Error {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('The workflow is draining');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
15
26
|
class WorkflowSuspended extends Error {
|
|
16
27
|
pending;
|
|
17
28
|
constructor(pending) {
|
|
@@ -40,10 +51,17 @@ export class Workflow {
|
|
|
40
51
|
}
|
|
41
52
|
stream(input, runOptions = {}) {
|
|
42
53
|
const threadId = runOptions.threadId ?? generatedId();
|
|
43
|
-
return this.execute(threadId, runOptions, async () =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
return this.execute(threadId, runOptions, async () => {
|
|
55
|
+
const latest = await this.latest(threadId);
|
|
56
|
+
const tenantId = runOptions.tenantId ?? runOptions.principal?.tenantId;
|
|
57
|
+
if (latest && tenantId !== undefined) {
|
|
58
|
+
const owner = latest.metadata?.tenantId;
|
|
59
|
+
if (typeof owner === 'string' && owner !== tenantId) {
|
|
60
|
+
throw new GraphError(`Thread "${threadId}" cannot be used by this tenant; choose another id`, 'GRAPH_VALIDATION_ERROR');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { ...this.fresh(threadId, input), step: latest?.step ?? 0 };
|
|
64
|
+
});
|
|
47
65
|
}
|
|
48
66
|
resume(threadId, value, runOptions = {}) {
|
|
49
67
|
return this.execute(threadId, runOptions, async () => {
|
|
@@ -132,16 +150,40 @@ export class Workflow {
|
|
|
132
150
|
}
|
|
133
151
|
execute(threadId, runOptions, start) {
|
|
134
152
|
const queue = new EventQueue();
|
|
135
|
-
|
|
153
|
+
let options;
|
|
154
|
+
try {
|
|
155
|
+
options = withPrincipal(runOptions);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
queue.fail(error);
|
|
159
|
+
return queue;
|
|
160
|
+
}
|
|
161
|
+
const metadata = {
|
|
162
|
+
threadId,
|
|
163
|
+
graph: this.name,
|
|
164
|
+
...(options.tenantId !== undefined ? { tenantId: options.tenantId } : {}),
|
|
165
|
+
...(options.principal?.userId !== undefined ? { userId: options.principal.userId } : {}),
|
|
166
|
+
};
|
|
167
|
+
void withLifecycle(this.options.lifecycle, { family: 'graph', operation: `workflow.${this.name}`, metadata }, options.signal ? { signal: options.signal } : {}, () => this.run(threadId, options, start, queue)).then(() => queue.close(), (error) => queue.fail(error));
|
|
136
168
|
return queue;
|
|
137
169
|
}
|
|
138
170
|
async run(threadId, runOptions, start, queue) {
|
|
139
171
|
const initial = await start();
|
|
172
|
+
const owner = typeof initial.metadata?.tenantId === 'string' ? initial.metadata.tenantId : undefined;
|
|
173
|
+
if (runOptions.tenantId !== undefined && owner !== undefined && owner !== runOptions.tenantId) {
|
|
174
|
+
throw new GraphThreadNotFoundError(threadId);
|
|
175
|
+
}
|
|
176
|
+
const tenantId = runOptions.tenantId ?? owner;
|
|
140
177
|
const checkpointer = this.checkpointer();
|
|
141
178
|
const state = structuredClone(initial.state);
|
|
142
179
|
state.steps ??= {};
|
|
143
180
|
const resolved = { ...initial.resolved };
|
|
144
|
-
const metadata = {
|
|
181
|
+
const metadata = {
|
|
182
|
+
...initial.metadata,
|
|
183
|
+
graph: this.name,
|
|
184
|
+
...runOptions.metadata,
|
|
185
|
+
...(tenantId === undefined ? {} : { tenantId }),
|
|
186
|
+
};
|
|
145
187
|
let stepNumber = initial.step;
|
|
146
188
|
let writes = Promise.resolve();
|
|
147
189
|
const persist = (checkpoint) => {
|
|
@@ -171,11 +213,17 @@ export class Workflow {
|
|
|
171
213
|
const inFlight = new Set();
|
|
172
214
|
const suspended = [];
|
|
173
215
|
let interruptIndex = 0;
|
|
216
|
+
let drained = false;
|
|
174
217
|
const limiter = new Limiter(this.options.maxConcurrency ?? 16);
|
|
218
|
+
const defaults = this.options.stepDefaults ?? {};
|
|
175
219
|
const context = {
|
|
176
220
|
threadId,
|
|
221
|
+
...(tenantId === undefined ? {} : { tenantId }),
|
|
222
|
+
...(runOptions.principal ? { principal: runOptions.principal } : {}),
|
|
177
223
|
signal: controller.signal,
|
|
178
|
-
...(this.options.store
|
|
224
|
+
...(this.options.store
|
|
225
|
+
? { store: tenantId === undefined ? this.options.store : tenantStore(this.options.store, tenantId) }
|
|
226
|
+
: {}),
|
|
179
227
|
step: (name, run, options = {}) => {
|
|
180
228
|
const count = (counts.get(name) ?? 0) + 1;
|
|
181
229
|
counts.set(name, count);
|
|
@@ -185,10 +233,22 @@ export class Workflow {
|
|
|
185
233
|
queue.push({ type: 'step', step: stepNumber, name, key, cached: true, state: snapshot(), status: 'running' });
|
|
186
234
|
return Promise.resolve(recorded.value);
|
|
187
235
|
}
|
|
236
|
+
if (runOptions.control?.draining) {
|
|
237
|
+
drained = true;
|
|
238
|
+
return Promise.reject(new WorkflowDraining());
|
|
239
|
+
}
|
|
240
|
+
const merged = {
|
|
241
|
+
...defaults,
|
|
242
|
+
...options,
|
|
243
|
+
timeout: { ...defaults.timeout, ...options.timeout },
|
|
244
|
+
};
|
|
188
245
|
const work = limiter
|
|
189
|
-
.run(() => this.runStep(name, key, run, { ...this.options.retry, ...options.retry },
|
|
190
|
-
.then(async (value) => {
|
|
191
|
-
state.steps[key] =
|
|
246
|
+
.run(() => this.runStep(name, key, run, { ...this.options.retry, ...defaults.retry, ...options.retry }, merged, controller.signal, runOptions, stepNumber))
|
|
247
|
+
.then(async ({ value, recovered }) => {
|
|
248
|
+
state.steps[key] = {
|
|
249
|
+
...(value === undefined ? {} : { value: structuredClone(value) }),
|
|
250
|
+
...(recovered ? { recovered } : {}),
|
|
251
|
+
};
|
|
192
252
|
await persist({ state: state, next: [this.name], status: 'running' });
|
|
193
253
|
queue.push({
|
|
194
254
|
type: 'step',
|
|
@@ -262,6 +322,18 @@ export class Workflow {
|
|
|
262
322
|
queue.push({ type: 'interrupt', step: stepNumber, state: snapshot(), status: 'interrupted' });
|
|
263
323
|
return;
|
|
264
324
|
}
|
|
325
|
+
if (drained && outcome.kind === 'failed') {
|
|
326
|
+
const reason = runOptions.control?.reason;
|
|
327
|
+
await persist({
|
|
328
|
+
state: state,
|
|
329
|
+
next: [this.name],
|
|
330
|
+
status: 'interrupted',
|
|
331
|
+
resolved,
|
|
332
|
+
drained: { ...(reason ? { reason } : {}), at: this.now().toISOString() },
|
|
333
|
+
});
|
|
334
|
+
await writes;
|
|
335
|
+
throw new GraphDrainedError(threadId, stepNumber, reason);
|
|
336
|
+
}
|
|
265
337
|
if (outcome.kind === 'failed') {
|
|
266
338
|
const error = outcome.error;
|
|
267
339
|
await persist({
|
|
@@ -284,41 +356,87 @@ export class Workflow {
|
|
|
284
356
|
}
|
|
285
357
|
async runStep(name, key, run, retry, options, signal, runOptions, step) {
|
|
286
358
|
const policy = { ...DEFAULT_RETRY, ...retry };
|
|
359
|
+
const runMs = options.timeoutMs ?? options.timeout?.runMs;
|
|
360
|
+
const idleMs = options.timeout?.idleMs;
|
|
287
361
|
for (let attempt = 1;; attempt += 1) {
|
|
288
362
|
notify(runOptions, { type: 'task_start', step, taskId: key, node: name, attempt });
|
|
289
363
|
const stepController = new AbortController();
|
|
290
364
|
const forward = () => stepController.abort(signal.reason);
|
|
291
365
|
signal.addEventListener('abort', forward, { once: true });
|
|
292
|
-
let
|
|
293
|
-
let
|
|
366
|
+
let runTimer;
|
|
367
|
+
let idleTimer;
|
|
368
|
+
let timedOut;
|
|
369
|
+
let expire = () => undefined;
|
|
370
|
+
const limits = runMs === undefined && idleMs === undefined
|
|
371
|
+
? undefined
|
|
372
|
+
: new Promise((_, reject) => {
|
|
373
|
+
expire = (error) => {
|
|
374
|
+
if (timedOut)
|
|
375
|
+
return;
|
|
376
|
+
timedOut = error;
|
|
377
|
+
reject(error);
|
|
378
|
+
stepController.abort();
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
limits?.catch(() => undefined);
|
|
382
|
+
if (runMs !== undefined)
|
|
383
|
+
runTimer = setTimeout(() => expire(new WorkflowStepTimeoutError(name, runMs)), runMs);
|
|
384
|
+
const armIdle = () => {
|
|
385
|
+
if (idleMs === undefined || timedOut)
|
|
386
|
+
return;
|
|
387
|
+
if (idleTimer)
|
|
388
|
+
clearTimeout(idleTimer);
|
|
389
|
+
idleTimer = setTimeout(() => expire(new WorkflowStepTimeoutError(name, idleMs, 'idle')), idleMs);
|
|
390
|
+
};
|
|
391
|
+
armIdle();
|
|
294
392
|
try {
|
|
295
|
-
const work = Promise.resolve(run({ name, key, attempt, signal: stepController.signal }));
|
|
296
|
-
const value =
|
|
297
|
-
? await work
|
|
298
|
-
: await Promise.race([
|
|
299
|
-
work,
|
|
300
|
-
new Promise((_, reject) => {
|
|
301
|
-
timer = setTimeout(() => {
|
|
302
|
-
timedOut = true;
|
|
303
|
-
reject(new WorkflowStepTimeoutError(name, options.timeoutMs));
|
|
304
|
-
stepController.abort();
|
|
305
|
-
}, options.timeoutMs);
|
|
306
|
-
}),
|
|
307
|
-
]);
|
|
393
|
+
const work = Promise.resolve(run({ name, key, attempt, signal: stepController.signal, heartbeat: armIdle }));
|
|
394
|
+
const value = limits ? await Promise.race([work, limits]) : await work;
|
|
308
395
|
if (timedOut)
|
|
309
|
-
throw
|
|
396
|
+
throw timedOut;
|
|
310
397
|
notify(runOptions, { type: 'task_end', step, taskId: key, node: name, update: value });
|
|
311
|
-
return value;
|
|
398
|
+
return { value };
|
|
312
399
|
}
|
|
313
400
|
catch (error) {
|
|
314
401
|
if (signal.aborted || error instanceof WorkflowSuspended)
|
|
315
402
|
throw error;
|
|
316
|
-
const failure = timedOut
|
|
403
|
+
const failure = timedOut ?? error;
|
|
317
404
|
const retryable = policy.retryOn
|
|
318
405
|
? policy.retryOn(failure, attempt)
|
|
319
|
-
: !(failure instanceof GraphError) || timedOut;
|
|
320
|
-
if (attempt >= Math.max(1, policy.maxAttempts) || !retryable)
|
|
321
|
-
|
|
406
|
+
: !(failure instanceof GraphError) || timedOut !== undefined;
|
|
407
|
+
if (attempt >= Math.max(1, policy.maxAttempts) || !retryable) {
|
|
408
|
+
if (!options.onError)
|
|
409
|
+
throw failure;
|
|
410
|
+
const failed = (recovered) => notify(runOptions, {
|
|
411
|
+
type: 'task_failed',
|
|
412
|
+
step,
|
|
413
|
+
taskId: key,
|
|
414
|
+
node: name,
|
|
415
|
+
attempts: attempt,
|
|
416
|
+
error: failure instanceof Error ? failure.message : String(failure),
|
|
417
|
+
recovered,
|
|
418
|
+
});
|
|
419
|
+
let value;
|
|
420
|
+
try {
|
|
421
|
+
value = await options.onError({ name, key, attempts: attempt, error: failure });
|
|
422
|
+
}
|
|
423
|
+
catch (decision) {
|
|
424
|
+
failed(false);
|
|
425
|
+
throw decision;
|
|
426
|
+
}
|
|
427
|
+
failed(true);
|
|
428
|
+
notify(runOptions, { type: 'task_end', step, taskId: key, node: name, update: value });
|
|
429
|
+
return {
|
|
430
|
+
value,
|
|
431
|
+
recovered: {
|
|
432
|
+
error: {
|
|
433
|
+
name: failure instanceof Error ? failure.name : 'Error',
|
|
434
|
+
message: failure instanceof Error ? failure.message : String(failure),
|
|
435
|
+
},
|
|
436
|
+
attempts: attempt,
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
322
440
|
notify(runOptions, {
|
|
323
441
|
type: 'task_retry',
|
|
324
442
|
step,
|
|
@@ -330,8 +448,10 @@ export class Workflow {
|
|
|
330
448
|
await sleep(backoff(policy, attempt), signal);
|
|
331
449
|
}
|
|
332
450
|
finally {
|
|
333
|
-
if (
|
|
334
|
-
clearTimeout(
|
|
451
|
+
if (runTimer)
|
|
452
|
+
clearTimeout(runTimer);
|
|
453
|
+
if (idleTimer)
|
|
454
|
+
clearTimeout(idleTimer);
|
|
335
455
|
signal.removeEventListener('abort', forward);
|
|
336
456
|
}
|
|
337
457
|
}
|
package/dist/graph/graph.d.ts
CHANGED
|
@@ -111,10 +111,17 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
111
111
|
* once the rewound checkpoint is written.
|
|
112
112
|
*/
|
|
113
113
|
resumeFrom(threadId: string, step: number, runOptions?: GraphRunOptions): AsyncIterable<GraphStepEvent<S>>;
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Latest checkpoint, or the one at `step`. With a `tenantId`, a thread another tenant owns reads
|
|
116
|
+
* as absent.
|
|
117
|
+
*/
|
|
118
|
+
state(threadId: string, step?: number, options?: {
|
|
119
|
+
tenantId?: string;
|
|
120
|
+
}): Promise<GraphCheckpoint<S> | undefined>;
|
|
121
|
+
/** Checkpoints for a thread, newest first. With a `tenantId`, another tenant's thread has none. */
|
|
122
|
+
history(threadId: string, limit?: number, options?: {
|
|
123
|
+
tenantId?: string;
|
|
124
|
+
}): Promise<Array<GraphCheckpoint<S>>>;
|
|
118
125
|
/**
|
|
119
126
|
* Wraps this graph as a node in another graph.
|
|
120
127
|
*
|
|
@@ -143,17 +150,20 @@ export declare class CompiledGraph<S extends ChannelSchema, I extends keyof S =
|
|
|
143
150
|
*/
|
|
144
151
|
updateState(threadId: string, update: StateUpdate<S>, options?: {
|
|
145
152
|
asNode?: string;
|
|
153
|
+
tenantId?: string;
|
|
146
154
|
}): Promise<GraphCheckpoint<S>>;
|
|
147
155
|
/**
|
|
148
156
|
* Copies a thread's history up to `step` into a new thread, and returns the new thread's id.
|
|
149
157
|
*
|
|
150
158
|
* Rewinding with `resumeFrom()` replaces the original timeline. A fork keeps it: both threads stay
|
|
151
159
|
* readable and runnable, so two answers to the same question can be compared side by side. Every
|
|
152
|
-
* copied checkpoint records `metadata.forkedFrom
|
|
160
|
+
* copied checkpoint records `metadata.forkedFrom`, and keeps the tenant of the thread it came from.
|
|
161
|
+
* With a `tenantId`, another tenant's thread is not found.
|
|
153
162
|
*/
|
|
154
163
|
fork(threadId: string, options?: {
|
|
155
164
|
step?: number;
|
|
156
165
|
threadId?: string;
|
|
166
|
+
tenantId?: string;
|
|
157
167
|
}): Promise<string>;
|
|
158
168
|
/**
|
|
159
169
|
* Runs the supersteps, as one operation of the compiled lifecycle when there is one. The run is
|