@dudousxd/nestjs-agent-core 0.39.0 → 0.40.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/dist/ag-ui/index.cjs +78 -0
- package/dist/ag-ui/index.cjs.map +1 -1
- package/dist/ag-ui/index.d.cts +12 -2
- package/dist/ag-ui/index.d.ts +12 -2
- package/dist/ag-ui/index.js +77 -0
- package/dist/ag-ui/index.js.map +1 -1
- package/dist/genui/builtins.cjs +2 -0
- package/dist/genui/builtins.cjs.map +1 -1
- package/dist/genui/builtins.js +2 -0
- package/dist/genui/builtins.js.map +1 -1
- package/dist/genui/index.cjs +120 -38
- package/dist/genui/index.cjs.map +1 -1
- package/dist/genui/index.d.cts +2 -2
- package/dist/genui/index.d.ts +2 -2
- package/dist/genui/index.js +116 -37
- package/dist/genui/index.js.map +1 -1
- package/dist/guardrails/index.d.cts +3 -3
- package/dist/guardrails/index.d.ts +3 -3
- package/dist/index.cjs +2340 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -93
- package/dist/index.d.ts +280 -93
- package/dist/index.js +2301 -131
- package/dist/index.js.map +1 -1
- package/dist/{processors-6p9nrqEi.d.cts → processors-B7nfbU6E.d.cts} +1 -1
- package/dist/{processors-Bp5XmF6V.d.ts → processors-BR3nICcp.d.ts} +1 -1
- package/dist/{stream-events-CbVEowYb.d.cts → stream-events-DAmes2Of.d.ts} +595 -86
- package/dist/{stream-events-CbVEowYb.d.ts → stream-events-M0jc7lSW.d.cts} +595 -86
- package/package.json +1 -1
- package/dist/tool-DZYLEKnl.d.cts +0 -121
- package/dist/tool-_iq4xRyk.d.ts +0 -121
package/dist/index.js
CHANGED
|
@@ -169,16 +169,16 @@ function observeTurnFrames(writer, now = Date.now) {
|
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
__name(observeTurnFrames, "observeTurnFrames");
|
|
172
|
-
function withTurnFrames(
|
|
172
|
+
function withTurnFrames(result2, frames) {
|
|
173
173
|
return {
|
|
174
|
-
...
|
|
175
|
-
...
|
|
174
|
+
...result2,
|
|
175
|
+
...result2.reasoning === void 0 && frames.reasoning !== void 0 ? {
|
|
176
176
|
reasoning: frames.reasoning
|
|
177
177
|
} : {},
|
|
178
|
-
...
|
|
178
|
+
...result2.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
|
|
179
179
|
reasoningMs: frames.reasoningMs
|
|
180
180
|
} : {},
|
|
181
|
-
...
|
|
181
|
+
...result2.ui === void 0 && frames.ui !== void 0 ? {
|
|
182
182
|
ui: frames.ui
|
|
183
183
|
} : {}
|
|
184
184
|
};
|
|
@@ -187,8 +187,9 @@ __name(withTurnFrames, "withTurnFrames");
|
|
|
187
187
|
|
|
188
188
|
// src/tool-ui.ts
|
|
189
189
|
var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
|
|
190
|
-
function wrapToolStepOutput(output, ui) {
|
|
191
|
-
|
|
190
|
+
function wrapToolStepOutput(output, ui, text) {
|
|
191
|
+
const reserved = typeof output === "object" && output !== null && TOOL_STEP_UI in output;
|
|
192
|
+
if (ui.length === 0 && !reserved && !text) {
|
|
192
193
|
return output;
|
|
193
194
|
}
|
|
194
195
|
const wrapped = {
|
|
@@ -196,17 +197,36 @@ function wrapToolStepOutput(output, ui) {
|
|
|
196
197
|
output,
|
|
197
198
|
ui: [
|
|
198
199
|
...ui
|
|
199
|
-
]
|
|
200
|
+
],
|
|
201
|
+
...text ? {
|
|
202
|
+
text
|
|
203
|
+
} : {}
|
|
200
204
|
};
|
|
201
205
|
return wrapped;
|
|
202
206
|
}
|
|
203
207
|
__name(wrapToolStepOutput, "wrapToolStepOutput");
|
|
208
|
+
function wrapToolPreflightDenied(reason) {
|
|
209
|
+
const wrapped = {
|
|
210
|
+
[TOOL_STEP_UI]: 1,
|
|
211
|
+
output: null,
|
|
212
|
+
ui: [],
|
|
213
|
+
preflightDenied: reason
|
|
214
|
+
};
|
|
215
|
+
return wrapped;
|
|
216
|
+
}
|
|
217
|
+
__name(wrapToolPreflightDenied, "wrapToolPreflightDenied");
|
|
204
218
|
function unwrapToolStepOutput(raw) {
|
|
205
219
|
if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
|
|
206
220
|
const wrapped = raw;
|
|
207
221
|
return {
|
|
208
222
|
output: wrapped.output,
|
|
209
|
-
ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
|
|
223
|
+
ui: Array.isArray(wrapped.ui) ? wrapped.ui : [],
|
|
224
|
+
...typeof wrapped.text === "string" ? {
|
|
225
|
+
text: wrapped.text
|
|
226
|
+
} : {},
|
|
227
|
+
...typeof wrapped.preflightDenied === "string" ? {
|
|
228
|
+
preflightDenied: wrapped.preflightDenied
|
|
229
|
+
} : {}
|
|
210
230
|
};
|
|
211
231
|
}
|
|
212
232
|
return {
|
|
@@ -255,6 +275,14 @@ function createUiCollector(toolCallId, write) {
|
|
|
255
275
|
...options.version !== void 0 ? {
|
|
256
276
|
version: options.version
|
|
257
277
|
} : {},
|
|
278
|
+
...options.fallbackText !== void 0 ? {
|
|
279
|
+
fallbackText: options.fallbackText
|
|
280
|
+
} : {},
|
|
281
|
+
...options.componentVersions !== void 0 ? {
|
|
282
|
+
componentVersions: {
|
|
283
|
+
...options.componentVersions
|
|
284
|
+
}
|
|
285
|
+
} : {},
|
|
258
286
|
toolCallId
|
|
259
287
|
};
|
|
260
288
|
pushed.set(id, entry);
|
|
@@ -289,6 +317,10 @@ function mergeUi(...lists) {
|
|
|
289
317
|
];
|
|
290
318
|
}
|
|
291
319
|
__name(mergeUi, "mergeUi");
|
|
320
|
+
function escapeUnsafeToolUiText(text) {
|
|
321
|
+
return text.replaceAll("\0", "\\u0000").replace(/[\uD800-\uDFFF]/gu, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
322
|
+
}
|
|
323
|
+
__name(escapeUnsafeToolUiText, "escapeUnsafeToolUiText");
|
|
292
324
|
|
|
293
325
|
// src/tool-presentation.ts
|
|
294
326
|
var ALL_AGENTS = "*";
|
|
@@ -500,12 +532,18 @@ function toolCallApprovalFromRow(row) {
|
|
|
500
532
|
if (row.approver === null || row.approver === void 0) {
|
|
501
533
|
return null;
|
|
502
534
|
}
|
|
503
|
-
const status = row.status === "pending_approval" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
|
|
535
|
+
const status = row.status === "pending_approval" || row.status === "proposed" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
|
|
504
536
|
const expiresAt = row.expiresAt instanceof Date ? row.expiresAt.toISOString() : typeof row.expiresAt === "string" ? row.expiresAt : void 0;
|
|
505
537
|
const decided = status === "approved" || status === "rejected";
|
|
506
538
|
return {
|
|
507
539
|
toolCallId: row.toolCallId,
|
|
540
|
+
...row.proposalId != null ? {
|
|
541
|
+
proposalId: row.proposalId
|
|
542
|
+
} : {},
|
|
508
543
|
approver: row.approver,
|
|
544
|
+
...row.confirmation != null ? {
|
|
545
|
+
confirmation: row.confirmation
|
|
546
|
+
} : {},
|
|
509
547
|
status,
|
|
510
548
|
...expiresAt !== void 0 ? {
|
|
511
549
|
expiresAt
|
|
@@ -556,9 +594,9 @@ function bucketByModel(rows, prices) {
|
|
|
556
594
|
bucket.costUsd += rowCost(row, prices);
|
|
557
595
|
byModel.set(row.modelId, bucket);
|
|
558
596
|
}
|
|
559
|
-
const
|
|
597
|
+
const result2 = [];
|
|
560
598
|
for (const [modelId, bucket] of byModel) {
|
|
561
|
-
|
|
599
|
+
result2.push({
|
|
562
600
|
modelId,
|
|
563
601
|
requests: bucket.requests,
|
|
564
602
|
inputTokens: bucket.inputTokens,
|
|
@@ -566,8 +604,8 @@ function bucketByModel(rows, prices) {
|
|
|
566
604
|
costUsd: bucket.costUsd
|
|
567
605
|
});
|
|
568
606
|
}
|
|
569
|
-
|
|
570
|
-
return
|
|
607
|
+
result2.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
|
|
608
|
+
return result2;
|
|
571
609
|
}
|
|
572
610
|
__name(bucketByModel, "bucketByModel");
|
|
573
611
|
function bucketByActor(rows, prices) {
|
|
@@ -585,9 +623,9 @@ function bucketByActor(rows, prices) {
|
|
|
585
623
|
bucket.threadIds.add(row.threadId);
|
|
586
624
|
byActor.set(row.actorRef, bucket);
|
|
587
625
|
}
|
|
588
|
-
const
|
|
626
|
+
const result2 = [];
|
|
589
627
|
for (const [actorRef, bucket] of byActor) {
|
|
590
|
-
|
|
628
|
+
result2.push({
|
|
591
629
|
actorRef,
|
|
592
630
|
requests: bucket.requests,
|
|
593
631
|
totalTokens: bucket.totalTokens,
|
|
@@ -595,8 +633,8 @@ function bucketByActor(rows, prices) {
|
|
|
595
633
|
threadCount: bucket.threadIds.size
|
|
596
634
|
});
|
|
597
635
|
}
|
|
598
|
-
|
|
599
|
-
return
|
|
636
|
+
result2.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
|
|
637
|
+
return result2;
|
|
600
638
|
}
|
|
601
639
|
__name(bucketByActor, "bucketByActor");
|
|
602
640
|
function bucketByThread(rows, prices, threads, options) {
|
|
@@ -612,13 +650,13 @@ function bucketByThread(rows, prices, threads, options) {
|
|
|
612
650
|
bucket.costUsd += rowCost(row, prices);
|
|
613
651
|
byThread.set(row.threadId, bucket);
|
|
614
652
|
}
|
|
615
|
-
const
|
|
653
|
+
const result2 = [];
|
|
616
654
|
for (const [threadId, bucket] of byThread) {
|
|
617
655
|
const thread = threads.get(threadId);
|
|
618
656
|
if (thread === void 0 && !options.includeUnknownThreads) {
|
|
619
657
|
continue;
|
|
620
658
|
}
|
|
621
|
-
|
|
659
|
+
result2.push({
|
|
622
660
|
threadId,
|
|
623
661
|
title: thread?.title ?? "",
|
|
624
662
|
actorRef: thread?.actorRef ?? "",
|
|
@@ -627,8 +665,8 @@ function bucketByThread(rows, prices, threads, options) {
|
|
|
627
665
|
costUsd: bucket.costUsd
|
|
628
666
|
});
|
|
629
667
|
}
|
|
630
|
-
|
|
631
|
-
return
|
|
668
|
+
result2.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
|
|
669
|
+
return result2.slice(0, options.limit);
|
|
632
670
|
}
|
|
633
671
|
__name(bucketByThread, "bucketByThread");
|
|
634
672
|
function rollupThreadUsage(rows, prices) {
|
|
@@ -660,16 +698,16 @@ function bucketUsageTrend(rows, prices) {
|
|
|
660
698
|
bucket.costUsd += rowCost(row, prices);
|
|
661
699
|
byDay.set(row.day, bucket);
|
|
662
700
|
}
|
|
663
|
-
const
|
|
701
|
+
const result2 = [];
|
|
664
702
|
for (const [day, bucket] of byDay) {
|
|
665
|
-
|
|
703
|
+
result2.push({
|
|
666
704
|
day,
|
|
667
705
|
totalTokens: bucket.totalTokens,
|
|
668
706
|
costUsd: bucket.costUsd
|
|
669
707
|
});
|
|
670
708
|
}
|
|
671
|
-
|
|
672
|
-
return
|
|
709
|
+
result2.sort((left, right) => left.day.localeCompare(right.day));
|
|
710
|
+
return result2;
|
|
673
711
|
}
|
|
674
712
|
__name(bucketUsageTrend, "bucketUsageTrend");
|
|
675
713
|
function dayBoundsUtc(range) {
|
|
@@ -1113,13 +1151,13 @@ async function validateStructured(schema, text, reported) {
|
|
|
1113
1151
|
issues: []
|
|
1114
1152
|
};
|
|
1115
1153
|
}
|
|
1116
|
-
const
|
|
1117
|
-
return
|
|
1154
|
+
const result2 = await schema["~standard"].validate(candidate);
|
|
1155
|
+
return result2.issues !== void 0 ? {
|
|
1118
1156
|
ok: false,
|
|
1119
|
-
issues:
|
|
1157
|
+
issues: result2.issues
|
|
1120
1158
|
} : {
|
|
1121
1159
|
ok: true,
|
|
1122
|
-
value:
|
|
1160
|
+
value: result2.value
|
|
1123
1161
|
};
|
|
1124
1162
|
}
|
|
1125
1163
|
__name(validateStructured, "validateStructured");
|
|
@@ -2298,12 +2336,12 @@ function memoryWriteVerdict(request) {
|
|
|
2298
2336
|
};
|
|
2299
2337
|
}
|
|
2300
2338
|
__name(memoryWriteVerdict, "memoryWriteVerdict");
|
|
2301
|
-
function memoryForgetVerdict({ record, actor }) {
|
|
2302
|
-
return
|
|
2339
|
+
function memoryForgetVerdict({ record: record2, actor }) {
|
|
2340
|
+
return record2.scope === actorScope(actor) ? {
|
|
2303
2341
|
allowed: true
|
|
2304
2342
|
} : {
|
|
2305
2343
|
allowed: false,
|
|
2306
|
-
reason: `"${
|
|
2344
|
+
reason: `"${record2.scope}" is not this actor's own scope; deleting it is an administrative action`
|
|
2307
2345
|
};
|
|
2308
2346
|
}
|
|
2309
2347
|
__name(memoryForgetVerdict, "memoryForgetVerdict");
|
|
@@ -2346,7 +2384,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
|
|
|
2346
2384
|
error: verdict.reason
|
|
2347
2385
|
};
|
|
2348
2386
|
}
|
|
2349
|
-
const
|
|
2387
|
+
const record2 = await write({
|
|
2350
2388
|
key,
|
|
2351
2389
|
text,
|
|
2352
2390
|
scope,
|
|
@@ -2360,7 +2398,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
|
|
|
2360
2398
|
});
|
|
2361
2399
|
return {
|
|
2362
2400
|
ok: true,
|
|
2363
|
-
record
|
|
2401
|
+
record: record2
|
|
2364
2402
|
};
|
|
2365
2403
|
}
|
|
2366
2404
|
__name(writeMemory, "writeMemory");
|
|
@@ -2412,7 +2450,7 @@ function danglingToolCallIds(messages) {
|
|
|
2412
2450
|
if (message.role !== "assistant" || calls.length === 0) {
|
|
2413
2451
|
continue;
|
|
2414
2452
|
}
|
|
2415
|
-
const settled = new Set((message.toolResults ?? []).map((
|
|
2453
|
+
const settled = new Set((message.toolResults ?? []).map((result2) => result2.id));
|
|
2416
2454
|
for (const call of calls) {
|
|
2417
2455
|
if (!settled.has(call.id)) {
|
|
2418
2456
|
ids.push(call.id);
|
|
@@ -2480,7 +2518,7 @@ function settleDanglingToolCalls(messages, outcomes = []) {
|
|
|
2480
2518
|
return message;
|
|
2481
2519
|
}
|
|
2482
2520
|
const results = message.toolResults ?? [];
|
|
2483
|
-
const settled = new Set(results.map((
|
|
2521
|
+
const settled = new Set(results.map((result2) => result2.id));
|
|
2484
2522
|
const missing = calls.filter((call) => !settled.has(call.id));
|
|
2485
2523
|
if (missing.length === 0) {
|
|
2486
2524
|
return message;
|
|
@@ -2588,6 +2626,1111 @@ async function settleUnsettledDelegation(args) {
|
|
|
2588
2626
|
}
|
|
2589
2627
|
__name(settleUnsettledDelegation, "settleUnsettledDelegation");
|
|
2590
2628
|
|
|
2629
|
+
// src/action-proposal-discovery.ts
|
|
2630
|
+
function validateActionProposalWorkerClaim(command, now) {
|
|
2631
|
+
if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
|
|
2632
|
+
if (typeof command.workerId !== "string" || command.workerId.length === 0) throw new TypeError("workerId must be a nonempty string");
|
|
2633
|
+
if (!Number.isSafeInteger(command.leaseMs) || command.leaseMs <= 0 || !Number.isSafeInteger(now + command.leaseMs)) throw new RangeError("leaseMs must be positive safe integer milliseconds");
|
|
2634
|
+
}
|
|
2635
|
+
__name(validateActionProposalWorkerClaim, "validateActionProposalWorkerClaim");
|
|
2636
|
+
function validateActionProposalExpiryBatch(command, now) {
|
|
2637
|
+
if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
|
|
2638
|
+
validateActionProposalDiscoveryIndexBatch(command);
|
|
2639
|
+
}
|
|
2640
|
+
__name(validateActionProposalExpiryBatch, "validateActionProposalExpiryBatch");
|
|
2641
|
+
function validateActionProposalDiscoveryIndexBatch(command) {
|
|
2642
|
+
if (!Number.isInteger(command.limit) || command.limit < 1 || command.limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
|
|
2643
|
+
}
|
|
2644
|
+
__name(validateActionProposalDiscoveryIndexBatch, "validateActionProposalDiscoveryIndexBatch");
|
|
2645
|
+
|
|
2646
|
+
// src/action-proposal-outcome.ts
|
|
2647
|
+
function attachActionProposalOutcome(row, now, ui = [], text) {
|
|
2648
|
+
if (row.outcome) return row;
|
|
2649
|
+
const execution = row.execution;
|
|
2650
|
+
const outcome = {
|
|
2651
|
+
id: `${row.id}:outcome:1`,
|
|
2652
|
+
proposalId: row.id,
|
|
2653
|
+
outcomeVersion: 1,
|
|
2654
|
+
tenantRef: row.tenantRef,
|
|
2655
|
+
actorRef: row.actorRef,
|
|
2656
|
+
threadId: row.threadId,
|
|
2657
|
+
originRunId: row.originRunId,
|
|
2658
|
+
originToolCallId: row.originToolCallId,
|
|
2659
|
+
toolName: row.toolName,
|
|
2660
|
+
decision: row.decision,
|
|
2661
|
+
ui,
|
|
2662
|
+
createdAt: now,
|
|
2663
|
+
...text !== void 0 ? {
|
|
2664
|
+
text
|
|
2665
|
+
} : {},
|
|
2666
|
+
...execution?.status === "succeeded" || execution?.status === "failed" ? {
|
|
2667
|
+
executionStatus: execution.status
|
|
2668
|
+
} : {},
|
|
2669
|
+
...execution && Object.hasOwn(execution, "result") ? {
|
|
2670
|
+
result: execution.result
|
|
2671
|
+
} : {},
|
|
2672
|
+
...execution?.error !== void 0 ? {
|
|
2673
|
+
error: execution.error
|
|
2674
|
+
} : row.decisionAudit?.reason !== void 0 ? {
|
|
2675
|
+
error: row.decisionAudit.reason
|
|
2676
|
+
} : {}
|
|
2677
|
+
};
|
|
2678
|
+
return snapshotActionProposal({
|
|
2679
|
+
...row,
|
|
2680
|
+
outcome,
|
|
2681
|
+
outcomeDelivery: {
|
|
2682
|
+
status: "pending",
|
|
2683
|
+
generation: 0,
|
|
2684
|
+
lease: null
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
__name(attachActionProposalOutcome, "attachActionProposalOutcome");
|
|
2689
|
+
function claimActionProposalOutcome(row, command, now, token) {
|
|
2690
|
+
validateActionProposalWorkerClaim(command, now);
|
|
2691
|
+
const delivery = row.outcomeDelivery;
|
|
2692
|
+
if (!row.outcome || !delivery || delivery.status !== "pending" || delivery.lease && now < delivery.lease.expiresAt) return null;
|
|
2693
|
+
const generation = delivery.generation + 1;
|
|
2694
|
+
return snapshotActionProposal({
|
|
2695
|
+
...row,
|
|
2696
|
+
outcomeDelivery: {
|
|
2697
|
+
...delivery,
|
|
2698
|
+
generation,
|
|
2699
|
+
lease: {
|
|
2700
|
+
token,
|
|
2701
|
+
generation,
|
|
2702
|
+
workerId: command.workerId,
|
|
2703
|
+
expiresAt: now + command.leaseMs
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
__name(claimActionProposalOutcome, "claimActionProposalOutcome");
|
|
2709
|
+
function actionProposalOutcomeFenceValid(row, command, now) {
|
|
2710
|
+
if (!Number.isSafeInteger(now)) throw new RangeError("Invalid server clock");
|
|
2711
|
+
if (typeof command.token !== "string" || !command.token || !Number.isSafeInteger(command.generation) || command.generation < 1 || typeof command.outcomeId !== "string" || !command.outcomeId) throw new TypeError("Invalid outcome fence");
|
|
2712
|
+
const delivery = row.outcomeDelivery;
|
|
2713
|
+
return row.outcome?.id === command.outcomeId && delivery?.status === "pending" && delivery.generation === command.generation && delivery.lease?.token === command.token && delivery.lease.generation === command.generation && now < delivery.lease.expiresAt;
|
|
2714
|
+
}
|
|
2715
|
+
__name(actionProposalOutcomeFenceValid, "actionProposalOutcomeFenceValid");
|
|
2716
|
+
function outcomeSummary(outcome) {
|
|
2717
|
+
if (outcome.executionStatus === "succeeded") return `Action ${JSON.stringify(outcome.toolName)} completed (proposal ${JSON.stringify(outcome.proposalId)}).${Object.hasOwn(outcome, "result") ? ` Result: ${JSON.stringify(outcome.result)}` : ""}`;
|
|
2718
|
+
if (outcome.executionStatus === "failed") return `Action ${JSON.stringify(outcome.toolName)} failed (proposal ${JSON.stringify(outcome.proposalId)}): ${JSON.stringify(outcome.error ?? "Execution failed")}`;
|
|
2719
|
+
return `Action ${JSON.stringify(outcome.toolName)} was ${outcome.decision} (proposal ${JSON.stringify(outcome.proposalId)}) and was not executed.${outcome.error ? ` Reason: ${JSON.stringify(outcome.error)}` : ""}`;
|
|
2720
|
+
}
|
|
2721
|
+
__name(outcomeSummary, "outcomeSummary");
|
|
2722
|
+
function actionProposalOutcomeScope(outcome) {
|
|
2723
|
+
return {
|
|
2724
|
+
tenantRef: outcome.tenantRef,
|
|
2725
|
+
actorRef: outcome.actorRef,
|
|
2726
|
+
threadId: outcome.threadId
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
__name(actionProposalOutcomeScope, "actionProposalOutcomeScope");
|
|
2730
|
+
function actionProposalOutcomeText(outcome) {
|
|
2731
|
+
return outcomeSummary(outcome) + (outcome.text ? ` UI: ${JSON.stringify(outcome.text)}` : "");
|
|
2732
|
+
}
|
|
2733
|
+
__name(actionProposalOutcomeText, "actionProposalOutcomeText");
|
|
2734
|
+
|
|
2735
|
+
// src/genui/schema.ts
|
|
2736
|
+
function isStandardSchema(schema) {
|
|
2737
|
+
return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"]?.validate === "function";
|
|
2738
|
+
}
|
|
2739
|
+
__name(isStandardSchema, "isStandardSchema");
|
|
2740
|
+
function toJsonSchema(schema) {
|
|
2741
|
+
if (!isStandardSchema(schema)) {
|
|
2742
|
+
return schema;
|
|
2743
|
+
}
|
|
2744
|
+
const standard = schema["~standard"];
|
|
2745
|
+
const converter = standard.jsonSchema;
|
|
2746
|
+
if (converter === void 0 || typeof converter.input !== "function") {
|
|
2747
|
+
return void 0;
|
|
2748
|
+
}
|
|
2749
|
+
try {
|
|
2750
|
+
return converter.input({
|
|
2751
|
+
target: "draft-07"
|
|
2752
|
+
});
|
|
2753
|
+
} catch {
|
|
2754
|
+
return void 0;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
__name(toJsonSchema, "toJsonSchema");
|
|
2758
|
+
async function validateProps(schema, value, validator) {
|
|
2759
|
+
if (isStandardSchema(schema)) {
|
|
2760
|
+
const result2 = await schema["~standard"].validate(value);
|
|
2761
|
+
if (result2.issues !== void 0) {
|
|
2762
|
+
return {
|
|
2763
|
+
ok: false,
|
|
2764
|
+
issues: toGenuiIssues(result2.issues)
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
return {
|
|
2768
|
+
ok: true,
|
|
2769
|
+
value: result2.value
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
const issues = validator.validate(schema, value);
|
|
2773
|
+
return issues.length > 0 ? {
|
|
2774
|
+
ok: false,
|
|
2775
|
+
issues
|
|
2776
|
+
} : {
|
|
2777
|
+
ok: true,
|
|
2778
|
+
value
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
__name(validateProps, "validateProps");
|
|
2782
|
+
function validatePropsSync(schema, value, validator) {
|
|
2783
|
+
if (!isStandardSchema(schema)) {
|
|
2784
|
+
const issues = validator.validate(schema, value);
|
|
2785
|
+
return issues.length > 0 ? {
|
|
2786
|
+
ok: false,
|
|
2787
|
+
issues
|
|
2788
|
+
} : {
|
|
2789
|
+
ok: true,
|
|
2790
|
+
value
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
const result2 = schema["~standard"].validate(value);
|
|
2794
|
+
if (result2 instanceof Promise) {
|
|
2795
|
+
result2.catch(() => void 0);
|
|
2796
|
+
return void 0;
|
|
2797
|
+
}
|
|
2798
|
+
return result2.issues !== void 0 ? {
|
|
2799
|
+
ok: false,
|
|
2800
|
+
issues: toGenuiIssues(result2.issues)
|
|
2801
|
+
} : {
|
|
2802
|
+
ok: true,
|
|
2803
|
+
value: result2.value
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
__name(validatePropsSync, "validatePropsSync");
|
|
2807
|
+
function toGenuiIssues(issues) {
|
|
2808
|
+
return issues.map((issue4) => ({
|
|
2809
|
+
path: (issue4.path ?? []).map((segment) => typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment),
|
|
2810
|
+
message: issue4.message
|
|
2811
|
+
}));
|
|
2812
|
+
}
|
|
2813
|
+
__name(toGenuiIssues, "toGenuiIssues");
|
|
2814
|
+
function formatIssues(issues) {
|
|
2815
|
+
return issues.map((issue4) => `${issue4.path.length > 0 ? formatPath(issue4.path) : "(root)"}: ${issue4.message}`).join("; ");
|
|
2816
|
+
}
|
|
2817
|
+
__name(formatIssues, "formatIssues");
|
|
2818
|
+
function formatPath(path) {
|
|
2819
|
+
return path.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
|
|
2820
|
+
}
|
|
2821
|
+
__name(formatPath, "formatPath");
|
|
2822
|
+
var builtinJsonSchemaValidator = {
|
|
2823
|
+
validate(schema, value) {
|
|
2824
|
+
const issues = [];
|
|
2825
|
+
check(schema, value, [], schema, issues, 0);
|
|
2826
|
+
return issues;
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
var MAX_DEPTH = 64;
|
|
2830
|
+
function typeOf(value) {
|
|
2831
|
+
if (value === null) return "null";
|
|
2832
|
+
if (Array.isArray(value)) return "array";
|
|
2833
|
+
return typeof value;
|
|
2834
|
+
}
|
|
2835
|
+
__name(typeOf, "typeOf");
|
|
2836
|
+
function matchesType(type, value) {
|
|
2837
|
+
switch (type) {
|
|
2838
|
+
case "integer":
|
|
2839
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
2840
|
+
case "number":
|
|
2841
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
2842
|
+
case "object":
|
|
2843
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2844
|
+
default:
|
|
2845
|
+
return typeOf(value) === type;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
__name(matchesType, "matchesType");
|
|
2849
|
+
function resolveRef(root, ref) {
|
|
2850
|
+
if (ref === "#") return root;
|
|
2851
|
+
if (!ref.startsWith("#/")) return void 0;
|
|
2852
|
+
let current = root;
|
|
2853
|
+
for (const raw of ref.slice(2).split("/")) {
|
|
2854
|
+
const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
2855
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
2856
|
+
current = current[key];
|
|
2857
|
+
}
|
|
2858
|
+
return typeof current === "object" && current !== null ? current : void 0;
|
|
2859
|
+
}
|
|
2860
|
+
__name(resolveRef, "resolveRef");
|
|
2861
|
+
function deepEqual(a, b) {
|
|
2862
|
+
if (a === b) return true;
|
|
2863
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
2864
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
2865
|
+
const keysA = Object.keys(a);
|
|
2866
|
+
const keysB = Object.keys(b);
|
|
2867
|
+
if (keysA.length !== keysB.length) return false;
|
|
2868
|
+
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
2869
|
+
}
|
|
2870
|
+
__name(deepEqual, "deepEqual");
|
|
2871
|
+
function check(schema, value, path, root, issues, depth) {
|
|
2872
|
+
if (schema === true || schema === void 0) return;
|
|
2873
|
+
if (schema === false) {
|
|
2874
|
+
issues.push({
|
|
2875
|
+
path,
|
|
2876
|
+
message: "is not allowed"
|
|
2877
|
+
});
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2880
|
+
if (typeof schema !== "object" || schema === null) return;
|
|
2881
|
+
if (depth > MAX_DEPTH) {
|
|
2882
|
+
issues.push({
|
|
2883
|
+
path,
|
|
2884
|
+
message: "is nested too deeply"
|
|
2885
|
+
});
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
const s = schema;
|
|
2889
|
+
if (typeof s.$ref === "string") {
|
|
2890
|
+
const target = resolveRef(root, s.$ref);
|
|
2891
|
+
if (target === void 0) {
|
|
2892
|
+
issues.push({
|
|
2893
|
+
path,
|
|
2894
|
+
message: `references unknown schema ${s.$ref}`
|
|
2895
|
+
});
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
check(target, value, path, root, issues, depth + 1);
|
|
2899
|
+
}
|
|
2900
|
+
if (s.type !== void 0) {
|
|
2901
|
+
const types = Array.isArray(s.type) ? s.type : [
|
|
2902
|
+
s.type
|
|
2903
|
+
];
|
|
2904
|
+
if (!types.some((type) => matchesType(type, value))) {
|
|
2905
|
+
issues.push({
|
|
2906
|
+
path,
|
|
2907
|
+
message: `must be ${types.join(" or ")}`
|
|
2908
|
+
});
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
if (Array.isArray(s.enum) && !s.enum.some((option) => deepEqual(option, value))) {
|
|
2913
|
+
issues.push({
|
|
2914
|
+
path,
|
|
2915
|
+
message: `must be one of ${s.enum.map((option) => JSON.stringify(option)).join(", ")}`
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2918
|
+
if ("const" in s && !deepEqual(s.const, value)) {
|
|
2919
|
+
issues.push({
|
|
2920
|
+
path,
|
|
2921
|
+
message: `must be ${JSON.stringify(s.const)}`
|
|
2922
|
+
});
|
|
2923
|
+
}
|
|
2924
|
+
if (typeof value === "string") {
|
|
2925
|
+
if (typeof s.minLength === "number" && value.length < s.minLength) {
|
|
2926
|
+
issues.push({
|
|
2927
|
+
path,
|
|
2928
|
+
message: `must be at least ${s.minLength} characters`
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
if (typeof s.maxLength === "number" && value.length > s.maxLength) {
|
|
2932
|
+
issues.push({
|
|
2933
|
+
path,
|
|
2934
|
+
message: `must be at most ${s.maxLength} characters`
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
if (typeof s.pattern === "string") {
|
|
2938
|
+
let pattern;
|
|
2939
|
+
try {
|
|
2940
|
+
pattern = new RegExp(s.pattern, "u");
|
|
2941
|
+
} catch {
|
|
2942
|
+
pattern = void 0;
|
|
2943
|
+
}
|
|
2944
|
+
if (pattern !== void 0 && !pattern.test(value)) {
|
|
2945
|
+
issues.push({
|
|
2946
|
+
path,
|
|
2947
|
+
message: `must match ${s.pattern}`
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
if (typeof value === "number") {
|
|
2953
|
+
if (typeof s.minimum === "number" && value < s.minimum) {
|
|
2954
|
+
issues.push({
|
|
2955
|
+
path,
|
|
2956
|
+
message: `must be >= ${s.minimum}`
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
if (typeof s.maximum === "number" && value > s.maximum) {
|
|
2960
|
+
issues.push({
|
|
2961
|
+
path,
|
|
2962
|
+
message: `must be <= ${s.maximum}`
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
if (Array.isArray(value)) {
|
|
2967
|
+
if (typeof s.minItems === "number" && value.length < s.minItems) {
|
|
2968
|
+
issues.push({
|
|
2969
|
+
path,
|
|
2970
|
+
message: `must have at least ${s.minItems} items`
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
if (typeof s.maxItems === "number" && value.length > s.maxItems) {
|
|
2974
|
+
issues.push({
|
|
2975
|
+
path,
|
|
2976
|
+
message: `must have at most ${s.maxItems} items`
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
if (s.items !== void 0 && !Array.isArray(s.items)) {
|
|
2980
|
+
value.forEach((item, index) => {
|
|
2981
|
+
check(s.items, item, [
|
|
2982
|
+
...path,
|
|
2983
|
+
index
|
|
2984
|
+
], root, issues, depth + 1);
|
|
2985
|
+
});
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
if (matchesType("object", value)) {
|
|
2989
|
+
const record2 = value;
|
|
2990
|
+
const properties = typeof s.properties === "object" && s.properties !== null ? s.properties : {};
|
|
2991
|
+
if (Array.isArray(s.required)) {
|
|
2992
|
+
for (const key of s.required) {
|
|
2993
|
+
if (record2[key] === void 0) {
|
|
2994
|
+
issues.push({
|
|
2995
|
+
path: [
|
|
2996
|
+
...path,
|
|
2997
|
+
key
|
|
2998
|
+
],
|
|
2999
|
+
message: "is required"
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
for (const [key, entry] of Object.entries(record2)) {
|
|
3005
|
+
if (key in properties) {
|
|
3006
|
+
if (entry !== void 0) {
|
|
3007
|
+
check(properties[key], entry, [
|
|
3008
|
+
...path,
|
|
3009
|
+
key
|
|
3010
|
+
], root, issues, depth + 1);
|
|
3011
|
+
}
|
|
3012
|
+
} else if (s.additionalProperties === false) {
|
|
3013
|
+
issues.push({
|
|
3014
|
+
path: [
|
|
3015
|
+
...path,
|
|
3016
|
+
key
|
|
3017
|
+
],
|
|
3018
|
+
message: "is not a known property"
|
|
3019
|
+
});
|
|
3020
|
+
} else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
|
|
3021
|
+
check(s.additionalProperties, entry, [
|
|
3022
|
+
...path,
|
|
3023
|
+
key
|
|
3024
|
+
], root, issues, depth + 1);
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
if (Array.isArray(s.allOf)) {
|
|
3029
|
+
for (const sub of s.allOf) check(sub, value, path, root, issues, depth + 1);
|
|
3030
|
+
}
|
|
3031
|
+
if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
|
|
3032
|
+
const options = Array.isArray(s.anyOf) ? s.anyOf : s.oneOf;
|
|
3033
|
+
const passing = options.filter((sub) => {
|
|
3034
|
+
const nested = [];
|
|
3035
|
+
check(sub, value, path, root, nested, depth + 1);
|
|
3036
|
+
return nested.length === 0;
|
|
3037
|
+
}).length;
|
|
3038
|
+
if (Array.isArray(s.anyOf) ? passing === 0 : passing !== 1) {
|
|
3039
|
+
issues.push({
|
|
3040
|
+
path,
|
|
3041
|
+
message: Array.isArray(s.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
if (s.not !== void 0) {
|
|
3046
|
+
const nested = [];
|
|
3047
|
+
check(s.not, value, path, root, nested, depth + 1);
|
|
3048
|
+
if (nested.length === 0) {
|
|
3049
|
+
issues.push({
|
|
3050
|
+
path,
|
|
3051
|
+
message: "matches a shape that is not allowed"
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
__name(check, "check");
|
|
3057
|
+
|
|
3058
|
+
// src/genui/catalog.ts
|
|
3059
|
+
var COMPONENT_NAME = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
|
|
3060
|
+
function defineComponent(definition) {
|
|
3061
|
+
if (!COMPONENT_NAME.test(definition.name)) {
|
|
3062
|
+
throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
|
|
3063
|
+
}
|
|
3064
|
+
if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
|
|
3065
|
+
if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
|
|
3066
|
+
return definition;
|
|
3067
|
+
}
|
|
3068
|
+
__name(defineComponent, "defineComponent");
|
|
3069
|
+
function defineCatalog(components, options = {}) {
|
|
3070
|
+
const byName = /* @__PURE__ */ new Map();
|
|
3071
|
+
for (const component of components) {
|
|
3072
|
+
defineComponent(component);
|
|
3073
|
+
if (byName.has(component.name)) {
|
|
3074
|
+
throw new Error(`genui: component "${component.name}" is defined twice in one catalog`);
|
|
3075
|
+
}
|
|
3076
|
+
byName.set(component.name, component);
|
|
3077
|
+
}
|
|
3078
|
+
return buildCatalog(byName, options.jsonSchemaValidator ?? builtinJsonSchemaValidator);
|
|
3079
|
+
}
|
|
3080
|
+
__name(defineCatalog, "defineCatalog");
|
|
3081
|
+
function buildCatalog(byName, validator) {
|
|
3082
|
+
const list = Object.freeze([
|
|
3083
|
+
...byName.values()
|
|
3084
|
+
]);
|
|
3085
|
+
return {
|
|
3086
|
+
components: list,
|
|
3087
|
+
validator,
|
|
3088
|
+
get: /* @__PURE__ */ __name((name) => byName.get(name), "get"),
|
|
3089
|
+
has: /* @__PURE__ */ __name((name) => byName.has(name), "has"),
|
|
3090
|
+
modelComponents: /* @__PURE__ */ __name(() => list.filter((component) => component.internal !== true), "modelComponents"),
|
|
3091
|
+
async validate(component, props) {
|
|
3092
|
+
const definition = byName.get(component);
|
|
3093
|
+
if (definition === void 0) {
|
|
3094
|
+
return {
|
|
3095
|
+
ok: false,
|
|
3096
|
+
issues: [
|
|
3097
|
+
{
|
|
3098
|
+
path: [],
|
|
3099
|
+
message: `unknown component "${component}"`
|
|
3100
|
+
}
|
|
3101
|
+
]
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
const result2 = await validateProps(definition.props, props, validator);
|
|
3105
|
+
return result2;
|
|
3106
|
+
},
|
|
3107
|
+
validateSync(component, props) {
|
|
3108
|
+
const definition = byName.get(component);
|
|
3109
|
+
if (definition === void 0) {
|
|
3110
|
+
return {
|
|
3111
|
+
ok: false,
|
|
3112
|
+
issues: [
|
|
3113
|
+
{
|
|
3114
|
+
path: [],
|
|
3115
|
+
message: `unknown component "${component}"`
|
|
3116
|
+
}
|
|
3117
|
+
]
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
return validatePropsSync(definition.props, props, validator);
|
|
3121
|
+
},
|
|
3122
|
+
jsonSchemaFor(component) {
|
|
3123
|
+
const definition = byName.get(component);
|
|
3124
|
+
return definition === void 0 ? void 0 : toJsonSchema(definition.props);
|
|
3125
|
+
},
|
|
3126
|
+
extend(more) {
|
|
3127
|
+
const next = new Map(byName);
|
|
3128
|
+
for (const component of more) {
|
|
3129
|
+
defineComponent(component);
|
|
3130
|
+
next.set(component.name, component);
|
|
3131
|
+
}
|
|
3132
|
+
return buildCatalog(next, validator);
|
|
3133
|
+
}
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
__name(buildCatalog, "buildCatalog");
|
|
3137
|
+
|
|
3138
|
+
// src/genui/tree.ts
|
|
3139
|
+
var GENUI_TREE_COMPONENT = "genui:tree";
|
|
3140
|
+
async function validateTree(catalog, tree, limits = {}) {
|
|
3141
|
+
const maxDepth = limits.maxDepth ?? 12;
|
|
3142
|
+
const maxNodes = limits.maxNodes ?? 200;
|
|
3143
|
+
const issues = [];
|
|
3144
|
+
let nodes = 0;
|
|
3145
|
+
const visit = /* @__PURE__ */ __name(async (node, path, depth) => {
|
|
3146
|
+
nodes += 1;
|
|
3147
|
+
if (nodes > maxNodes) {
|
|
3148
|
+
if (nodes === maxNodes + 1) {
|
|
3149
|
+
issues.push({
|
|
3150
|
+
path,
|
|
3151
|
+
message: `the tree has more than ${maxNodes} elements`
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
return void 0;
|
|
3155
|
+
}
|
|
3156
|
+
if (depth > maxDepth) {
|
|
3157
|
+
issues.push({
|
|
3158
|
+
path,
|
|
3159
|
+
message: `the tree is nested deeper than ${maxDepth} levels`
|
|
3160
|
+
});
|
|
3161
|
+
return void 0;
|
|
3162
|
+
}
|
|
3163
|
+
if (typeof node !== "object" || node === null || Array.isArray(node)) {
|
|
3164
|
+
issues.push({
|
|
3165
|
+
path,
|
|
3166
|
+
message: "must be an element object { type, props, children? }"
|
|
3167
|
+
});
|
|
3168
|
+
return void 0;
|
|
3169
|
+
}
|
|
3170
|
+
const element = node;
|
|
3171
|
+
if (typeof element.type !== "string") {
|
|
3172
|
+
issues.push({
|
|
3173
|
+
path: [
|
|
3174
|
+
...path,
|
|
3175
|
+
"type"
|
|
3176
|
+
],
|
|
3177
|
+
message: "must be a component name"
|
|
3178
|
+
});
|
|
3179
|
+
return void 0;
|
|
3180
|
+
}
|
|
3181
|
+
const definition = catalog.get(element.type);
|
|
3182
|
+
if (definition === void 0 || definition.internal === true) {
|
|
3183
|
+
issues.push({
|
|
3184
|
+
path: [
|
|
3185
|
+
...path,
|
|
3186
|
+
"type"
|
|
3187
|
+
],
|
|
3188
|
+
message: `unknown component "${element.type}" (allowed: ${catalog.modelComponents().map((component) => component.name).join(", ")})`
|
|
3189
|
+
});
|
|
3190
|
+
return void 0;
|
|
3191
|
+
}
|
|
3192
|
+
const validated = await catalog.validate(element.type, element.props ?? {});
|
|
3193
|
+
let props = {};
|
|
3194
|
+
if (!validated.ok) {
|
|
3195
|
+
for (const issue4 of validated.issues) {
|
|
3196
|
+
issues.push({
|
|
3197
|
+
path: [
|
|
3198
|
+
...path,
|
|
3199
|
+
"props",
|
|
3200
|
+
...issue4.path
|
|
3201
|
+
],
|
|
3202
|
+
message: issue4.message
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
} else {
|
|
3206
|
+
props = validated.value;
|
|
3207
|
+
}
|
|
3208
|
+
const rawChildren = parseChildrenIfStringified(element.children);
|
|
3209
|
+
if (rawChildren === void 0 || rawChildren === null) {
|
|
3210
|
+
return {
|
|
3211
|
+
type: element.type,
|
|
3212
|
+
props
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
if (!Array.isArray(rawChildren)) {
|
|
3216
|
+
issues.push({
|
|
3217
|
+
path: [
|
|
3218
|
+
...path,
|
|
3219
|
+
"children"
|
|
3220
|
+
],
|
|
3221
|
+
message: "must be an array of elements (a literal array, not a string)"
|
|
3222
|
+
});
|
|
3223
|
+
return void 0;
|
|
3224
|
+
}
|
|
3225
|
+
if (rawChildren.length > 0 && definition.children !== true) {
|
|
3226
|
+
issues.push({
|
|
3227
|
+
path: [
|
|
3228
|
+
...path,
|
|
3229
|
+
"children"
|
|
3230
|
+
],
|
|
3231
|
+
message: `${element.type} does not take children`
|
|
3232
|
+
});
|
|
3233
|
+
return void 0;
|
|
3234
|
+
}
|
|
3235
|
+
const children = [];
|
|
3236
|
+
for (const [index, child] of rawChildren.entries()) {
|
|
3237
|
+
const next = await visit(child, [
|
|
3238
|
+
...path,
|
|
3239
|
+
"children",
|
|
3240
|
+
index
|
|
3241
|
+
], depth + 1);
|
|
3242
|
+
if (next !== void 0) {
|
|
3243
|
+
children.push(next);
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
return children.length > 0 ? {
|
|
3247
|
+
type: element.type,
|
|
3248
|
+
props,
|
|
3249
|
+
children
|
|
3250
|
+
} : {
|
|
3251
|
+
type: element.type,
|
|
3252
|
+
props
|
|
3253
|
+
};
|
|
3254
|
+
}, "visit");
|
|
3255
|
+
const root = await visit(tree, [], 1);
|
|
3256
|
+
return issues.length > 0 || root === void 0 ? {
|
|
3257
|
+
ok: false,
|
|
3258
|
+
issues: issues.length > 0 ? issues : [
|
|
3259
|
+
{
|
|
3260
|
+
path: [],
|
|
3261
|
+
message: "is invalid"
|
|
3262
|
+
}
|
|
3263
|
+
]
|
|
3264
|
+
} : {
|
|
3265
|
+
ok: true,
|
|
3266
|
+
value: root
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
__name(validateTree, "validateTree");
|
|
3270
|
+
function parseChildrenIfStringified(value) {
|
|
3271
|
+
if (typeof value !== "string") return value;
|
|
3272
|
+
const trimmed = value.trim();
|
|
3273
|
+
if (!trimmed.startsWith("[")) return value;
|
|
3274
|
+
return leadingJsonArray(trimmed) ?? value;
|
|
3275
|
+
}
|
|
3276
|
+
__name(parseChildrenIfStringified, "parseChildrenIfStringified");
|
|
3277
|
+
function leadingJsonArray(text) {
|
|
3278
|
+
let depth = 0;
|
|
3279
|
+
let inString = false;
|
|
3280
|
+
let escaped = false;
|
|
3281
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
3282
|
+
const char = text[index];
|
|
3283
|
+
if (inString) {
|
|
3284
|
+
if (escaped) escaped = false;
|
|
3285
|
+
else if (char === "\\") escaped = true;
|
|
3286
|
+
else if (char === '"') inString = false;
|
|
3287
|
+
continue;
|
|
3288
|
+
}
|
|
3289
|
+
if (char === '"') inString = true;
|
|
3290
|
+
else if (char === "[" || char === "{") depth += 1;
|
|
3291
|
+
else if (char === "]" || char === "}") {
|
|
3292
|
+
depth -= 1;
|
|
3293
|
+
if (depth === 0) {
|
|
3294
|
+
try {
|
|
3295
|
+
const parsed = JSON.parse(text.slice(0, index + 1));
|
|
3296
|
+
return Array.isArray(parsed) ? parsed : void 0;
|
|
3297
|
+
} catch {
|
|
3298
|
+
return void 0;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
return void 0;
|
|
3304
|
+
}
|
|
3305
|
+
__name(leadingJsonArray, "leadingJsonArray");
|
|
3306
|
+
|
|
3307
|
+
// src/genui/text.ts
|
|
3308
|
+
function componentToText(catalog, component, props) {
|
|
3309
|
+
if (component === GENUI_TREE_COMPONENT) {
|
|
3310
|
+
const root = props.root;
|
|
3311
|
+
return root === void 0 ? "" : treeToText(catalog, root);
|
|
3312
|
+
}
|
|
3313
|
+
const fallback = catalog.get(component)?.fallbackText;
|
|
3314
|
+
if (fallback !== void 0) {
|
|
3315
|
+
try {
|
|
3316
|
+
const text = fallback(props);
|
|
3317
|
+
if (typeof text === "string" && text.trim().length > 0) return text;
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
return jsonBlock(props);
|
|
3322
|
+
}
|
|
3323
|
+
__name(componentToText, "componentToText");
|
|
3324
|
+
function treeToText(catalog, root) {
|
|
3325
|
+
const out = [];
|
|
3326
|
+
const visit = /* @__PURE__ */ __name((node, depth) => {
|
|
3327
|
+
if (depth > 32) return;
|
|
3328
|
+
const definition = catalog.get(node.type);
|
|
3329
|
+
if (definition?.fallbackText !== void 0) {
|
|
3330
|
+
try {
|
|
3331
|
+
const text = definition.fallbackText(node.props);
|
|
3332
|
+
out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
|
|
3333
|
+
} catch {
|
|
3334
|
+
out.push(jsonBlock(node.props));
|
|
3335
|
+
}
|
|
3336
|
+
} else if (definition?.children !== true) {
|
|
3337
|
+
out.push(jsonBlock(node.props));
|
|
3338
|
+
}
|
|
3339
|
+
for (const child of node.children ?? []) visit(child, depth + 1);
|
|
3340
|
+
}, "visit");
|
|
3341
|
+
visit(root, 0);
|
|
3342
|
+
return out.filter((line) => line.trim().length > 0).join("\n");
|
|
3343
|
+
}
|
|
3344
|
+
__name(treeToText, "treeToText");
|
|
3345
|
+
function jsonBlock(props) {
|
|
3346
|
+
const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
|
|
3347
|
+
` : "";
|
|
3348
|
+
return `${title}\`\`\`
|
|
3349
|
+
${JSON.stringify(props, null, 2)}
|
|
3350
|
+
\`\`\``;
|
|
3351
|
+
}
|
|
3352
|
+
__name(jsonBlock, "jsonBlock");
|
|
3353
|
+
|
|
3354
|
+
// src/genui/capabilities.ts
|
|
3355
|
+
function validateUiCapabilities(value) {
|
|
3356
|
+
if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
|
|
3357
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3358
|
+
const components = value.components.map((component) => {
|
|
3359
|
+
if (!record(component) || Object.keys(component).some((key) => key !== "name" && key !== "version") || typeof component.name !== "string" || !COMPONENT_NAME.test(component.name) || !Number.isSafeInteger(component.version) || Number(component.version) <= 0 || seen.has(component.name)) throw new TypeError("Invalid UI component capability");
|
|
3360
|
+
seen.add(component.name);
|
|
3361
|
+
return {
|
|
3362
|
+
name: component.name,
|
|
3363
|
+
version: Number(component.version)
|
|
3364
|
+
};
|
|
3365
|
+
});
|
|
3366
|
+
return {
|
|
3367
|
+
components
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
__name(validateUiCapabilities, "validateUiCapabilities");
|
|
3371
|
+
function record(value) {
|
|
3372
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3373
|
+
}
|
|
3374
|
+
__name(record, "record");
|
|
3375
|
+
function negotiateCatalog(catalog, capabilities) {
|
|
3376
|
+
if (capabilities === void 0) return catalog;
|
|
3377
|
+
const supported = validateUiCapabilities(capabilities).components;
|
|
3378
|
+
return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
|
|
3379
|
+
jsonSchemaValidator: catalog.validator
|
|
3380
|
+
});
|
|
3381
|
+
}
|
|
3382
|
+
__name(negotiateCatalog, "negotiateCatalog");
|
|
3383
|
+
async function prepareUiEmission(catalog, capabilities, component, props, version) {
|
|
3384
|
+
const drawable = negotiateCatalog(catalog, capabilities);
|
|
3385
|
+
if (component === GENUI_TREE_COMPONENT) {
|
|
3386
|
+
if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
|
|
3387
|
+
const validated2 = await validateTree(catalog, props.root);
|
|
3388
|
+
if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
|
|
3389
|
+
const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
|
|
3390
|
+
const versions = /* @__PURE__ */ new Map();
|
|
3391
|
+
const collectVersions = /* @__PURE__ */ __name((node) => {
|
|
3392
|
+
versions.set(node.type, catalog.get(node.type)?.version ?? 1);
|
|
3393
|
+
for (const child of node.children ?? []) collectVersions(child);
|
|
3394
|
+
}, "collectVersions");
|
|
3395
|
+
collectVersions(validated2.value);
|
|
3396
|
+
return supported(validated2.value) ? {
|
|
3397
|
+
kind: "ui",
|
|
3398
|
+
component,
|
|
3399
|
+
props: {
|
|
3400
|
+
root: validated2.value
|
|
3401
|
+
},
|
|
3402
|
+
version: 1,
|
|
3403
|
+
fallbackText: treeToText(catalog, validated2.value),
|
|
3404
|
+
componentVersions: Object.fromEntries(versions)
|
|
3405
|
+
} : {
|
|
3406
|
+
kind: "text",
|
|
3407
|
+
text: treeToText(catalog, validated2.value)
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
const definition = catalog.get(component);
|
|
3411
|
+
if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
|
|
3412
|
+
const currentVersion = definition.version ?? 1;
|
|
3413
|
+
if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
|
|
3414
|
+
const validated = await catalog.validate(component, props);
|
|
3415
|
+
if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
|
|
3416
|
+
return drawable.has(component) ? {
|
|
3417
|
+
kind: "ui",
|
|
3418
|
+
component,
|
|
3419
|
+
props: validated.value,
|
|
3420
|
+
version: currentVersion,
|
|
3421
|
+
fallbackText: componentToText(catalog, component, validated.value)
|
|
3422
|
+
} : {
|
|
3423
|
+
kind: "text",
|
|
3424
|
+
text: componentToText(catalog, component, validated.value)
|
|
3425
|
+
};
|
|
3426
|
+
}
|
|
3427
|
+
__name(prepareUiEmission, "prepareUiEmission");
|
|
3428
|
+
|
|
3429
|
+
// src/action-proposal-transitions.ts
|
|
3430
|
+
function checkTime(value) {
|
|
3431
|
+
if (!Number.isSafeInteger(value)) throw new RangeError("Action proposal timestamps must be safe integer milliseconds");
|
|
3432
|
+
}
|
|
3433
|
+
__name(checkTime, "checkTime");
|
|
3434
|
+
function checkString(value) {
|
|
3435
|
+
if (typeof value !== "string" || value.length === 0) throw new TypeError("Action proposal identifiers must be nonempty strings");
|
|
3436
|
+
}
|
|
3437
|
+
__name(checkString, "checkString");
|
|
3438
|
+
function validateActionProposalCreation(input) {
|
|
3439
|
+
const allowed = [
|
|
3440
|
+
"id",
|
|
3441
|
+
"tenantRef",
|
|
3442
|
+
"actorRef",
|
|
3443
|
+
"threadId",
|
|
3444
|
+
"originRunId",
|
|
3445
|
+
"originMessageId",
|
|
3446
|
+
"originToolCallId",
|
|
3447
|
+
"toolName",
|
|
3448
|
+
"input",
|
|
3449
|
+
"preparationInput",
|
|
3450
|
+
"executionContext",
|
|
3451
|
+
"confirmation",
|
|
3452
|
+
"approver",
|
|
3453
|
+
"expiresAt",
|
|
3454
|
+
"idempotencyKey",
|
|
3455
|
+
"replacementKey"
|
|
3456
|
+
];
|
|
3457
|
+
if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("Unknown action proposal creation field");
|
|
3458
|
+
for (const key of [
|
|
3459
|
+
"id",
|
|
3460
|
+
"actorRef",
|
|
3461
|
+
"threadId",
|
|
3462
|
+
"originRunId",
|
|
3463
|
+
"originMessageId",
|
|
3464
|
+
"originToolCallId",
|
|
3465
|
+
"toolName",
|
|
3466
|
+
"approver",
|
|
3467
|
+
"idempotencyKey"
|
|
3468
|
+
]) checkString(input[key]);
|
|
3469
|
+
if (input.tenantRef !== null && typeof input.tenantRef !== "string") throw new TypeError("tenantRef must be an explicit string or null");
|
|
3470
|
+
for (const value of [
|
|
3471
|
+
input.id,
|
|
3472
|
+
input.actorRef,
|
|
3473
|
+
input.threadId,
|
|
3474
|
+
input.tenantRef
|
|
3475
|
+
]) {
|
|
3476
|
+
if (value !== null && value.length > 255) throw new RangeError("Action proposal id and scope must fit 255 UTF-16 code units");
|
|
3477
|
+
}
|
|
3478
|
+
if (input.replacementKey !== void 0) {
|
|
3479
|
+
checkString(input.replacementKey);
|
|
3480
|
+
if (input.replacementKey.length > 255) throw new RangeError("replacementKey must fit 255 UTF-16 code units");
|
|
3481
|
+
}
|
|
3482
|
+
if (Object.hasOwn(input, "executionContext")) {
|
|
3483
|
+
const context = input.executionContext;
|
|
3484
|
+
if (context === null || typeof context !== "object" || Array.isArray(context)) throw new TypeError("executionContext must be a JSON object");
|
|
3485
|
+
const allowedContext = [
|
|
3486
|
+
"agentName",
|
|
3487
|
+
"persona",
|
|
3488
|
+
"requestId",
|
|
3489
|
+
"pageContext",
|
|
3490
|
+
"uiCapabilities"
|
|
3491
|
+
];
|
|
3492
|
+
if (Object.keys(context).some((key) => !allowedContext.includes(key))) throw new TypeError("Unknown execution context field");
|
|
3493
|
+
checkString(context.requestId);
|
|
3494
|
+
if (Object.hasOwn(context, "uiCapabilities")) validateUiCapabilities(context.uiCapabilities);
|
|
3495
|
+
if (Object.hasOwn(context, "agentName")) checkString(context.agentName);
|
|
3496
|
+
if (Object.hasOwn(context, "persona")) checkString(context.persona);
|
|
3497
|
+
if (Object.hasOwn(context, "pageContext")) {
|
|
3498
|
+
const page = context.pageContext;
|
|
3499
|
+
if (page === null || typeof page !== "object" || Array.isArray(page)) throw new TypeError("pageContext must be a JSON object");
|
|
3500
|
+
if (Object.hasOwn(page, "kind") && typeof page.kind !== "string") throw new TypeError("pageContext.kind must be a string");
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
if (input.expiresAt !== null) checkTime(input.expiresAt);
|
|
3504
|
+
checkString(input.confirmation?.title);
|
|
3505
|
+
checkString(input.confirmation?.verb);
|
|
3506
|
+
canonicalActionProposalJson(input);
|
|
3507
|
+
}
|
|
3508
|
+
__name(validateActionProposalCreation, "validateActionProposalCreation");
|
|
3509
|
+
function validateActionProposalListQuery(query = {}) {
|
|
3510
|
+
const limit = query.limit === void 0 ? 100 : query.limit;
|
|
3511
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
|
|
3512
|
+
if (query.decision !== void 0 && ![
|
|
3513
|
+
"pending",
|
|
3514
|
+
"approved",
|
|
3515
|
+
"rejected",
|
|
3516
|
+
"expired",
|
|
3517
|
+
"superseded"
|
|
3518
|
+
].includes(query.decision)) throw new TypeError("Unsupported action proposal list decision");
|
|
3519
|
+
if (query.after !== void 0) {
|
|
3520
|
+
const after = query.after;
|
|
3521
|
+
if (typeof after !== "object" || after === null || Array.isArray(after) || Object.keys(after).some((key) => key !== "createdAt" && key !== "id")) throw new TypeError("Invalid action proposal cursor");
|
|
3522
|
+
checkTime(after.createdAt);
|
|
3523
|
+
checkString(after.id);
|
|
3524
|
+
if (after.id.length > 255) throw new TypeError("Action proposal cursor id is too long");
|
|
3525
|
+
}
|
|
3526
|
+
return limit;
|
|
3527
|
+
}
|
|
3528
|
+
__name(validateActionProposalListQuery, "validateActionProposalListQuery");
|
|
3529
|
+
function canonicalActionProposalJson(value) {
|
|
3530
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
3531
|
+
const sort = /* @__PURE__ */ __name((item) => {
|
|
3532
|
+
if (item === null || typeof item === "string" || typeof item === "boolean") return item;
|
|
3533
|
+
if (typeof item === "number" && Number.isFinite(item)) return item;
|
|
3534
|
+
if (typeof item !== "object" || item === null || ancestors.has(item)) throw new TypeError("Action proposals require finite, acyclic JSON values");
|
|
3535
|
+
const prototype = Object.getPrototypeOf(item);
|
|
3536
|
+
if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null) throw new TypeError("Action proposals require plain JSON objects");
|
|
3537
|
+
ancestors.add(item);
|
|
3538
|
+
const sorted = Array.isArray(item) ? Array.from(item, sort) : Object.fromEntries(Object.keys(item).sort().map((key) => [
|
|
3539
|
+
key,
|
|
3540
|
+
sort(item[key])
|
|
3541
|
+
]));
|
|
3542
|
+
ancestors.delete(item);
|
|
3543
|
+
return sorted;
|
|
3544
|
+
}, "sort");
|
|
3545
|
+
return JSON.stringify(sort(value));
|
|
3546
|
+
}
|
|
3547
|
+
__name(canonicalActionProposalJson, "canonicalActionProposalJson");
|
|
3548
|
+
function snapshotActionProposal(value) {
|
|
3549
|
+
return JSON.parse(canonicalActionProposalJson(value));
|
|
3550
|
+
}
|
|
3551
|
+
__name(snapshotActionProposal, "snapshotActionProposal");
|
|
3552
|
+
function actionProposalScopeMatches(row, scope) {
|
|
3553
|
+
return row.tenantRef === scope.tenantRef && row.actorRef === scope.actorRef && row.threadId === scope.threadId;
|
|
3554
|
+
}
|
|
3555
|
+
__name(actionProposalScopeMatches, "actionProposalScopeMatches");
|
|
3556
|
+
function initialActionProposal(input, now) {
|
|
3557
|
+
validateActionProposalCreation(input);
|
|
3558
|
+
checkTime(now);
|
|
3559
|
+
return snapshotActionProposal({
|
|
3560
|
+
...input,
|
|
3561
|
+
decision: "pending",
|
|
3562
|
+
decisionAudit: null,
|
|
3563
|
+
execution: null,
|
|
3564
|
+
createdAt: now,
|
|
3565
|
+
updatedAt: now
|
|
3566
|
+
});
|
|
3567
|
+
}
|
|
3568
|
+
__name(initialActionProposal, "initialActionProposal");
|
|
3569
|
+
function actionProposalCreationMatches(row, input) {
|
|
3570
|
+
validateActionProposalCreation(input);
|
|
3571
|
+
const { decision: _decision, decisionAudit: _audit, execution: _execution, createdAt: _created, updatedAt: _updated, supersededBy: _supersededBy, outcome: _outcome, outcomeDelivery: _delivery, ...original } = row;
|
|
3572
|
+
return canonicalActionProposalJson(original) === canonicalActionProposalJson(input);
|
|
3573
|
+
}
|
|
3574
|
+
__name(actionProposalCreationMatches, "actionProposalCreationMatches");
|
|
3575
|
+
function result(status, proposal) {
|
|
3576
|
+
return {
|
|
3577
|
+
status,
|
|
3578
|
+
proposal: snapshotActionProposal(proposal)
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
__name(result, "result");
|
|
3582
|
+
function transitionActionProposalDecision(row, command, now) {
|
|
3583
|
+
checkTime(now);
|
|
3584
|
+
if (![
|
|
3585
|
+
"approved",
|
|
3586
|
+
"rejected",
|
|
3587
|
+
"expired"
|
|
3588
|
+
].includes(command.decision)) throw new TypeError("Unsupported action proposal decision");
|
|
3589
|
+
checkString(command.actorRef);
|
|
3590
|
+
checkString(command.via);
|
|
3591
|
+
if (command.reason !== void 0 && typeof command.reason !== "string") throw new TypeError("reason must be a string");
|
|
3592
|
+
if (command.remember !== void 0 && typeof command.remember !== "boolean") throw new TypeError("remember must be a boolean");
|
|
3593
|
+
if (row.decision !== "pending") {
|
|
3594
|
+
return result(row.decision === command.decision ? "unchanged" : row.decision === "expired" ? "expired" : "conflict", row);
|
|
3595
|
+
}
|
|
3596
|
+
const next = snapshotActionProposal(row);
|
|
3597
|
+
if (row.expiresAt !== null && now >= row.expiresAt) {
|
|
3598
|
+
next.decision = "expired";
|
|
3599
|
+
next.decisionAudit = {
|
|
3600
|
+
actorRef: "system",
|
|
3601
|
+
via: "expiry",
|
|
3602
|
+
at: now
|
|
3603
|
+
};
|
|
3604
|
+
next.updatedAt = now;
|
|
3605
|
+
return result(command.decision === "expired" ? "applied" : "expired", attachActionProposalOutcome(next, now));
|
|
3606
|
+
}
|
|
3607
|
+
if (command.decision === "expired") return result("conflict", row);
|
|
3608
|
+
const { decision, ...audit } = command;
|
|
3609
|
+
next.decision = decision;
|
|
3610
|
+
next.decisionAudit = {
|
|
3611
|
+
actorRef: audit.actorRef,
|
|
3612
|
+
via: audit.via,
|
|
3613
|
+
at: now,
|
|
3614
|
+
...audit.reason === void 0 ? {} : {
|
|
3615
|
+
reason: audit.reason
|
|
3616
|
+
},
|
|
3617
|
+
...audit.remember === void 0 ? {} : {
|
|
3618
|
+
remember: audit.remember
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
next.updatedAt = now;
|
|
3622
|
+
if (decision === "approved") next.execution = {
|
|
3623
|
+
status: "queued",
|
|
3624
|
+
generation: 0,
|
|
3625
|
+
lease: null
|
|
3626
|
+
};
|
|
3627
|
+
return result("applied", decision === "approved" ? next : attachActionProposalOutcome(next, now));
|
|
3628
|
+
}
|
|
3629
|
+
__name(transitionActionProposalDecision, "transitionActionProposalDecision");
|
|
3630
|
+
function checkLeaseMs(leaseMs, now) {
|
|
3631
|
+
checkTime(now);
|
|
3632
|
+
if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0 || !Number.isSafeInteger(now + leaseMs)) throw new RangeError("leaseMs must be finite and positive");
|
|
3633
|
+
}
|
|
3634
|
+
__name(checkLeaseMs, "checkLeaseMs");
|
|
3635
|
+
function transitionActionProposalClaim(row, command, now, token) {
|
|
3636
|
+
checkLeaseMs(command.leaseMs, now);
|
|
3637
|
+
checkString(command.workerId);
|
|
3638
|
+
checkString(token);
|
|
3639
|
+
const execution = row.execution;
|
|
3640
|
+
if (row.decision !== "approved" || !execution || execution.status !== "queued" && !(execution.status === "executing" && execution.lease && now >= execution.lease.expiresAt)) return result("conflict", row);
|
|
3641
|
+
const next = snapshotActionProposal(row);
|
|
3642
|
+
const generation = execution.generation + 1;
|
|
3643
|
+
next.execution = {
|
|
3644
|
+
status: "executing",
|
|
3645
|
+
generation,
|
|
3646
|
+
lease: {
|
|
3647
|
+
token,
|
|
3648
|
+
generation,
|
|
3649
|
+
workerId: command.workerId,
|
|
3650
|
+
expiresAt: now + command.leaseMs
|
|
3651
|
+
}
|
|
3652
|
+
};
|
|
3653
|
+
next.updatedAt = now;
|
|
3654
|
+
return result("applied", next);
|
|
3655
|
+
}
|
|
3656
|
+
__name(transitionActionProposalClaim, "transitionActionProposalClaim");
|
|
3657
|
+
function leaseStatus(row, command, now) {
|
|
3658
|
+
checkTime(now);
|
|
3659
|
+
checkString(command.token);
|
|
3660
|
+
if (!Number.isSafeInteger(command.generation) || command.generation < 1) throw new RangeError("Invalid lease generation");
|
|
3661
|
+
const work = row.execution;
|
|
3662
|
+
if (row.decision !== "approved" || work?.status !== "executing" || !work.lease || work.generation !== command.generation || work.lease.generation !== command.generation || work.lease.token !== command.token) return "conflict";
|
|
3663
|
+
return now >= work.lease.expiresAt ? "expired" : "valid";
|
|
3664
|
+
}
|
|
3665
|
+
__name(leaseStatus, "leaseStatus");
|
|
3666
|
+
function transitionActionProposalLease(row, command, now) {
|
|
3667
|
+
checkLeaseMs(command.leaseMs, now);
|
|
3668
|
+
const status = leaseStatus(row, command, now);
|
|
3669
|
+
if (status !== "valid") return result(status, row);
|
|
3670
|
+
const next = snapshotActionProposal(row);
|
|
3671
|
+
if (next.execution?.lease) next.execution.lease.expiresAt = Math.max(next.execution.lease.expiresAt, now + command.leaseMs);
|
|
3672
|
+
next.updatedAt = now;
|
|
3673
|
+
return result("applied", next);
|
|
3674
|
+
}
|
|
3675
|
+
__name(transitionActionProposalLease, "transitionActionProposalLease");
|
|
3676
|
+
function transitionActionProposalSettlement(row, command, now) {
|
|
3677
|
+
checkTime(now);
|
|
3678
|
+
if (command.status !== "succeeded" && command.status !== "failed") throw new TypeError("Unsupported settlement status");
|
|
3679
|
+
if (command.status === "succeeded" && command.error !== void 0 || command.status === "failed" && (typeof command.error !== "string" || command.result !== void 0)) throw new TypeError("Ambiguous action proposal settlement");
|
|
3680
|
+
const status = leaseStatus(row, command, now);
|
|
3681
|
+
if (status !== "valid") return result(status, row);
|
|
3682
|
+
const next = snapshotActionProposal(row);
|
|
3683
|
+
next.execution = command.status === "succeeded" ? {
|
|
3684
|
+
status: "succeeded",
|
|
3685
|
+
generation: command.generation,
|
|
3686
|
+
lease: null,
|
|
3687
|
+
...command.result === void 0 ? {} : {
|
|
3688
|
+
result: command.result
|
|
3689
|
+
}
|
|
3690
|
+
} : {
|
|
3691
|
+
status: "failed",
|
|
3692
|
+
generation: command.generation,
|
|
3693
|
+
lease: null,
|
|
3694
|
+
error: command.error
|
|
3695
|
+
};
|
|
3696
|
+
if (command.ui !== void 0) canonicalActionProposalJson(command.ui);
|
|
3697
|
+
if (command.text !== void 0 && typeof command.text !== "string") throw new TypeError("Outcome text must be a string");
|
|
3698
|
+
next.updatedAt = now;
|
|
3699
|
+
return result("applied", attachActionProposalOutcome(next, now, command.ui, command.text));
|
|
3700
|
+
}
|
|
3701
|
+
__name(transitionActionProposalSettlement, "transitionActionProposalSettlement");
|
|
3702
|
+
function transitionActionProposalSupersession(row, replacement, command, now) {
|
|
3703
|
+
checkTime(now);
|
|
3704
|
+
checkString(command.replacementProposalId);
|
|
3705
|
+
checkString(command.actorRef);
|
|
3706
|
+
checkString(command.via);
|
|
3707
|
+
if (replacement.id !== command.replacementProposalId || row.id === replacement.id || !actionProposalScopeMatches(row, replacement) || row.toolName !== replacement.toolName || !row.replacementKey || row.replacementKey !== replacement.replacementKey) return result("conflict", row);
|
|
3708
|
+
if (row.decision === "superseded") return result(row.supersededBy === replacement.id ? "unchanged" : "conflict", row);
|
|
3709
|
+
if (row.decision !== "pending") return result("conflict", row);
|
|
3710
|
+
if (row.expiresAt !== null && now >= row.expiresAt) return {
|
|
3711
|
+
...transitionActionProposalDecision(row, {
|
|
3712
|
+
decision: "expired",
|
|
3713
|
+
actorRef: "system",
|
|
3714
|
+
via: "expiry"
|
|
3715
|
+
}, now),
|
|
3716
|
+
status: "expired"
|
|
3717
|
+
};
|
|
3718
|
+
const next = snapshotActionProposal({
|
|
3719
|
+
...row,
|
|
3720
|
+
decision: "superseded",
|
|
3721
|
+
supersededBy: replacement.id,
|
|
3722
|
+
decisionAudit: {
|
|
3723
|
+
actorRef: command.actorRef,
|
|
3724
|
+
via: command.via,
|
|
3725
|
+
replacementProposalId: replacement.id,
|
|
3726
|
+
at: now
|
|
3727
|
+
},
|
|
3728
|
+
updatedAt: now
|
|
3729
|
+
});
|
|
3730
|
+
return result("applied", attachActionProposalOutcome(next, now));
|
|
3731
|
+
}
|
|
3732
|
+
__name(transitionActionProposalSupersession, "transitionActionProposalSupersession");
|
|
3733
|
+
|
|
2591
3734
|
// src/tool-registry.ts
|
|
2592
3735
|
var ToolForbiddenError = class extends Error {
|
|
2593
3736
|
static {
|
|
@@ -2630,6 +3773,35 @@ var ToolInputInvalidError = class extends Error {
|
|
|
2630
3773
|
this.name = "ToolInputInvalidError";
|
|
2631
3774
|
}
|
|
2632
3775
|
};
|
|
3776
|
+
var ToolPreflightDeniedError = class extends Error {
|
|
3777
|
+
static {
|
|
3778
|
+
__name(this, "ToolPreflightDeniedError");
|
|
3779
|
+
}
|
|
3780
|
+
toolName;
|
|
3781
|
+
reason;
|
|
3782
|
+
constructor(toolName, reason) {
|
|
3783
|
+
super(reason), this.toolName = toolName, this.reason = reason;
|
|
3784
|
+
this.name = "ToolPreflightDeniedError";
|
|
3785
|
+
}
|
|
3786
|
+
};
|
|
3787
|
+
var ToolInputDriftError = class extends Error {
|
|
3788
|
+
static {
|
|
3789
|
+
__name(this, "ToolInputDriftError");
|
|
3790
|
+
}
|
|
3791
|
+
toolName;
|
|
3792
|
+
constructor(toolName) {
|
|
3793
|
+
super(`Tool "${toolName}" input differs from its trusted snapshot`), this.toolName = toolName;
|
|
3794
|
+
this.name = "ToolInputDriftError";
|
|
3795
|
+
}
|
|
3796
|
+
};
|
|
3797
|
+
function assertTrustedInput(name, value, approved) {
|
|
3798
|
+
try {
|
|
3799
|
+
if (canonicalActionProposalJson(value) === canonicalActionProposalJson(approved)) return;
|
|
3800
|
+
} catch {
|
|
3801
|
+
}
|
|
3802
|
+
throw new ToolInputDriftError(name);
|
|
3803
|
+
}
|
|
3804
|
+
__name(assertTrustedInput, "assertTrustedInput");
|
|
2633
3805
|
var ToolRegistry = class {
|
|
2634
3806
|
static {
|
|
2635
3807
|
__name(this, "ToolRegistry");
|
|
@@ -2677,11 +3849,12 @@ var ToolRegistry = class {
|
|
|
2677
3849
|
*/
|
|
2678
3850
|
async definitionsFor(actor, policy, allowedTools, scope = {}) {
|
|
2679
3851
|
const visible = await this.visibleEntries(actor, policy, allowedTools);
|
|
2680
|
-
|
|
3852
|
+
const definitions = await Promise.all(visible.map(async ({ spec, handler }) => {
|
|
2681
3853
|
const override = handler.describe === void 0 ? void 0 : await handler.describe({
|
|
2682
3854
|
actor,
|
|
2683
3855
|
...scope
|
|
2684
3856
|
});
|
|
3857
|
+
if (override?.available === false) return null;
|
|
2685
3858
|
return {
|
|
2686
3859
|
name: spec.name,
|
|
2687
3860
|
kind: spec.kind,
|
|
@@ -2689,6 +3862,7 @@ var ToolRegistry = class {
|
|
|
2689
3862
|
inputSchema: override?.inputSchema ?? spec.inputSchema
|
|
2690
3863
|
};
|
|
2691
3864
|
}));
|
|
3865
|
+
return definitions.filter((definition) => definition !== null);
|
|
2692
3866
|
}
|
|
2693
3867
|
/**
|
|
2694
3868
|
* The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
|
|
@@ -2714,6 +3888,40 @@ var ToolRegistry = class {
|
|
|
2714
3888
|
* moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
|
|
2715
3889
|
*/
|
|
2716
3890
|
async invoke(name, input, ctx, policy, options = {}) {
|
|
3891
|
+
const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
|
|
3892
|
+
const trusted = Object.hasOwn(options, "approvedInput");
|
|
3893
|
+
if (trusted) assertTrustedInput(name, value, options.approvedInput);
|
|
3894
|
+
const executionInput = trusted ? snapshotActionProposal(value) : value;
|
|
3895
|
+
const hookInput = trusted ? snapshotActionProposal(executionInput) : value;
|
|
3896
|
+
const result2 = entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
|
|
3897
|
+
phase: "execute"
|
|
3898
|
+
}) : void 0;
|
|
3899
|
+
if (trusted) assertTrustedInput(name, hookInput, executionInput);
|
|
3900
|
+
if (result2?.status === "denied") throw new ToolPreflightDeniedError(name, result2.reason);
|
|
3901
|
+
if (result2?.status === "completed") return result2.output;
|
|
3902
|
+
return entry.handler.execute(executionInput, withEmit);
|
|
3903
|
+
}
|
|
3904
|
+
/** Authorize and validate without executing. The caller journals this result before approval. */
|
|
3905
|
+
async prepare(name, input, ctx, policy, options = {}) {
|
|
3906
|
+
return (await this.prepareValidated(name, input, ctx, policy, options)).preflight;
|
|
3907
|
+
}
|
|
3908
|
+
/** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */
|
|
3909
|
+
async prepareValidated(name, input, ctx, policy, options = {}) {
|
|
3910
|
+
const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
|
|
3911
|
+
const normalized = options.snapshotInput === true ? snapshotActionProposal(value) : value;
|
|
3912
|
+
const hookInput = options.snapshotInput === true ? snapshotActionProposal(normalized) : value;
|
|
3913
|
+
const preflight = (entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
|
|
3914
|
+
phase: "prepare"
|
|
3915
|
+
}) : void 0) ?? {
|
|
3916
|
+
status: "ready"
|
|
3917
|
+
};
|
|
3918
|
+
if (options.snapshotInput === true) assertTrustedInput(name, hookInput, normalized);
|
|
3919
|
+
return {
|
|
3920
|
+
input: normalized,
|
|
3921
|
+
preflight
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
async validated(name, input, ctx, policy, options) {
|
|
2717
3925
|
const entry = this.entries.get(name);
|
|
2718
3926
|
if (entry === void 0) {
|
|
2719
3927
|
throw new ToolNotFoundError(name);
|
|
@@ -2738,7 +3946,11 @@ var ToolRegistry = class {
|
|
|
2738
3946
|
...ctx,
|
|
2739
3947
|
emitUi: createNoopEmitUi(ctx.requestId)
|
|
2740
3948
|
};
|
|
2741
|
-
return
|
|
3949
|
+
return {
|
|
3950
|
+
entry,
|
|
3951
|
+
value: validation.value,
|
|
3952
|
+
ctx: withEmit
|
|
3953
|
+
};
|
|
2742
3954
|
}
|
|
2743
3955
|
};
|
|
2744
3956
|
var DefaultRolesPolicy = class {
|
|
@@ -2785,6 +3997,69 @@ __name(closeEmptyRoles, "closeEmptyRoles");
|
|
|
2785
3997
|
import { createHash } from "crypto";
|
|
2786
3998
|
import { trace } from "@dudousxd/nestjs-diagnostics";
|
|
2787
3999
|
|
|
4000
|
+
// src/action-proposal-approval.ts
|
|
4001
|
+
async function resolveActionProposalApproval(input) {
|
|
4002
|
+
const policy = input.policy ?? new DefaultApprovalPolicy();
|
|
4003
|
+
const requirement = await policy.requirementFor(input.tool, input.actor, input.thread);
|
|
4004
|
+
if (!requirement.required) return {
|
|
4005
|
+
mode: "auto"
|
|
4006
|
+
};
|
|
4007
|
+
const remembered = await input.store.rememberedApprovals?.(input.thread.threadId) ?? [];
|
|
4008
|
+
if (remembered.includes(input.tool.name)) return {
|
|
4009
|
+
mode: "remembered",
|
|
4010
|
+
approver: requirement.approver
|
|
4011
|
+
};
|
|
4012
|
+
const roundedTtl = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) ? Math.floor(requirement.ttlMs) : 0;
|
|
4013
|
+
if (roundedTtl <= 0) return {
|
|
4014
|
+
mode: "ask",
|
|
4015
|
+
approver: requirement.approver
|
|
4016
|
+
};
|
|
4017
|
+
const now = (input.clock ?? Date.now)();
|
|
4018
|
+
if (!Number.isSafeInteger(now) || !Number.isSafeInteger(roundedTtl) || !Number.isSafeInteger(now + roundedTtl)) throw new RangeError("Invalid action approval deadline");
|
|
4019
|
+
return {
|
|
4020
|
+
mode: "ask",
|
|
4021
|
+
approver: requirement.approver,
|
|
4022
|
+
ttlMs: roundedTtl,
|
|
4023
|
+
expiresAt: new Date(now + roundedTtl).toISOString()
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
__name(resolveActionProposalApproval, "resolveActionProposalApproval");
|
|
4027
|
+
|
|
4028
|
+
// src/action-proposal-preparation.ts
|
|
4029
|
+
async function prepareActionProposal(registry, name, input, ctx, policy, options = {}) {
|
|
4030
|
+
const preparationInput = snapshotActionProposal(input);
|
|
4031
|
+
const prepared = await registry.prepareValidated(name, snapshotActionProposal(preparationInput), ctx, policy, {
|
|
4032
|
+
...options,
|
|
4033
|
+
snapshotInput: true
|
|
4034
|
+
});
|
|
4035
|
+
let replacementKey;
|
|
4036
|
+
const replacement = registry.spec(name)?.replacementKey;
|
|
4037
|
+
if (prepared.preflight.status === "ready" && replacement !== void 0) {
|
|
4038
|
+
replacementKey = typeof replacement === "string" ? replacement : await replacement(snapshotActionProposal(prepared.input), ctx);
|
|
4039
|
+
if (replacementKey !== void 0 && (typeof replacementKey !== "string" || !replacementKey || replacementKey.length > 255)) throw new TypeError("replacementKey must be a nonempty string of at most 255 UTF-16 code units");
|
|
4040
|
+
}
|
|
4041
|
+
return {
|
|
4042
|
+
...replacementKey !== void 0 ? {
|
|
4043
|
+
replacementKey
|
|
4044
|
+
} : {},
|
|
4045
|
+
preparationInput,
|
|
4046
|
+
input: prepared.input,
|
|
4047
|
+
// Completed results keep their normal handler contract; they never create a proposal.
|
|
4048
|
+
preflight: prepared.preflight.status === "ready" ? snapshotActionProposal(prepared.preflight) : prepared.preflight
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
__name(prepareActionProposal, "prepareActionProposal");
|
|
4052
|
+
|
|
4053
|
+
// src/action-proposal-receipt.ts
|
|
4054
|
+
function actionProposalReceipt(proposalId) {
|
|
4055
|
+
return {
|
|
4056
|
+
proposalId,
|
|
4057
|
+
status: "pending",
|
|
4058
|
+
executed: false
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
__name(actionProposalReceipt, "actionProposalReceipt");
|
|
4062
|
+
|
|
2788
4063
|
// src/diagnostics.ts
|
|
2789
4064
|
import { emit } from "@dudousxd/nestjs-diagnostics";
|
|
2790
4065
|
function publishAgentRunStarted(payload) {
|
|
@@ -2860,6 +4135,56 @@ function agentDiagnosticKey(event) {
|
|
|
2860
4135
|
}
|
|
2861
4136
|
__name(agentDiagnosticKey, "agentDiagnosticKey");
|
|
2862
4137
|
|
|
4138
|
+
// src/negotiated-tool-ui.ts
|
|
4139
|
+
function createNegotiatedUiCollector(toolCallId, scope, resolveCatalog, write) {
|
|
4140
|
+
const collector = createUiCollector(toolCallId, write);
|
|
4141
|
+
const texts = /* @__PURE__ */ new Map();
|
|
4142
|
+
let next = 0;
|
|
4143
|
+
return {
|
|
4144
|
+
components: collector.components,
|
|
4145
|
+
text: /* @__PURE__ */ __name(() => [
|
|
4146
|
+
...texts.values()
|
|
4147
|
+
].join("\n"), "text"),
|
|
4148
|
+
restart: /* @__PURE__ */ __name(() => {
|
|
4149
|
+
next = 0;
|
|
4150
|
+
texts.clear();
|
|
4151
|
+
collector.restart();
|
|
4152
|
+
}, "restart"),
|
|
4153
|
+
emit: /* @__PURE__ */ __name(async (component, props, options = {}) => {
|
|
4154
|
+
if (!resolveCatalog) {
|
|
4155
|
+
if (scope.uiCapabilities !== void 0) throw new Error("UI capabilities require an authorized server catalog");
|
|
4156
|
+
return collector.emit(component, props, options);
|
|
4157
|
+
}
|
|
4158
|
+
const capturedProps = snapshotActionProposal(props);
|
|
4159
|
+
const capturedOptions = {
|
|
4160
|
+
...options
|
|
4161
|
+
};
|
|
4162
|
+
const catalog = await resolveCatalog(scope);
|
|
4163
|
+
const prepared = await prepareUiEmission(catalog, scope.uiCapabilities, component, capturedProps, capturedOptions.version);
|
|
4164
|
+
const id = capturedOptions.id ?? `${toolCallId}:ui:${next++}`;
|
|
4165
|
+
if (prepared.kind === "text") {
|
|
4166
|
+
texts.set(id, prepared.text);
|
|
4167
|
+
await write?.({
|
|
4168
|
+
kind: "text",
|
|
4169
|
+
text: prepared.text
|
|
4170
|
+
});
|
|
4171
|
+
return {
|
|
4172
|
+
id
|
|
4173
|
+
};
|
|
4174
|
+
}
|
|
4175
|
+
return collector.emit(prepared.component, prepared.props, {
|
|
4176
|
+
id,
|
|
4177
|
+
version: prepared.version,
|
|
4178
|
+
fallbackText: prepared.fallbackText,
|
|
4179
|
+
...prepared.componentVersions !== void 0 ? {
|
|
4180
|
+
componentVersions: prepared.componentVersions
|
|
4181
|
+
} : {}
|
|
4182
|
+
});
|
|
4183
|
+
}, "emit")
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
__name(createNegotiatedUiCollector, "createNegotiatedUiCollector");
|
|
4187
|
+
|
|
2863
4188
|
// src/tool-retry.ts
|
|
2864
4189
|
function hasTransientShape(error) {
|
|
2865
4190
|
if (typeof error !== "object" || error === null) {
|
|
@@ -2885,6 +4210,7 @@ function hasTransientShape(error) {
|
|
|
2885
4210
|
}
|
|
2886
4211
|
__name(hasTransientShape, "hasTransientShape");
|
|
2887
4212
|
function isTransientToolError(error) {
|
|
4213
|
+
if (error instanceof ToolPreflightDeniedError) return false;
|
|
2888
4214
|
if (hasTransientShape(error)) {
|
|
2889
4215
|
return true;
|
|
2890
4216
|
}
|
|
@@ -2927,7 +4253,7 @@ async function invokeWithTransientRetry(fn, setting, options) {
|
|
|
2927
4253
|
try {
|
|
2928
4254
|
return await fn();
|
|
2929
4255
|
} catch (error) {
|
|
2930
|
-
if (isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
|
|
4256
|
+
if (error instanceof ToolPreflightDeniedError || isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
|
|
2931
4257
|
throw error;
|
|
2932
4258
|
}
|
|
2933
4259
|
const attemptsRemain = attempt < attempts;
|
|
@@ -3107,6 +4433,9 @@ function promptContext(input, persona) {
|
|
|
3107
4433
|
...input.pageContext !== void 0 ? {
|
|
3108
4434
|
pageContext: input.pageContext
|
|
3109
4435
|
} : {},
|
|
4436
|
+
...input.uiCapabilities !== void 0 ? {
|
|
4437
|
+
uiCapabilities: input.uiCapabilities
|
|
4438
|
+
} : {},
|
|
3110
4439
|
...persona !== void 0 ? {
|
|
3111
4440
|
persona: {
|
|
3112
4441
|
id: persona.id,
|
|
@@ -3303,7 +4632,8 @@ async function loadSelectedHistory(deps, input, hooks) {
|
|
|
3303
4632
|
messages: keep,
|
|
3304
4633
|
dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
|
|
3305
4634
|
title: thread.title,
|
|
3306
|
-
hasAssistantMessage: thread.hasAssistantMessage
|
|
4635
|
+
hasAssistantMessage: thread.hasAssistantMessage,
|
|
4636
|
+
actionApprovalMode: deps.actionApprovalMode ?? "blocking"
|
|
3307
4637
|
};
|
|
3308
4638
|
});
|
|
3309
4639
|
return {
|
|
@@ -3467,10 +4797,10 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
|
|
|
3467
4797
|
modelId: turn.modelId
|
|
3468
4798
|
} : {}
|
|
3469
4799
|
};
|
|
3470
|
-
}, (
|
|
3471
|
-
inputTokens:
|
|
3472
|
-
outputTokens:
|
|
3473
|
-
textLength:
|
|
4800
|
+
}, (result2) => ({
|
|
4801
|
+
inputTokens: result2.usage.inputTokens,
|
|
4802
|
+
outputTokens: result2.usage.outputTokens,
|
|
4803
|
+
textLength: result2.text.length
|
|
3474
4804
|
})));
|
|
3475
4805
|
await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store.recordUsage({
|
|
3476
4806
|
threadId: input.threadId,
|
|
@@ -3500,13 +4830,16 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
|
|
|
3500
4830
|
throw new StructuredOutputError(issues, text, maxAttempts);
|
|
3501
4831
|
}
|
|
3502
4832
|
__name(structureAnswer, "structureAnswer");
|
|
3503
|
-
function stampToolKinds(
|
|
4833
|
+
function stampToolKinds(result2, deps) {
|
|
3504
4834
|
return {
|
|
3505
|
-
...
|
|
3506
|
-
toolCalls:
|
|
3507
|
-
...call
|
|
3508
|
-
kind
|
|
3509
|
-
|
|
4835
|
+
...result2,
|
|
4836
|
+
toolCalls: result2.toolCalls.map((call) => {
|
|
4837
|
+
const { preflight: _untrusted, prepared: _untrustedPrepared, actionApproval: _untrustedApproval, ...trusted } = call;
|
|
4838
|
+
return trusted.kind === void 0 ? {
|
|
4839
|
+
...trusted,
|
|
4840
|
+
kind: declaredKind(deps, trusted.name)
|
|
4841
|
+
} : trusted;
|
|
4842
|
+
})
|
|
3510
4843
|
};
|
|
3511
4844
|
}
|
|
3512
4845
|
__name(stampToolKinds, "stampToolKinds");
|
|
@@ -3569,6 +4902,9 @@ function toolContext(deps, input, hooks) {
|
|
|
3569
4902
|
...input.pageContext !== void 0 ? {
|
|
3570
4903
|
pageContext: input.pageContext
|
|
3571
4904
|
} : {},
|
|
4905
|
+
...input.uiCapabilities !== void 0 ? {
|
|
4906
|
+
uiCapabilities: input.uiCapabilities
|
|
4907
|
+
} : {},
|
|
3572
4908
|
...deps.host !== void 0 ? {
|
|
3573
4909
|
host: deps.host
|
|
3574
4910
|
} : {},
|
|
@@ -3644,16 +4980,16 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
|
|
|
3644
4980
|
}
|
|
3645
4981
|
const messageId = asked.messageId;
|
|
3646
4982
|
const reply = await awaitElicitation(hooks, request, toolContext(deps, input, hooks));
|
|
3647
|
-
const
|
|
4983
|
+
const result2 = settleElicitation(request, reply);
|
|
3648
4984
|
await hooks.step("intake:answers", async () => {
|
|
3649
4985
|
await deps.store.updateToolCall({
|
|
3650
4986
|
toolCallId: request.id,
|
|
3651
4987
|
// A skip is not an answer. Both leave the agent holding the same values, but only one of them
|
|
3652
4988
|
// is evidence the user chose them, and a reader auditing what the agent was told has to be
|
|
3653
4989
|
// able to tell those apart.
|
|
3654
|
-
status:
|
|
3655
|
-
output:
|
|
3656
|
-
...
|
|
4990
|
+
status: result2.skipped ? "rejected" : "executed",
|
|
4991
|
+
output: result2,
|
|
4992
|
+
...result2.skipped ? {
|
|
3657
4993
|
error: "skipped by the user"
|
|
3658
4994
|
} : {},
|
|
3659
4995
|
...reply.answeredByRef !== void 0 ? {
|
|
@@ -3664,13 +5000,13 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
|
|
|
3664
5000
|
{
|
|
3665
5001
|
id: request.id,
|
|
3666
5002
|
name: ASK_TOOL_NAME,
|
|
3667
|
-
output:
|
|
5003
|
+
output: result2
|
|
3668
5004
|
}
|
|
3669
5005
|
]);
|
|
3670
5006
|
await writer.write(encodeStreamEvent({
|
|
3671
5007
|
kind: "tool-output",
|
|
3672
5008
|
id: request.id,
|
|
3673
|
-
output:
|
|
5009
|
+
output: result2
|
|
3674
5010
|
}));
|
|
3675
5011
|
});
|
|
3676
5012
|
return {
|
|
@@ -3683,7 +5019,7 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
|
|
|
3683
5019
|
{
|
|
3684
5020
|
id: request.id,
|
|
3685
5021
|
name: ASK_TOOL_NAME,
|
|
3686
|
-
output:
|
|
5022
|
+
output: result2
|
|
3687
5023
|
}
|
|
3688
5024
|
]
|
|
3689
5025
|
};
|
|
@@ -3729,16 +5065,140 @@ async function claimToolCall(turn, call) {
|
|
|
3729
5065
|
const spec = deps.registry.spec(call.name);
|
|
3730
5066
|
const kind = call.kind ?? declaredKind(deps, call.name);
|
|
3731
5067
|
const awaitsHuman = kind === "action" || kind === "ask";
|
|
3732
|
-
const
|
|
3733
|
-
|
|
5068
|
+
const independentApproval = turn.actionApprovalMode === "independent" && kind === "action" ? hooks.dispatchLlm !== void 0 && call.actionApproval !== void 0 ? call.actionApproval : await resolveActionProposalApproval({
|
|
5069
|
+
actor: input.actor,
|
|
5070
|
+
store: deps.store,
|
|
5071
|
+
tool: {
|
|
5072
|
+
name: call.name,
|
|
5073
|
+
kind: "action",
|
|
5074
|
+
...spec !== void 0 ? {
|
|
5075
|
+
spec
|
|
5076
|
+
} : {}
|
|
5077
|
+
},
|
|
5078
|
+
thread: {
|
|
5079
|
+
threadId: input.threadId,
|
|
5080
|
+
runId: hooks.runId,
|
|
5081
|
+
...input.agentName !== void 0 ? {
|
|
5082
|
+
agentName: input.agentName
|
|
5083
|
+
} : {}
|
|
5084
|
+
},
|
|
5085
|
+
...deps.approvalPolicy !== void 0 ? {
|
|
5086
|
+
policy: deps.approvalPolicy
|
|
5087
|
+
} : {}
|
|
5088
|
+
}) : void 0;
|
|
5089
|
+
let preflight;
|
|
5090
|
+
let prepared;
|
|
5091
|
+
if (turn.actionApprovalMode === "independent" && call.prepared !== void 0 && hooks.dispatchLlm !== void 0) prepared = call.prepared;
|
|
5092
|
+
if (kind === "action" && hooks.dispatchLlm !== void 0 && call.preflight !== void 0) {
|
|
5093
|
+
preflight = call.preflight;
|
|
5094
|
+
} else if (kind === "action" && hooks.dispatchLlm !== void 0 && spec === void 0) {
|
|
5095
|
+
preflight = {
|
|
5096
|
+
status: "ready"
|
|
5097
|
+
};
|
|
5098
|
+
} else if (kind === "action") {
|
|
5099
|
+
try {
|
|
5100
|
+
const allowedTools = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
|
|
5101
|
+
if (independentApproval?.mode === "ask") {
|
|
5102
|
+
prepared = await prepareActionProposal(deps.registry, call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
|
|
5103
|
+
allowedTools
|
|
5104
|
+
});
|
|
5105
|
+
preflight = prepared.preflight;
|
|
5106
|
+
} else {
|
|
5107
|
+
preflight = await deps.registry.prepare(call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
|
|
5108
|
+
allowedTools
|
|
5109
|
+
});
|
|
5110
|
+
}
|
|
5111
|
+
} catch (error) {
|
|
5112
|
+
if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) throw error;
|
|
5113
|
+
preflight = {
|
|
5114
|
+
status: "failed",
|
|
5115
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5116
|
+
};
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
let confirmation = preflight?.status === "ready" ? preflight.confirmation : void 0;
|
|
5120
|
+
const approval = kind === "action" && preflight?.status === "ready" ? independentApproval ?? await claimApproval(turn, call, spec) : void 0;
|
|
5121
|
+
let proposalId;
|
|
5122
|
+
if (turn.actionApprovalMode === "independent" && approval?.mode === "ask") {
|
|
5123
|
+
if (!prepared) throw new Error("Independent action lacks trusted prepared input");
|
|
5124
|
+
const proposalStore = deps.store;
|
|
5125
|
+
if (typeof proposalStore.createActionProposal !== "function") throw new Error("Independent action proposal store unavailable");
|
|
5126
|
+
proposalId = `proposal-${createHash("sha256").update(JSON.stringify([
|
|
5127
|
+
hooks.runId,
|
|
5128
|
+
call.id
|
|
5129
|
+
])).digest("hex")}`;
|
|
5130
|
+
const display = confirmation ?? spec?.presentation?.confirm ?? {
|
|
5131
|
+
title: `Allow ${call.name}?`,
|
|
5132
|
+
verb: "Approve"
|
|
5133
|
+
};
|
|
5134
|
+
const fill = /* @__PURE__ */ __name((value) => value.replace(/\{([^}]+)\}/g, (_match, path) => {
|
|
5135
|
+
let current = prepared?.input;
|
|
5136
|
+
for (const part of path.split(".")) current = current !== null && typeof current === "object" ? current[part] : void 0;
|
|
5137
|
+
return current === void 0 ? "" : String(current);
|
|
5138
|
+
}), "fill");
|
|
5139
|
+
const create = prepared.replacementKey === void 0 ? proposalStore.createActionProposal.bind(proposalStore) : (() => {
|
|
5140
|
+
const store = proposalStore;
|
|
5141
|
+
if (typeof store.createReplacingActionProposal !== "function") throw new Error("Independent replacement requires atomic supersession store");
|
|
5142
|
+
return store.createReplacingActionProposal.bind(store);
|
|
5143
|
+
})();
|
|
5144
|
+
const created = await create({
|
|
5145
|
+
...prepared.replacementKey !== void 0 ? {
|
|
5146
|
+
replacementKey: prepared.replacementKey
|
|
5147
|
+
} : {},
|
|
5148
|
+
id: proposalId,
|
|
5149
|
+
tenantRef: input.actor.tenantRef ?? null,
|
|
5150
|
+
actorRef: input.actor.id,
|
|
5151
|
+
threadId: input.threadId,
|
|
5152
|
+
originRunId: hooks.runId,
|
|
5153
|
+
originMessageId: messageId,
|
|
5154
|
+
originToolCallId: call.id,
|
|
5155
|
+
toolName: call.name,
|
|
5156
|
+
input: prepared.input,
|
|
5157
|
+
preparationInput: prepared.preparationInput,
|
|
5158
|
+
executionContext: {
|
|
5159
|
+
requestId: hooks.runId,
|
|
5160
|
+
...input.agentName !== void 0 ? {
|
|
5161
|
+
agentName: input.agentName
|
|
5162
|
+
} : {},
|
|
5163
|
+
...input.persona !== void 0 ? {
|
|
5164
|
+
persona: input.persona
|
|
5165
|
+
} : {},
|
|
5166
|
+
...input.pageContext !== void 0 ? {
|
|
5167
|
+
pageContext: input.pageContext
|
|
5168
|
+
} : {},
|
|
5169
|
+
...input.uiCapabilities !== void 0 ? {
|
|
5170
|
+
uiCapabilities: input.uiCapabilities
|
|
5171
|
+
} : {}
|
|
5172
|
+
},
|
|
5173
|
+
confirmation: confirmation ?? {
|
|
5174
|
+
title: fill(display.title),
|
|
5175
|
+
verb: fill(display.verb),
|
|
5176
|
+
...display.detail !== void 0 ? {
|
|
5177
|
+
detail: fill(display.detail)
|
|
5178
|
+
} : {}
|
|
5179
|
+
},
|
|
5180
|
+
approver: approval.approver,
|
|
5181
|
+
expiresAt: approval.expiresAt === void 0 ? null : Date.parse(approval.expiresAt),
|
|
5182
|
+
idempotencyKey: toolCallContext(toolContext(deps, input, hooks), call.id).idempotencyKey
|
|
5183
|
+
});
|
|
5184
|
+
if (created.status === "conflict") throw new Error("Action proposal replay payload conflict");
|
|
5185
|
+
confirmation = created.proposal?.confirmation ?? confirmation;
|
|
5186
|
+
}
|
|
5187
|
+
const parks = kind === "ask" || approval?.mode === "ask" && proposalId === void 0;
|
|
3734
5188
|
await deps.store.recordToolCall({
|
|
3735
5189
|
toolCallId: call.id,
|
|
3736
5190
|
messageId,
|
|
3737
5191
|
toolName: call.name,
|
|
3738
5192
|
toolType: awaitsHuman ? "action" : "read",
|
|
3739
5193
|
input: call.input,
|
|
3740
|
-
status: parks ? "pending_approval" : "auto_executed",
|
|
5194
|
+
status: proposalId !== void 0 ? "proposed" : parks ? "pending_approval" : "auto_executed",
|
|
5195
|
+
...proposalId !== void 0 ? {
|
|
5196
|
+
proposalId
|
|
5197
|
+
} : {},
|
|
3741
5198
|
runId: hooks.runId,
|
|
5199
|
+
...confirmation !== void 0 ? {
|
|
5200
|
+
confirmation
|
|
5201
|
+
} : {},
|
|
3742
5202
|
...approval !== void 0 && approval.mode !== "auto" ? {
|
|
3743
5203
|
approver: approval.approver
|
|
3744
5204
|
} : {},
|
|
@@ -3750,7 +5210,16 @@ async function claimToolCall(turn, call) {
|
|
|
3750
5210
|
await turn.writer.write(encodeStreamEvent({
|
|
3751
5211
|
kind: "approval-requested",
|
|
3752
5212
|
id: call.id,
|
|
5213
|
+
...proposalId !== void 0 ? {
|
|
5214
|
+
target: {
|
|
5215
|
+
kind: "proposal",
|
|
5216
|
+
proposalId
|
|
5217
|
+
}
|
|
5218
|
+
} : {},
|
|
3753
5219
|
approver: approval.approver,
|
|
5220
|
+
...confirmation !== void 0 ? {
|
|
5221
|
+
confirmation
|
|
5222
|
+
} : {},
|
|
3754
5223
|
...approval.expiresAt !== void 0 ? {
|
|
3755
5224
|
expiresAt: approval.expiresAt
|
|
3756
5225
|
} : {}
|
|
@@ -3773,6 +5242,12 @@ async function claimToolCall(turn, call) {
|
|
|
3773
5242
|
} : {},
|
|
3774
5243
|
...approval !== void 0 ? {
|
|
3775
5244
|
approval
|
|
5245
|
+
} : {},
|
|
5246
|
+
...preflight !== void 0 ? {
|
|
5247
|
+
preflight
|
|
5248
|
+
} : {},
|
|
5249
|
+
...proposalId !== void 0 ? {
|
|
5250
|
+
proposalId
|
|
3776
5251
|
} : {}
|
|
3777
5252
|
};
|
|
3778
5253
|
});
|
|
@@ -3798,6 +5273,12 @@ async function claimToolCall(turn, call) {
|
|
|
3798
5273
|
...toolType === "action" && persisted?.approval !== void 0 ? {
|
|
3799
5274
|
approval: persisted.approval
|
|
3800
5275
|
} : {},
|
|
5276
|
+
...persisted?.preflight !== void 0 ? {
|
|
5277
|
+
preflight: persisted.preflight
|
|
5278
|
+
} : {},
|
|
5279
|
+
...persisted?.proposalId !== void 0 ? {
|
|
5280
|
+
proposalId: persisted.proposalId
|
|
5281
|
+
} : {},
|
|
3801
5282
|
ctx: toolContext(deps, input, hooks)
|
|
3802
5283
|
};
|
|
3803
5284
|
}
|
|
@@ -3945,6 +5426,9 @@ function skillContext(input) {
|
|
|
3945
5426
|
} : {},
|
|
3946
5427
|
...input.pageContext !== void 0 ? {
|
|
3947
5428
|
pageContext: input.pageContext
|
|
5429
|
+
} : {},
|
|
5430
|
+
...input.uiCapabilities !== void 0 ? {
|
|
5431
|
+
uiCapabilities: input.uiCapabilities
|
|
3948
5432
|
} : {}
|
|
3949
5433
|
};
|
|
3950
5434
|
}
|
|
@@ -3961,7 +5445,7 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3961
5445
|
if (claimed.toolType === "memory") {
|
|
3962
5446
|
return await rememberIntoTurn(turn, claimed, startedAt);
|
|
3963
5447
|
}
|
|
3964
|
-
const invokeAllowList = turn.persona?.allowedTools === void 0 ?
|
|
5448
|
+
const invokeAllowList = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
|
|
3965
5449
|
let raw;
|
|
3966
5450
|
if (hooks.dispatchTool) {
|
|
3967
5451
|
const stepCtx = {
|
|
@@ -3977,6 +5461,9 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3977
5461
|
} : {},
|
|
3978
5462
|
...input.pageContext !== void 0 ? {
|
|
3979
5463
|
pageContext: input.pageContext
|
|
5464
|
+
} : {},
|
|
5465
|
+
...input.uiCapabilities !== void 0 ? {
|
|
5466
|
+
uiCapabilities: input.uiCapabilities
|
|
3980
5467
|
} : {}
|
|
3981
5468
|
};
|
|
3982
5469
|
const envelope = {
|
|
@@ -3998,37 +5485,49 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3998
5485
|
raw = await hooks.dispatchTool(resolvedCall, envelope);
|
|
3999
5486
|
} else {
|
|
4000
5487
|
const invocation = hooks.step(`tool:${call.id}`, async () => {
|
|
4001
|
-
const ui2 =
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
5488
|
+
const ui2 = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
|
|
5489
|
+
let output2;
|
|
5490
|
+
try {
|
|
5491
|
+
output2 = await traceToolExecution(hooks.runId, {
|
|
5492
|
+
toolCallId: call.id,
|
|
5493
|
+
toolName: call.name,
|
|
5494
|
+
toolType
|
|
5495
|
+
}, () => invokeWithTransientRetry(() => {
|
|
5496
|
+
ui2.restart();
|
|
5497
|
+
return deps.registry.invoke(call.name, call.input, {
|
|
5498
|
+
...toolCallContext(ctx, call.id),
|
|
5499
|
+
emitUi: ui2.emit
|
|
5500
|
+
}, deps.rolesPolicy, invokeAllowList !== void 0 ? {
|
|
5501
|
+
allowedTools: invokeAllowList
|
|
5502
|
+
} : {});
|
|
5503
|
+
}, deps.toolTransientRetry ?? {}, {
|
|
5504
|
+
...hooks.isControlFlowError !== void 0 ? {
|
|
5505
|
+
isControlFlowError: hooks.isControlFlowError
|
|
5506
|
+
} : {},
|
|
5507
|
+
onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
|
|
5508
|
+
publishAgentToolRetry({
|
|
5509
|
+
toolName: call.name,
|
|
5510
|
+
toolCallId: call.id,
|
|
5511
|
+
attempt,
|
|
5512
|
+
message: retryError instanceof Error ? retryError.message : String(retryError)
|
|
5513
|
+
});
|
|
5514
|
+
}, "onRetry")
|
|
5515
|
+
}));
|
|
5516
|
+
} catch (error) {
|
|
5517
|
+
if (error instanceof Error && error.name === "ToolPreflightDeniedError") return wrapToolPreflightDenied(error.message);
|
|
5518
|
+
throw error;
|
|
5519
|
+
}
|
|
5520
|
+
return wrapToolStepOutput(output2, ui2.components(), ui2.text());
|
|
4028
5521
|
});
|
|
4029
5522
|
raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
|
|
4030
5523
|
}
|
|
4031
|
-
const { output, ui } = unwrapToolStepOutput(raw);
|
|
5524
|
+
const { output, ui, preflightDenied, text } = unwrapToolStepOutput(raw);
|
|
5525
|
+
if (text) turn.toolText.set(call.id, text);
|
|
5526
|
+
if (preflightDenied !== void 0) return {
|
|
5527
|
+
status: "denied",
|
|
5528
|
+
reason: preflightDenied,
|
|
5529
|
+
executionMs: Date.now() - startedAt
|
|
5530
|
+
};
|
|
4032
5531
|
if (ui.length > 0) {
|
|
4033
5532
|
turn.toolUi.set(call.id, ui);
|
|
4034
5533
|
}
|
|
@@ -4044,6 +5543,13 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
4044
5543
|
if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) {
|
|
4045
5544
|
throw error;
|
|
4046
5545
|
}
|
|
5546
|
+
if (error instanceof Error && error.name === "ToolPreflightDeniedError") {
|
|
5547
|
+
return {
|
|
5548
|
+
status: "denied",
|
|
5549
|
+
reason: error.message,
|
|
5550
|
+
executionMs: Date.now() - startedAt
|
|
5551
|
+
};
|
|
5552
|
+
}
|
|
4047
5553
|
return {
|
|
4048
5554
|
status: "failed",
|
|
4049
5555
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -4085,6 +5591,27 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef, meta) {
|
|
|
4085
5591
|
} : {}
|
|
4086
5592
|
}));
|
|
4087
5593
|
}, "settle");
|
|
5594
|
+
if (outcome.status === "denied") {
|
|
5595
|
+
await hooks.step(`persist:toolreject:${call.id}`, async () => {
|
|
5596
|
+
await deps.store.updateToolCall({
|
|
5597
|
+
toolCallId: call.id,
|
|
5598
|
+
status: meta === void 0 ? "rejected" : "failed",
|
|
5599
|
+
error: outcome.reason,
|
|
5600
|
+
...decided
|
|
5601
|
+
});
|
|
5602
|
+
await settle();
|
|
5603
|
+
});
|
|
5604
|
+
return {
|
|
5605
|
+
id: call.id,
|
|
5606
|
+
name: call.name,
|
|
5607
|
+
output: {
|
|
5608
|
+
rejected: true,
|
|
5609
|
+
reason: outcome.reason
|
|
5610
|
+
},
|
|
5611
|
+
denied: true,
|
|
5612
|
+
error: `This action was denied by its preflight check: ${outcome.reason}. The requested mutation did not run.`
|
|
5613
|
+
};
|
|
5614
|
+
}
|
|
4088
5615
|
if (outcome.status === "failed") {
|
|
4089
5616
|
await hooks.step(`persist:toolfail:${call.id}`, async () => {
|
|
4090
5617
|
await deps.store.updateToolCall({
|
|
@@ -4225,12 +5752,12 @@ async function elicitToolCall(turn, claimed) {
|
|
|
4225
5752
|
}));
|
|
4226
5753
|
});
|
|
4227
5754
|
const reply = await awaitElicitation(hooks, request, ctx);
|
|
4228
|
-
const
|
|
4229
|
-
await hooks.step(
|
|
5755
|
+
const result2 = settleElicitation(request, reply);
|
|
5756
|
+
await hooks.step(result2.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
|
|
4230
5757
|
toolCallId: call.id,
|
|
4231
|
-
status:
|
|
4232
|
-
output:
|
|
4233
|
-
...
|
|
5758
|
+
status: result2.skipped ? "rejected" : "executed",
|
|
5759
|
+
output: result2,
|
|
5760
|
+
...result2.skipped ? {
|
|
4234
5761
|
error: "skipped by the user"
|
|
4235
5762
|
} : {},
|
|
4236
5763
|
...reply.answeredByRef !== void 0 ? {
|
|
@@ -4241,24 +5768,50 @@ async function elicitToolCall(turn, claimed) {
|
|
|
4241
5768
|
runId: hooks.runId,
|
|
4242
5769
|
toolName: call.name,
|
|
4243
5770
|
toolType: "action",
|
|
4244
|
-
status:
|
|
5771
|
+
status: result2.skipped ? "rejected" : "executed"
|
|
4245
5772
|
});
|
|
4246
5773
|
return {
|
|
4247
5774
|
id: call.id,
|
|
4248
5775
|
name: call.name,
|
|
4249
|
-
output:
|
|
5776
|
+
output: result2
|
|
4250
5777
|
};
|
|
4251
5778
|
}
|
|
4252
5779
|
__name(elicitToolCall, "elicitToolCall");
|
|
4253
5780
|
async function runClaimedToolCall(turn, claimed) {
|
|
4254
5781
|
const { deps, input, hooks } = turn;
|
|
4255
5782
|
const { call, toolType, ctx } = claimed;
|
|
5783
|
+
if (claimed.proposalId !== void 0) {
|
|
5784
|
+
turn.halt.independentProposal = true;
|
|
5785
|
+
return {
|
|
5786
|
+
id: call.id,
|
|
5787
|
+
name: call.name,
|
|
5788
|
+
output: actionProposalReceipt(claimed.proposalId)
|
|
5789
|
+
};
|
|
5790
|
+
}
|
|
4256
5791
|
if (toolType === "agent") {
|
|
4257
5792
|
return delegateToolCall(turn, claimed);
|
|
4258
5793
|
}
|
|
4259
5794
|
if (toolType === "ask") {
|
|
4260
5795
|
return elicitToolCall(turn, claimed);
|
|
4261
5796
|
}
|
|
5797
|
+
if (claimed.preflight !== void 0 && claimed.preflight.status !== "ready") {
|
|
5798
|
+
const result2 = claimed.preflight;
|
|
5799
|
+
if (result2.status === "completed" && claimed.terminal === true) turn.halt.terminal = true;
|
|
5800
|
+
const outcome = result2.status === "completed" ? {
|
|
5801
|
+
status: "executed",
|
|
5802
|
+
output: result2.output,
|
|
5803
|
+
executionMs: 0
|
|
5804
|
+
} : result2.status === "denied" ? {
|
|
5805
|
+
status: "denied",
|
|
5806
|
+
reason: result2.reason,
|
|
5807
|
+
executionMs: 0
|
|
5808
|
+
} : {
|
|
5809
|
+
status: "failed",
|
|
5810
|
+
error: result2.error,
|
|
5811
|
+
executionMs: 0
|
|
5812
|
+
};
|
|
5813
|
+
return recordToolOutcome(turn, claimed, outcome, input.actor.id);
|
|
5814
|
+
}
|
|
4262
5815
|
let deciderRef = input.actor.id;
|
|
4263
5816
|
let meta;
|
|
4264
5817
|
if (toolType === "action" && claimed.approval?.mode === "remembered") {
|
|
@@ -4383,30 +5936,30 @@ function expiryNarrative() {
|
|
|
4383
5936
|
return "This action needed approval, and the approval request expired before anyone decided. Nothing ran and nothing changed. Do not run it again on your own; tell the person it was not approved in time and ask whether they still want it.";
|
|
4384
5937
|
}
|
|
4385
5938
|
__name(expiryNarrative, "expiryNarrative");
|
|
4386
|
-
function outputFrame(
|
|
4387
|
-
if (
|
|
4388
|
-
const reason = refusalReason(
|
|
5939
|
+
function outputFrame(result2) {
|
|
5940
|
+
if (result2.denied === true) {
|
|
5941
|
+
const reason = refusalReason(result2);
|
|
4389
5942
|
return {
|
|
4390
5943
|
kind: "tool-output-denied",
|
|
4391
|
-
id:
|
|
5944
|
+
id: result2.id,
|
|
4392
5945
|
...reason !== void 0 ? {
|
|
4393
5946
|
reason
|
|
4394
5947
|
} : {}
|
|
4395
5948
|
};
|
|
4396
5949
|
}
|
|
4397
|
-
return
|
|
5950
|
+
return result2.error !== void 0 ? {
|
|
4398
5951
|
kind: "tool-output-error",
|
|
4399
|
-
id:
|
|
4400
|
-
error:
|
|
5952
|
+
id: result2.id,
|
|
5953
|
+
error: result2.error
|
|
4401
5954
|
} : {
|
|
4402
5955
|
kind: "tool-output",
|
|
4403
|
-
id:
|
|
4404
|
-
output:
|
|
5956
|
+
id: result2.id,
|
|
5957
|
+
output: result2.output
|
|
4405
5958
|
};
|
|
4406
5959
|
}
|
|
4407
5960
|
__name(outputFrame, "outputFrame");
|
|
4408
|
-
function refusalReason(
|
|
4409
|
-
const { output } =
|
|
5961
|
+
function refusalReason(result2) {
|
|
5962
|
+
const { output } = result2;
|
|
4410
5963
|
if (output !== null && typeof output === "object" && "reason" in output) {
|
|
4411
5964
|
const reason = output.reason;
|
|
4412
5965
|
return typeof reason === "string" && reason !== DEFAULT_REFUSAL_REASON ? reason : void 0;
|
|
@@ -4646,6 +6199,25 @@ ${block}`;
|
|
|
4646
6199
|
messages: prompt.messages,
|
|
4647
6200
|
actor: input.actor,
|
|
4648
6201
|
threadId: input.threadId,
|
|
6202
|
+
actionApprovalMode: history.actionApprovalMode ?? "blocking",
|
|
6203
|
+
preflightContext: {
|
|
6204
|
+
actor: input.actor,
|
|
6205
|
+
threadId: input.threadId,
|
|
6206
|
+
runId: hooks.runId,
|
|
6207
|
+
requestId: hooks.runId,
|
|
6208
|
+
...input.agentName !== void 0 ? {
|
|
6209
|
+
agentName: input.agentName
|
|
6210
|
+
} : {},
|
|
6211
|
+
...input.persona !== void 0 ? {
|
|
6212
|
+
persona: input.persona
|
|
6213
|
+
} : {},
|
|
6214
|
+
...input.pageContext !== void 0 ? {
|
|
6215
|
+
pageContext: input.pageContext
|
|
6216
|
+
} : {},
|
|
6217
|
+
...input.uiCapabilities !== void 0 ? {
|
|
6218
|
+
uiCapabilities: input.uiCapabilities
|
|
6219
|
+
} : {}
|
|
6220
|
+
},
|
|
4649
6221
|
...gated ? {
|
|
4650
6222
|
bufferOutput: true
|
|
4651
6223
|
} : {},
|
|
@@ -4662,6 +6234,9 @@ ${block}`;
|
|
|
4662
6234
|
tools: withAskTool({
|
|
4663
6235
|
tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllowLists(deps.toolAllowList, persona?.allowedTools), {
|
|
4664
6236
|
threadId: input.threadId,
|
|
6237
|
+
...input.uiCapabilities !== void 0 ? {
|
|
6238
|
+
uiCapabilities: input.uiCapabilities
|
|
6239
|
+
} : {},
|
|
4665
6240
|
...input.agentName !== void 0 ? {
|
|
4666
6241
|
agentName: input.agentName
|
|
4667
6242
|
} : {}
|
|
@@ -4681,7 +6256,7 @@ ${block}`;
|
|
|
4681
6256
|
}) : void 0;
|
|
4682
6257
|
const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
|
|
4683
6258
|
const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
|
|
4684
|
-
const
|
|
6259
|
+
const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
|
|
4685
6260
|
system: prompt.system,
|
|
4686
6261
|
messages: prompt.messages,
|
|
4687
6262
|
tools,
|
|
@@ -4691,15 +6266,15 @@ ${block}`;
|
|
|
4691
6266
|
await incremental.settled();
|
|
4692
6267
|
const refusal = incremental.rejection();
|
|
4693
6268
|
return {
|
|
4694
|
-
...
|
|
6269
|
+
...result2,
|
|
4695
6270
|
releasedText: incremental.released(),
|
|
4696
6271
|
...refusal !== void 0 ? {
|
|
4697
6272
|
gateRejection: refusal
|
|
4698
6273
|
} : {}
|
|
4699
6274
|
};
|
|
4700
6275
|
}
|
|
4701
|
-
return buffer === void 0 ?
|
|
4702
|
-
...
|
|
6276
|
+
return buffer === void 0 ? result2 : {
|
|
6277
|
+
...result2,
|
|
4703
6278
|
bufferedFrames: buffer.frames()
|
|
4704
6279
|
};
|
|
4705
6280
|
});
|
|
@@ -4793,12 +6368,12 @@ ${block}`;
|
|
|
4793
6368
|
role: "assistant",
|
|
4794
6369
|
content: turn.text
|
|
4795
6370
|
}
|
|
4796
|
-
], count), (
|
|
4797
|
-
followUps:
|
|
4798
|
-
inputTokens:
|
|
4799
|
-
outputTokens:
|
|
4800
|
-
...
|
|
4801
|
-
modelId:
|
|
6371
|
+
], count), (result2) => ({
|
|
6372
|
+
followUps: result2.followUps.length,
|
|
6373
|
+
inputTokens: result2.usage.inputTokens,
|
|
6374
|
+
outputTokens: result2.usage.outputTokens,
|
|
6375
|
+
...result2.modelId !== void 0 ? {
|
|
6376
|
+
modelId: result2.modelId
|
|
4802
6377
|
} : {}
|
|
4803
6378
|
})));
|
|
4804
6379
|
let suggestions = generated.followUps;
|
|
@@ -4909,7 +6484,7 @@ ${block}`;
|
|
|
4909
6484
|
};
|
|
4910
6485
|
modelMessages.push(assistantMessage);
|
|
4911
6486
|
for (const entry of synthetic) {
|
|
4912
|
-
const { call, result } = entry;
|
|
6487
|
+
const { call, result: result2 } = entry;
|
|
4913
6488
|
await hooks.step(`${entry.step}:${assistant.id}`, async () => {
|
|
4914
6489
|
await deps.store.recordToolCall({
|
|
4915
6490
|
toolCallId: call.id,
|
|
@@ -4923,7 +6498,7 @@ ${block}`;
|
|
|
4923
6498
|
await deps.store.updateToolCall({
|
|
4924
6499
|
toolCallId: call.id,
|
|
4925
6500
|
status: "executed",
|
|
4926
|
-
output:
|
|
6501
|
+
output: result2.output
|
|
4927
6502
|
});
|
|
4928
6503
|
await writer.write(encodeStreamEvent({
|
|
4929
6504
|
kind: "tool-input-available",
|
|
@@ -4935,7 +6510,7 @@ ${block}`;
|
|
|
4935
6510
|
await writer.write(encodeStreamEvent({
|
|
4936
6511
|
kind: "tool-output",
|
|
4937
6512
|
id: call.id,
|
|
4938
|
-
output:
|
|
6513
|
+
output: result2.output
|
|
4939
6514
|
}));
|
|
4940
6515
|
});
|
|
4941
6516
|
}
|
|
@@ -4964,9 +6539,11 @@ ${block}`;
|
|
|
4964
6539
|
messageId: assistant.id,
|
|
4965
6540
|
writer,
|
|
4966
6541
|
toolUi: /* @__PURE__ */ new Map(),
|
|
6542
|
+
toolText: /* @__PURE__ */ new Map(),
|
|
4967
6543
|
halt: {
|
|
4968
6544
|
terminal: false
|
|
4969
6545
|
},
|
|
6546
|
+
actionApprovalMode: history.actionApprovalMode ?? "blocking",
|
|
4970
6547
|
...skillOffer !== void 0 ? {
|
|
4971
6548
|
skills: skillOffer
|
|
4972
6549
|
} : {},
|
|
@@ -4999,17 +6576,35 @@ ${block}`;
|
|
|
4999
6576
|
...results,
|
|
5000
6577
|
...syntheticResults
|
|
5001
6578
|
];
|
|
6579
|
+
const toolText = escapeUnsafeToolUiText(toolCallsWithKind.map((call) => turnCalls.toolText.get(call.id)).filter((text) => text !== void 0).join("\n"));
|
|
5002
6580
|
assistantMessage.toolResults = settledResults;
|
|
5003
6581
|
await hooks.step(`stream:tool-outputs:${i}`, async () => {
|
|
5004
6582
|
await deps.store.setMessageToolResults(assistant.id, settledResults);
|
|
6583
|
+
if (toolText) await deps.store.appendMessage({
|
|
6584
|
+
threadId: input.threadId,
|
|
6585
|
+
role: "assistant",
|
|
6586
|
+
content: toolText,
|
|
6587
|
+
runId: hooks.runId,
|
|
6588
|
+
...input.agentName !== void 0 ? {
|
|
6589
|
+
agentName: input.agentName
|
|
6590
|
+
} : {}
|
|
6591
|
+
});
|
|
5005
6592
|
const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
|
|
5006
6593
|
if (toolUi.length > 0) {
|
|
5007
6594
|
await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
|
|
5008
6595
|
}
|
|
5009
|
-
for (const
|
|
5010
|
-
await writer.write(encodeStreamEvent(outputFrame(
|
|
6596
|
+
for (const result2 of results) {
|
|
6597
|
+
await writer.write(encodeStreamEvent(outputFrame(result2)));
|
|
5011
6598
|
}
|
|
5012
6599
|
});
|
|
6600
|
+
if (toolText) {
|
|
6601
|
+
modelMessages.push({
|
|
6602
|
+
role: "assistant",
|
|
6603
|
+
content: toolText
|
|
6604
|
+
});
|
|
6605
|
+
lastText += `
|
|
6606
|
+
${toolText}`;
|
|
6607
|
+
}
|
|
5013
6608
|
await hooks.step(`stream:step-finish:${i}`, async () => {
|
|
5014
6609
|
await writer.write(encodeStreamEvent({
|
|
5015
6610
|
kind: "step-finish",
|
|
@@ -5023,7 +6618,7 @@ ${block}`;
|
|
|
5023
6618
|
} : {}
|
|
5024
6619
|
}));
|
|
5025
6620
|
});
|
|
5026
|
-
if (turnCalls.halt.terminal) {
|
|
6621
|
+
if (turnCalls.halt.terminal || turnCalls.halt.independentProposal) {
|
|
5027
6622
|
break;
|
|
5028
6623
|
}
|
|
5029
6624
|
}
|
|
@@ -5079,8 +6674,152 @@ ${block}`;
|
|
|
5079
6674
|
}
|
|
5080
6675
|
__name(runAgentLoop, "runAgentLoop");
|
|
5081
6676
|
|
|
6677
|
+
// src/in-memory-action-proposal-store.ts
|
|
6678
|
+
import { randomUUID } from "crypto";
|
|
6679
|
+
var InMemoryActionProposalStore = class {
|
|
6680
|
+
static {
|
|
6681
|
+
__name(this, "InMemoryActionProposalStore");
|
|
6682
|
+
}
|
|
6683
|
+
rows = /* @__PURE__ */ new Map();
|
|
6684
|
+
clock;
|
|
6685
|
+
constructor(options = {}) {
|
|
6686
|
+
this.clock = options.clock ?? Date.now;
|
|
6687
|
+
}
|
|
6688
|
+
async createActionProposal(input) {
|
|
6689
|
+
validateActionProposalCreation(input);
|
|
6690
|
+
const row = this.rows.get(input.id);
|
|
6691
|
+
if (row) {
|
|
6692
|
+
if (!actionProposalScopeMatches(row, input)) return {
|
|
6693
|
+
status: "conflict"
|
|
6694
|
+
};
|
|
6695
|
+
return {
|
|
6696
|
+
status: actionProposalCreationMatches(row, input) ? "unchanged" : "conflict",
|
|
6697
|
+
proposal: snapshotActionProposal(row)
|
|
6698
|
+
};
|
|
6699
|
+
}
|
|
6700
|
+
const proposal = initialActionProposal(input, this.clock());
|
|
6701
|
+
this.rows.set(input.id, proposal);
|
|
6702
|
+
return {
|
|
6703
|
+
status: "created",
|
|
6704
|
+
proposal: snapshotActionProposal(proposal)
|
|
6705
|
+
};
|
|
6706
|
+
}
|
|
6707
|
+
async getActionProposal(scope, id) {
|
|
6708
|
+
const row = this.rows.get(id);
|
|
6709
|
+
return row && actionProposalScopeMatches(row, scope) ? snapshotActionProposal(row) : null;
|
|
6710
|
+
}
|
|
6711
|
+
async listActionProposals(scope, query = {}) {
|
|
6712
|
+
const limit = validateActionProposalListQuery(query);
|
|
6713
|
+
return [
|
|
6714
|
+
...this.rows.values()
|
|
6715
|
+
].filter((row) => actionProposalScopeMatches(row, scope) && (query.decision === void 0 || row.decision === query.decision) && (query.after === void 0 || row.createdAt > query.after.createdAt || row.createdAt === query.after.createdAt && row.id > query.after.id)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, limit).map(snapshotActionProposal);
|
|
6716
|
+
}
|
|
6717
|
+
mutate(scope, id, transition) {
|
|
6718
|
+
const row = this.rows.get(id);
|
|
6719
|
+
if (!row || !actionProposalScopeMatches(row, scope)) return {
|
|
6720
|
+
status: "not_found"
|
|
6721
|
+
};
|
|
6722
|
+
const result2 = transition(row, this.clock());
|
|
6723
|
+
if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
|
|
6724
|
+
return result2;
|
|
6725
|
+
}
|
|
6726
|
+
async decideActionProposal(scope, id, command) {
|
|
6727
|
+
return this.mutate(scope, id, (row, now) => transitionActionProposalDecision(row, command, now));
|
|
6728
|
+
}
|
|
6729
|
+
async claimActionProposal(scope, id, command) {
|
|
6730
|
+
return this.mutate(scope, id, (row, now) => transitionActionProposalClaim(row, command, now, randomUUID()));
|
|
6731
|
+
}
|
|
6732
|
+
async extendActionProposalLease(scope, id, command) {
|
|
6733
|
+
return this.mutate(scope, id, (row, now) => transitionActionProposalLease(row, command, now));
|
|
6734
|
+
}
|
|
6735
|
+
async settleActionProposal(scope, id, command) {
|
|
6736
|
+
return this.mutate(scope, id, (row, now) => transitionActionProposalSettlement(row, command, now));
|
|
6737
|
+
}
|
|
6738
|
+
async claimNextActionProposal(command) {
|
|
6739
|
+
const now = this.clock();
|
|
6740
|
+
validateActionProposalWorkerClaim(command, now);
|
|
6741
|
+
const candidates = [
|
|
6742
|
+
...this.rows.values()
|
|
6743
|
+
].filter((row) => row.decision === "approved" && (row.execution?.status === "queued" || row.execution?.status === "executing" && row.execution.lease !== null && now >= row.execution.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
|
|
6744
|
+
for (const candidate of candidates) {
|
|
6745
|
+
const claimed = await this.claimActionProposal(candidate, candidate.id, command);
|
|
6746
|
+
if (claimed.status === "applied" && claimed.proposal) return claimed.proposal;
|
|
6747
|
+
}
|
|
6748
|
+
return null;
|
|
6749
|
+
}
|
|
6750
|
+
async expireActionProposals(command) {
|
|
6751
|
+
const now = this.clock();
|
|
6752
|
+
validateActionProposalExpiryBatch(command, now);
|
|
6753
|
+
const candidates = [
|
|
6754
|
+
...this.rows.values()
|
|
6755
|
+
].filter((row) => row.decision === "pending" && row.expiresAt !== null && now >= row.expiresAt).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, command.limit);
|
|
6756
|
+
let expired = 0;
|
|
6757
|
+
for (const candidate of candidates) {
|
|
6758
|
+
const result2 = await this.decideActionProposal(candidate, candidate.id, {
|
|
6759
|
+
decision: "expired",
|
|
6760
|
+
actorRef: "system",
|
|
6761
|
+
via: "expiry"
|
|
6762
|
+
});
|
|
6763
|
+
if (result2.status === "applied") expired++;
|
|
6764
|
+
}
|
|
6765
|
+
return expired;
|
|
6766
|
+
}
|
|
6767
|
+
async claimNextActionProposalOutcome(command) {
|
|
6768
|
+
const now = this.clock();
|
|
6769
|
+
validateActionProposalWorkerClaim(command, now);
|
|
6770
|
+
const candidates = [
|
|
6771
|
+
...this.rows.values()
|
|
6772
|
+
].filter((row) => row.outcomeDelivery?.status === "pending" && (!row.outcomeDelivery.lease || now >= row.outcomeDelivery.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
|
|
6773
|
+
for (const candidate of candidates) {
|
|
6774
|
+
const row = this.rows.get(candidate.id);
|
|
6775
|
+
if (!row) continue;
|
|
6776
|
+
const next = claimActionProposalOutcome(row, command, this.clock(), randomUUID());
|
|
6777
|
+
if (!next?.outcome || !next.outcomeDelivery?.lease) continue;
|
|
6778
|
+
this.rows.set(next.id, next);
|
|
6779
|
+
return {
|
|
6780
|
+
outcome: snapshotActionProposal(next.outcome),
|
|
6781
|
+
lease: {
|
|
6782
|
+
outcomeId: next.outcome.id,
|
|
6783
|
+
token: next.outcomeDelivery.lease.token,
|
|
6784
|
+
generation: next.outcomeDelivery.generation
|
|
6785
|
+
}
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
return null;
|
|
6789
|
+
}
|
|
6790
|
+
async createReplacingActionProposal(input) {
|
|
6791
|
+
validateActionProposalCreation(input);
|
|
6792
|
+
const existing = this.rows.get(input.id);
|
|
6793
|
+
if (existing) return this.createActionProposal(input);
|
|
6794
|
+
const proposal = initialActionProposal(input, this.clock());
|
|
6795
|
+
const replacements = [];
|
|
6796
|
+
if (input.replacementKey) for (const row of this.rows.values()) {
|
|
6797
|
+
if (row.decision !== "pending" || !actionProposalScopeMatches(row, input) || row.toolName !== input.toolName || row.replacementKey !== input.replacementKey) continue;
|
|
6798
|
+
const result2 = transitionActionProposalSupersession(row, proposal, {
|
|
6799
|
+
replacementProposalId: proposal.id,
|
|
6800
|
+
actorRef: input.actorRef,
|
|
6801
|
+
via: "supersession"
|
|
6802
|
+
}, this.clock());
|
|
6803
|
+
if (result2.proposal) replacements.push(result2.proposal);
|
|
6804
|
+
}
|
|
6805
|
+
this.rows.set(proposal.id, proposal);
|
|
6806
|
+
for (const row of replacements) this.rows.set(row.id, row);
|
|
6807
|
+
return {
|
|
6808
|
+
status: "created",
|
|
6809
|
+
proposal: snapshotActionProposal(proposal)
|
|
6810
|
+
};
|
|
6811
|
+
}
|
|
6812
|
+
async supersedeActionProposal(scope, id, command) {
|
|
6813
|
+
const replacement = this.rows.get(command.replacementProposalId);
|
|
6814
|
+
if (!replacement || !actionProposalScopeMatches(replacement, scope)) return {
|
|
6815
|
+
status: "not_found"
|
|
6816
|
+
};
|
|
6817
|
+
return this.mutate(scope, id, (row, now) => transitionActionProposalSupersession(row, replacement, command, now));
|
|
6818
|
+
}
|
|
6819
|
+
};
|
|
6820
|
+
|
|
5082
6821
|
// src/in-memory-store.ts
|
|
5083
|
-
var InMemoryAgentStore = class {
|
|
6822
|
+
var InMemoryAgentStore = class extends InMemoryActionProposalStore {
|
|
5084
6823
|
static {
|
|
5085
6824
|
__name(this, "InMemoryAgentStore");
|
|
5086
6825
|
}
|
|
@@ -5103,6 +6842,7 @@ var InMemoryAgentStore = class {
|
|
|
5103
6842
|
const row = {
|
|
5104
6843
|
id,
|
|
5105
6844
|
actorRef: input.actor.id,
|
|
6845
|
+
tenantRef: input.actor.tenantRef ?? null,
|
|
5106
6846
|
title: input.title ?? "New chat",
|
|
5107
6847
|
transient: input.transient ?? false,
|
|
5108
6848
|
createdAt: ts,
|
|
@@ -5138,8 +6878,12 @@ var InMemoryAgentStore = class {
|
|
|
5138
6878
|
}
|
|
5139
6879
|
const approval = toolCallApprovalFromRow({
|
|
5140
6880
|
toolCallId: call.toolCallId,
|
|
6881
|
+
...call.proposalId !== void 0 ? {
|
|
6882
|
+
proposalId: call.proposalId
|
|
6883
|
+
} : {},
|
|
5141
6884
|
status: call.status,
|
|
5142
6885
|
approver: call.approver,
|
|
6886
|
+
confirmation: call.confirmation,
|
|
5143
6887
|
expiresAt: call.expiresAt,
|
|
5144
6888
|
remember: call.remember,
|
|
5145
6889
|
executedByRef: call.executedByRef,
|
|
@@ -5162,6 +6906,10 @@ var InMemoryAgentStore = class {
|
|
|
5162
6906
|
names.add(call.toolName);
|
|
5163
6907
|
}
|
|
5164
6908
|
}
|
|
6909
|
+
const thread = this.threads.get(threadId);
|
|
6910
|
+
if (thread) {
|
|
6911
|
+
for (const proposal of this.rows.values()) if (proposal.threadId === threadId && proposal.actorRef === thread.actorRef && proposal.tenantRef === thread.tenantRef && proposal.decisionAudit?.remember === true && (proposal.execution?.status === "succeeded" || proposal.execution?.status === "failed")) names.add(proposal.toolName);
|
|
6912
|
+
}
|
|
5165
6913
|
return [
|
|
5166
6914
|
...names
|
|
5167
6915
|
];
|
|
@@ -5202,6 +6950,7 @@ var InMemoryAgentStore = class {
|
|
|
5202
6950
|
const row = {
|
|
5203
6951
|
id,
|
|
5204
6952
|
actorRef: source.actorRef,
|
|
6953
|
+
tenantRef: source.tenantRef,
|
|
5205
6954
|
title: source.title,
|
|
5206
6955
|
transient: false,
|
|
5207
6956
|
createdAt: ts,
|
|
@@ -5402,6 +7151,9 @@ var InMemoryAgentStore = class {
|
|
|
5402
7151
|
...input.pageContext !== void 0 ? {
|
|
5403
7152
|
pageContext: input.pageContext
|
|
5404
7153
|
} : {},
|
|
7154
|
+
...input.uiCapabilities !== void 0 ? {
|
|
7155
|
+
uiCapabilities: input.uiCapabilities
|
|
7156
|
+
} : {},
|
|
5405
7157
|
...input.interrupt === true ? {
|
|
5406
7158
|
interrupt: true
|
|
5407
7159
|
} : {},
|
|
@@ -5506,6 +7258,78 @@ var InMemoryAgentStore = class {
|
|
|
5506
7258
|
}
|
|
5507
7259
|
return void 0;
|
|
5508
7260
|
}
|
|
7261
|
+
async getThreadActionProposalScope(threadId) {
|
|
7262
|
+
const row = this.threads.get(threadId);
|
|
7263
|
+
return row ? {
|
|
7264
|
+
threadId,
|
|
7265
|
+
actorRef: row.actorRef,
|
|
7266
|
+
tenantRef: row.tenantRef
|
|
7267
|
+
} : null;
|
|
7268
|
+
}
|
|
7269
|
+
async admitActionProposalOutcome(command) {
|
|
7270
|
+
const row = [
|
|
7271
|
+
...this.rows.values()
|
|
7272
|
+
].find((row2) => row2.outcome?.id === command.outcomeId);
|
|
7273
|
+
if (!row?.outcome || !row.outcomeDelivery) return {
|
|
7274
|
+
status: "not_found"
|
|
7275
|
+
};
|
|
7276
|
+
if (row.outcomeDelivery.status === "admitted") return {
|
|
7277
|
+
status: "unchanged",
|
|
7278
|
+
...row.outcomeDelivery.messageId !== void 0 ? {
|
|
7279
|
+
messageId: row.outcomeDelivery.messageId
|
|
7280
|
+
} : {}
|
|
7281
|
+
};
|
|
7282
|
+
if (row.outcomeDelivery.status === "discarded") return {
|
|
7283
|
+
status: "discarded"
|
|
7284
|
+
};
|
|
7285
|
+
if (!actionProposalOutcomeFenceValid(row, command, this.clock())) return {
|
|
7286
|
+
status: "conflict"
|
|
7287
|
+
};
|
|
7288
|
+
const thread = this.threads.get(row.threadId);
|
|
7289
|
+
if (!thread) {
|
|
7290
|
+
this.rows.set(row.id, snapshotActionProposal({
|
|
7291
|
+
...row,
|
|
7292
|
+
outcomeDelivery: {
|
|
7293
|
+
...row.outcomeDelivery,
|
|
7294
|
+
status: "discarded",
|
|
7295
|
+
lease: null
|
|
7296
|
+
}
|
|
7297
|
+
}));
|
|
7298
|
+
return {
|
|
7299
|
+
status: "discarded"
|
|
7300
|
+
};
|
|
7301
|
+
}
|
|
7302
|
+
if (thread.actorRef !== row.actorRef || thread.tenantRef !== row.tenantRef) return {
|
|
7303
|
+
status: "conflict"
|
|
7304
|
+
};
|
|
7305
|
+
if (thread.activeStreamId) return {
|
|
7306
|
+
status: "busy"
|
|
7307
|
+
};
|
|
7308
|
+
const message = {
|
|
7309
|
+
id: crypto.randomUUID(),
|
|
7310
|
+
role: "assistant",
|
|
7311
|
+
content: actionProposalOutcomeText(row.outcome),
|
|
7312
|
+
createdAt: this.now(),
|
|
7313
|
+
actionProposalOutcome: snapshotActionProposal(row.outcome),
|
|
7314
|
+
ui: snapshotActionProposal(row.outcome.ui)
|
|
7315
|
+
};
|
|
7316
|
+
const next = snapshotActionProposal({
|
|
7317
|
+
...row,
|
|
7318
|
+
outcomeDelivery: {
|
|
7319
|
+
...row.outcomeDelivery,
|
|
7320
|
+
status: "admitted",
|
|
7321
|
+
lease: null,
|
|
7322
|
+
messageId: message.id
|
|
7323
|
+
}
|
|
7324
|
+
});
|
|
7325
|
+
thread.messages.push(message);
|
|
7326
|
+
thread.updatedAt = message.createdAt;
|
|
7327
|
+
this.rows.set(row.id, next);
|
|
7328
|
+
return {
|
|
7329
|
+
status: "applied",
|
|
7330
|
+
messageId: message.id
|
|
7331
|
+
};
|
|
7332
|
+
}
|
|
5509
7333
|
async appendMessage(input) {
|
|
5510
7334
|
const row = this.threads.get(input.threadId);
|
|
5511
7335
|
if (row === void 0) {
|
|
@@ -5548,6 +7372,9 @@ var InMemoryAgentStore = class {
|
|
|
5548
7372
|
} : {},
|
|
5549
7373
|
...input.ui !== void 0 ? {
|
|
5550
7374
|
ui: input.ui
|
|
7375
|
+
} : {},
|
|
7376
|
+
...input.actionProposalOutcome !== void 0 ? {
|
|
7377
|
+
actionProposalOutcome: input.actionProposalOutcome
|
|
5551
7378
|
} : {}
|
|
5552
7379
|
};
|
|
5553
7380
|
row.messages.push(message);
|
|
@@ -5639,6 +7466,9 @@ var InMemoryAgentStore = class {
|
|
|
5639
7466
|
async recordToolCall(input) {
|
|
5640
7467
|
this.toolCalls.set(input.toolCallId, {
|
|
5641
7468
|
toolCallId: input.toolCallId,
|
|
7469
|
+
...input.proposalId !== void 0 ? {
|
|
7470
|
+
proposalId: input.proposalId
|
|
7471
|
+
} : {},
|
|
5642
7472
|
messageId: input.messageId,
|
|
5643
7473
|
threadId: this.threadIdForMessage(input.messageId) ?? "",
|
|
5644
7474
|
toolName: input.toolName,
|
|
@@ -5652,6 +7482,9 @@ var InMemoryAgentStore = class {
|
|
|
5652
7482
|
...input.approver !== void 0 ? {
|
|
5653
7483
|
approver: input.approver
|
|
5654
7484
|
} : {},
|
|
7485
|
+
...input.confirmation !== void 0 ? {
|
|
7486
|
+
confirmation: structuredClone(input.confirmation)
|
|
7487
|
+
} : {},
|
|
5655
7488
|
...input.expiresAt !== void 0 ? {
|
|
5656
7489
|
expiresAt: input.expiresAt
|
|
5657
7490
|
} : {}
|
|
@@ -6156,12 +7989,12 @@ function withConfirmFields(schema) {
|
|
|
6156
7989
|
});
|
|
6157
7990
|
}
|
|
6158
7991
|
const { args, fields } = splitConfirm(value);
|
|
6159
|
-
const
|
|
6160
|
-
if (
|
|
7992
|
+
const result2 = await inner.validate(args);
|
|
7993
|
+
if (result2.issues !== void 0) {
|
|
6161
7994
|
return {
|
|
6162
7995
|
issues: [
|
|
6163
7996
|
...issues,
|
|
6164
|
-
...
|
|
7997
|
+
...result2.issues
|
|
6165
7998
|
]
|
|
6166
7999
|
};
|
|
6167
8000
|
}
|
|
@@ -6171,10 +8004,10 @@ function withConfirmFields(schema) {
|
|
|
6171
8004
|
};
|
|
6172
8005
|
}
|
|
6173
8006
|
return {
|
|
6174
|
-
value: isRecord(
|
|
6175
|
-
...
|
|
8007
|
+
value: isRecord(result2.value) ? {
|
|
8008
|
+
...result2.value,
|
|
6176
8009
|
...fields
|
|
6177
|
-
} :
|
|
8010
|
+
} : result2.value
|
|
6178
8011
|
};
|
|
6179
8012
|
}, "validate"),
|
|
6180
8013
|
jsonSchema: {
|
|
@@ -6506,6 +8339,309 @@ var SqlTokenStreamSink = class {
|
|
|
6506
8339
|
});
|
|
6507
8340
|
}
|
|
6508
8341
|
};
|
|
8342
|
+
|
|
8343
|
+
// src/action-proposal-text.ts
|
|
8344
|
+
function resolveTextActionProposalDecision(text, proposals) {
|
|
8345
|
+
const command = parseTextActionProposalCommand(text);
|
|
8346
|
+
if (command.status === "unmatched") return command;
|
|
8347
|
+
const { decision, remember } = command;
|
|
8348
|
+
const pending = proposals.filter((proposal) => proposal.decision === "pending");
|
|
8349
|
+
const explicitId = command.proposalId;
|
|
8350
|
+
if (explicitId !== void 0) {
|
|
8351
|
+
if (!pending.some((proposal) => proposal.id === explicitId)) return {
|
|
8352
|
+
status: "unmatched"
|
|
8353
|
+
};
|
|
8354
|
+
return {
|
|
8355
|
+
status: "decision",
|
|
8356
|
+
proposalId: explicitId,
|
|
8357
|
+
decision,
|
|
8358
|
+
remember
|
|
8359
|
+
};
|
|
8360
|
+
}
|
|
8361
|
+
if (pending.length === 0) return {
|
|
8362
|
+
status: "unmatched"
|
|
8363
|
+
};
|
|
8364
|
+
if (pending.length > 1) return {
|
|
8365
|
+
status: "ambiguous",
|
|
8366
|
+
proposalIds: pending.map(({ id }) => id)
|
|
8367
|
+
};
|
|
8368
|
+
const candidate = pending[0];
|
|
8369
|
+
if (!candidate) return {
|
|
8370
|
+
status: "unmatched"
|
|
8371
|
+
};
|
|
8372
|
+
return {
|
|
8373
|
+
status: "decision",
|
|
8374
|
+
proposalId: candidate.id,
|
|
8375
|
+
decision,
|
|
8376
|
+
remember
|
|
8377
|
+
};
|
|
8378
|
+
}
|
|
8379
|
+
__name(resolveTextActionProposalDecision, "resolveTextActionProposalDecision");
|
|
8380
|
+
function parseTextActionProposalCommand(text) {
|
|
8381
|
+
if (/[\r\n]/u.test(text)) return {
|
|
8382
|
+
status: "unmatched"
|
|
8383
|
+
};
|
|
8384
|
+
const normalized = text.trim();
|
|
8385
|
+
const match = /^(sim|confirmo|confirmar|aprovar|aprovo|cancelar|rejeitar|rejeito|não|nao)(?: (sempre nesta conversa))?(?: #([^\s]+?))?[.!]?$/iu.exec(normalized);
|
|
8386
|
+
if (!match) return {
|
|
8387
|
+
status: "unmatched"
|
|
8388
|
+
};
|
|
8389
|
+
const verb = match[1]?.toLocaleLowerCase("pt-BR");
|
|
8390
|
+
if (verb === void 0) return {
|
|
8391
|
+
status: "unmatched"
|
|
8392
|
+
};
|
|
8393
|
+
const decision = [
|
|
8394
|
+
"cancelar",
|
|
8395
|
+
"rejeitar",
|
|
8396
|
+
"rejeito",
|
|
8397
|
+
"n\xE3o",
|
|
8398
|
+
"nao"
|
|
8399
|
+
].includes(verb) ? "rejected" : "approved";
|
|
8400
|
+
const remember = match[2] !== void 0;
|
|
8401
|
+
if (remember && decision !== "approved") return {
|
|
8402
|
+
status: "unmatched"
|
|
8403
|
+
};
|
|
8404
|
+
return {
|
|
8405
|
+
status: "command",
|
|
8406
|
+
decision,
|
|
8407
|
+
remember,
|
|
8408
|
+
...match[3] !== void 0 ? {
|
|
8409
|
+
proposalId: match[3]
|
|
8410
|
+
} : {}
|
|
8411
|
+
};
|
|
8412
|
+
}
|
|
8413
|
+
__name(parseTextActionProposalCommand, "parseTextActionProposalCommand");
|
|
8414
|
+
|
|
8415
|
+
// src/action-proposal-executor.ts
|
|
8416
|
+
var ActionProposalExecutor = class {
|
|
8417
|
+
static {
|
|
8418
|
+
__name(this, "ActionProposalExecutor");
|
|
8419
|
+
}
|
|
8420
|
+
options;
|
|
8421
|
+
constructor(options) {
|
|
8422
|
+
this.options = options;
|
|
8423
|
+
}
|
|
8424
|
+
async execute(proposal) {
|
|
8425
|
+
let ui = createNegotiatedUiCollector(proposal.originToolCallId, {
|
|
8426
|
+
actor: {
|
|
8427
|
+
id: proposal.actorRef
|
|
8428
|
+
},
|
|
8429
|
+
threadId: proposal.threadId
|
|
8430
|
+
});
|
|
8431
|
+
try {
|
|
8432
|
+
const actor = await this.options.resolver.resolve({
|
|
8433
|
+
actorRef: proposal.actorRef,
|
|
8434
|
+
tenantRef: proposal.tenantRef
|
|
8435
|
+
});
|
|
8436
|
+
if (!actor || actor.id !== proposal.actorRef || (actor.tenantRef ?? null) !== proposal.tenantRef) throw new Error("Action proposal requester no longer exists or scope changed");
|
|
8437
|
+
const deps = await this.options.resolveExecution(proposal, actor);
|
|
8438
|
+
if (deps.registry.spec(proposal.toolName)?.kind !== "action") throw new Error("Action proposal tool no longer exists as an action");
|
|
8439
|
+
const context = proposal.executionContext;
|
|
8440
|
+
ui = createNegotiatedUiCollector(proposal.originToolCallId, {
|
|
8441
|
+
actor,
|
|
8442
|
+
threadId: proposal.threadId,
|
|
8443
|
+
...context?.agentName !== void 0 ? {
|
|
8444
|
+
agentName: context.agentName
|
|
8445
|
+
} : {},
|
|
8446
|
+
...context?.uiCapabilities !== void 0 ? {
|
|
8447
|
+
uiCapabilities: context.uiCapabilities
|
|
8448
|
+
} : {}
|
|
8449
|
+
}, deps.resolveUiCatalog);
|
|
8450
|
+
const output = await deps.registry.invoke(proposal.toolName, snapshotActionProposal(Object.hasOwn(proposal, "preparationInput") ? proposal.preparationInput : proposal.input), {
|
|
8451
|
+
actor,
|
|
8452
|
+
threadId: proposal.threadId,
|
|
8453
|
+
runId: proposal.originRunId,
|
|
8454
|
+
requestId: context?.requestId ?? proposal.originRunId,
|
|
8455
|
+
toolCallId: proposal.originToolCallId,
|
|
8456
|
+
idempotencyKey: proposal.idempotencyKey,
|
|
8457
|
+
...context?.agentName !== void 0 ? {
|
|
8458
|
+
agentName: context.agentName
|
|
8459
|
+
} : {},
|
|
8460
|
+
...context?.persona !== void 0 ? {
|
|
8461
|
+
persona: context.persona
|
|
8462
|
+
} : {},
|
|
8463
|
+
...context?.pageContext !== void 0 ? {
|
|
8464
|
+
pageContext: context.pageContext
|
|
8465
|
+
} : {},
|
|
8466
|
+
...context?.uiCapabilities !== void 0 ? {
|
|
8467
|
+
uiCapabilities: context.uiCapabilities
|
|
8468
|
+
} : {},
|
|
8469
|
+
...deps.host !== void 0 ? {
|
|
8470
|
+
host: deps.host
|
|
8471
|
+
} : {},
|
|
8472
|
+
emitUi: ui.emit
|
|
8473
|
+
}, deps.rolesPolicy, {
|
|
8474
|
+
approvedInput: proposal.input,
|
|
8475
|
+
...deps.allowedTools !== void 0 ? {
|
|
8476
|
+
allowedTools: deps.allowedTools
|
|
8477
|
+
} : {}
|
|
8478
|
+
});
|
|
8479
|
+
return snapshotActionProposal({
|
|
8480
|
+
status: "succeeded",
|
|
8481
|
+
...output !== void 0 ? {
|
|
8482
|
+
result: output
|
|
8483
|
+
} : {},
|
|
8484
|
+
ui: ui.components(),
|
|
8485
|
+
...ui.text() ? {
|
|
8486
|
+
text: ui.text()
|
|
8487
|
+
} : {}
|
|
8488
|
+
});
|
|
8489
|
+
} catch (error) {
|
|
8490
|
+
return {
|
|
8491
|
+
status: "failed",
|
|
8492
|
+
error: error instanceof Error ? error.message : String(error),
|
|
8493
|
+
ui: ui.components(),
|
|
8494
|
+
...ui.text() ? {
|
|
8495
|
+
text: ui.text()
|
|
8496
|
+
} : {}
|
|
8497
|
+
};
|
|
8498
|
+
}
|
|
8499
|
+
}
|
|
8500
|
+
};
|
|
8501
|
+
|
|
8502
|
+
// src/action-proposal-worker.ts
|
|
8503
|
+
var ActionProposalWorker = class {
|
|
8504
|
+
static {
|
|
8505
|
+
__name(this, "ActionProposalWorker");
|
|
8506
|
+
}
|
|
8507
|
+
options;
|
|
8508
|
+
leaseMs;
|
|
8509
|
+
pollIntervalMs;
|
|
8510
|
+
maxConcurrency;
|
|
8511
|
+
timer;
|
|
8512
|
+
stopped = false;
|
|
8513
|
+
renewals = /* @__PURE__ */ new Set();
|
|
8514
|
+
active;
|
|
8515
|
+
constructor(options) {
|
|
8516
|
+
this.options = options;
|
|
8517
|
+
this.leaseMs = options.leaseMs ?? 3e4;
|
|
8518
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
|
|
8519
|
+
this.maxConcurrency = options.maxConcurrency ?? 1;
|
|
8520
|
+
for (const value of [
|
|
8521
|
+
this.leaseMs,
|
|
8522
|
+
this.pollIntervalMs,
|
|
8523
|
+
this.maxConcurrency
|
|
8524
|
+
]) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("Worker configuration must be positive safe integers");
|
|
8525
|
+
if (this.pollIntervalMs >= this.leaseMs / 3) throw new RangeError("Worker poll interval must be below one third of lease");
|
|
8526
|
+
if (!options.workerId) throw new TypeError("workerId is required");
|
|
8527
|
+
}
|
|
8528
|
+
start() {
|
|
8529
|
+
if (this.timer !== void 0 || this.stopped) return;
|
|
8530
|
+
const tick = /* @__PURE__ */ __name(async () => {
|
|
8531
|
+
if (this.stopped) return;
|
|
8532
|
+
try {
|
|
8533
|
+
await this.runOnce();
|
|
8534
|
+
} catch (error) {
|
|
8535
|
+
this.options.onError?.(error);
|
|
8536
|
+
}
|
|
8537
|
+
if (!this.stopped) this.timer = setTimeout(tick, this.pollIntervalMs);
|
|
8538
|
+
}, "tick");
|
|
8539
|
+
this.timer = setTimeout(tick, 0);
|
|
8540
|
+
}
|
|
8541
|
+
async stop() {
|
|
8542
|
+
this.stopped = true;
|
|
8543
|
+
for (const timer of this.renewals) clearInterval(timer);
|
|
8544
|
+
this.renewals.clear();
|
|
8545
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
8546
|
+
await this.active;
|
|
8547
|
+
}
|
|
8548
|
+
runOnce() {
|
|
8549
|
+
if (this.active) return this.active;
|
|
8550
|
+
if (this.stopped) return Promise.resolve();
|
|
8551
|
+
const task = this.tick();
|
|
8552
|
+
this.active = task;
|
|
8553
|
+
return task.finally(() => {
|
|
8554
|
+
if (this.active === task) this.active = void 0;
|
|
8555
|
+
});
|
|
8556
|
+
}
|
|
8557
|
+
async tick() {
|
|
8558
|
+
await this.options.store.expireActionProposals({
|
|
8559
|
+
limit: 100
|
|
8560
|
+
});
|
|
8561
|
+
const work = [];
|
|
8562
|
+
for (let index = 0; index < this.maxConcurrency && !this.stopped; index++) {
|
|
8563
|
+
const proposal = await this.options.store.claimNextActionProposal({
|
|
8564
|
+
workerId: this.options.workerId,
|
|
8565
|
+
leaseMs: this.leaseMs
|
|
8566
|
+
});
|
|
8567
|
+
if (!proposal) break;
|
|
8568
|
+
work.push(this.execute(proposal));
|
|
8569
|
+
}
|
|
8570
|
+
await Promise.all(work);
|
|
8571
|
+
for (let index = 0; index < 32 && !this.stopped; index++) {
|
|
8572
|
+
const delivery = await this.options.store.claimNextActionProposalOutcome({
|
|
8573
|
+
workerId: this.options.workerId,
|
|
8574
|
+
leaseMs: this.leaseMs
|
|
8575
|
+
});
|
|
8576
|
+
if (!delivery) break;
|
|
8577
|
+
await this.options.store.admitActionProposalOutcome(delivery.lease);
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8580
|
+
async execute(proposal) {
|
|
8581
|
+
const lease = proposal.execution?.lease;
|
|
8582
|
+
if (!lease) throw new Error("Claimed action lacks execution lease");
|
|
8583
|
+
let fenced = true;
|
|
8584
|
+
let renewing;
|
|
8585
|
+
const renew = /* @__PURE__ */ __name(() => {
|
|
8586
|
+
if (renewing || !fenced) return;
|
|
8587
|
+
renewing = this.options.store.extendActionProposalLease(proposal, proposal.id, {
|
|
8588
|
+
token: lease.token,
|
|
8589
|
+
generation: lease.generation,
|
|
8590
|
+
leaseMs: this.leaseMs
|
|
8591
|
+
}).then((result2) => {
|
|
8592
|
+
if (result2.status !== "applied") fenced = false;
|
|
8593
|
+
}, (error) => {
|
|
8594
|
+
fenced = false;
|
|
8595
|
+
this.options.onError?.(error);
|
|
8596
|
+
}).finally(() => {
|
|
8597
|
+
renewing = void 0;
|
|
8598
|
+
});
|
|
8599
|
+
}, "renew");
|
|
8600
|
+
const timer = setInterval(renew, this.leaseMs / 3);
|
|
8601
|
+
this.renewals.add(timer);
|
|
8602
|
+
try {
|
|
8603
|
+
const result2 = await this.options.executor.execute(proposal);
|
|
8604
|
+
clearInterval(timer);
|
|
8605
|
+
await renewing;
|
|
8606
|
+
if (!fenced) return;
|
|
8607
|
+
const settled = await this.options.store.settleActionProposal(proposal, proposal.id, {
|
|
8608
|
+
...result2,
|
|
8609
|
+
token: lease.token,
|
|
8610
|
+
generation: lease.generation
|
|
8611
|
+
});
|
|
8612
|
+
if (settled.status === "applied" && settled.proposal) await this.options.onSettled?.(settled.proposal);
|
|
8613
|
+
} finally {
|
|
8614
|
+
clearInterval(timer);
|
|
8615
|
+
this.renewals.delete(timer);
|
|
8616
|
+
await renewing;
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
};
|
|
8620
|
+
|
|
8621
|
+
// src/action-proposal-capabilities.ts
|
|
8622
|
+
function assertIndependentActionCapabilities(store, resolver) {
|
|
8623
|
+
const candidate = store;
|
|
8624
|
+
if (!resolver || typeof resolver.resolve !== "function") throw new Error("Independent action mode requires a BackgroundActorResolver");
|
|
8625
|
+
if (!isChatQueueStore(store) || candidate.actionProposalAdmissionSupported === false) throw new Error("Independent action mode requires transactional thread admission");
|
|
8626
|
+
const required = [
|
|
8627
|
+
"createActionProposal",
|
|
8628
|
+
"getActionProposal",
|
|
8629
|
+
"listActionProposals",
|
|
8630
|
+
"decideActionProposal",
|
|
8631
|
+
"claimActionProposal",
|
|
8632
|
+
"extendActionProposalLease",
|
|
8633
|
+
"settleActionProposal",
|
|
8634
|
+
"claimNextActionProposal",
|
|
8635
|
+
"expireActionProposals",
|
|
8636
|
+
"getThreadActionProposalScope",
|
|
8637
|
+
"claimNextActionProposalOutcome",
|
|
8638
|
+
"admitActionProposalOutcome",
|
|
8639
|
+
"createReplacingActionProposal",
|
|
8640
|
+
"supersedeActionProposal"
|
|
8641
|
+
];
|
|
8642
|
+
if (required.some((method) => typeof candidate[method] !== "function")) throw new Error("Independent action mode requires proposal, worker, outcome and admission capabilities on the conversation store");
|
|
8643
|
+
}
|
|
8644
|
+
__name(assertIndependentActionCapabilities, "assertIndependentActionCapabilities");
|
|
6509
8645
|
export {
|
|
6510
8646
|
AGENT_ACTOR_DIRECTORY,
|
|
6511
8647
|
AGENT_ACTOR_RESOLVER,
|
|
@@ -6539,6 +8675,8 @@ export {
|
|
|
6539
8675
|
APPROVAL_EXPIRED_REASON,
|
|
6540
8676
|
ASK_TOOL_DESCRIPTION,
|
|
6541
8677
|
ASK_TOOL_NAME,
|
|
8678
|
+
ActionProposalExecutor,
|
|
8679
|
+
ActionProposalWorker,
|
|
6542
8680
|
AgentRegistry,
|
|
6543
8681
|
AgentStreamError,
|
|
6544
8682
|
CONFIRM_JSON_SCHEMA_PROPERTIES,
|
|
@@ -6559,6 +8697,7 @@ export {
|
|
|
6559
8697
|
DefaultRolesPolicy,
|
|
6560
8698
|
ELICITATION_INPUT_TYPES,
|
|
6561
8699
|
GLOBAL_SCOPE,
|
|
8700
|
+
InMemoryActionProposalStore,
|
|
6562
8701
|
InMemoryAgentStore,
|
|
6563
8702
|
InMemoryConfirmTokenStore,
|
|
6564
8703
|
MAX_ASK_QUESTIONS,
|
|
@@ -6582,15 +8721,25 @@ export {
|
|
|
6582
8721
|
THREAD_DETAIL_CONTENT_CHARS,
|
|
6583
8722
|
ToolDisabledError,
|
|
6584
8723
|
ToolForbiddenError,
|
|
8724
|
+
ToolInputDriftError,
|
|
6585
8725
|
ToolInputInvalidError,
|
|
6586
8726
|
ToolNotFoundError,
|
|
8727
|
+
ToolPreflightDeniedError,
|
|
6587
8728
|
ToolRegistry,
|
|
6588
8729
|
UNFINISHED_TOOL_CALL,
|
|
8730
|
+
actionProposalCreationMatches,
|
|
8731
|
+
actionProposalOutcomeFenceValid,
|
|
8732
|
+
actionProposalOutcomeScope,
|
|
8733
|
+
actionProposalOutcomeText,
|
|
8734
|
+
actionProposalReceipt,
|
|
8735
|
+
actionProposalScopeMatches,
|
|
6589
8736
|
actorScope,
|
|
6590
8737
|
agentDiagnosticKey,
|
|
6591
8738
|
agentFailureCode,
|
|
6592
8739
|
askInputSchema,
|
|
6593
8740
|
askToolDefinition,
|
|
8741
|
+
assertIndependentActionCapabilities,
|
|
8742
|
+
attachActionProposalOutcome,
|
|
6594
8743
|
bucketByActor,
|
|
6595
8744
|
bucketByModel,
|
|
6596
8745
|
bucketByThread,
|
|
@@ -6598,12 +8747,15 @@ export {
|
|
|
6598
8747
|
buildMemoryBlock,
|
|
6599
8748
|
buildSkillsBlock,
|
|
6600
8749
|
canActorUseTool,
|
|
8750
|
+
canonicalActionProposalJson,
|
|
6601
8751
|
canonicalJson,
|
|
8752
|
+
claimActionProposalOutcome,
|
|
6602
8753
|
closeEmptyRoles,
|
|
6603
8754
|
compositeSkillProvider,
|
|
6604
8755
|
confirmTokenExpiry,
|
|
6605
8756
|
createFrameBuffer,
|
|
6606
8757
|
createIncrementalGate,
|
|
8758
|
+
createNegotiatedUiCollector,
|
|
6607
8759
|
createNoopEmitUi,
|
|
6608
8760
|
createUiCollector,
|
|
6609
8761
|
danglingToolCallIds,
|
|
@@ -6616,6 +8768,7 @@ export {
|
|
|
6616
8768
|
detachedStarted,
|
|
6617
8769
|
detachedUnsettled,
|
|
6618
8770
|
encodeStreamEvent,
|
|
8771
|
+
escapeUnsafeToolUiText,
|
|
6619
8772
|
estimateCost,
|
|
6620
8773
|
estimateMessageTokens,
|
|
6621
8774
|
exhaustedWindow,
|
|
@@ -6630,6 +8783,7 @@ export {
|
|
|
6630
8783
|
gateFollowUps,
|
|
6631
8784
|
gateTail,
|
|
6632
8785
|
hashConfirmToken,
|
|
8786
|
+
initialActionProposal,
|
|
6633
8787
|
intersectAllowLists,
|
|
6634
8788
|
invokeWithTransientRetry,
|
|
6635
8789
|
isChatQueueStore,
|
|
@@ -6648,7 +8802,9 @@ export {
|
|
|
6648
8802
|
observeTurnFrames,
|
|
6649
8803
|
offerMemories,
|
|
6650
8804
|
offerSkills,
|
|
8805
|
+
parseTextActionProposalCommand,
|
|
6651
8806
|
personaCatalogEntry,
|
|
8807
|
+
prepareActionProposal,
|
|
6652
8808
|
publishAgentDelegated,
|
|
6653
8809
|
publishAgentMemoryResolved,
|
|
6654
8810
|
publishAgentMemoryWritten,
|
|
@@ -6674,12 +8830,14 @@ export {
|
|
|
6674
8830
|
rememberToolDefinition,
|
|
6675
8831
|
renderElicitationAnswers,
|
|
6676
8832
|
repairInstruction,
|
|
8833
|
+
resolveActionProposalApproval,
|
|
6677
8834
|
resolveElicitation,
|
|
6678
8835
|
resolveGateLookback,
|
|
6679
8836
|
resolveMemoryDigest,
|
|
6680
8837
|
resolveOutputGateMode,
|
|
6681
8838
|
resolvePersonaAlias,
|
|
6682
8839
|
resolveSkillCatalog,
|
|
8840
|
+
resolveTextActionProposalDecision,
|
|
6683
8841
|
resolveToolTransientRetryNumbers,
|
|
6684
8842
|
rollupThreadUsage,
|
|
6685
8843
|
runAgentLoop,
|
|
@@ -6696,6 +8854,7 @@ export {
|
|
|
6696
8854
|
skillInputSchema,
|
|
6697
8855
|
skillToolDefinition,
|
|
6698
8856
|
skillWriteVerdict,
|
|
8857
|
+
snapshotActionProposal,
|
|
6699
8858
|
stampToolKinds,
|
|
6700
8859
|
staticModelCatalog,
|
|
6701
8860
|
staticSkillProvider,
|
|
@@ -6706,8 +8865,18 @@ export {
|
|
|
6706
8865
|
toolCallContext,
|
|
6707
8866
|
traceLlmTurn,
|
|
6708
8867
|
traceToolExecution,
|
|
8868
|
+
transitionActionProposalClaim,
|
|
8869
|
+
transitionActionProposalDecision,
|
|
8870
|
+
transitionActionProposalLease,
|
|
8871
|
+
transitionActionProposalSettlement,
|
|
8872
|
+
transitionActionProposalSupersession,
|
|
6709
8873
|
truncateDetailContent,
|
|
6710
8874
|
unwrapToolStepOutput,
|
|
8875
|
+
validateActionProposalCreation,
|
|
8876
|
+
validateActionProposalDiscoveryIndexBatch,
|
|
8877
|
+
validateActionProposalExpiryBatch,
|
|
8878
|
+
validateActionProposalListQuery,
|
|
8879
|
+
validateActionProposalWorkerClaim,
|
|
6711
8880
|
validateElicitationAnswer,
|
|
6712
8881
|
validateElicitationValue,
|
|
6713
8882
|
validateStructured,
|
|
@@ -6720,6 +8889,7 @@ export {
|
|
|
6720
8889
|
withSkillTool,
|
|
6721
8890
|
withToolTimeout,
|
|
6722
8891
|
withTurnFrames,
|
|
8892
|
+
wrapToolPreflightDenied,
|
|
6723
8893
|
wrapToolStepOutput,
|
|
6724
8894
|
writeMemory
|
|
6725
8895
|
};
|