@kortyx/hooks 0.17.0 → 0.18.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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.17.1...hooks-v0.18.0) (2026-08-02)
4
+
5
+
6
+ ### Features
7
+
8
+ * **studio:** release self-hosted observability preview ([4e9d3fd](https://github.com/kortyx-io/kortyx/commit/4e9d3fdbc7233f9b1de94ca1afe6dbae9cc9157f))
9
+ * **studio:** release self-hosted observability preview ([#139](https://github.com/kortyx-io/kortyx/issues/139)) ([4e9d3fd](https://github.com/kortyx-io/kortyx/commit/4e9d3fdbc7233f9b1de94ca1afe6dbae9cc9157f))
10
+ * **telemetry:** add persisted studio read models ([cebb06b](https://github.com/kortyx-io/kortyx/commit/cebb06bb74ccd4422e263f0ff4efa0fee5a16240))
11
+
12
+
13
+ ### Dependencies
14
+
15
+ * The following workspace dependencies were updated
16
+ * dependencies
17
+ * @kortyx/telemetry-contracts bumped to 0.2.0
18
+
19
+ ## [0.17.1](https://github.com/kortyx-io/kortyx/compare/hooks-v0.17.0...hooks-v0.17.1) (2026-05-28)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * pass same metadata as useReason ([7b08754](https://github.com/kortyx-io/kortyx/commit/7b087542e54e9b0fe51bd3596a787dee41d5544a))
25
+ * pass same metadata as useReason ([5540ef4](https://github.com/kortyx-io/kortyx/commit/5540ef4b0a339fd43b09721b826f6b01a670c2a7))
26
+
3
27
  ## [0.17.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.16.0...hooks-v0.17.0) (2026-05-26)
4
28
 
5
29
 
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
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
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
- 2. Grant of Copyright License. Subject to the terms and conditions of
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
- 3. Grant of Patent License. Subject to the terms and conditions of
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
- 4. Redistribution. You may reproduce and distribute copies of the
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
- Derivative Works a copy of this License; and
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
- stating that You changed the files; and
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
- that You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
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
- distribution, then any Derivative Works that You distribute must
108
- include a readable copy of the attribution notices contained
109
- within such NOTICE file, excluding those notices that do not
110
- pertain to any part of the Derivative Works, in at least one
111
- of the following places: within a NOTICE text file distributed
112
- as part of the Derivative Works; within the Source form or
113
- documentation, if provided along with the Derivative Works; or,
114
- within a display generated by the Derivative Works, if and
115
- wherever such third-party notices normally appear. The contents
116
- of the NOTICE file are for informational purposes only and
117
- do not modify the License. You may add Your own attribution
118
- notices within Derivative Works that You distribute, alongside
119
- or as an addendum to the NOTICE text from the Work, provided
120
- that such additional attribution notices cannot be construed
121
- as modifying the License.
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
- 5. Submission of Contributions. Unless You explicitly state otherwise,
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
- 6. Trademarks. This License does not grant permission to use the trade
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
- 7. Disclaimer of Warranty. Unless required by applicable law or
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
- 8. Limitation of Liability. In no event and under no legal theory,
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
- 9. Accepting Warranty or Additional Liability. While redistributing
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
- END OF TERMS AND CONDITIONS
184
+ END OF TERMS AND CONDITIONS
177
185
 
178
- APPENDIX: How to apply the Apache License to your work.
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
- Copyright [yyyy] [name of copyright owner]
197
+ Copyright [yyyy] [name of copyright owner]
190
198
 
191
- Licensed under the Apache License, Version 2.0 (the "License");
192
- you may not use this file except in compliance with the License.
193
- You may obtain a copy of the License at
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
- Unless required by applicable law or agreed to in writing, software
198
- distributed under the License is distributed on an "AS IS" BASIS,
199
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
- See the License for the specific language governing permissions and
201
- limitations under the License.
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/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { NodeConfig, InterruptInput, InterruptResult, GraphState } from '@kortyx/core';
2
- import { R as ReasonTraceAdapter, K as KortyxTraceMetadata } from './tracing-DhjgeiDS.js';
3
- export { a as KortyxTelemetryConfig, b as KortyxTelemetryContentCapture, c as KortyxTelemetryPrompt, d as KortyxTraceAdapter, e as ReasonTraceAttributes, f as ReasonTraceSpan, g as ReasonTraceSpanEndArgs, h as ReasonTraceSpanStartArgs } from './tracing-DhjgeiDS.js';
2
+ import { R as ReasonTraceAdapter, K as KortyxTraceMetadata } from './tracing-DeYHBpY1.js';
3
+ export { E as EnsureWorkflowTopologyRequest, a as EnsureWorkflowTopologyResponse, b as KortyxTelemetryConfig, c as KortyxTelemetryContentCapture, d as KortyxTelemetryCorrelation, e as KortyxTelemetryEvent, f as KortyxTelemetryEventType, 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-DeYHBpY1.js';
4
4
  import { ProviderModelRef, KortyxReasoningOptions, KortyxResponseFormat, KortyxExecutableTool, KortyxUsage, KortyxFinishReason, KortyxProviderMetadata, KortyxWarning, KortyxToolCall, KortyxToolResult } from '@kortyx/providers';
5
+ import '@kortyx/telemetry-contracts';
5
6
 
6
7
  type HookNodeRuntimeContext = {
7
8
  graph: {
package/dist/index.js CHANGED
@@ -175,6 +175,48 @@ function getHookContext() {
175
175
  return ctx;
176
176
  }
177
177
 
178
+ // src/reason/checkpoint.ts
179
+ var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
180
+ var readReasonCheckpoint = (raw) => {
181
+ if (!raw || typeof raw !== "object") return void 0;
182
+ const value = raw;
183
+ if (value.status !== "awaiting_interrupt") return void 0;
184
+ if (typeof value.firstText !== "string") return void 0;
185
+ if (!value.request || typeof value.request !== "object") return void 0;
186
+ return {
187
+ status: "awaiting_interrupt",
188
+ request: value.request,
189
+ firstText: value.firstText,
190
+ ...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
191
+ ...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
192
+ ...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
193
+ ...Object.hasOwn(value, "firstProviderMetadata") ? {
194
+ firstProviderMetadata: value.firstProviderMetadata
195
+ } : {},
196
+ ...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
197
+ ...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
198
+ };
199
+ };
200
+ var readReasonCompletedCheckpoint = (raw) => {
201
+ if (!raw || typeof raw !== "object") return void 0;
202
+ const value = raw;
203
+ if (value.status !== "completed") return void 0;
204
+ if (!value.result || typeof value.result !== "object") return void 0;
205
+ return {
206
+ status: "completed",
207
+ result: value.result
208
+ };
209
+ };
210
+ var resolveHookStatePatch = (args) => ({
211
+ __kortyx: {
212
+ nodeState: {
213
+ nodeId: args.nodeId,
214
+ state: args.currentNodeState
215
+ },
216
+ workflowState: args.workflowState
217
+ }
218
+ });
219
+
178
220
  // src/validation.ts
179
221
  var parseWithSchema = (schema, value, label) => {
180
222
  if (!schema) return value;
@@ -193,12 +235,21 @@ var awaitInterruptInternal = (args) => {
193
235
  args.request,
194
236
  "useInterrupt request"
195
237
  );
238
+ const userMeta = args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? args.meta : {};
239
+ const resumeStatePatch = resolveHookStatePatch({
240
+ nodeId: ctx.node.graph.node,
241
+ currentNodeState: ctx.currentNodeState,
242
+ workflowState: ctx.workflowState
243
+ });
196
244
  const enrichedRequest = {
197
245
  ...request,
198
246
  ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
199
247
  ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
200
248
  ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
201
- ...args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? { meta: args.meta } : {}
249
+ meta: {
250
+ ...userMeta,
251
+ __kortyxResumeStatePatch: resumeStatePatch
252
+ }
202
253
  };
203
254
  const raw = ctx.node.awaitInterrupt(enrichedRequest);
204
255
  const response = parseWithSchema(
@@ -301,48 +352,6 @@ var emitStructuredData = (args) => {
301
352
  var shouldEmitStructured = (cfg) => Boolean(cfg);
302
353
  var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
303
354
 
304
- // src/reason/checkpoint.ts
305
- var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
306
- var readReasonCheckpoint = (raw) => {
307
- if (!raw || typeof raw !== "object") return void 0;
308
- const value = raw;
309
- if (value.status !== "awaiting_interrupt") return void 0;
310
- if (typeof value.firstText !== "string") return void 0;
311
- if (!value.request || typeof value.request !== "object") return void 0;
312
- return {
313
- status: "awaiting_interrupt",
314
- request: value.request,
315
- firstText: value.firstText,
316
- ...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
317
- ...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
318
- ...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
319
- ...Object.hasOwn(value, "firstProviderMetadata") ? {
320
- firstProviderMetadata: value.firstProviderMetadata
321
- } : {},
322
- ...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
323
- ...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
324
- };
325
- };
326
- var readReasonCompletedCheckpoint = (raw) => {
327
- if (!raw || typeof raw !== "object") return void 0;
328
- const value = raw;
329
- if (value.status !== "completed") return void 0;
330
- if (!value.result || typeof value.result !== "object") return void 0;
331
- return {
332
- status: "completed",
333
- result: value.result
334
- };
335
- };
336
- var resolveHookStatePatch = (args) => ({
337
- __kortyx: {
338
- nodeState: {
339
- nodeId: args.nodeId,
340
- state: args.currentNodeState
341
- },
342
- workflowState: args.workflowState
343
- }
344
- });
345
-
346
355
  // src/reason/engine.ts
347
356
  var import_node_crypto3 = require("crypto");
348
357
 
@@ -415,6 +424,17 @@ async function runReasonEngine(args) {
415
424
  input: args.telemetry?.input ?? messages
416
425
  }
417
426
  });
427
+ const modelRequestStartedAt = Date.now();
428
+ let firstTextTokenAt;
429
+ let lastTextTokenAt;
430
+ const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
431
+ ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
432
+ streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
433
+ timeToLastTokenMs: Math.max(
434
+ 0,
435
+ lastTextTokenAt - modelRequestStartedAt
436
+ )
437
+ };
418
438
  const endTrace = (span, result) => {
419
439
  span?.end?.({
420
440
  ...result.usage !== void 0 ? { usage: result.usage } : {},
@@ -422,7 +442,8 @@ async function runReasonEngine(args) {
422
442
  ...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
423
443
  ...result.warnings !== void 0 ? { warnings: result.warnings } : {},
424
444
  attributes: {
425
- textLength: result.text.length
445
+ textLength: result.text.length,
446
+ ...generationTimingAttributes()
426
447
  },
427
448
  telemetry: {
428
449
  ...args.telemetry ?? {},
@@ -457,6 +478,9 @@ async function runReasonEngine(args) {
457
478
  switch (chunk.type) {
458
479
  case "text-delta": {
459
480
  if (chunk.delta.length === 0) break;
481
+ const tokenAt = Date.now();
482
+ firstTextTokenAt ??= tokenAt;
483
+ lastTextTokenAt = tokenAt;
460
484
  final += chunk.delta;
461
485
  args.onTextChunk?.(chunk.delta);
462
486
  if (chunk.raw !== void 0) {
@@ -1700,6 +1724,11 @@ var runReasonToolLoop = async (args) => {
1700
1724
  tool.name,
1701
1725
  false
1702
1726
  );
1727
+ traceSpan?.addEvent?.("useReason.tool-call.start", {
1728
+ stepIndex,
1729
+ tool: tool.name,
1730
+ toolCallId: toolCall.id
1731
+ });
1703
1732
  if (shouldEmitTool) {
1704
1733
  emitToolEvent("tool-call-start", {
1705
1734
  tool: tool.name,