@kortyx/hooks 0.31.0 → 0.32.1
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 +17 -0
- package/README.md +25 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +316 -255
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -286,7 +286,8 @@ var readReasonCompletedCheckpoint = (raw) => {
|
|
|
286
286
|
if (!value.result || typeof value.result !== "object") return void 0;
|
|
287
287
|
return {
|
|
288
288
|
status: "completed",
|
|
289
|
-
result: value.result
|
|
289
|
+
result: value.result,
|
|
290
|
+
...Array.isArray(value.resolvedInterrupts) ? { resolvedInterrupts: value.resolvedInterrupts } : {}
|
|
290
291
|
};
|
|
291
292
|
};
|
|
292
293
|
var resolveHookStatePatch = (args) => ({
|
|
@@ -348,6 +349,10 @@ var awaitInterruptInternal = (args) => {
|
|
|
348
349
|
raw,
|
|
349
350
|
"useInterrupt response"
|
|
350
351
|
);
|
|
352
|
+
if (args.onResolved) {
|
|
353
|
+
const { __kortyxResumeStatePatch: _patch, ...meta } = enrichedRequest.meta ?? {};
|
|
354
|
+
args.onResolved({ ...enrichedRequest, meta });
|
|
355
|
+
}
|
|
351
356
|
return Promise.resolve(response);
|
|
352
357
|
};
|
|
353
358
|
|
|
@@ -2978,7 +2983,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2978
2983
|
interruptInstructions: [
|
|
2979
2984
|
"Human-input rules:",
|
|
2980
2985
|
"- The kortyx_request_input__* tools pause this reasoning operation and return the validated human response as a normal tool result.",
|
|
2981
|
-
"-
|
|
2986
|
+
"- 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.",
|
|
2982
2987
|
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."
|
|
2983
2988
|
].join("\n")
|
|
2984
2989
|
} : {},
|
|
@@ -2988,7 +2993,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2988
2993
|
"- The kortyx_emit__* tools publish validated structured output and return an acknowledgement. You may continue with text or more tools afterward.",
|
|
2989
2994
|
"- The kortyx_return__* tools publish a validated result and end this reasoning operation. If return tools are available, call exactly one before finishing.",
|
|
2990
2995
|
"- 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.",
|
|
2991
|
-
"-
|
|
2996
|
+
"- 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."
|
|
2992
2997
|
].join("\n")
|
|
2993
2998
|
} : {}
|
|
2994
2999
|
});
|
|
@@ -3017,6 +3022,19 @@ var runReasonToolLoop = async (args) => {
|
|
|
3017
3022
|
);
|
|
3018
3023
|
let pending = checkpoint?.pending;
|
|
3019
3024
|
let pendingStepIndex = checkpoint?.pendingStepIndex;
|
|
3025
|
+
let outputContractCorrections = checkpoint?.outputContractCorrections ?? 0;
|
|
3026
|
+
const correctionExhausted = () => {
|
|
3027
|
+
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.`;
|
|
3028
|
+
return new import_errors6.ProviderRequestError(message, {
|
|
3029
|
+
code: "REASON_OUTPUT_CONTRACT_CORRECTION_EXHAUSTED",
|
|
3030
|
+
operation: "useReason",
|
|
3031
|
+
safeMessage: message,
|
|
3032
|
+
retryable: false,
|
|
3033
|
+
details: { maxSteps, outputContractCorrections }
|
|
3034
|
+
});
|
|
3035
|
+
};
|
|
3036
|
+
const resolvedInterrupts = checkpoint?.resolvedInterrupts ?? [];
|
|
3037
|
+
for (const request of resolvedInterrupts) ctx.node.replayInterrupt?.(request);
|
|
3020
3038
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
3021
3039
|
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
3022
3040
|
const emissions = checkpoint?.emissions ?? [];
|
|
@@ -3028,8 +3046,10 @@ var runReasonToolLoop = async (args) => {
|
|
|
3028
3046
|
messages,
|
|
3029
3047
|
steps,
|
|
3030
3048
|
approvedCalls,
|
|
3049
|
+
resolvedInterrupts,
|
|
3031
3050
|
interruptHistory,
|
|
3032
3051
|
emissions,
|
|
3052
|
+
outputContractCorrections,
|
|
3033
3053
|
...returned ? { returned } : {},
|
|
3034
3054
|
finalizing,
|
|
3035
3055
|
...pending ? { pending } : {},
|
|
@@ -3037,6 +3057,10 @@ var runReasonToolLoop = async (args) => {
|
|
|
3037
3057
|
};
|
|
3038
3058
|
ctx.stateDirty = true;
|
|
3039
3059
|
};
|
|
3060
|
+
const rememberInterrupt = (request) => {
|
|
3061
|
+
resolvedInterrupts.push(request);
|
|
3062
|
+
save();
|
|
3063
|
+
};
|
|
3040
3064
|
const emitToolEvent = (event, payload) => {
|
|
3041
3065
|
ctx.node.emit(event, {
|
|
3042
3066
|
...payload,
|
|
@@ -3046,7 +3070,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3046
3070
|
});
|
|
3047
3071
|
};
|
|
3048
3072
|
try {
|
|
3049
|
-
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex
|
|
3073
|
+
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex = steps.length) {
|
|
3050
3074
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
3051
3075
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
3052
3076
|
stepIndex,
|
|
@@ -3183,273 +3207,301 @@ var runReasonToolLoop = async (args) => {
|
|
|
3183
3207
|
const outputCalls = toolCalls.filter(
|
|
3184
3208
|
(call) => outputByToolName.has(call.name)
|
|
3185
3209
|
);
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
const call
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3210
|
+
const returnCalls = outputCalls.filter(
|
|
3211
|
+
(call) => outputByToolName.get(call.name)?.kind === "return"
|
|
3212
|
+
);
|
|
3213
|
+
if (returnCalls.length > 1) {
|
|
3214
|
+
let corrected = false;
|
|
3215
|
+
for (const call of returnCalls) {
|
|
3216
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id))
|
|
3217
|
+
continue;
|
|
3218
|
+
const result2 = {
|
|
3219
|
+
toolCallId: call.id,
|
|
3220
|
+
name: call.name,
|
|
3221
|
+
isError: true,
|
|
3222
|
+
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."
|
|
3223
|
+
};
|
|
3224
|
+
toolResults.push(result2);
|
|
3225
|
+
messages.push({ role: "tool", ...result2 });
|
|
3226
|
+
corrected = true;
|
|
3203
3227
|
}
|
|
3204
|
-
if (
|
|
3205
|
-
|
|
3206
|
-
|
|
3228
|
+
if (corrected) outputContractCorrections += 1;
|
|
3229
|
+
save();
|
|
3230
|
+
}
|
|
3231
|
+
const orderedCalls = [
|
|
3232
|
+
...toolCalls.filter(
|
|
3233
|
+
(call) => outputByToolName.get(call.name)?.kind !== "return"
|
|
3234
|
+
),
|
|
3235
|
+
...returnCalls
|
|
3236
|
+
];
|
|
3237
|
+
for (const toolCall of orderedCalls) {
|
|
3238
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
3239
|
+
if (outputByToolName.has(toolCall.name)) {
|
|
3240
|
+
const call = toolCall;
|
|
3241
|
+
const hasEvidenceCalls = toolResults.some(
|
|
3242
|
+
(result3) => !outputByToolName.has(result3.name)
|
|
3207
3243
|
);
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
if (!stream)
|
|
3211
|
-
throw new Error(
|
|
3212
|
-
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
3213
|
-
);
|
|
3214
|
-
if (stepIndex + 1 >= maxSteps)
|
|
3244
|
+
const resolved = outputByToolName.get(call.name);
|
|
3245
|
+
if (!resolved)
|
|
3215
3246
|
throw new Error(
|
|
3216
|
-
`useReason output
|
|
3247
|
+
`Unknown useReason output contract tool "${call.name}".`
|
|
3217
3248
|
);
|
|
3218
|
-
|
|
3219
|
-
|
|
3249
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id)) {
|
|
3250
|
+
if (resolved.kind === "return" && returned) completed = true;
|
|
3251
|
+
continue;
|
|
3252
|
+
}
|
|
3253
|
+
if (resolved.kind === "emit" && emissions.length >= (useReasonArgs.outputs?.maxEmissions ?? 16))
|
|
3220
3254
|
throw new Error(
|
|
3221
|
-
`useReason
|
|
3222
|
-
);
|
|
3223
|
-
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
3224
|
-
const structured = {
|
|
3225
|
-
dataType: resolved.contract.schemaId,
|
|
3226
|
-
schemaId: resolved.contract.schemaId,
|
|
3227
|
-
schemaVersion: resolved.contract.schemaVersion,
|
|
3228
|
-
fields: resolved.contract.stream?.fields
|
|
3229
|
-
};
|
|
3230
|
-
const inferred = inferOutputFormat(
|
|
3231
|
-
resolved.contract.schema,
|
|
3232
|
-
void 0
|
|
3233
|
-
);
|
|
3234
|
-
try {
|
|
3235
|
-
const outputStep = await reasonEngine(
|
|
3236
|
-
{
|
|
3237
|
-
...useReasonArgs,
|
|
3238
|
-
tools: [],
|
|
3239
|
-
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3240
|
-
messages: [
|
|
3241
|
-
...messages,
|
|
3242
|
-
{
|
|
3243
|
-
role: "tool",
|
|
3244
|
-
toolCallId: call.id,
|
|
3245
|
-
name: call.name,
|
|
3246
|
-
content: "Generating the requested structured output."
|
|
3247
|
-
},
|
|
3248
|
-
{
|
|
3249
|
-
role: "user",
|
|
3250
|
-
content: withOutputGuardrails(
|
|
3251
|
-
instruction,
|
|
3252
|
-
resolved.contract.schema
|
|
3253
|
-
)
|
|
3254
|
-
}
|
|
3255
|
-
],
|
|
3256
|
-
emit: false,
|
|
3257
|
-
stream,
|
|
3258
|
-
...emit && stream ? {
|
|
3259
|
-
onTextChunk: createStructuredOutputStreamer(
|
|
3260
|
-
{ structured },
|
|
3261
|
-
id,
|
|
3262
|
-
streamId
|
|
3263
|
-
)
|
|
3264
|
-
} : {}
|
|
3265
|
-
},
|
|
3266
|
-
{ ...id ? { id } : {}, opId }
|
|
3255
|
+
`useReason reached outputs.maxEmissions (${useReasonArgs.outputs?.maxEmissions ?? 16}).`
|
|
3267
3256
|
);
|
|
3268
|
-
|
|
3257
|
+
let data;
|
|
3258
|
+
if (resolved.stream || hasEvidenceCalls) {
|
|
3259
|
+
if (resolved.stream && !stream)
|
|
3269
3260
|
throw new Error(
|
|
3270
|
-
`
|
|
3261
|
+
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
3271
3262
|
);
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3263
|
+
if (steps.length >= maxSteps) {
|
|
3264
|
+
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.`;
|
|
3265
|
+
throw new import_errors6.ProviderRequestError(message, {
|
|
3266
|
+
code: "REASON_OUTPUT_BUDGET_EXHAUSTED",
|
|
3267
|
+
operation: "useReason",
|
|
3268
|
+
safeMessage: message,
|
|
3269
|
+
retryable: false,
|
|
3270
|
+
details: { maxSteps, modelPasses: steps.length }
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
const streamInstruction = call.input && typeof call.input === "object" && !Array.isArray(call.input) ? call.input.instruction : void 0;
|
|
3274
|
+
if (resolved.stream && (typeof streamInstruction !== "string" || streamInstruction.trim().length === 0))
|
|
3275
|
+
throw new Error(
|
|
3276
|
+
`useReason streaming output contract "${resolved.name}" requires an instruction.`
|
|
3277
|
+
);
|
|
3278
|
+
const instruction = [
|
|
3279
|
+
`Generate output contract "${resolved.name}": ${resolved.contract.description}`,
|
|
3280
|
+
"Use the completed tool and human-input results. Do not assume results for pending calls.",
|
|
3281
|
+
resolved.stream ? String(streamInstruction) : `Regenerate this original draft, which was written before those results were available: ${JSON.stringify(call.input)}`
|
|
3282
|
+
].join("\n\n");
|
|
3283
|
+
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
3284
|
+
const structured = {
|
|
3285
|
+
dataType: resolved.contract.schemaId,
|
|
3286
|
+
schemaId: resolved.contract.schemaId,
|
|
3287
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3288
|
+
fields: resolved.contract.stream?.fields
|
|
3289
|
+
};
|
|
3290
|
+
const inferred = inferOutputFormat(
|
|
3291
|
+
resolved.contract.schema,
|
|
3292
|
+
void 0
|
|
3281
3293
|
);
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
data = parseReasonOutputWithSchema({
|
|
3285
|
-
text: outputStep.text,
|
|
3286
|
-
schema: resolved.contract.schema,
|
|
3287
|
-
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3288
|
-
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3289
|
-
label: `useReason output contract "${resolved.name}"`
|
|
3290
|
-
});
|
|
3291
|
-
steps.push({
|
|
3292
|
-
stepIndex: stepIndex + 1,
|
|
3294
|
+
const outputRecord = {
|
|
3295
|
+
stepIndex: steps.length,
|
|
3293
3296
|
kind: "output",
|
|
3294
|
-
text:
|
|
3297
|
+
text: "",
|
|
3295
3298
|
toolCalls: [],
|
|
3296
|
-
toolResults: []
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3299
|
+
toolResults: []
|
|
3300
|
+
};
|
|
3301
|
+
steps.push(outputRecord);
|
|
3302
|
+
save();
|
|
3303
|
+
try {
|
|
3304
|
+
const outputStep = await reasonEngine(
|
|
3305
|
+
{
|
|
3306
|
+
...useReasonArgs,
|
|
3307
|
+
tools: [],
|
|
3308
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3309
|
+
messages: [
|
|
3310
|
+
...messages,
|
|
3311
|
+
...toolCalls.filter(
|
|
3312
|
+
(pendingCall) => !toolResults.some(
|
|
3313
|
+
(result3) => result3.toolCallId === pendingCall.id
|
|
3314
|
+
)
|
|
3315
|
+
).map(
|
|
3316
|
+
(pendingCall) => ({
|
|
3317
|
+
role: "tool",
|
|
3318
|
+
toolCallId: pendingCall.id,
|
|
3319
|
+
name: pendingCall.name,
|
|
3320
|
+
content: "This call has not completed yet. Use only completed results; do not assume a result for this pending call."
|
|
3321
|
+
})
|
|
3322
|
+
),
|
|
3323
|
+
{
|
|
3324
|
+
role: "user",
|
|
3325
|
+
content: withOutputGuardrails(
|
|
3326
|
+
instruction,
|
|
3327
|
+
resolved.contract.schema
|
|
3328
|
+
)
|
|
3329
|
+
}
|
|
3330
|
+
],
|
|
3331
|
+
emit: false,
|
|
3332
|
+
stream: resolved.stream && stream,
|
|
3333
|
+
...emit && resolved.stream && stream ? {
|
|
3334
|
+
onTextChunk: createStructuredOutputStreamer(
|
|
3335
|
+
{ structured },
|
|
3336
|
+
id,
|
|
3337
|
+
streamId
|
|
3338
|
+
)
|
|
3339
|
+
} : {}
|
|
3340
|
+
},
|
|
3341
|
+
{ ...id ? { id } : {}, opId }
|
|
3342
|
+
);
|
|
3343
|
+
if (outputStep.toolCalls?.length)
|
|
3344
|
+
throw new Error(
|
|
3345
|
+
`Provider requested a tool during streamed output contract "${resolved.name}".`
|
|
3346
|
+
);
|
|
3347
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, outputStep.usage);
|
|
3348
|
+
accumulateTokenUsage(outputStep.usage);
|
|
3349
|
+
aggregatedWarnings = mergeWarnings(
|
|
3350
|
+
aggregatedWarnings,
|
|
3351
|
+
mergeWarnings(inferred.warnings, outputStep.warnings)
|
|
3352
|
+
);
|
|
3353
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3354
|
+
aggregatedProviderMetadata,
|
|
3355
|
+
outputStep.providerMetadata
|
|
3356
|
+
);
|
|
3357
|
+
finalFinishReason = outputStep.finishReason;
|
|
3358
|
+
finalRaw = outputStep.raw;
|
|
3359
|
+
Object.assign(outputRecord, {
|
|
3360
|
+
stepIndex: outputRecord.stepIndex,
|
|
3361
|
+
kind: "output",
|
|
3362
|
+
text: outputStep.text,
|
|
3363
|
+
toolCalls: [],
|
|
3364
|
+
toolResults: [],
|
|
3365
|
+
...outputStep.usage ? { usage: outputStep.usage } : {},
|
|
3366
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3367
|
+
...outputStep.providerMetadata ? { providerMetadata: outputStep.providerMetadata } : {},
|
|
3368
|
+
...outputStep.warnings ? { warnings: outputStep.warnings } : {}
|
|
3369
|
+
});
|
|
3370
|
+
save();
|
|
3371
|
+
data = parseReasonOutputWithSchema({
|
|
3372
|
+
text: outputStep.text,
|
|
3373
|
+
schema: resolved.contract.schema,
|
|
3374
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3375
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3376
|
+
label: `useReason output contract "${resolved.name}"`
|
|
3377
|
+
});
|
|
3378
|
+
} catch (error) {
|
|
3379
|
+
traceSpan?.addEvent?.("useReason.output.invalidated", {
|
|
3380
|
+
contract: resolved.name,
|
|
3381
|
+
kind: resolved.kind,
|
|
3382
|
+
schemaId: resolved.contract.schemaId,
|
|
3383
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3384
|
+
streamId
|
|
3385
|
+
});
|
|
3386
|
+
if (emit && resolved.stream) {
|
|
3387
|
+
try {
|
|
3388
|
+
ctx.node.emit("structured_data_invalidated", {
|
|
3389
|
+
node: ctx.node.graph.node,
|
|
3390
|
+
streamId
|
|
3391
|
+
});
|
|
3392
|
+
} catch {
|
|
3393
|
+
}
|
|
3318
3394
|
}
|
|
3395
|
+
throw error;
|
|
3319
3396
|
}
|
|
3320
|
-
|
|
3397
|
+
} else {
|
|
3398
|
+
data = parseWithSchema(
|
|
3399
|
+
resolved.contract.schema,
|
|
3400
|
+
call.input,
|
|
3401
|
+
`useReason output contract "${resolved.name}"`
|
|
3402
|
+
);
|
|
3321
3403
|
}
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
)
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3404
|
+
const entry = { contract: resolved.name, data };
|
|
3405
|
+
const emissionIndex = emissions.length;
|
|
3406
|
+
if (resolved.kind === "emit")
|
|
3407
|
+
emissions.push(entry);
|
|
3408
|
+
else returned = entry;
|
|
3409
|
+
if (emit)
|
|
3410
|
+
emitStructuredData({
|
|
3411
|
+
kind: "final",
|
|
3412
|
+
data,
|
|
3413
|
+
dataType: resolved.contract.schemaId,
|
|
3414
|
+
schemaId: resolved.contract.schemaId,
|
|
3415
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3416
|
+
streamId: `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissionIndex : "return"}`,
|
|
3417
|
+
...id ? { id } : {}
|
|
3418
|
+
});
|
|
3419
|
+
traceSpan?.addEvent?.("useReason.output.emitted", {
|
|
3420
|
+
contract: resolved.name,
|
|
3421
|
+
kind: resolved.kind,
|
|
3339
3422
|
schemaId: resolved.contract.schemaId,
|
|
3340
3423
|
schemaVersion: resolved.contract.schemaVersion,
|
|
3341
|
-
|
|
3342
|
-
|
|
3424
|
+
emissionIndex,
|
|
3425
|
+
data
|
|
3343
3426
|
});
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
pending = void 0;
|
|
3354
|
-
pendingStepIndex = void 0;
|
|
3355
|
-
completed = true;
|
|
3427
|
+
const result2 = normalizeToolResult(call, { emitted: true, data });
|
|
3428
|
+
toolResults.push(result2);
|
|
3429
|
+
messages.push({
|
|
3430
|
+
role: "tool",
|
|
3431
|
+
content: result2.content,
|
|
3432
|
+
toolCallId: result2.toolCallId,
|
|
3433
|
+
name: result2.name
|
|
3434
|
+
});
|
|
3435
|
+
if (resolved.kind === "return") completed = true;
|
|
3356
3436
|
save();
|
|
3357
|
-
|
|
3437
|
+
continue;
|
|
3358
3438
|
}
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
if (toolCalls.length !== 1)
|
|
3377
|
-
throw new Error(
|
|
3378
|
-
"A useReason interrupt contract call must be the only tool call in its model turn."
|
|
3379
|
-
);
|
|
3380
|
-
const interruptCall = interruptCalls[0];
|
|
3381
|
-
if (!interruptCall)
|
|
3382
|
-
throw new Error("Missing useReason interrupt control call.");
|
|
3383
|
-
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
3384
|
-
throw new Error(
|
|
3385
|
-
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3386
|
-
);
|
|
3387
|
-
const resolved = interruptByToolName.get(interruptCall.name);
|
|
3388
|
-
if (!resolved)
|
|
3389
|
-
throw new Error(
|
|
3390
|
-
`Unknown useReason interrupt contract tool "${interruptCall.name}".`
|
|
3439
|
+
if (interruptByToolName.has(toolCall.name)) {
|
|
3440
|
+
const resolved = interruptByToolName.get(toolCall.name);
|
|
3441
|
+
if (!resolved)
|
|
3442
|
+
throw new Error(
|
|
3443
|
+
`Unknown useReason interrupt contract tool "${toolCall.name}".`
|
|
3444
|
+
);
|
|
3445
|
+
if (toolResults.some((result3) => result3.toolCallId === toolCall.id)) {
|
|
3446
|
+
continue;
|
|
3447
|
+
}
|
|
3448
|
+
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
3449
|
+
throw new Error(
|
|
3450
|
+
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3451
|
+
);
|
|
3452
|
+
const request = parseWithSchema(
|
|
3453
|
+
resolved.contract.requestSchema,
|
|
3454
|
+
toolCall.input,
|
|
3455
|
+
`useReason interrupt contract "${resolved.name}" request`
|
|
3391
3456
|
);
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3457
|
+
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3458
|
+
checkpointKey,
|
|
3459
|
+
contract: resolved.name,
|
|
3460
|
+
interruptIndex: interruptHistory.length
|
|
3461
|
+
});
|
|
3462
|
+
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
3463
|
+
const response = await awaitInterruptInternal({
|
|
3464
|
+
onResolved: rememberInterrupt,
|
|
3465
|
+
request: {
|
|
3466
|
+
kind: "custom",
|
|
3467
|
+
request,
|
|
3468
|
+
contract: resolved.name,
|
|
3469
|
+
...question ? { question } : {},
|
|
3470
|
+
schemaId: resolved.contract.schemaId,
|
|
3471
|
+
schemaVersion: resolved.contract.schemaVersion
|
|
3472
|
+
},
|
|
3473
|
+
responseSchema: resolved.contract.responseSchema,
|
|
3474
|
+
id: `${id ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
3475
|
+
meta: {
|
|
3476
|
+
__kortyxReason: {
|
|
3477
|
+
opId,
|
|
3478
|
+
stepIndex,
|
|
3479
|
+
interruptIndex: interruptHistory.length,
|
|
3480
|
+
contract: resolved.name
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
});
|
|
3484
|
+
const result2 = normalizeToolResult(toolCall, response);
|
|
3485
|
+
toolResults.push(result2);
|
|
3486
|
+
messages.push({
|
|
3487
|
+
role: "tool",
|
|
3488
|
+
content: result2.content,
|
|
3489
|
+
toolCallId: result2.toolCallId,
|
|
3490
|
+
name: result2.name
|
|
3491
|
+
});
|
|
3492
|
+
interruptHistory.push({
|
|
3493
|
+
contract: resolved.name,
|
|
3494
|
+
request,
|
|
3495
|
+
response
|
|
3496
|
+
});
|
|
3497
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3498
|
+
checkpointKey,
|
|
3499
|
+
contract: resolved.name,
|
|
3500
|
+
interruptIndex: interruptHistory.length - 1
|
|
3501
|
+
});
|
|
3395
3502
|
save();
|
|
3396
3503
|
continue;
|
|
3397
3504
|
}
|
|
3398
|
-
const request = parseWithSchema(
|
|
3399
|
-
resolved.contract.requestSchema,
|
|
3400
|
-
interruptCall.input,
|
|
3401
|
-
`useReason interrupt contract "${resolved.name}" request`
|
|
3402
|
-
);
|
|
3403
|
-
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3404
|
-
checkpointKey,
|
|
3405
|
-
contract: resolved.name,
|
|
3406
|
-
interruptIndex: interruptHistory.length
|
|
3407
|
-
});
|
|
3408
|
-
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
3409
|
-
const response = await awaitInterruptInternal({
|
|
3410
|
-
request: {
|
|
3411
|
-
kind: "custom",
|
|
3412
|
-
request,
|
|
3413
|
-
contract: resolved.name,
|
|
3414
|
-
...question ? { question } : {},
|
|
3415
|
-
schemaId: resolved.contract.schemaId,
|
|
3416
|
-
schemaVersion: resolved.contract.schemaVersion
|
|
3417
|
-
},
|
|
3418
|
-
responseSchema: resolved.contract.responseSchema,
|
|
3419
|
-
id: `${id ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
3420
|
-
meta: {
|
|
3421
|
-
__kortyxReason: {
|
|
3422
|
-
opId,
|
|
3423
|
-
stepIndex,
|
|
3424
|
-
interruptIndex: interruptHistory.length,
|
|
3425
|
-
contract: resolved.name
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
});
|
|
3429
|
-
const result2 = normalizeToolResult(interruptCall, response);
|
|
3430
|
-
toolResults.push(result2);
|
|
3431
|
-
messages.push({
|
|
3432
|
-
role: "tool",
|
|
3433
|
-
content: result2.content,
|
|
3434
|
-
toolCallId: result2.toolCallId,
|
|
3435
|
-
name: result2.name
|
|
3436
|
-
});
|
|
3437
|
-
interruptHistory.push({
|
|
3438
|
-
contract: resolved.name,
|
|
3439
|
-
request,
|
|
3440
|
-
response
|
|
3441
|
-
});
|
|
3442
|
-
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3443
|
-
checkpointKey,
|
|
3444
|
-
contract: resolved.name,
|
|
3445
|
-
interruptIndex: interruptHistory.length - 1
|
|
3446
|
-
});
|
|
3447
|
-
pending = void 0;
|
|
3448
|
-
pendingStepIndex = void 0;
|
|
3449
|
-
save();
|
|
3450
|
-
continue;
|
|
3451
|
-
}
|
|
3452
|
-
for (const toolCall of toolCalls) {
|
|
3453
3505
|
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
3454
3506
|
await replayToolObservations(
|
|
3455
3507
|
steps[stepIndex]?.toolObservations?.filter(
|
|
@@ -3495,6 +3547,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3495
3547
|
"waiting"
|
|
3496
3548
|
);
|
|
3497
3549
|
const approval = await awaitInterruptInternal({
|
|
3550
|
+
onResolved: rememberInterrupt,
|
|
3498
3551
|
id: `tool:${toolCall.id}`,
|
|
3499
3552
|
request: {
|
|
3500
3553
|
kind: "choice",
|
|
@@ -3609,22 +3662,24 @@ var runReasonToolLoop = async (args) => {
|
|
|
3609
3662
|
});
|
|
3610
3663
|
}
|
|
3611
3664
|
}
|
|
3612
|
-
const
|
|
3613
|
-
if (
|
|
3665
|
+
const completedToolResult = toolResults.at(-1);
|
|
3666
|
+
if (completedToolResult)
|
|
3614
3667
|
messages.push({
|
|
3615
3668
|
role: "tool",
|
|
3616
|
-
content:
|
|
3617
|
-
toolCallId:
|
|
3618
|
-
name:
|
|
3619
|
-
...
|
|
3669
|
+
content: completedToolResult.content,
|
|
3670
|
+
toolCallId: completedToolResult.toolCallId,
|
|
3671
|
+
name: completedToolResult.name,
|
|
3672
|
+
...completedToolResult.isError !== void 0 ? { isError: completedToolResult.isError } : {}
|
|
3620
3673
|
});
|
|
3621
3674
|
save();
|
|
3622
3675
|
}
|
|
3623
3676
|
pending = void 0;
|
|
3624
3677
|
pendingStepIndex = void 0;
|
|
3625
3678
|
save();
|
|
3679
|
+
if (completed) break;
|
|
3626
3680
|
}
|
|
3627
3681
|
if (!completed) {
|
|
3682
|
+
if (outputContractCorrections > 0) throw correctionExhausted();
|
|
3628
3683
|
throw new Error(
|
|
3629
3684
|
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
3630
3685
|
);
|
|
@@ -3686,7 +3741,11 @@ var runReasonToolLoop = async (args) => {
|
|
|
3686
3741
|
output: useReasonArgs.telemetry?.output ?? returned?.data ?? finalOutput ?? finalText
|
|
3687
3742
|
}
|
|
3688
3743
|
});
|
|
3689
|
-
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3744
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3745
|
+
status: "completed",
|
|
3746
|
+
result,
|
|
3747
|
+
resolvedInterrupts
|
|
3748
|
+
};
|
|
3690
3749
|
ctx.stateDirty = true;
|
|
3691
3750
|
return result;
|
|
3692
3751
|
} catch (error) {
|
|
@@ -3763,6 +3822,8 @@ async function runReason(args) {
|
|
|
3763
3822
|
const opId = checkpointValue && typeof checkpointValue === "object" && "status" in checkpointValue && checkpointValue.status === "tool_loop" && "opId" in checkpointValue && typeof checkpointValue.opId === "string" ? checkpointValue.opId : provisionalOpId;
|
|
3764
3823
|
const existingCompleted = readReasonCompletedCheckpoint(checkpointValue);
|
|
3765
3824
|
if (existingCompleted) {
|
|
3825
|
+
for (const request of existingCompleted.resolvedInterrupts ?? [])
|
|
3826
|
+
ctx.node.replayInterrupt?.(request);
|
|
3766
3827
|
await replayToolObservations(
|
|
3767
3828
|
existingCompleted.result.steps?.flatMap(
|
|
3768
3829
|
(step) => step.toolObservations ?? []
|