@dudousxd/nestjs-agent 1.2.0 → 1.3.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/README.md +30 -0
- package/dist/{agent-deps.factory-OjrLyAZo.d.cts → agent-deps.factory-CUyVPsa1.d.cts} +10 -1
- package/dist/{agent-deps.factory-OjrLyAZo.d.ts → agent-deps.factory-CUyVPsa1.d.ts} +10 -1
- package/dist/durable/index.cjs +158 -12
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -1
- package/dist/durable/index.d.ts +1 -1
- package/dist/durable/index.js +160 -14
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +139 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -4
- package/dist/index.d.ts +38 -4
- package/dist/index.js +141 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -130,6 +130,36 @@ nobody is streaming (including one that has already finished). In-process caller
|
|
|
130
130
|
authorized elsewhere use `AgentService.subscribe(runId)`; anything reachable from a request must go
|
|
131
131
|
through `AgentService.subscribeAs(actor, runId)`.
|
|
132
132
|
|
|
133
|
+
### Who approves an action, and for how long
|
|
134
|
+
|
|
135
|
+
Every `action` tool call waits on the person chatting by default. `approvalPolicy` changes that per
|
|
136
|
+
call:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
AgentModule.forRoot({
|
|
140
|
+
// …
|
|
141
|
+
approvalPolicy: {
|
|
142
|
+
requirementFor: (tool, actor) =>
|
|
143
|
+
tool.name === 'issueRefund'
|
|
144
|
+
? { required: true, approver: 'finance', ttlMs: 15 * 60_000 }
|
|
145
|
+
: { required: true, approver: 'requester' },
|
|
146
|
+
// Optional. Default: 'requester' = the thread's own actor, anything else = a role the decider holds.
|
|
147
|
+
// canDecide: (actor, { approver, requesterRef }) => gate.forUser(actor).allows('approve', approver),
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- `POST /agent/tool-call/approve` `{ toolCallId, remember?, via? }` and `…/reject` `{ toolCallId,
|
|
153
|
+
reason?, via? }` enforce the recorded approver (`403` otherwise) and refuse a request that has
|
|
154
|
+
lapsed (`410`). The decider is recorded as `executedByRef`, `via` as `decided_via` (`'web'` when
|
|
155
|
+
omitted; the console approvals port takes `decidedVia` too).
|
|
156
|
+
- `remember: true` approves later calls of the same tool in the same thread without asking.
|
|
157
|
+
- A `ttlMs` becomes the approval wait's timeout — `ctx.waitForSignal(…, { timeoutMs })` on the
|
|
158
|
+
durable runner, a timer inline. On expiry the call settles `expired`, the model is told nobody
|
|
159
|
+
approved it in time, and the stream carries `tool-output-denied` + `approval-settled`.
|
|
160
|
+
- The tool-call row gains `approver`, `expires_at`, `remember` and `decided_via`; a reloaded thread
|
|
161
|
+
carries them as `StoredMessage.approvals`.
|
|
162
|
+
|
|
133
163
|
### A durable turn's model call and tools run in a worker
|
|
134
164
|
|
|
135
165
|
Under `durable: true` the turn's model call and each of its tool executions are **dispatched steps**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry } from '@dudousxd/nestjs-agent-core';
|
|
1
|
+
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
import { Provider, FactoryProvider, Type, CanActivate, ModuleMetadata, InjectionToken, OptionalFactoryDependency } from '@nestjs/common';
|
|
4
4
|
|
|
@@ -8,6 +8,8 @@ interface AgentDeps {
|
|
|
8
8
|
store: AgentStore;
|
|
9
9
|
registry: ToolRegistry;
|
|
10
10
|
rolesPolicy: RolesPolicy;
|
|
11
|
+
/** Who approves an action call, and for how long (from `forRoot({ approvalPolicy })`). Undefined → the requester, no expiry. */
|
|
12
|
+
approvalPolicy?: ApprovalPolicy;
|
|
11
13
|
quota?: QuotaStore;
|
|
12
14
|
sink: TokenStreamSink;
|
|
13
15
|
/** Fallback accounting label; the provider's turn result overrides it when set. */
|
|
@@ -205,6 +207,13 @@ interface AgentModuleOptions {
|
|
|
205
207
|
quotaLimitTokens?: number;
|
|
206
208
|
/** Tool authorization gate. Defaults to role-in-`defaultRoles`. */
|
|
207
209
|
rolesPolicy?: RolesPolicy;
|
|
210
|
+
/**
|
|
211
|
+
* Who has to approve an `action` tool call, and how long the request stays open. Defaults to the
|
|
212
|
+
* requester (the thread's own actor) with no expiry. A requirement naming another approver (a
|
|
213
|
+
* role) is enforced by `POST tool-call/approve|reject` through `canDecide` — by default, the
|
|
214
|
+
* deciding actor must hold that role. See `ApprovalPolicy`.
|
|
215
|
+
*/
|
|
216
|
+
approvalPolicy?: ApprovalPolicy;
|
|
208
217
|
/** Default roles a tool requires when its `roles` is omitted. Defaults to `['ADMIN']`. */
|
|
209
218
|
defaultRoles?: string[];
|
|
210
219
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry } from '@dudousxd/nestjs-agent-core';
|
|
1
|
+
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
import { Provider, FactoryProvider, Type, CanActivate, ModuleMetadata, InjectionToken, OptionalFactoryDependency } from '@nestjs/common';
|
|
4
4
|
|
|
@@ -8,6 +8,8 @@ interface AgentDeps {
|
|
|
8
8
|
store: AgentStore;
|
|
9
9
|
registry: ToolRegistry;
|
|
10
10
|
rolesPolicy: RolesPolicy;
|
|
11
|
+
/** Who approves an action call, and for how long (from `forRoot({ approvalPolicy })`). Undefined → the requester, no expiry. */
|
|
12
|
+
approvalPolicy?: ApprovalPolicy;
|
|
11
13
|
quota?: QuotaStore;
|
|
12
14
|
sink: TokenStreamSink;
|
|
13
15
|
/** Fallback accounting label; the provider's turn result overrides it when set. */
|
|
@@ -205,6 +207,13 @@ interface AgentModuleOptions {
|
|
|
205
207
|
quotaLimitTokens?: number;
|
|
206
208
|
/** Tool authorization gate. Defaults to role-in-`defaultRoles`. */
|
|
207
209
|
rolesPolicy?: RolesPolicy;
|
|
210
|
+
/**
|
|
211
|
+
* Who has to approve an `action` tool call, and how long the request stays open. Defaults to the
|
|
212
|
+
* requester (the thread's own actor) with no expiry. A requirement naming another approver (a
|
|
213
|
+
* role) is enforced by `POST tool-call/approve|reject` through `canDecide` — by default, the
|
|
214
|
+
* deciding actor must hold that role. See `ApprovalPolicy`.
|
|
215
|
+
*/
|
|
216
|
+
approvalPolicy?: ApprovalPolicy;
|
|
208
217
|
/** Default roles a tool requires when its `roles` is omitted. Defaults to `['ADMIN']`. */
|
|
209
218
|
defaultRoles?: string[];
|
|
210
219
|
/**
|
package/dist/durable/index.cjs
CHANGED
|
@@ -304,7 +304,21 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
304
304
|
runId: ctx.runId,
|
|
305
305
|
// A child forwards into the top-level sink but must not end/fail it (the top-level run owns it).
|
|
306
306
|
openSink: /* @__PURE__ */ __name(async () => input.sinkRunId !== void 0 ? childSinkWriter(await deps.sink.open(sinkRunId)) : deps.sink.open(ctx.runId), "openSink"),
|
|
307
|
-
|
|
307
|
+
// A policy's time to live becomes the signal wait's own timeout: the runtime journals the
|
|
308
|
+
// deadline on the first call (a position of its own, reached only for a call the journal says
|
|
309
|
+
// has a ttl) and wakes the run when it passes. The lapse comes back as a Decision rather than a
|
|
310
|
+
// throw, so the loop settles the call `expired` on its ordinary rejection checkpoint.
|
|
311
|
+
awaitApproval: /* @__PURE__ */ __name((call, _toolCtx, opts) => opts?.timeoutMs === void 0 ? ctx.waitForSignal(`tool:${ctx.runId}:${call.id}`) : ctx.waitForSignal(`tool:${ctx.runId}:${call.id}`, {
|
|
312
|
+
timeoutMs: opts.timeoutMs
|
|
313
|
+
}).catch((error) => {
|
|
314
|
+
if (isSignalTimeout(error)) {
|
|
315
|
+
return {
|
|
316
|
+
approved: false,
|
|
317
|
+
expired: true
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
throw error;
|
|
321
|
+
}), "awaitApproval"),
|
|
308
322
|
// The same wait, on the same signal key, carrying a typed answer instead of a yes/no — so a
|
|
309
323
|
// question set and an approval reach a parked run through one path, and the HTTP surface that
|
|
310
324
|
// settles either one is the same `POST /agent/tool-call/*` family.
|
|
@@ -478,6 +492,10 @@ AgentRunWorkflow = _ts_decorate2([
|
|
|
478
492
|
typeof RunGateway === "undefined" ? Object : RunGateway
|
|
479
493
|
])
|
|
480
494
|
], AgentRunWorkflow);
|
|
495
|
+
function isSignalTimeout(error) {
|
|
496
|
+
return error instanceof Error && error.name === "SignalTimeoutError";
|
|
497
|
+
}
|
|
498
|
+
__name(isSignalTimeout, "isSignalTimeout");
|
|
481
499
|
|
|
482
500
|
// src/durable/durable-agent-runner.ts
|
|
483
501
|
var import_node_crypto = require("crypto");
|
|
@@ -908,6 +926,9 @@ var AgentDepsFactory2 = class {
|
|
|
908
926
|
store: this.store,
|
|
909
927
|
sink: this.sink,
|
|
910
928
|
rolesPolicy: this.rolesPolicy,
|
|
929
|
+
...this.options.approvalPolicy !== void 0 ? {
|
|
930
|
+
approvalPolicy: this.options.approvalPolicy
|
|
931
|
+
} : {},
|
|
911
932
|
registry: this.registry,
|
|
912
933
|
promptContributors: this.promptContributors,
|
|
913
934
|
systemPrompt: definition?.systemPrompt ?? "You are a helpful assistant.",
|
|
@@ -1191,18 +1212,35 @@ var AgentService = class {
|
|
|
1191
1212
|
await this.assertOwnsActiveStream(actor, runId);
|
|
1192
1213
|
return this.subscribe(runId);
|
|
1193
1214
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1215
|
+
/**
|
|
1216
|
+
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
1217
|
+
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
1218
|
+
* thread, and `via` names the surface the decision came through (`'web'`, `'slack'`, …) — both
|
|
1219
|
+
* persisted with the call.
|
|
1220
|
+
*/
|
|
1221
|
+
async approve(actor, toolCallId2, opts = {}) {
|
|
1222
|
+
await this.assertMayDecide(actor, toolCallId2);
|
|
1196
1223
|
return this.signalToolCall(toolCallId2, {
|
|
1197
|
-
approved: true
|
|
1224
|
+
approved: true,
|
|
1225
|
+
executedByRef: actor.id,
|
|
1226
|
+
...opts.remember === true ? {
|
|
1227
|
+
remember: true
|
|
1228
|
+
} : {},
|
|
1229
|
+
...opts.via !== void 0 ? {
|
|
1230
|
+
decidedVia: opts.via
|
|
1231
|
+
} : {}
|
|
1198
1232
|
});
|
|
1199
1233
|
}
|
|
1200
|
-
async reject(actor, toolCallId2, reason) {
|
|
1201
|
-
await this.
|
|
1234
|
+
async reject(actor, toolCallId2, reason, opts = {}) {
|
|
1235
|
+
await this.assertMayDecide(actor, toolCallId2);
|
|
1202
1236
|
return this.signalToolCall(toolCallId2, {
|
|
1203
1237
|
approved: false,
|
|
1238
|
+
executedByRef: actor.id,
|
|
1204
1239
|
...reason !== void 0 ? {
|
|
1205
1240
|
reason
|
|
1241
|
+
} : {},
|
|
1242
|
+
...opts.via !== void 0 ? {
|
|
1243
|
+
decidedVia: opts.via
|
|
1206
1244
|
} : {}
|
|
1207
1245
|
});
|
|
1208
1246
|
}
|
|
@@ -1243,6 +1281,7 @@ var AgentService = class {
|
|
|
1243
1281
|
* not the thread's own actor).
|
|
1244
1282
|
*/
|
|
1245
1283
|
async signalToolCall(toolCallId2, reply) {
|
|
1284
|
+
await this.assertNotExpired(toolCallId2);
|
|
1246
1285
|
const runId = await this.resolveRunForToolCall(toolCallId2);
|
|
1247
1286
|
return this.runner.signal(runId, toolCallId2, reply);
|
|
1248
1287
|
}
|
|
@@ -1481,6 +1520,47 @@ var AgentService = class {
|
|
|
1481
1520
|
}
|
|
1482
1521
|
}
|
|
1483
1522
|
/**
|
|
1523
|
+
* Authorization seam for approve/reject. The call's recorded approver decides who may settle it:
|
|
1524
|
+
* the requester (the default, and every call recorded before approvers existed) is the thread's
|
|
1525
|
+
* own actor — the ownership check this always was; any other approver goes through the policy's
|
|
1526
|
+
* `canDecide`, which by default asks whether the actor holds that role.
|
|
1527
|
+
*/
|
|
1528
|
+
async assertMayDecide(actor, toolCallId2) {
|
|
1529
|
+
const approval = await this.store.toolCallApproval?.(toolCallId2);
|
|
1530
|
+
const approver = approval?.approver ?? import_nestjs_agent_core8.REQUESTER_APPROVER;
|
|
1531
|
+
if (approver === import_nestjs_agent_core8.REQUESTER_APPROVER) {
|
|
1532
|
+
return this.assertOwnsToolCall(actor, toolCallId2);
|
|
1533
|
+
}
|
|
1534
|
+
const requesterRef = await this.store.ownerOfToolCall(toolCallId2);
|
|
1535
|
+
if (requesterRef === null) {
|
|
1536
|
+
throw new import_common6.NotFoundException(`tool call ${toolCallId2} not found`);
|
|
1537
|
+
}
|
|
1538
|
+
const allowed = await (0, import_nestjs_agent_core8.mayDecideApproval)(this.deps.forAgent().approvalPolicy, actor, {
|
|
1539
|
+
toolCallId: toolCallId2,
|
|
1540
|
+
approver,
|
|
1541
|
+
requesterRef
|
|
1542
|
+
});
|
|
1543
|
+
if (!allowed) {
|
|
1544
|
+
throw new import_common6.ForbiddenException(`this approval is for ${approver}`);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Refuse a decision on a request that has already lapsed — recorded `expired`, or past its
|
|
1549
|
+
* `expiresAt` with the run's own timer about to fire. Signalling it anyway would race the timeout:
|
|
1550
|
+
* a durable runtime buffers a signal nobody is waiting on, and a late approval must not be what a
|
|
1551
|
+
* later replay of the run reads back.
|
|
1552
|
+
*/
|
|
1553
|
+
async assertNotExpired(toolCallId2) {
|
|
1554
|
+
const approval = await this.store.toolCallApproval?.(toolCallId2);
|
|
1555
|
+
if (approval === null || approval === void 0) {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
const lapsed = approval.status === "expired" || approval.status === "pending_approval" && approval.expiresAt !== null && Date.parse(approval.expiresAt) <= Date.now();
|
|
1559
|
+
if (lapsed) {
|
|
1560
|
+
throw new import_common6.GoneException(`the approval request for tool call ${toolCallId2} has expired`);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1484
1564
|
* Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
|
|
1485
1565
|
* never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
|
|
1486
1566
|
* call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
|
|
@@ -1558,6 +1638,12 @@ var AgentApprovalPortAdapter = class {
|
|
|
1558
1638
|
approved: true,
|
|
1559
1639
|
...opts?.executedByRef !== void 0 ? {
|
|
1560
1640
|
executedByRef: opts.executedByRef
|
|
1641
|
+
} : {},
|
|
1642
|
+
...opts?.remember === true ? {
|
|
1643
|
+
remember: true
|
|
1644
|
+
} : {},
|
|
1645
|
+
...opts?.decidedVia !== void 0 ? {
|
|
1646
|
+
decidedVia: opts.decidedVia
|
|
1561
1647
|
} : {}
|
|
1562
1648
|
});
|
|
1563
1649
|
}
|
|
@@ -1569,6 +1655,9 @@ var AgentApprovalPortAdapter = class {
|
|
|
1569
1655
|
} : {},
|
|
1570
1656
|
...opts?.reason !== void 0 ? {
|
|
1571
1657
|
reason: opts.reason
|
|
1658
|
+
} : {},
|
|
1659
|
+
...opts?.decidedVia !== void 0 ? {
|
|
1660
|
+
decidedVia: opts.decidedVia
|
|
1572
1661
|
} : {}
|
|
1573
1662
|
});
|
|
1574
1663
|
}
|
|
@@ -2488,6 +2577,8 @@ var MAX_ANSWERED_QUESTIONS = 100;
|
|
|
2488
2577
|
var MAX_VALUES_PER_QUESTION = 100;
|
|
2489
2578
|
var MAX_ANSWER_VALUE_LENGTH = 4096;
|
|
2490
2579
|
var MAX_REJECT_REASON_LENGTH = 4096;
|
|
2580
|
+
var MAX_VIA_LENGTH = 64;
|
|
2581
|
+
var DEFAULT_VIA = "web";
|
|
2491
2582
|
function toolCallId(claimed) {
|
|
2492
2583
|
if (typeof claimed !== "string" || claimed.length === 0) {
|
|
2493
2584
|
throw new import_common15.BadRequestException("toolCallId must be a non-empty string");
|
|
@@ -2508,6 +2599,26 @@ function rejectReason(claimed) {
|
|
|
2508
2599
|
return claimed;
|
|
2509
2600
|
}
|
|
2510
2601
|
__name(rejectReason, "rejectReason");
|
|
2602
|
+
function remember(claimed) {
|
|
2603
|
+
if (claimed === void 0) {
|
|
2604
|
+
return false;
|
|
2605
|
+
}
|
|
2606
|
+
if (typeof claimed !== "boolean") {
|
|
2607
|
+
throw new import_common15.BadRequestException("remember must be a boolean");
|
|
2608
|
+
}
|
|
2609
|
+
return claimed;
|
|
2610
|
+
}
|
|
2611
|
+
__name(remember, "remember");
|
|
2612
|
+
function via(claimed) {
|
|
2613
|
+
if (claimed === void 0) {
|
|
2614
|
+
return DEFAULT_VIA;
|
|
2615
|
+
}
|
|
2616
|
+
if (typeof claimed !== "string" || claimed.length === 0 || claimed.length > MAX_VIA_LENGTH) {
|
|
2617
|
+
throw new import_common15.BadRequestException(`via must be a string of 1-${MAX_VIA_LENGTH} characters`);
|
|
2618
|
+
}
|
|
2619
|
+
return claimed;
|
|
2620
|
+
}
|
|
2621
|
+
__name(via, "via");
|
|
2511
2622
|
function answers(claimed) {
|
|
2512
2623
|
if (claimed === void 0) {
|
|
2513
2624
|
return void 0;
|
|
@@ -2552,14 +2663,19 @@ var ToolCallController = class {
|
|
|
2552
2663
|
}
|
|
2553
2664
|
async approve(req, body) {
|
|
2554
2665
|
const actor = await this.actorResolver.resolve(req);
|
|
2555
|
-
await this.agent.approve(actor, toolCallId(body.toolCallId)
|
|
2666
|
+
await this.agent.approve(actor, toolCallId(body.toolCallId), {
|
|
2667
|
+
remember: remember(body.remember),
|
|
2668
|
+
via: via(body.via)
|
|
2669
|
+
});
|
|
2556
2670
|
return {
|
|
2557
2671
|
ok: true
|
|
2558
2672
|
};
|
|
2559
2673
|
}
|
|
2560
2674
|
async reject(req, body) {
|
|
2561
2675
|
const actor = await this.actorResolver.resolve(req);
|
|
2562
|
-
await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason)
|
|
2676
|
+
await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason), {
|
|
2677
|
+
via: via(body.via)
|
|
2678
|
+
});
|
|
2563
2679
|
return {
|
|
2564
2680
|
ok: true
|
|
2565
2681
|
};
|
|
@@ -2625,7 +2741,7 @@ _ts_decorate15([
|
|
|
2625
2741
|
_ts_metadata14("design:type", Function),
|
|
2626
2742
|
_ts_metadata14("design:paramtypes", [
|
|
2627
2743
|
typeof Request === "undefined" ? Object : Request,
|
|
2628
|
-
|
|
2744
|
+
Object
|
|
2629
2745
|
]),
|
|
2630
2746
|
_ts_metadata14("design:returntype", Promise)
|
|
2631
2747
|
], ToolCallController.prototype, "skip", null);
|
|
@@ -3370,6 +3486,36 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
3370
3486
|
* shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
|
|
3371
3487
|
* cancel settles the wait too, by rejecting it.
|
|
3372
3488
|
*/
|
|
3489
|
+
/**
|
|
3490
|
+
* {@link park} for an approval, bounded by the policy's time to live when it has one: the timer
|
|
3491
|
+
* settles the wait as `expired` — the same Decision the durable runner builds from its signal
|
|
3492
|
+
* timeout — and a decision that arrives first disarms it.
|
|
3493
|
+
*/
|
|
3494
|
+
parkApproval(runId, toolCallId2, timeoutMs) {
|
|
3495
|
+
const waiting = this.park(runId, toolCallId2);
|
|
3496
|
+
if (timeoutMs === void 0) {
|
|
3497
|
+
return waiting;
|
|
3498
|
+
}
|
|
3499
|
+
const key = `${runId}:${toolCallId2}`;
|
|
3500
|
+
let timer;
|
|
3501
|
+
const lapse = new Promise((resolve) => {
|
|
3502
|
+
timer = setTimeout(() => {
|
|
3503
|
+
const entry = this.pending.get(key);
|
|
3504
|
+
if (entry !== void 0) {
|
|
3505
|
+
this.pending.delete(key);
|
|
3506
|
+
resolve({
|
|
3507
|
+
approved: false,
|
|
3508
|
+
expired: true
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
}, timeoutMs);
|
|
3512
|
+
timer.unref?.();
|
|
3513
|
+
});
|
|
3514
|
+
return Promise.race([
|
|
3515
|
+
waiting,
|
|
3516
|
+
lapse
|
|
3517
|
+
]).finally(() => clearTimeout(timer));
|
|
3518
|
+
}
|
|
3373
3519
|
park(runId, toolCallId2) {
|
|
3374
3520
|
return new Promise((resolve, reject) => {
|
|
3375
3521
|
this.pending.set(`${runId}:${toolCallId2}`, {
|
|
@@ -3383,7 +3529,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
3383
3529
|
return {
|
|
3384
3530
|
runId,
|
|
3385
3531
|
openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
|
|
3386
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
3532
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
3387
3533
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
3388
3534
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
3389
3535
|
parallel: import_nestjs_agent_core22.settleAll,
|
|
@@ -3434,7 +3580,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
3434
3580
|
const hooks = {
|
|
3435
3581
|
runId,
|
|
3436
3582
|
openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
|
|
3437
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
3583
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
3438
3584
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
3439
3585
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
3440
3586
|
parallel: import_nestjs_agent_core22.settleAll,
|
|
@@ -3538,7 +3684,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
3538
3684
|
runId,
|
|
3539
3685
|
// Forward into the top-level stream; the top-level run owns end/fail on that shared sink.
|
|
3540
3686
|
openSink: /* @__PURE__ */ __name(async () => childSinkWriter(await deps.sink.open(sinkRunId)), "openSink"),
|
|
3541
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
3687
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
3542
3688
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
3543
3689
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
3544
3690
|
parallel: import_nestjs_agent_core22.settleAll,
|