@kortyx/hooks 0.17.1 → 0.19.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/CHANGELOG.md +31 -0
- package/LICENSE +51 -43
- package/README.md +28 -0
- package/dist/index.d.ts +65 -4
- package/dist/index.js +269 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.js +16 -1
- package/dist/internal.js.map +1 -1
- package/dist/tracing-BI6YKHSf.d.ts +94 -0
- package/package.json +4 -3
- package/dist/tracing-DhjgeiDS.d.ts +0 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.19.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.18.0...hooks-v0.19.0) (2026-09-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add resumable child workflows and Studio inspection ([#157](https://github.com/kortyx-io/kortyx/issues/157)) ([542ba29](https://github.com/kortyx-io/kortyx/commit/542ba29b3b57217e6e5d98c1b409bbec5ce3261f))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @kortyx/core bumped to 0.6.0
|
|
16
|
+
* @kortyx/telemetry-contracts bumped to 0.3.0
|
|
17
|
+
|
|
18
|
+
## [0.18.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.17.1...hooks-v0.18.0) (2026-08-02)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **studio:** release self-hosted observability preview ([4e9d3fd](https://github.com/kortyx-io/kortyx/commit/4e9d3fdbc7233f9b1de94ca1afe6dbae9cc9157f))
|
|
24
|
+
* **studio:** release self-hosted observability preview ([#139](https://github.com/kortyx-io/kortyx/issues/139)) ([4e9d3fd](https://github.com/kortyx-io/kortyx/commit/4e9d3fdbc7233f9b1de94ca1afe6dbae9cc9157f))
|
|
25
|
+
* **telemetry:** add persisted studio read models ([cebb06b](https://github.com/kortyx-io/kortyx/commit/cebb06bb74ccd4422e263f0ff4efa0fee5a16240))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Dependencies
|
|
29
|
+
|
|
30
|
+
* The following workspace dependencies were updated
|
|
31
|
+
* dependencies
|
|
32
|
+
* @kortyx/telemetry-contracts bumped to 0.2.0
|
|
33
|
+
|
|
3
34
|
## [0.17.1](https://github.com/kortyx-io/kortyx/compare/hooks-v0.17.0...hooks-v0.17.1) (2026-05-28)
|
|
4
35
|
|
|
5
36
|
|
package/LICENSE
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
Copyright 2026 Kortyx contributors
|
|
2
|
+
|
|
3
|
+
Portions of this software are licensed as follows:
|
|
4
|
+
|
|
5
|
+
- All content that resides under the `apps/studio/` directory of this repository is licensed under the license defined in `apps/studio/LICENSE.md`.
|
|
6
|
+
- All content that resides under a `plugins/*/` directory of this repository is licensed under the license defined in that plugin's `LICENSE.md`.
|
|
7
|
+
- All third party components incorporated into the Kortyx software are licensed under the original license provided by the owner of the applicable component.
|
|
8
|
+
- Content outside of the above mentioned directories or restrictions is available under the Apache License, Version 2.0, as defined below.
|
|
9
|
+
|
|
1
10
|
Apache License
|
|
2
11
|
Version 2.0, January 2004
|
|
3
12
|
http://www.apache.org/licenses/
|
|
4
13
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
1. Definitions.
|
|
14
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
15
|
+
1. Definitions.
|
|
8
16
|
|
|
9
17
|
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
18
|
and distribution as defined by Sections 1 through 9 of this document.
|
|
@@ -63,14 +71,14 @@
|
|
|
63
71
|
on behalf of whom a Contribution has been received by Licensor and
|
|
64
72
|
subsequently incorporated within the Work.
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
75
|
this License, each Contributor hereby grants to You a perpetual,
|
|
68
76
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
77
|
copyright license to reproduce, prepare Derivative Works of,
|
|
70
78
|
publicly display, publicly perform, sublicense, and distribute the
|
|
71
79
|
Work and such Derivative Works in Source or Object form.
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
82
|
this License, each Contributor hereby grants to You a perpetual,
|
|
75
83
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
84
|
(except as stated in this section) patent license to make, have made,
|
|
@@ -86,39 +94,39 @@
|
|
|
86
94
|
granted to You under this License for that Work shall terminate
|
|
87
95
|
as of the date such litigation is filed.
|
|
88
96
|
|
|
89
|
-
|
|
97
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
98
|
Work or Derivative Works thereof in any medium, with or without
|
|
91
99
|
modifications, and in Source or Object form, provided that You
|
|
92
100
|
meet the following conditions:
|
|
93
101
|
|
|
94
102
|
(a) You must give any other recipients of the Work or
|
|
95
|
-
|
|
103
|
+
Derivative Works a copy of this License; and
|
|
96
104
|
|
|
97
105
|
(b) You must cause any modified files to carry prominent notices
|
|
98
|
-
|
|
106
|
+
stating that You changed the files; and
|
|
99
107
|
|
|
100
108
|
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
109
|
+
that You distribute, all copyright, patent, trademark, and
|
|
110
|
+
attribution notices from the Source form of the Work,
|
|
111
|
+
excluding those notices that do not pertain to any part of
|
|
112
|
+
the Derivative Works; and
|
|
105
113
|
|
|
106
114
|
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
distribution, then any Derivative Works that You distribute must
|
|
116
|
+
include a readable copy of the attribution notices contained
|
|
117
|
+
within such NOTICE file, excluding those notices that do not
|
|
118
|
+
pertain to any part of the Derivative Works, in at least one
|
|
119
|
+
of the following places: within a NOTICE text file distributed
|
|
120
|
+
as part of the Derivative Works; within the Source form or
|
|
121
|
+
documentation, if provided along with the Derivative Works; or,
|
|
122
|
+
within a display generated by the Derivative Works, if and
|
|
123
|
+
wherever such third-party notices normally appear. The contents
|
|
124
|
+
of the NOTICE file are for informational purposes only and
|
|
125
|
+
do not modify the License. You may add Your own attribution
|
|
126
|
+
notices within Derivative Works that You distribute, alongside
|
|
127
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
128
|
+
that such additional attribution notices cannot be construed
|
|
129
|
+
as modifying the License.
|
|
122
130
|
|
|
123
131
|
You may add Your own copyright statement to Your modifications and
|
|
124
132
|
may provide additional or different license terms and conditions
|
|
@@ -127,7 +135,7 @@
|
|
|
127
135
|
reproduction, and distribution of the Work otherwise complies with
|
|
128
136
|
the conditions stated in this License.
|
|
129
137
|
|
|
130
|
-
|
|
138
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
139
|
any Contribution intentionally submitted for inclusion in the Work
|
|
132
140
|
by You to the Licensor shall be under the terms and conditions of
|
|
133
141
|
this License, without any additional terms or conditions.
|
|
@@ -135,12 +143,12 @@
|
|
|
135
143
|
the terms of any separate license agreement you may have executed
|
|
136
144
|
with Licensor regarding such Contributions.
|
|
137
145
|
|
|
138
|
-
|
|
146
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
147
|
names, trademarks, service marks, or product names of the Licensor,
|
|
140
148
|
except as required for reasonable and customary use in describing the
|
|
141
149
|
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
150
|
|
|
143
|
-
|
|
151
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
152
|
agreed to in writing, Licensor provides the Work (and each
|
|
145
153
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
154
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
@@ -150,7 +158,7 @@
|
|
|
150
158
|
appropriateness of using or redistributing the Work and assume any
|
|
151
159
|
risks associated with Your exercise of permissions under this License.
|
|
152
160
|
|
|
153
|
-
|
|
161
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
162
|
whether in tort (including negligence), contract, or otherwise,
|
|
155
163
|
unless required by applicable law (such as deliberate and grossly
|
|
156
164
|
negligent acts) or agreed to in writing, shall any Contributor be
|
|
@@ -162,7 +170,7 @@
|
|
|
162
170
|
other commercial damages or losses), even if such Contributor
|
|
163
171
|
has been advised of the possibility of such damages.
|
|
164
172
|
|
|
165
|
-
|
|
173
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
174
|
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
175
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
176
|
or other liability obligations and/or rights consistent with this
|
|
@@ -173,9 +181,9 @@
|
|
|
173
181
|
incurred by, or claims asserted against, such Contributor by reason
|
|
174
182
|
of your accepting any such warranty or additional liability.
|
|
175
183
|
|
|
176
|
-
|
|
184
|
+
END OF TERMS AND CONDITIONS
|
|
177
185
|
|
|
178
|
-
|
|
186
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
187
|
|
|
180
188
|
To apply the Apache License to your work, attach the following
|
|
181
189
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
@@ -186,16 +194,16 @@
|
|
|
186
194
|
same "printed page" as the copyright notice for easier
|
|
187
195
|
identification within third-party archives.
|
|
188
196
|
|
|
189
|
-
|
|
197
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
198
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
199
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
200
|
+
you may not use this file except in compliance with the License.
|
|
201
|
+
You may obtain a copy of the License at
|
|
194
202
|
|
|
195
203
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
204
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
205
|
+
Unless required by applicable law or agreed to in writing, software
|
|
206
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
207
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
208
|
+
See the License for the specific language governing permissions and
|
|
209
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -53,3 +53,31 @@ Workarounds:
|
|
|
53
53
|
## License
|
|
54
54
|
|
|
55
55
|
Apache-2.0. See [LICENSE](https://github.com/kortyx-io/kortyx/blob/main/LICENSE).
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Child workflows
|
|
59
|
+
|
|
60
|
+
Call a registered workflow from a node or custom hook and continue with its
|
|
61
|
+
result:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const result = await useWorkflow({
|
|
65
|
+
id: "research",
|
|
66
|
+
workflow: researchWorkflow,
|
|
67
|
+
input: { topic: "AI agents" },
|
|
68
|
+
});
|
|
69
|
+
return { data: { summary: result.data.summary } };
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The child defines `inputSchema` and `outputSchema` on `defineWorkflow`.
|
|
73
|
+
Inputs and returned data are inferred from those schemas and validated at
|
|
74
|
+
runtime. The result is the child's accumulated data parsed by its output
|
|
75
|
+
schema. Use `createWorkflowHooks({ research: researchWorkflow })` for typed
|
|
76
|
+
string ids. Register the same definitions with the agent.
|
|
77
|
+
|
|
78
|
+
Calls need stable ids. A child interrupt pauses the chain; resuming replays
|
|
79
|
+
the enclosing node and reuses saved child results. Keep effects before calls
|
|
80
|
+
idempotent. Fork/rollback retain nested checkpoints; use Redis for persistence
|
|
81
|
+
across server restarts. Concurrent calls are unsupported.
|
|
82
|
+
|
|
83
|
+
See [the execution and replay contract](../../docs/design-specs/child-workflows.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { R as ReasonTraceAdapter,
|
|
3
|
-
export {
|
|
1
|
+
import { WorkflowDefinition, TokenUsage, InterruptInput, NodeConfig, InterruptResult, GraphState } from '@kortyx/core';
|
|
2
|
+
import { K as KortyxTelemetryConfig, R as ReasonTraceAdapter, a as KortyxTraceMetadata, b as KortyxTelemetryEventType } from './tracing-BI6YKHSf.js';
|
|
3
|
+
export { E as EnsureWorkflowTopologyRequest, c as EnsureWorkflowTopologyResponse, d as KortyxTelemetryContentCapture, e as KortyxTelemetryCorrelation, f as KortyxTelemetryEvent, g as KortyxTelemetryPrompt, h as KortyxTelemetryReporter, i as KortyxTelemetryService, j as KortyxTraceAdapter, k as ReasonTraceAttributes, l as ReasonTraceSpan, m as ReasonTraceSpanEndArgs, n as ReasonTraceSpanStartArgs } from './tracing-BI6YKHSf.js';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
import { ProviderModelRef, KortyxReasoningOptions, KortyxResponseFormat, KortyxExecutableTool, KortyxUsage, KortyxFinishReason, KortyxProviderMetadata, KortyxWarning, KortyxToolCall, KortyxToolResult } from '@kortyx/providers';
|
|
6
|
+
import '@kortyx/telemetry-contracts';
|
|
7
|
+
|
|
8
|
+
type WorkflowCallOutcome = {
|
|
9
|
+
status: "completed";
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
usage?: TokenUsage | undefined;
|
|
12
|
+
} | {
|
|
13
|
+
status: "interrupted";
|
|
14
|
+
request: InterruptInput;
|
|
15
|
+
snapshot: unknown;
|
|
16
|
+
};
|
|
17
|
+
type WorkflowCallService = (args: {
|
|
18
|
+
id: string;
|
|
19
|
+
workflow: string;
|
|
20
|
+
input: unknown;
|
|
21
|
+
snapshot?: unknown;
|
|
22
|
+
definition?: WorkflowDefinition | undefined;
|
|
23
|
+
invocationId: string;
|
|
24
|
+
response?: string | string[] | undefined;
|
|
25
|
+
callerNodeExecutionId?: string | undefined;
|
|
26
|
+
}) => Promise<WorkflowCallOutcome>;
|
|
27
|
+
declare class WorkflowCallError extends Error {
|
|
28
|
+
readonly workflow: string;
|
|
29
|
+
constructor(workflow: string, message: string);
|
|
30
|
+
}
|
|
31
|
+
declare function workflowCallFingerprint(value: unknown): string;
|
|
32
|
+
type SchematizedWorkflow = WorkflowDefinition & {
|
|
33
|
+
inputSchema: z.ZodType;
|
|
34
|
+
outputSchema: z.ZodType<Record<string, unknown>>;
|
|
35
|
+
};
|
|
36
|
+
/** Bind string workflow ids to the same definitions registered with the agent. */
|
|
37
|
+
declare function createWorkflowHooks<const W extends Record<string, SchematizedWorkflow>>(workflows: W): {
|
|
38
|
+
useWorkflow: <K extends keyof W & string>(args: {
|
|
39
|
+
id: string;
|
|
40
|
+
workflow: K;
|
|
41
|
+
input: z.input<W[K]["inputSchema"]>;
|
|
42
|
+
}) => Promise<{
|
|
43
|
+
data: z.output<W[K]["outputSchema"]>;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
/** Typed references avoid caller-supplied result type assertions. */
|
|
47
|
+
declare function useWorkflow<W extends SchematizedWorkflow>(args: {
|
|
48
|
+
id: string;
|
|
49
|
+
workflow: W;
|
|
50
|
+
input: z.input<W["inputSchema"]>;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
data: z.output<W["outputSchema"]>;
|
|
53
|
+
}>;
|
|
54
|
+
declare function useWorkflow(args: {
|
|
55
|
+
id: string;
|
|
56
|
+
workflow: string;
|
|
57
|
+
input: unknown;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
data: Record<string, unknown>;
|
|
60
|
+
}>;
|
|
5
61
|
|
|
6
62
|
type HookNodeRuntimeContext = {
|
|
7
63
|
graph: {
|
|
@@ -11,6 +67,8 @@ type HookNodeRuntimeContext = {
|
|
|
11
67
|
config: NodeConfig;
|
|
12
68
|
emit: (event: string, payload: unknown) => void;
|
|
13
69
|
awaitInterrupt: (args: InterruptInput) => InterruptResult;
|
|
70
|
+
callWorkflow?: WorkflowCallService;
|
|
71
|
+
workflowCallTelemetry?: KortyxTelemetryConfig | undefined;
|
|
14
72
|
};
|
|
15
73
|
type HookRuntimeContext = {
|
|
16
74
|
node: HookNodeRuntimeContext;
|
|
@@ -160,4 +218,7 @@ declare function useRuntimeContext<TContext extends Record<string, unknown> = Re
|
|
|
160
218
|
declare function useNodeState<T>(initialValue: T): [T, StateSetter<T>];
|
|
161
219
|
declare function useWorkflowState<T>(key: string, ...rest: [] | [T]): [T, StateSetter<T>];
|
|
162
220
|
|
|
163
|
-
|
|
221
|
+
/** Best effort lifecycle facts. Engine snapshots and interrupt tokens never leave the runtime. */
|
|
222
|
+
declare function emitWorkflowCall(telemetry: KortyxTelemetryConfig | undefined, type: KortyxTelemetryEventType, payload: Record<string, unknown>): void;
|
|
223
|
+
|
|
224
|
+
export { type HookRuntimeContext, KortyxTelemetryConfig, KortyxTelemetryEventType, KortyxTraceMetadata, ReasonTraceAdapter, type UseInterruptArgs, type UseReasonArgs, type UseReasonResult, type UseReasonStep, type UseReasonToolExecution, type UseStructuredDataArgs, WorkflowCallError, type WorkflowCallOutcome, type WorkflowCallService, createWorkflowHooks, emitWorkflowCall, runWithHookContext, useInterrupt, useNodeState, useReason, useRuntimeContext, useStructuredData, useWorkflow, useWorkflowState, workflowCallFingerprint };
|
package/dist/index.js
CHANGED
|
@@ -19,13 +19,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// src/index.ts
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
|
+
WorkflowCallError: () => WorkflowCallError,
|
|
23
|
+
createWorkflowHooks: () => createWorkflowHooks,
|
|
24
|
+
emitWorkflowCall: () => emitWorkflowCall,
|
|
22
25
|
runWithHookContext: () => runWithHookContext,
|
|
23
26
|
useInterrupt: () => useInterrupt,
|
|
24
27
|
useNodeState: () => useNodeState,
|
|
25
28
|
useReason: () => useReason2,
|
|
26
29
|
useRuntimeContext: () => useRuntimeContext,
|
|
27
30
|
useStructuredData: () => useStructuredData,
|
|
28
|
-
|
|
31
|
+
useWorkflow: () => useWorkflow,
|
|
32
|
+
useWorkflowState: () => useWorkflowState,
|
|
33
|
+
workflowCallFingerprint: () => workflowCallFingerprint
|
|
29
34
|
});
|
|
30
35
|
module.exports = __toCommonJS(index_exports);
|
|
31
36
|
|
|
@@ -78,6 +83,8 @@ var createInternalContext = (ctx) => {
|
|
|
78
83
|
const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
|
|
79
84
|
return {
|
|
80
85
|
...ctx,
|
|
86
|
+
workflowCallActive: false,
|
|
87
|
+
workflowCallIds: /* @__PURE__ */ new Set(),
|
|
81
88
|
nodeStateIndex: 0,
|
|
82
89
|
reasonCallIndex: 0,
|
|
83
90
|
currentNodeState,
|
|
@@ -151,6 +158,12 @@ async function runWithHookContext(ctx, fn) {
|
|
|
151
158
|
try {
|
|
152
159
|
const result = await storage.run(internal, fn);
|
|
153
160
|
cleanupCompletedReasonCheckpoints(internal);
|
|
161
|
+
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
162
|
+
if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
|
|
163
|
+
internal.currentNodeState.byKey[key] = null;
|
|
164
|
+
internal.stateDirty = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
154
167
|
return { result, runtimeUpdates: buildRuntimeStateUpdates(internal) };
|
|
155
168
|
} catch (err) {
|
|
156
169
|
const runtimeUpdates = buildRuntimeStateUpdates(internal);
|
|
@@ -248,7 +261,10 @@ var awaitInterruptInternal = (args) => {
|
|
|
248
261
|
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
249
262
|
meta: {
|
|
250
263
|
...userMeta,
|
|
251
|
-
__kortyxResumeStatePatch:
|
|
264
|
+
__kortyxResumeStatePatch: {
|
|
265
|
+
...resumeStatePatch,
|
|
266
|
+
...ctx.tokenUsage ? { tokenUsage: ctx.tokenUsage } : {}
|
|
267
|
+
}
|
|
252
268
|
}
|
|
253
269
|
};
|
|
254
270
|
const raw = ctx.node.awaitInterrupt(enrichedRequest);
|
|
@@ -424,6 +440,17 @@ async function runReasonEngine(args) {
|
|
|
424
440
|
input: args.telemetry?.input ?? messages
|
|
425
441
|
}
|
|
426
442
|
});
|
|
443
|
+
const modelRequestStartedAt = Date.now();
|
|
444
|
+
let firstTextTokenAt;
|
|
445
|
+
let lastTextTokenAt;
|
|
446
|
+
const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
|
|
447
|
+
ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
|
|
448
|
+
streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
|
|
449
|
+
timeToLastTokenMs: Math.max(
|
|
450
|
+
0,
|
|
451
|
+
lastTextTokenAt - modelRequestStartedAt
|
|
452
|
+
)
|
|
453
|
+
};
|
|
427
454
|
const endTrace = (span, result) => {
|
|
428
455
|
span?.end?.({
|
|
429
456
|
...result.usage !== void 0 ? { usage: result.usage } : {},
|
|
@@ -431,7 +458,8 @@ async function runReasonEngine(args) {
|
|
|
431
458
|
...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
|
|
432
459
|
...result.warnings !== void 0 ? { warnings: result.warnings } : {},
|
|
433
460
|
attributes: {
|
|
434
|
-
textLength: result.text.length
|
|
461
|
+
textLength: result.text.length,
|
|
462
|
+
...generationTimingAttributes()
|
|
435
463
|
},
|
|
436
464
|
telemetry: {
|
|
437
465
|
...args.telemetry ?? {},
|
|
@@ -466,6 +494,9 @@ async function runReasonEngine(args) {
|
|
|
466
494
|
switch (chunk.type) {
|
|
467
495
|
case "text-delta": {
|
|
468
496
|
if (chunk.delta.length === 0) break;
|
|
497
|
+
const tokenAt = Date.now();
|
|
498
|
+
firstTextTokenAt ??= tokenAt;
|
|
499
|
+
lastTextTokenAt = tokenAt;
|
|
469
500
|
final += chunk.delta;
|
|
470
501
|
args.onTextChunk?.(chunk.delta);
|
|
471
502
|
if (chunk.raw !== void 0) {
|
|
@@ -1709,6 +1740,11 @@ var runReasonToolLoop = async (args) => {
|
|
|
1709
1740
|
tool.name,
|
|
1710
1741
|
false
|
|
1711
1742
|
);
|
|
1743
|
+
traceSpan?.addEvent?.("useReason.tool-call.start", {
|
|
1744
|
+
stepIndex,
|
|
1745
|
+
tool: tool.name,
|
|
1746
|
+
toolCallId: toolCall.id
|
|
1747
|
+
});
|
|
1712
1748
|
if (shouldEmitTool) {
|
|
1713
1749
|
emitToolEvent("tool-call-start", {
|
|
1714
1750
|
tool: tool.name,
|
|
@@ -2311,14 +2347,243 @@ function useWorkflowState(key, ...rest) {
|
|
|
2311
2347
|
};
|
|
2312
2348
|
return [getValue(), setValue];
|
|
2313
2349
|
}
|
|
2350
|
+
|
|
2351
|
+
// src/workflow.ts
|
|
2352
|
+
var import_node_crypto5 = require("crypto");
|
|
2353
|
+
|
|
2354
|
+
// src/workflow-telemetry.ts
|
|
2355
|
+
var import_node_crypto4 = require("crypto");
|
|
2356
|
+
function emitWorkflowCall(telemetry, type, payload) {
|
|
2357
|
+
const base = telemetry?.correlation;
|
|
2358
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
2359
|
+
return;
|
|
2360
|
+
try {
|
|
2361
|
+
const active = telemetry.trace?.getActiveContext?.();
|
|
2362
|
+
void Promise.resolve(
|
|
2363
|
+
telemetry.reporter.emit([
|
|
2364
|
+
{
|
|
2365
|
+
schemaVersion: 1,
|
|
2366
|
+
eventId: (0, import_node_crypto4.randomUUID)(),
|
|
2367
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2368
|
+
environment: telemetry.environment,
|
|
2369
|
+
service: telemetry.service,
|
|
2370
|
+
correlation: {
|
|
2371
|
+
...base,
|
|
2372
|
+
runId: base.runId,
|
|
2373
|
+
workflowId: base.workflowId,
|
|
2374
|
+
...active
|
|
2375
|
+
},
|
|
2376
|
+
type,
|
|
2377
|
+
payload
|
|
2378
|
+
}
|
|
2379
|
+
])
|
|
2380
|
+
).catch(() => {
|
|
2381
|
+
});
|
|
2382
|
+
} catch {
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
function workflowCallContent(telemetry, side, value) {
|
|
2386
|
+
const policy = telemetry?.captureContent;
|
|
2387
|
+
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
2388
|
+
return {};
|
|
2389
|
+
const serialized = JSON.stringify(value);
|
|
2390
|
+
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// src/workflow.ts
|
|
2394
|
+
var WorkflowCallError = class extends Error {
|
|
2395
|
+
constructor(workflow, message) {
|
|
2396
|
+
super(`Workflow '${workflow}': ${message}`);
|
|
2397
|
+
this.workflow = workflow;
|
|
2398
|
+
this.name = "WorkflowCallError";
|
|
2399
|
+
}
|
|
2400
|
+
workflow;
|
|
2401
|
+
};
|
|
2402
|
+
function workflowCallFingerprint(value) {
|
|
2403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2404
|
+
const canonical = (v) => {
|
|
2405
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2406
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2407
|
+
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2408
|
+
throw new Error("Workflow input and output must be JSON serializable.");
|
|
2409
|
+
}
|
|
2410
|
+
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2411
|
+
throw new Error(
|
|
2412
|
+
"Workflow input and output must contain plain JSON objects."
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
seen.add(v);
|
|
2416
|
+
const result = Array.isArray(v) ? v.map(canonical) : Object.fromEntries(
|
|
2417
|
+
Object.keys(v).sort().map((key) => [
|
|
2418
|
+
key,
|
|
2419
|
+
canonical(v[key])
|
|
2420
|
+
])
|
|
2421
|
+
);
|
|
2422
|
+
seen.delete(v);
|
|
2423
|
+
return result;
|
|
2424
|
+
};
|
|
2425
|
+
return JSON.stringify(canonical(value));
|
|
2426
|
+
}
|
|
2427
|
+
function createWorkflowHooks(workflows) {
|
|
2428
|
+
return {
|
|
2429
|
+
useWorkflow: (args) => {
|
|
2430
|
+
const workflow = workflows[args.workflow];
|
|
2431
|
+
if (!workflow || workflow.id !== args.workflow)
|
|
2432
|
+
throw new Error("Workflow registry keys must match definition ids.");
|
|
2433
|
+
return useWorkflow({ ...args, workflow });
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
async function useWorkflow(args) {
|
|
2438
|
+
const ctx = getHookContext();
|
|
2439
|
+
const service = ctx.node.callWorkflow;
|
|
2440
|
+
if (!service)
|
|
2441
|
+
throw new Error("useWorkflow requires an agent workflow registry.");
|
|
2442
|
+
if (!args.id.trim())
|
|
2443
|
+
throw new Error("useWorkflow requires a stable, nonempty id.");
|
|
2444
|
+
if (ctx.workflowCallActive)
|
|
2445
|
+
throw new Error(
|
|
2446
|
+
"Concurrent workflow calls are not supported; await each call."
|
|
2447
|
+
);
|
|
2448
|
+
if (ctx.workflowCallIds.has(args.id))
|
|
2449
|
+
throw new Error(
|
|
2450
|
+
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
2451
|
+
);
|
|
2452
|
+
if (ctx.workflowCallIds.size >= 64)
|
|
2453
|
+
throw new Error(
|
|
2454
|
+
"A node may call at most 64 child workflows per activation."
|
|
2455
|
+
);
|
|
2456
|
+
ctx.workflowCallIds.add(args.id);
|
|
2457
|
+
const definition = typeof args.workflow === "string" ? void 0 : args.workflow;
|
|
2458
|
+
const workflow = definition?.id ?? args.workflow;
|
|
2459
|
+
const input = args.input;
|
|
2460
|
+
const fingerprint = workflowCallFingerprint({
|
|
2461
|
+
workflow,
|
|
2462
|
+
input,
|
|
2463
|
+
version: definition?.version ?? null
|
|
2464
|
+
});
|
|
2465
|
+
const key = `__useWorkflow:${args.id}`;
|
|
2466
|
+
let record = ctx.currentNodeState.byKey[key];
|
|
2467
|
+
if (record && record.fingerprint !== fingerprint)
|
|
2468
|
+
throw new WorkflowCallError(
|
|
2469
|
+
workflow,
|
|
2470
|
+
"call input or target changed during replay"
|
|
2471
|
+
);
|
|
2472
|
+
record ??= {
|
|
2473
|
+
fingerprint,
|
|
2474
|
+
invocationId: (0, import_node_crypto5.randomUUID)(),
|
|
2475
|
+
status: "running",
|
|
2476
|
+
interrupts: []
|
|
2477
|
+
};
|
|
2478
|
+
ctx.currentNodeState.byKey[key] = record;
|
|
2479
|
+
ctx.stateDirty = true;
|
|
2480
|
+
ctx.workflowCallActive = true;
|
|
2481
|
+
const telemetry = ctx.node.workflowCallTelemetry;
|
|
2482
|
+
const activationKey = "__useWorkflowActivation";
|
|
2483
|
+
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
|
|
2484
|
+
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
2485
|
+
const metadata = {
|
|
2486
|
+
invocationId: record.invocationId,
|
|
2487
|
+
parentInvocationId: telemetry?.correlation?.invocationId ?? null,
|
|
2488
|
+
branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
|
|
2489
|
+
callId: args.id,
|
|
2490
|
+
callerNodeId: ctx.node.graph.node,
|
|
2491
|
+
callerNodeExecutionId,
|
|
2492
|
+
sourceWorkflowId: ctx.node.graph.name,
|
|
2493
|
+
targetWorkflowId: workflow,
|
|
2494
|
+
targetVersion: definition?.version ?? null
|
|
2495
|
+
};
|
|
2496
|
+
record.telemetry = metadata;
|
|
2497
|
+
const report = (status, content = {}) => {
|
|
2498
|
+
record.sequence = (record.sequence ?? 0) + 1;
|
|
2499
|
+
emitWorkflowCall(telemetry, `workflow.call.${status}`, {
|
|
2500
|
+
...metadata,
|
|
2501
|
+
sequence: record.sequence,
|
|
2502
|
+
...content
|
|
2503
|
+
});
|
|
2504
|
+
};
|
|
2505
|
+
const cached = record.status === "completed";
|
|
2506
|
+
try {
|
|
2507
|
+
for (const interruption of record.interrupts) {
|
|
2508
|
+
interruption.response = await awaitInterruptInternal({
|
|
2509
|
+
request: interruption.request,
|
|
2510
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
if (record.status === "failed")
|
|
2514
|
+
throw new WorkflowCallError(workflow, record.error ?? "child failed");
|
|
2515
|
+
while (record.status !== "completed") {
|
|
2516
|
+
let outcome;
|
|
2517
|
+
try {
|
|
2518
|
+
report(
|
|
2519
|
+
record.snapshot ? "resumed" : "started",
|
|
2520
|
+
workflowCallContent(telemetry, "input", input)
|
|
2521
|
+
);
|
|
2522
|
+
outcome = await service({
|
|
2523
|
+
callerNodeExecutionId,
|
|
2524
|
+
id: args.id,
|
|
2525
|
+
workflow,
|
|
2526
|
+
input,
|
|
2527
|
+
definition,
|
|
2528
|
+
invocationId: record.invocationId,
|
|
2529
|
+
...record.snapshot ? { snapshot: record.snapshot } : {},
|
|
2530
|
+
...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
|
|
2531
|
+
});
|
|
2532
|
+
} catch (error) {
|
|
2533
|
+
record.status = "failed";
|
|
2534
|
+
record.error = error instanceof Error ? error.message : String(error);
|
|
2535
|
+
report("failed", { error: "Child workflow failed" });
|
|
2536
|
+
throw new WorkflowCallError(workflow, record.error);
|
|
2537
|
+
}
|
|
2538
|
+
if (outcome.status === "completed") {
|
|
2539
|
+
record.data = outcome.data;
|
|
2540
|
+
accumulateTokenUsage(outcome.usage);
|
|
2541
|
+
workflowCallFingerprint(record.data);
|
|
2542
|
+
record.status = "completed";
|
|
2543
|
+
delete record.snapshot;
|
|
2544
|
+
report(
|
|
2545
|
+
"completed",
|
|
2546
|
+
workflowCallContent(telemetry, "output", record.data)
|
|
2547
|
+
);
|
|
2548
|
+
} else {
|
|
2549
|
+
record.status = "interrupted";
|
|
2550
|
+
record.snapshot = outcome.snapshot;
|
|
2551
|
+
report("suspended", {
|
|
2552
|
+
leaf: outcome.request.meta?.workflowCall ?? null
|
|
2553
|
+
});
|
|
2554
|
+
const interruption = {
|
|
2555
|
+
request: outcome.request
|
|
2556
|
+
};
|
|
2557
|
+
record.interrupts.push(interruption);
|
|
2558
|
+
interruption.response = await awaitInterruptInternal({
|
|
2559
|
+
request: interruption.request,
|
|
2560
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
if (cached)
|
|
2565
|
+
report("reused", workflowCallContent(telemetry, "output", record.data));
|
|
2566
|
+
const data = record.data ?? {};
|
|
2567
|
+
return {
|
|
2568
|
+
data: JSON.parse(JSON.stringify(data))
|
|
2569
|
+
};
|
|
2570
|
+
} finally {
|
|
2571
|
+
ctx.workflowCallActive = false;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2314
2574
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2315
2575
|
0 && (module.exports = {
|
|
2576
|
+
WorkflowCallError,
|
|
2577
|
+
createWorkflowHooks,
|
|
2578
|
+
emitWorkflowCall,
|
|
2316
2579
|
runWithHookContext,
|
|
2317
2580
|
useInterrupt,
|
|
2318
2581
|
useNodeState,
|
|
2319
2582
|
useReason,
|
|
2320
2583
|
useRuntimeContext,
|
|
2321
2584
|
useStructuredData,
|
|
2322
|
-
|
|
2585
|
+
useWorkflow,
|
|
2586
|
+
useWorkflowState,
|
|
2587
|
+
workflowCallFingerprint
|
|
2323
2588
|
});
|
|
2324
2589
|
//# sourceMappingURL=index.js.map
|