@kortyx/agent 0.22.2 → 0.24.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 +30 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +142 -78
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.24.0](https://github.com/kortyx-io/kortyx/compare/agent-v0.23.0...agent-v0.24.0) (2026-09-16)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **runtime:** add PostgreSQL persistence, caching composition, and retention ([#207](https://github.com/kortyx-io/kortyx/issues/207)) ([6c1e7df](https://github.com/kortyx-io/kortyx/commit/6c1e7df22ffe8c792bf52b7dba5ae6ea04c1eb2a))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @kortyx/runtime bumped to 0.19.0
|
|
16
|
+
* devDependencies
|
|
17
|
+
* @kortyx/hooks bumped to 0.25.3
|
|
18
|
+
|
|
19
|
+
## [0.23.0](https://github.com/kortyx-io/kortyx/compare/agent-v0.22.2...agent-v0.23.0) (2026-09-16)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* **runtime:** support durable child calls in parallel DAGs ([#203](https://github.com/kortyx-io/kortyx/issues/203)) ([525e0e7](https://github.com/kortyx-io/kortyx/commit/525e0e7a73fa5f9d78232ca3155d86be7bc1022d))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Dependencies
|
|
28
|
+
|
|
29
|
+
* The following workspace dependencies were updated
|
|
30
|
+
* dependencies
|
|
31
|
+
* @kortyx/runtime bumped to 0.18.0
|
|
32
|
+
|
|
3
33
|
## [0.22.2](https://github.com/kortyx-io/kortyx/compare/agent-v0.22.1...agent-v0.22.2) (2026-09-15)
|
|
4
34
|
|
|
5
35
|
|
package/dist/index.d.ts
CHANGED
|
@@ -254,6 +254,8 @@ type OrchestrationOutcome = {
|
|
|
254
254
|
error?: unknown;
|
|
255
255
|
};
|
|
256
256
|
interface OrchestrateArgs {
|
|
257
|
+
/** Resume owns the lease while restoring its snapshot, then transfers it to execution. */
|
|
258
|
+
executionLeaseRelease?: (() => Promise<void>) | undefined;
|
|
257
259
|
executionSignal?: AbortSignal | undefined;
|
|
258
260
|
onExecution?: ((completion: Promise<void>) => void) | undefined;
|
|
259
261
|
abortSignal?: AbortSignal | undefined;
|
|
@@ -272,7 +274,7 @@ interface OrchestrateArgs {
|
|
|
272
274
|
* Orchestrates runtime execution with mid-stream transitions emitted by
|
|
273
275
|
* runtime events.
|
|
274
276
|
*/
|
|
275
|
-
declare function orchestrateGraphStream({ sessionId, runId, graph, state, config: initialConfig, abortSignal: requestSignal, executionSignal, onExecution, emitOutput, onOutcome, selectWorkflow, knownWorkflowIds, frameworkAdapter, }: OrchestrateArgs): Promise<NodeJS.ReadableStream>;
|
|
277
|
+
declare function orchestrateGraphStream({ sessionId, runId, graph, state, config: initialConfig, abortSignal: requestSignal, executionSignal, onExecution, emitOutput, onOutcome, selectWorkflow, knownWorkflowIds, frameworkAdapter, executionLeaseRelease, }: OrchestrateArgs): Promise<NodeJS.ReadableStream>;
|
|
276
278
|
|
|
277
279
|
interface ResumeMeta {
|
|
278
280
|
token: string;
|
package/dist/index.js
CHANGED
|
@@ -3540,6 +3540,7 @@ async function* transformGraphStreamForUI(stream, options = {}) {
|
|
|
3540
3540
|
let latestGraphState = null;
|
|
3541
3541
|
for await (const event of stream) {
|
|
3542
3542
|
const { event: type, name, data } = event ?? {};
|
|
3543
|
+
if (name?.startsWith("__kortyx_parallel_")) continue;
|
|
3543
3544
|
if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
|
|
3544
3545
|
switch (type) {
|
|
3545
3546
|
case "on_chain_start":
|
|
@@ -3893,10 +3894,15 @@ async function orchestrateGraphStream({
|
|
|
3893
3894
|
onOutcome,
|
|
3894
3895
|
selectWorkflow,
|
|
3895
3896
|
knownWorkflowIds,
|
|
3896
|
-
frameworkAdapter
|
|
3897
|
+
frameworkAdapter,
|
|
3898
|
+
executionLeaseRelease
|
|
3897
3899
|
}) {
|
|
3898
3900
|
const out = new import_node_stream.PassThrough({ objectMode: true });
|
|
3899
3901
|
const write = (chunk) => {
|
|
3902
|
+
if (isRecord3(chunk) && chunk.type === "done" && isGraphState(chunk.data)) {
|
|
3903
|
+
const { __kortyxParallelGraph: _journal, ...runtime } = chunk.data.runtime;
|
|
3904
|
+
chunk = { ...chunk, data: { ...chunk.data, runtime } };
|
|
3905
|
+
}
|
|
3900
3906
|
if (emitOutput && !response.closed && !out.destroyed) out.write(chunk);
|
|
3901
3907
|
};
|
|
3902
3908
|
let outcomeState = state;
|
|
@@ -3907,11 +3913,13 @@ async function orchestrateGraphStream({
|
|
|
3907
3913
|
let currentGraph = graph;
|
|
3908
3914
|
let currentState = state;
|
|
3909
3915
|
let finished = false;
|
|
3916
|
+
const leaseAbort = new AbortController();
|
|
3917
|
+
let releaseLease = executionLeaseRelease;
|
|
3910
3918
|
const response = createResponseLifecycle({
|
|
3911
3919
|
enabled: emitOutput,
|
|
3912
3920
|
restored: initialConfig.responseCompleted === true,
|
|
3913
3921
|
requestSignal,
|
|
3914
|
-
executionSignal,
|
|
3922
|
+
executionSignal: executionSignal ? AbortSignal.any([executionSignal, leaseAbort.signal]) : leaseAbort.signal,
|
|
3915
3923
|
onClosed: () => {
|
|
3916
3924
|
out.write({ type: "done" });
|
|
3917
3925
|
out.end();
|
|
@@ -4226,6 +4234,8 @@ async function orchestrateGraphStream({
|
|
|
4226
4234
|
return;
|
|
4227
4235
|
}
|
|
4228
4236
|
if (event === "status") {
|
|
4237
|
+
const statusNode = payload?.node;
|
|
4238
|
+
if (typeof statusNode === "string") touchedNodes.add(statusNode);
|
|
4229
4239
|
if (!debugEnabled) return;
|
|
4230
4240
|
const statusMessage = payload?.message;
|
|
4231
4241
|
const msg = statusMessage === null || statusMessage === void 0 ? "" : String(statusMessage);
|
|
@@ -4788,10 +4798,18 @@ async function orchestrateGraphStream({
|
|
|
4788
4798
|
if (!traceAdapter?.withSpan) {
|
|
4789
4799
|
emitTraceChunk();
|
|
4790
4800
|
}
|
|
4791
|
-
const
|
|
4801
|
+
const startRun = () => traceAdapter?.withSpan ? traceAdapter.withSpan(runSpanArgs, (runTraceSpan) => {
|
|
4792
4802
|
emitTraceChunk();
|
|
4793
4803
|
return runLoop(runTraceSpan);
|
|
4794
4804
|
}) : runLoop(fallbackRunSpan ?? {});
|
|
4805
|
+
const runPromise = frameworkAdapter?.acquireRunLease && !releaseLease ? frameworkAdapter.acquireRunLease({
|
|
4806
|
+
runId,
|
|
4807
|
+
...sessionId ? { sessionId } : {},
|
|
4808
|
+
onLost: (cause) => leaseAbort.abort(cause)
|
|
4809
|
+
}).then((release) => {
|
|
4810
|
+
releaseLease = release;
|
|
4811
|
+
return startRun();
|
|
4812
|
+
}) : startRun();
|
|
4795
4813
|
const finishCancelled = async () => {
|
|
4796
4814
|
emitTelemetryEvent({
|
|
4797
4815
|
config,
|
|
@@ -4821,6 +4839,12 @@ async function orchestrateGraphStream({
|
|
|
4821
4839
|
out.end();
|
|
4822
4840
|
};
|
|
4823
4841
|
const completion = runPromise.catch(async (err) => {
|
|
4842
|
+
const runtimePatch = err?.__kortyxHookStatePatch;
|
|
4843
|
+
if (runtimePatch)
|
|
4844
|
+
outcomeState = {
|
|
4845
|
+
...outcomeState,
|
|
4846
|
+
runtime: { ...outcomeState.runtime, ...runtimePatch }
|
|
4847
|
+
};
|
|
4824
4848
|
if (abortSignal?.aborted || (0, import_core2.isExecutionCancelled)(err)) {
|
|
4825
4849
|
await finishCancelled();
|
|
4826
4850
|
return;
|
|
@@ -4949,7 +4973,12 @@ async function orchestrateGraphStream({
|
|
|
4949
4973
|
write({ type: "done" });
|
|
4950
4974
|
finished = true;
|
|
4951
4975
|
out.end();
|
|
4952
|
-
}).finally(() => {
|
|
4976
|
+
}).finally(async () => {
|
|
4977
|
+
try {
|
|
4978
|
+
await releaseLease?.();
|
|
4979
|
+
} catch (error) {
|
|
4980
|
+
console.error("[execution:releaseLease]", (0, import_errors4.serializeFailure)(error));
|
|
4981
|
+
}
|
|
4953
4982
|
response.dispose();
|
|
4954
4983
|
onOutcome?.({
|
|
4955
4984
|
state: outcomeState,
|
|
@@ -5010,7 +5039,7 @@ async function tryPrepareResumeStream({
|
|
|
5010
5039
|
if (!meta) return null;
|
|
5011
5040
|
const store = frameworkAdapter?.pendingRequests;
|
|
5012
5041
|
if (!store) return null;
|
|
5013
|
-
|
|
5042
|
+
let pending = await store.get(meta.token);
|
|
5014
5043
|
if (!pending || pending.requestId !== meta.requestId) {
|
|
5015
5044
|
throw new ExecutionRequestError(
|
|
5016
5045
|
"INVALID_RESUME",
|
|
@@ -5040,34 +5069,48 @@ async function tryPrepareResumeStream({
|
|
|
5040
5069
|
yield { type: "done" };
|
|
5041
5070
|
})();
|
|
5042
5071
|
}
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
"Interrupt has already been resumed or cancelled."
|
|
5047
|
-
);
|
|
5048
|
-
if (meta.cancel) {
|
|
5049
|
-
await store.delete(pending.token);
|
|
5050
|
-
emitTelemetryEvent({
|
|
5051
|
-
config,
|
|
5052
|
-
type: "interrupt.cancelled",
|
|
5053
|
-
correlation: {
|
|
5054
|
-
runId: pending.runId,
|
|
5055
|
-
sessionId,
|
|
5056
|
-
workflowId: pending.workflow,
|
|
5057
|
-
nodeId: pending.node
|
|
5058
|
-
},
|
|
5059
|
-
payload: {
|
|
5060
|
-
interruptId: pending.requestId,
|
|
5061
|
-
reason: "cancelled_by_client"
|
|
5062
|
-
},
|
|
5063
|
-
flush: true
|
|
5064
|
-
});
|
|
5065
|
-
onOutcome?.({ state: pending.state, cancelled: true });
|
|
5066
|
-
return (async function* () {
|
|
5067
|
-
yield { type: "done" };
|
|
5068
|
-
})();
|
|
5069
|
-
}
|
|
5072
|
+
const leaseAbort = new AbortController();
|
|
5073
|
+
let releaseLease;
|
|
5074
|
+
let consumed = false;
|
|
5070
5075
|
try {
|
|
5076
|
+
releaseLease = await frameworkAdapter?.acquireRunLease?.({
|
|
5077
|
+
runId: pending.runId,
|
|
5078
|
+
sessionId,
|
|
5079
|
+
onLost: (cause) => leaseAbort.abort(cause)
|
|
5080
|
+
});
|
|
5081
|
+
leaseAbort.signal.throwIfAborted();
|
|
5082
|
+
if (store.take) {
|
|
5083
|
+
const taken = await store.take(meta.token);
|
|
5084
|
+
if (!taken)
|
|
5085
|
+
throw new ExecutionRequestError(
|
|
5086
|
+
"INVALID_RESUME",
|
|
5087
|
+
"Interrupt has already been resumed or cancelled."
|
|
5088
|
+
);
|
|
5089
|
+
pending = taken;
|
|
5090
|
+
}
|
|
5091
|
+
consumed = true;
|
|
5092
|
+
if (meta.cancel) {
|
|
5093
|
+
await store.delete(pending.token);
|
|
5094
|
+
emitTelemetryEvent({
|
|
5095
|
+
config,
|
|
5096
|
+
type: "interrupt.cancelled",
|
|
5097
|
+
correlation: {
|
|
5098
|
+
runId: pending.runId,
|
|
5099
|
+
sessionId,
|
|
5100
|
+
workflowId: pending.workflow,
|
|
5101
|
+
nodeId: pending.node
|
|
5102
|
+
},
|
|
5103
|
+
payload: {
|
|
5104
|
+
interruptId: pending.requestId,
|
|
5105
|
+
reason: "cancelled_by_client"
|
|
5106
|
+
},
|
|
5107
|
+
flush: true
|
|
5108
|
+
});
|
|
5109
|
+
onOutcome?.({ state: pending.state, cancelled: true });
|
|
5110
|
+
return (async function* () {
|
|
5111
|
+
yield { type: "done" };
|
|
5112
|
+
})();
|
|
5113
|
+
}
|
|
5071
5114
|
const resumeData = isLimitPause ? {} : applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
|
|
5072
5115
|
const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
|
|
5073
5116
|
const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
|
|
@@ -5122,6 +5165,7 @@ async function tryPrepareResumeStream({
|
|
|
5122
5165
|
...resumeStatePatch
|
|
5123
5166
|
};
|
|
5124
5167
|
}
|
|
5168
|
+
leaseAbort.signal.throwIfAborted();
|
|
5125
5169
|
if (pending.graphSnapshot && frameworkAdapter)
|
|
5126
5170
|
await (0, import_runtime2.restoreGraphSnapshot)(
|
|
5127
5171
|
frameworkAdapter.checkpointer,
|
|
@@ -5171,9 +5215,11 @@ async function tryPrepareResumeStream({
|
|
|
5171
5215
|
},
|
|
5172
5216
|
flush: true
|
|
5173
5217
|
});
|
|
5218
|
+
const continuedSignal = pending.responseCompleted && emitOutput === false ? abortSignal : executionSignal;
|
|
5174
5219
|
const args = {
|
|
5175
5220
|
abortSignal,
|
|
5176
|
-
executionSignal:
|
|
5221
|
+
executionSignal: continuedSignal ? AbortSignal.any([continuedSignal, leaseAbort.signal]) : leaseAbort.signal,
|
|
5222
|
+
executionLeaseRelease: releaseLease,
|
|
5177
5223
|
onExecution,
|
|
5178
5224
|
emitOutput,
|
|
5179
5225
|
onOutcome,
|
|
@@ -5194,11 +5240,15 @@ async function tryPrepareResumeStream({
|
|
|
5194
5240
|
knownWorkflowIds,
|
|
5195
5241
|
frameworkAdapter
|
|
5196
5242
|
};
|
|
5243
|
+
leaseAbort.signal.throwIfAborted();
|
|
5197
5244
|
const stream = await orchestrateGraphStream(args);
|
|
5245
|
+
releaseLease = void 0;
|
|
5198
5246
|
return stream;
|
|
5199
5247
|
} catch (error) {
|
|
5200
|
-
await store.save(pending);
|
|
5248
|
+
if (consumed && !leaseAbort.signal.aborted) await store.save(pending);
|
|
5201
5249
|
throw error;
|
|
5250
|
+
} finally {
|
|
5251
|
+
await releaseLease?.();
|
|
5202
5252
|
}
|
|
5203
5253
|
}
|
|
5204
5254
|
|
|
@@ -5894,30 +5944,38 @@ function createAgent(args) {
|
|
|
5894
5944
|
const getCheckpoint = (id) => resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
5895
5945
|
const rollbackTo = async (id) => {
|
|
5896
5946
|
const target = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
5897
|
-
const
|
|
5898
|
-
const
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
activePendingRequests,
|
|
5905
|
-
telemetry,
|
|
5906
|
-
target?.runId ?? "unknown"
|
|
5907
|
-
);
|
|
5908
|
-
await Promise.all(
|
|
5909
|
-
activePendingRequests.map(
|
|
5910
|
-
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
5911
|
-
)
|
|
5947
|
+
const sourceRunId = target?.runId ?? "unknown";
|
|
5948
|
+
const managed = resolvedFrameworkAdapter.sessionCheckpoints.managesPendingRequests;
|
|
5949
|
+
const result = await resolvedFrameworkAdapter.sessionCheckpoints.rollbackTo(
|
|
5950
|
+
id,
|
|
5951
|
+
managed ? {
|
|
5952
|
+
preparePendingRequests: (requests) => restoreWorkflowCallBranch(requests, telemetry, sourceRunId)
|
|
5953
|
+
} : void 0
|
|
5912
5954
|
);
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5955
|
+
let activePendingRequests;
|
|
5956
|
+
if (managed) {
|
|
5957
|
+
activePendingRequests = result.activePendingRequests;
|
|
5958
|
+
} else {
|
|
5959
|
+
activePendingRequests = await Promise.all(
|
|
5960
|
+
result.activePendingRequests.map(
|
|
5961
|
+
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
5962
|
+
)
|
|
5963
|
+
);
|
|
5964
|
+
restoreWorkflowCallBranch(activePendingRequests, telemetry, sourceRunId);
|
|
5965
|
+
await Promise.all(
|
|
5966
|
+
activePendingRequests.map(
|
|
5967
|
+
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
5968
|
+
)
|
|
5969
|
+
);
|
|
5970
|
+
await Promise.all([
|
|
5971
|
+
...result.invalidatedInterruptTokens.map(
|
|
5972
|
+
(token) => resolvedFrameworkAdapter.pendingRequests.delete(token)
|
|
5973
|
+
),
|
|
5974
|
+
...activePendingRequests.map(
|
|
5975
|
+
(request) => resolvedFrameworkAdapter.pendingRequests.save(request)
|
|
5976
|
+
)
|
|
5977
|
+
]);
|
|
5978
|
+
}
|
|
5921
5979
|
if (target) {
|
|
5922
5980
|
emitTelemetryEvent({
|
|
5923
5981
|
config: { ...telemetry ? { telemetry } : {} },
|
|
@@ -5937,30 +5995,36 @@ function createAgent(args) {
|
|
|
5937
5995
|
};
|
|
5938
5996
|
const fork = async (id, options) => {
|
|
5939
5997
|
const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
5998
|
+
const sourceRunId = source?.runId ?? "unknown";
|
|
5999
|
+
const managed = resolvedFrameworkAdapter.sessionCheckpoints.managesPendingRequests;
|
|
5940
6000
|
const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
|
|
5941
6001
|
id,
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
5947
|
-
)
|
|
5948
|
-
);
|
|
5949
|
-
restoreWorkflowCallBranch(
|
|
5950
|
-
activePendingRequests,
|
|
5951
|
-
telemetry,
|
|
5952
|
-
source?.runId ?? "unknown"
|
|
5953
|
-
);
|
|
5954
|
-
await Promise.all(
|
|
5955
|
-
activePendingRequests.map(
|
|
5956
|
-
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
5957
|
-
)
|
|
5958
|
-
);
|
|
5959
|
-
await Promise.all(
|
|
5960
|
-
activePendingRequests.map(
|
|
5961
|
-
(request) => resolvedFrameworkAdapter.pendingRequests.save(request)
|
|
5962
|
-
)
|
|
6002
|
+
managed ? {
|
|
6003
|
+
...options,
|
|
6004
|
+
preparePendingRequests: (requests) => restoreWorkflowCallBranch(requests, telemetry, sourceRunId)
|
|
6005
|
+
} : options
|
|
5963
6006
|
);
|
|
6007
|
+
let activePendingRequests;
|
|
6008
|
+
if (managed) {
|
|
6009
|
+
activePendingRequests = result.checkpoint.activePendingRequests;
|
|
6010
|
+
} else {
|
|
6011
|
+
activePendingRequests = await Promise.all(
|
|
6012
|
+
result.checkpoint.activePendingRequests.map(
|
|
6013
|
+
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
6014
|
+
)
|
|
6015
|
+
);
|
|
6016
|
+
restoreWorkflowCallBranch(activePendingRequests, telemetry, sourceRunId);
|
|
6017
|
+
await Promise.all(
|
|
6018
|
+
activePendingRequests.map(
|
|
6019
|
+
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
6020
|
+
)
|
|
6021
|
+
);
|
|
6022
|
+
await Promise.all(
|
|
6023
|
+
activePendingRequests.map(
|
|
6024
|
+
(request) => resolvedFrameworkAdapter.pendingRequests.save(request)
|
|
6025
|
+
)
|
|
6026
|
+
);
|
|
6027
|
+
}
|
|
5964
6028
|
emitTelemetryEvent({
|
|
5965
6029
|
config: { ...telemetry ? { telemetry } : {} },
|
|
5966
6030
|
type: "session.forked",
|