@kortyx/agent 0.32.0 → 0.33.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 +16 -0
- package/dist/index.js +316 -255
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2247,7 +2247,8 @@ var require_dist2 = __commonJS({
|
|
|
2247
2247
|
if (!value.result || typeof value.result !== "object") return void 0;
|
|
2248
2248
|
return {
|
|
2249
2249
|
status: "completed",
|
|
2250
|
-
result: value.result
|
|
2250
|
+
result: value.result,
|
|
2251
|
+
...Array.isArray(value.resolvedInterrupts) ? { resolvedInterrupts: value.resolvedInterrupts } : {}
|
|
2251
2252
|
};
|
|
2252
2253
|
};
|
|
2253
2254
|
var resolveHookStatePatch = (args) => ({
|
|
@@ -2305,6 +2306,10 @@ var require_dist2 = __commonJS({
|
|
|
2305
2306
|
raw,
|
|
2306
2307
|
"useInterrupt response"
|
|
2307
2308
|
);
|
|
2309
|
+
if (args.onResolved) {
|
|
2310
|
+
const { __kortyxResumeStatePatch: _patch, ...meta } = enrichedRequest.meta ?? {};
|
|
2311
|
+
args.onResolved({ ...enrichedRequest, meta });
|
|
2312
|
+
}
|
|
2308
2313
|
return Promise.resolve(response);
|
|
2309
2314
|
};
|
|
2310
2315
|
var CONTROL_TOOL_PREFIX = "kortyx_request_input__";
|
|
@@ -4889,7 +4894,7 @@ ${tryStringify2(args.response)}`
|
|
|
4889
4894
|
interruptInstructions: [
|
|
4890
4895
|
"Human-input rules:",
|
|
4891
4896
|
"- The kortyx_request_input__* tools pause this reasoning operation and return the validated human response as a normal tool result.",
|
|
4892
|
-
"-
|
|
4897
|
+
"- You may combine human-input tools with domain tools and output controls. Tools, emitted outputs, and human-input requests run in their requested order. Terminal output publishes last using the completed results.",
|
|
4893
4898
|
interruptConfig.mode === "required" ? "- You must call at least one human-input tool before returning the final answer." : "- Human input is optional; call a human-input tool only when the request cannot be completed safely without it."
|
|
4894
4899
|
].join("\n")
|
|
4895
4900
|
} : {},
|
|
@@ -4899,7 +4904,7 @@ ${tryStringify2(args.response)}`
|
|
|
4899
4904
|
"- The kortyx_emit__* tools publish validated structured output and return an acknowledgement. You may continue with text or more tools afterward.",
|
|
4900
4905
|
"- The kortyx_return__* tools publish a validated result and end this reasoning operation. If return tools are available, call exactly one before finishing.",
|
|
4901
4906
|
"- For contracts with realtime streaming, use kortyx_stream_emit__* or kortyx_stream_return__*. These start a separate streamed JSON generation; its fields become visible as tokens arrive.",
|
|
4902
|
-
"-
|
|
4907
|
+
"- You may combine multiple emit controls, domain tools, and human-input requests in one turn. Tools, emitted outputs, and human-input requests run in their requested order; the terminal return runs last. Outputs after domain tools or human input are regenerated using their completed results. Request at most one terminal return per turn."
|
|
4903
4908
|
].join("\n")
|
|
4904
4909
|
} : {}
|
|
4905
4910
|
});
|
|
@@ -4928,6 +4933,19 @@ ${tryStringify2(args.response)}`
|
|
|
4928
4933
|
);
|
|
4929
4934
|
let pending = checkpoint?.pending;
|
|
4930
4935
|
let pendingStepIndex = checkpoint?.pendingStepIndex;
|
|
4936
|
+
let outputContractCorrections = checkpoint?.outputContractCorrections ?? 0;
|
|
4937
|
+
const correctionExhausted = () => {
|
|
4938
|
+
const message = `useReason could not complete output-contract correction within toolExecution.maxSteps (${maxSteps}). Select exactly one terminal return contract; multiple emit contracts are allowed. Increase maxSteps to allow correction and output generation.`;
|
|
4939
|
+
return new import_errors62.ProviderRequestError(message, {
|
|
4940
|
+
code: "REASON_OUTPUT_CONTRACT_CORRECTION_EXHAUSTED",
|
|
4941
|
+
operation: "useReason",
|
|
4942
|
+
safeMessage: message,
|
|
4943
|
+
retryable: false,
|
|
4944
|
+
details: { maxSteps, outputContractCorrections }
|
|
4945
|
+
});
|
|
4946
|
+
};
|
|
4947
|
+
const resolvedInterrupts = checkpoint?.resolvedInterrupts ?? [];
|
|
4948
|
+
for (const request of resolvedInterrupts) ctx.node.replayInterrupt?.(request);
|
|
4931
4949
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
4932
4950
|
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
4933
4951
|
const emissions = checkpoint?.emissions ?? [];
|
|
@@ -4939,8 +4957,10 @@ ${tryStringify2(args.response)}`
|
|
|
4939
4957
|
messages,
|
|
4940
4958
|
steps,
|
|
4941
4959
|
approvedCalls,
|
|
4960
|
+
resolvedInterrupts,
|
|
4942
4961
|
interruptHistory,
|
|
4943
4962
|
emissions,
|
|
4963
|
+
outputContractCorrections,
|
|
4944
4964
|
...returned ? { returned } : {},
|
|
4945
4965
|
finalizing,
|
|
4946
4966
|
...pending ? { pending } : {},
|
|
@@ -4948,6 +4968,10 @@ ${tryStringify2(args.response)}`
|
|
|
4948
4968
|
};
|
|
4949
4969
|
ctx.stateDirty = true;
|
|
4950
4970
|
};
|
|
4971
|
+
const rememberInterrupt = (request) => {
|
|
4972
|
+
resolvedInterrupts.push(request);
|
|
4973
|
+
save();
|
|
4974
|
+
};
|
|
4951
4975
|
const emitToolEvent = (event, payload) => {
|
|
4952
4976
|
ctx.node.emit(event, {
|
|
4953
4977
|
...payload,
|
|
@@ -4957,7 +4981,7 @@ ${tryStringify2(args.response)}`
|
|
|
4957
4981
|
});
|
|
4958
4982
|
};
|
|
4959
4983
|
try {
|
|
4960
|
-
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex
|
|
4984
|
+
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex = steps.length) {
|
|
4961
4985
|
(0, import_core52.throwIfExecutionAborted)(abortSignal);
|
|
4962
4986
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
4963
4987
|
stepIndex,
|
|
@@ -5094,273 +5118,301 @@ ${tryStringify2(args.response)}`
|
|
|
5094
5118
|
const outputCalls = toolCalls.filter(
|
|
5095
5119
|
(call) => outputByToolName.has(call.name)
|
|
5096
5120
|
);
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
const call
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5121
|
+
const returnCalls = outputCalls.filter(
|
|
5122
|
+
(call) => outputByToolName.get(call.name)?.kind === "return"
|
|
5123
|
+
);
|
|
5124
|
+
if (returnCalls.length > 1) {
|
|
5125
|
+
let corrected = false;
|
|
5126
|
+
for (const call of returnCalls) {
|
|
5127
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id))
|
|
5128
|
+
continue;
|
|
5129
|
+
const result2 = {
|
|
5130
|
+
toolCallId: call.id,
|
|
5131
|
+
name: call.name,
|
|
5132
|
+
isError: true,
|
|
5133
|
+
content: "MULTIPLE_TERMINAL_OUTPUTS: No terminal output was published. Select exactly one return contract using the tool and human-input results. Do not repeat completed calls. Multiple emit contracts are allowed."
|
|
5134
|
+
};
|
|
5135
|
+
toolResults.push(result2);
|
|
5136
|
+
messages.push({ role: "tool", ...result2 });
|
|
5137
|
+
corrected = true;
|
|
5114
5138
|
}
|
|
5115
|
-
if (
|
|
5116
|
-
|
|
5117
|
-
|
|
5139
|
+
if (corrected) outputContractCorrections += 1;
|
|
5140
|
+
save();
|
|
5141
|
+
}
|
|
5142
|
+
const orderedCalls = [
|
|
5143
|
+
...toolCalls.filter(
|
|
5144
|
+
(call) => outputByToolName.get(call.name)?.kind !== "return"
|
|
5145
|
+
),
|
|
5146
|
+
...returnCalls
|
|
5147
|
+
];
|
|
5148
|
+
for (const toolCall of orderedCalls) {
|
|
5149
|
+
(0, import_core52.throwIfExecutionAborted)(abortSignal);
|
|
5150
|
+
if (outputByToolName.has(toolCall.name)) {
|
|
5151
|
+
const call = toolCall;
|
|
5152
|
+
const hasEvidenceCalls = toolResults.some(
|
|
5153
|
+
(result3) => !outputByToolName.has(result3.name)
|
|
5118
5154
|
);
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
if (!stream)
|
|
5155
|
+
const resolved = outputByToolName.get(call.name);
|
|
5156
|
+
if (!resolved)
|
|
5122
5157
|
throw new Error(
|
|
5123
|
-
`useReason
|
|
5158
|
+
`Unknown useReason output contract tool "${call.name}".`
|
|
5124
5159
|
);
|
|
5125
|
-
if (
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
if (typeof instruction !== "string" || instruction.trim().length === 0)
|
|
5160
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id)) {
|
|
5161
|
+
if (resolved.kind === "return" && returned) completed = true;
|
|
5162
|
+
continue;
|
|
5163
|
+
}
|
|
5164
|
+
if (resolved.kind === "emit" && emissions.length >= (useReasonArgs.outputs?.maxEmissions ?? 16))
|
|
5131
5165
|
throw new Error(
|
|
5132
|
-
`useReason
|
|
5133
|
-
);
|
|
5134
|
-
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
5135
|
-
const structured = {
|
|
5136
|
-
dataType: resolved.contract.schemaId,
|
|
5137
|
-
schemaId: resolved.contract.schemaId,
|
|
5138
|
-
schemaVersion: resolved.contract.schemaVersion,
|
|
5139
|
-
fields: resolved.contract.stream?.fields
|
|
5140
|
-
};
|
|
5141
|
-
const inferred = inferOutputFormat(
|
|
5142
|
-
resolved.contract.schema,
|
|
5143
|
-
void 0
|
|
5144
|
-
);
|
|
5145
|
-
try {
|
|
5146
|
-
const outputStep = await reasonEngine(
|
|
5147
|
-
{
|
|
5148
|
-
...useReasonArgs,
|
|
5149
|
-
tools: [],
|
|
5150
|
-
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
5151
|
-
messages: [
|
|
5152
|
-
...messages,
|
|
5153
|
-
{
|
|
5154
|
-
role: "tool",
|
|
5155
|
-
toolCallId: call.id,
|
|
5156
|
-
name: call.name,
|
|
5157
|
-
content: "Generating the requested structured output."
|
|
5158
|
-
},
|
|
5159
|
-
{
|
|
5160
|
-
role: "user",
|
|
5161
|
-
content: withOutputGuardrails(
|
|
5162
|
-
instruction,
|
|
5163
|
-
resolved.contract.schema
|
|
5164
|
-
)
|
|
5165
|
-
}
|
|
5166
|
-
],
|
|
5167
|
-
emit: false,
|
|
5168
|
-
stream,
|
|
5169
|
-
...emit && stream ? {
|
|
5170
|
-
onTextChunk: createStructuredOutputStreamer(
|
|
5171
|
-
{ structured },
|
|
5172
|
-
id3,
|
|
5173
|
-
streamId
|
|
5174
|
-
)
|
|
5175
|
-
} : {}
|
|
5176
|
-
},
|
|
5177
|
-
{ ...id3 ? { id: id3 } : {}, opId }
|
|
5166
|
+
`useReason reached outputs.maxEmissions (${useReasonArgs.outputs?.maxEmissions ?? 16}).`
|
|
5178
5167
|
);
|
|
5179
|
-
|
|
5168
|
+
let data;
|
|
5169
|
+
if (resolved.stream || hasEvidenceCalls) {
|
|
5170
|
+
if (resolved.stream && !stream)
|
|
5180
5171
|
throw new Error(
|
|
5181
|
-
`
|
|
5172
|
+
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
5182
5173
|
);
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5174
|
+
if (steps.length >= maxSteps) {
|
|
5175
|
+
const message = `useReason output generation requires another model pass within toolExecution.maxSteps (${maxSteps}). Increase maxSteps to allow outputs to use completed tool and interrupt results.`;
|
|
5176
|
+
throw new import_errors62.ProviderRequestError(message, {
|
|
5177
|
+
code: "REASON_OUTPUT_BUDGET_EXHAUSTED",
|
|
5178
|
+
operation: "useReason",
|
|
5179
|
+
safeMessage: message,
|
|
5180
|
+
retryable: false,
|
|
5181
|
+
details: { maxSteps, modelPasses: steps.length }
|
|
5182
|
+
});
|
|
5183
|
+
}
|
|
5184
|
+
const streamInstruction = call.input && typeof call.input === "object" && !Array.isArray(call.input) ? call.input.instruction : void 0;
|
|
5185
|
+
if (resolved.stream && (typeof streamInstruction !== "string" || streamInstruction.trim().length === 0))
|
|
5186
|
+
throw new Error(
|
|
5187
|
+
`useReason streaming output contract "${resolved.name}" requires an instruction.`
|
|
5188
|
+
);
|
|
5189
|
+
const instruction = [
|
|
5190
|
+
`Generate output contract "${resolved.name}": ${resolved.contract.description}`,
|
|
5191
|
+
"Use the completed tool and human-input results. Do not assume results for pending calls.",
|
|
5192
|
+
resolved.stream ? String(streamInstruction) : `Regenerate this original draft, which was written before those results were available: ${JSON.stringify(call.input)}`
|
|
5193
|
+
].join("\n\n");
|
|
5194
|
+
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
5195
|
+
const structured = {
|
|
5196
|
+
dataType: resolved.contract.schemaId,
|
|
5197
|
+
schemaId: resolved.contract.schemaId,
|
|
5198
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
5199
|
+
fields: resolved.contract.stream?.fields
|
|
5200
|
+
};
|
|
5201
|
+
const inferred = inferOutputFormat(
|
|
5202
|
+
resolved.contract.schema,
|
|
5203
|
+
void 0
|
|
5192
5204
|
);
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
data = parseReasonOutputWithSchema({
|
|
5196
|
-
text: outputStep.text,
|
|
5197
|
-
schema: resolved.contract.schema,
|
|
5198
|
-
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
5199
|
-
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
5200
|
-
label: `useReason output contract "${resolved.name}"`
|
|
5201
|
-
});
|
|
5202
|
-
steps.push({
|
|
5203
|
-
stepIndex: stepIndex + 1,
|
|
5205
|
+
const outputRecord = {
|
|
5206
|
+
stepIndex: steps.length,
|
|
5204
5207
|
kind: "output",
|
|
5205
|
-
text:
|
|
5208
|
+
text: "",
|
|
5206
5209
|
toolCalls: [],
|
|
5207
|
-
toolResults: []
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5210
|
+
toolResults: []
|
|
5211
|
+
};
|
|
5212
|
+
steps.push(outputRecord);
|
|
5213
|
+
save();
|
|
5214
|
+
try {
|
|
5215
|
+
const outputStep = await reasonEngine(
|
|
5216
|
+
{
|
|
5217
|
+
...useReasonArgs,
|
|
5218
|
+
tools: [],
|
|
5219
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
5220
|
+
messages: [
|
|
5221
|
+
...messages,
|
|
5222
|
+
...toolCalls.filter(
|
|
5223
|
+
(pendingCall) => !toolResults.some(
|
|
5224
|
+
(result3) => result3.toolCallId === pendingCall.id
|
|
5225
|
+
)
|
|
5226
|
+
).map(
|
|
5227
|
+
(pendingCall) => ({
|
|
5228
|
+
role: "tool",
|
|
5229
|
+
toolCallId: pendingCall.id,
|
|
5230
|
+
name: pendingCall.name,
|
|
5231
|
+
content: "This call has not completed yet. Use only completed results; do not assume a result for this pending call."
|
|
5232
|
+
})
|
|
5233
|
+
),
|
|
5234
|
+
{
|
|
5235
|
+
role: "user",
|
|
5236
|
+
content: withOutputGuardrails(
|
|
5237
|
+
instruction,
|
|
5238
|
+
resolved.contract.schema
|
|
5239
|
+
)
|
|
5240
|
+
}
|
|
5241
|
+
],
|
|
5242
|
+
emit: false,
|
|
5243
|
+
stream: resolved.stream && stream,
|
|
5244
|
+
...emit && resolved.stream && stream ? {
|
|
5245
|
+
onTextChunk: createStructuredOutputStreamer(
|
|
5246
|
+
{ structured },
|
|
5247
|
+
id3,
|
|
5248
|
+
streamId
|
|
5249
|
+
)
|
|
5250
|
+
} : {}
|
|
5251
|
+
},
|
|
5252
|
+
{ ...id3 ? { id: id3 } : {}, opId }
|
|
5253
|
+
);
|
|
5254
|
+
if (outputStep.toolCalls?.length)
|
|
5255
|
+
throw new Error(
|
|
5256
|
+
`Provider requested a tool during streamed output contract "${resolved.name}".`
|
|
5257
|
+
);
|
|
5258
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, outputStep.usage);
|
|
5259
|
+
accumulateTokenUsage(outputStep.usage);
|
|
5260
|
+
aggregatedWarnings = mergeWarnings(
|
|
5261
|
+
aggregatedWarnings,
|
|
5262
|
+
mergeWarnings(inferred.warnings, outputStep.warnings)
|
|
5263
|
+
);
|
|
5264
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
5265
|
+
aggregatedProviderMetadata,
|
|
5266
|
+
outputStep.providerMetadata
|
|
5267
|
+
);
|
|
5268
|
+
finalFinishReason = outputStep.finishReason;
|
|
5269
|
+
finalRaw = outputStep.raw;
|
|
5270
|
+
Object.assign(outputRecord, {
|
|
5271
|
+
stepIndex: outputRecord.stepIndex,
|
|
5272
|
+
kind: "output",
|
|
5273
|
+
text: outputStep.text,
|
|
5274
|
+
toolCalls: [],
|
|
5275
|
+
toolResults: [],
|
|
5276
|
+
...outputStep.usage ? { usage: outputStep.usage } : {},
|
|
5277
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
5278
|
+
...outputStep.providerMetadata ? { providerMetadata: outputStep.providerMetadata } : {},
|
|
5279
|
+
...outputStep.warnings ? { warnings: outputStep.warnings } : {}
|
|
5280
|
+
});
|
|
5281
|
+
save();
|
|
5282
|
+
data = parseReasonOutputWithSchema({
|
|
5283
|
+
text: outputStep.text,
|
|
5284
|
+
schema: resolved.contract.schema,
|
|
5285
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
5286
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
5287
|
+
label: `useReason output contract "${resolved.name}"`
|
|
5288
|
+
});
|
|
5289
|
+
} catch (error) {
|
|
5290
|
+
traceSpan?.addEvent?.("useReason.output.invalidated", {
|
|
5291
|
+
contract: resolved.name,
|
|
5292
|
+
kind: resolved.kind,
|
|
5293
|
+
schemaId: resolved.contract.schemaId,
|
|
5294
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
5295
|
+
streamId
|
|
5296
|
+
});
|
|
5297
|
+
if (emit && resolved.stream) {
|
|
5298
|
+
try {
|
|
5299
|
+
ctx.node.emit("structured_data_invalidated", {
|
|
5300
|
+
node: ctx.node.graph.node,
|
|
5301
|
+
streamId
|
|
5302
|
+
});
|
|
5303
|
+
} catch {
|
|
5304
|
+
}
|
|
5229
5305
|
}
|
|
5306
|
+
throw error;
|
|
5230
5307
|
}
|
|
5231
|
-
|
|
5308
|
+
} else {
|
|
5309
|
+
data = parseWithSchema(
|
|
5310
|
+
resolved.contract.schema,
|
|
5311
|
+
call.input,
|
|
5312
|
+
`useReason output contract "${resolved.name}"`
|
|
5313
|
+
);
|
|
5232
5314
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
)
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5315
|
+
const entry = { contract: resolved.name, data };
|
|
5316
|
+
const emissionIndex = emissions.length;
|
|
5317
|
+
if (resolved.kind === "emit")
|
|
5318
|
+
emissions.push(entry);
|
|
5319
|
+
else returned = entry;
|
|
5320
|
+
if (emit)
|
|
5321
|
+
emitStructuredData({
|
|
5322
|
+
kind: "final",
|
|
5323
|
+
data,
|
|
5324
|
+
dataType: resolved.contract.schemaId,
|
|
5325
|
+
schemaId: resolved.contract.schemaId,
|
|
5326
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
5327
|
+
streamId: `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissionIndex : "return"}`,
|
|
5328
|
+
...id3 ? { id: id3 } : {}
|
|
5329
|
+
});
|
|
5330
|
+
traceSpan?.addEvent?.("useReason.output.emitted", {
|
|
5331
|
+
contract: resolved.name,
|
|
5332
|
+
kind: resolved.kind,
|
|
5250
5333
|
schemaId: resolved.contract.schemaId,
|
|
5251
5334
|
schemaVersion: resolved.contract.schemaVersion,
|
|
5252
|
-
|
|
5253
|
-
|
|
5335
|
+
emissionIndex,
|
|
5336
|
+
data
|
|
5254
5337
|
});
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
pending = void 0;
|
|
5265
|
-
pendingStepIndex = void 0;
|
|
5266
|
-
completed = true;
|
|
5338
|
+
const result2 = normalizeToolResult(call, { emitted: true, data });
|
|
5339
|
+
toolResults.push(result2);
|
|
5340
|
+
messages.push({
|
|
5341
|
+
role: "tool",
|
|
5342
|
+
content: result2.content,
|
|
5343
|
+
toolCallId: result2.toolCallId,
|
|
5344
|
+
name: result2.name
|
|
5345
|
+
});
|
|
5346
|
+
if (resolved.kind === "return") completed = true;
|
|
5267
5347
|
save();
|
|
5268
|
-
|
|
5348
|
+
continue;
|
|
5269
5349
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
if (toolCalls.length !== 1)
|
|
5288
|
-
throw new Error(
|
|
5289
|
-
"A useReason interrupt contract call must be the only tool call in its model turn."
|
|
5290
|
-
);
|
|
5291
|
-
const interruptCall = interruptCalls[0];
|
|
5292
|
-
if (!interruptCall)
|
|
5293
|
-
throw new Error("Missing useReason interrupt control call.");
|
|
5294
|
-
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
5295
|
-
throw new Error(
|
|
5296
|
-
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
5297
|
-
);
|
|
5298
|
-
const resolved = interruptByToolName.get(interruptCall.name);
|
|
5299
|
-
if (!resolved)
|
|
5300
|
-
throw new Error(
|
|
5301
|
-
`Unknown useReason interrupt contract tool "${interruptCall.name}".`
|
|
5350
|
+
if (interruptByToolName.has(toolCall.name)) {
|
|
5351
|
+
const resolved = interruptByToolName.get(toolCall.name);
|
|
5352
|
+
if (!resolved)
|
|
5353
|
+
throw new Error(
|
|
5354
|
+
`Unknown useReason interrupt contract tool "${toolCall.name}".`
|
|
5355
|
+
);
|
|
5356
|
+
if (toolResults.some((result3) => result3.toolCallId === toolCall.id)) {
|
|
5357
|
+
continue;
|
|
5358
|
+
}
|
|
5359
|
+
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
5360
|
+
throw new Error(
|
|
5361
|
+
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
5362
|
+
);
|
|
5363
|
+
const request = parseWithSchema(
|
|
5364
|
+
resolved.contract.requestSchema,
|
|
5365
|
+
toolCall.input,
|
|
5366
|
+
`useReason interrupt contract "${resolved.name}" request`
|
|
5302
5367
|
);
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5368
|
+
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
5369
|
+
checkpointKey,
|
|
5370
|
+
contract: resolved.name,
|
|
5371
|
+
interruptIndex: interruptHistory.length
|
|
5372
|
+
});
|
|
5373
|
+
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
5374
|
+
const response = await awaitInterruptInternal({
|
|
5375
|
+
onResolved: rememberInterrupt,
|
|
5376
|
+
request: {
|
|
5377
|
+
kind: "custom",
|
|
5378
|
+
request,
|
|
5379
|
+
contract: resolved.name,
|
|
5380
|
+
...question ? { question } : {},
|
|
5381
|
+
schemaId: resolved.contract.schemaId,
|
|
5382
|
+
schemaVersion: resolved.contract.schemaVersion
|
|
5383
|
+
},
|
|
5384
|
+
responseSchema: resolved.contract.responseSchema,
|
|
5385
|
+
id: `${id3 ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
5386
|
+
meta: {
|
|
5387
|
+
__kortyxReason: {
|
|
5388
|
+
opId,
|
|
5389
|
+
stepIndex,
|
|
5390
|
+
interruptIndex: interruptHistory.length,
|
|
5391
|
+
contract: resolved.name
|
|
5392
|
+
}
|
|
5393
|
+
}
|
|
5394
|
+
});
|
|
5395
|
+
const result2 = normalizeToolResult(toolCall, response);
|
|
5396
|
+
toolResults.push(result2);
|
|
5397
|
+
messages.push({
|
|
5398
|
+
role: "tool",
|
|
5399
|
+
content: result2.content,
|
|
5400
|
+
toolCallId: result2.toolCallId,
|
|
5401
|
+
name: result2.name
|
|
5402
|
+
});
|
|
5403
|
+
interruptHistory.push({
|
|
5404
|
+
contract: resolved.name,
|
|
5405
|
+
request,
|
|
5406
|
+
response
|
|
5407
|
+
});
|
|
5408
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
5409
|
+
checkpointKey,
|
|
5410
|
+
contract: resolved.name,
|
|
5411
|
+
interruptIndex: interruptHistory.length - 1
|
|
5412
|
+
});
|
|
5306
5413
|
save();
|
|
5307
5414
|
continue;
|
|
5308
5415
|
}
|
|
5309
|
-
const request = parseWithSchema(
|
|
5310
|
-
resolved.contract.requestSchema,
|
|
5311
|
-
interruptCall.input,
|
|
5312
|
-
`useReason interrupt contract "${resolved.name}" request`
|
|
5313
|
-
);
|
|
5314
|
-
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
5315
|
-
checkpointKey,
|
|
5316
|
-
contract: resolved.name,
|
|
5317
|
-
interruptIndex: interruptHistory.length
|
|
5318
|
-
});
|
|
5319
|
-
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
5320
|
-
const response = await awaitInterruptInternal({
|
|
5321
|
-
request: {
|
|
5322
|
-
kind: "custom",
|
|
5323
|
-
request,
|
|
5324
|
-
contract: resolved.name,
|
|
5325
|
-
...question ? { question } : {},
|
|
5326
|
-
schemaId: resolved.contract.schemaId,
|
|
5327
|
-
schemaVersion: resolved.contract.schemaVersion
|
|
5328
|
-
},
|
|
5329
|
-
responseSchema: resolved.contract.responseSchema,
|
|
5330
|
-
id: `${id3 ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
5331
|
-
meta: {
|
|
5332
|
-
__kortyxReason: {
|
|
5333
|
-
opId,
|
|
5334
|
-
stepIndex,
|
|
5335
|
-
interruptIndex: interruptHistory.length,
|
|
5336
|
-
contract: resolved.name
|
|
5337
|
-
}
|
|
5338
|
-
}
|
|
5339
|
-
});
|
|
5340
|
-
const result2 = normalizeToolResult(interruptCall, response);
|
|
5341
|
-
toolResults.push(result2);
|
|
5342
|
-
messages.push({
|
|
5343
|
-
role: "tool",
|
|
5344
|
-
content: result2.content,
|
|
5345
|
-
toolCallId: result2.toolCallId,
|
|
5346
|
-
name: result2.name
|
|
5347
|
-
});
|
|
5348
|
-
interruptHistory.push({
|
|
5349
|
-
contract: resolved.name,
|
|
5350
|
-
request,
|
|
5351
|
-
response
|
|
5352
|
-
});
|
|
5353
|
-
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
5354
|
-
checkpointKey,
|
|
5355
|
-
contract: resolved.name,
|
|
5356
|
-
interruptIndex: interruptHistory.length - 1
|
|
5357
|
-
});
|
|
5358
|
-
pending = void 0;
|
|
5359
|
-
pendingStepIndex = void 0;
|
|
5360
|
-
save();
|
|
5361
|
-
continue;
|
|
5362
|
-
}
|
|
5363
|
-
for (const toolCall of toolCalls) {
|
|
5364
5416
|
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
5365
5417
|
await replayToolObservations(
|
|
5366
5418
|
steps[stepIndex]?.toolObservations?.filter(
|
|
@@ -5406,6 +5458,7 @@ ${tryStringify2(args.response)}`
|
|
|
5406
5458
|
"waiting"
|
|
5407
5459
|
);
|
|
5408
5460
|
const approval = await awaitInterruptInternal({
|
|
5461
|
+
onResolved: rememberInterrupt,
|
|
5409
5462
|
id: `tool:${toolCall.id}`,
|
|
5410
5463
|
request: {
|
|
5411
5464
|
kind: "choice",
|
|
@@ -5520,22 +5573,24 @@ ${tryStringify2(args.response)}`
|
|
|
5520
5573
|
});
|
|
5521
5574
|
}
|
|
5522
5575
|
}
|
|
5523
|
-
const
|
|
5524
|
-
if (
|
|
5576
|
+
const completedToolResult = toolResults.at(-1);
|
|
5577
|
+
if (completedToolResult)
|
|
5525
5578
|
messages.push({
|
|
5526
5579
|
role: "tool",
|
|
5527
|
-
content:
|
|
5528
|
-
toolCallId:
|
|
5529
|
-
name:
|
|
5530
|
-
...
|
|
5580
|
+
content: completedToolResult.content,
|
|
5581
|
+
toolCallId: completedToolResult.toolCallId,
|
|
5582
|
+
name: completedToolResult.name,
|
|
5583
|
+
...completedToolResult.isError !== void 0 ? { isError: completedToolResult.isError } : {}
|
|
5531
5584
|
});
|
|
5532
5585
|
save();
|
|
5533
5586
|
}
|
|
5534
5587
|
pending = void 0;
|
|
5535
5588
|
pendingStepIndex = void 0;
|
|
5536
5589
|
save();
|
|
5590
|
+
if (completed) break;
|
|
5537
5591
|
}
|
|
5538
5592
|
if (!completed) {
|
|
5593
|
+
if (outputContractCorrections > 0) throw correctionExhausted();
|
|
5539
5594
|
throw new Error(
|
|
5540
5595
|
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
5541
5596
|
);
|
|
@@ -5597,7 +5652,11 @@ ${tryStringify2(args.response)}`
|
|
|
5597
5652
|
output: useReasonArgs.telemetry?.output ?? returned?.data ?? finalOutput ?? finalText
|
|
5598
5653
|
}
|
|
5599
5654
|
});
|
|
5600
|
-
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
5655
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
5656
|
+
status: "completed",
|
|
5657
|
+
result,
|
|
5658
|
+
resolvedInterrupts
|
|
5659
|
+
};
|
|
5601
5660
|
ctx.stateDirty = true;
|
|
5602
5661
|
return result;
|
|
5603
5662
|
} catch (error) {
|
|
@@ -5672,6 +5731,8 @@ ${tryStringify2(args.response)}`
|
|
|
5672
5731
|
const opId = checkpointValue && typeof checkpointValue === "object" && "status" in checkpointValue && checkpointValue.status === "tool_loop" && "opId" in checkpointValue && typeof checkpointValue.opId === "string" ? checkpointValue.opId : provisionalOpId;
|
|
5673
5732
|
const existingCompleted = readReasonCompletedCheckpoint(checkpointValue);
|
|
5674
5733
|
if (existingCompleted) {
|
|
5734
|
+
for (const request of existingCompleted.resolvedInterrupts ?? [])
|
|
5735
|
+
ctx.node.replayInterrupt?.(request);
|
|
5675
5736
|
await replayToolObservations(
|
|
5676
5737
|
existingCompleted.result.steps?.flatMap(
|
|
5677
5738
|
(step) => step.toolObservations ?? []
|