@dudousxd/nestjs-agent 1.2.0 → 1.4.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 +180 -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 +182 -14
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +161 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -4
- package/dist/index.d.ts +47 -4
- package/dist/index.js +163 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DynamicModule, OnModuleInit, Type, OnApplicationBootstrap } from '@nestjs/common';
|
|
2
|
-
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-
|
|
3
|
-
export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, i as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-
|
|
2
|
+
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-CUyVPsa1.cjs';
|
|
3
|
+
export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, i as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-CUyVPsa1.cjs';
|
|
4
4
|
import { AgentRunner, AgentStore, QuotaStore, AttachmentStagingStore, Actor, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, StagedAttachment, QuotaView, AgentApprovalPort, ActorResolver, MessageAttachment, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
|
|
5
5
|
export * from '@dudousxd/nestjs-agent-core';
|
|
6
6
|
import { Request } from 'express';
|
|
@@ -80,8 +80,19 @@ declare class AgentService {
|
|
|
80
80
|
* ownership is derived from.
|
|
81
81
|
*/
|
|
82
82
|
subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
85
|
+
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
86
|
+
* thread, and `via` names the surface the decision came through (`'web'`, `'slack'`, …) — both
|
|
87
|
+
* persisted with the call.
|
|
88
|
+
*/
|
|
89
|
+
approve(actor: Actor, toolCallId: string, opts?: {
|
|
90
|
+
remember?: boolean;
|
|
91
|
+
via?: string;
|
|
92
|
+
}): Promise<void>;
|
|
93
|
+
reject(actor: Actor, toolCallId: string, reason?: string, opts?: {
|
|
94
|
+
via?: string;
|
|
95
|
+
}): Promise<void>;
|
|
85
96
|
/**
|
|
86
97
|
* Answer a parked question set. An omitted question takes the request's own pre-picked default —
|
|
87
98
|
* resolved by the LOOP against the request it already holds, so "the user just submitted" and
|
|
@@ -193,6 +204,29 @@ declare class AgentService {
|
|
|
193
204
|
* adapter can resolve+signal a decision WITHOUT this check — it is authorized upstream instead.
|
|
194
205
|
*/
|
|
195
206
|
private assertOwnsToolCall;
|
|
207
|
+
/**
|
|
208
|
+
* Check a reply against the questions it answers before it is signalled: every submitted value a
|
|
209
|
+
* question's own rules accept (a typed input's type, bounds and pattern; a pick from the offered
|
|
210
|
+
* options), and every `required` question answered — by the reply, or by the defaults an omitted
|
|
211
|
+
* question falls back to. A reply the loop would have to drop parts of is refused here instead, so
|
|
212
|
+
* the person who typed it hears why. Needs the store to hand back the call's recorded questions;
|
|
213
|
+
* without that seam the loop's own filtering is the only check.
|
|
214
|
+
*/
|
|
215
|
+
private assertAnswersFit;
|
|
216
|
+
/**
|
|
217
|
+
* Authorization seam for approve/reject. The call's recorded approver decides who may settle it:
|
|
218
|
+
* the requester (the default, and every call recorded before approvers existed) is the thread's
|
|
219
|
+
* own actor — the ownership check this always was; any other approver goes through the policy's
|
|
220
|
+
* `canDecide`, which by default asks whether the actor holds that role.
|
|
221
|
+
*/
|
|
222
|
+
private assertMayDecide;
|
|
223
|
+
/**
|
|
224
|
+
* Refuse a decision on a request that has already lapsed — recorded `expired`, or past its
|
|
225
|
+
* `expiresAt` with the run's own timer about to fire. Signalling it anyway would race the timeout:
|
|
226
|
+
* a durable runtime buffers a signal nobody is waiting on, and a late approval must not be what a
|
|
227
|
+
* later replay of the run reads back.
|
|
228
|
+
*/
|
|
229
|
+
private assertNotExpired;
|
|
196
230
|
/**
|
|
197
231
|
* Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
|
|
198
232
|
* never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
|
|
@@ -223,10 +257,13 @@ declare class AgentApprovalPortAdapter implements AgentApprovalPort {
|
|
|
223
257
|
constructor(agent: AgentService);
|
|
224
258
|
approve(toolCallId: string, opts?: {
|
|
225
259
|
executedByRef?: string;
|
|
260
|
+
remember?: boolean;
|
|
261
|
+
decidedVia?: string;
|
|
226
262
|
}): Promise<void>;
|
|
227
263
|
reject(toolCallId: string, opts?: {
|
|
228
264
|
executedByRef?: string;
|
|
229
265
|
reason?: string;
|
|
266
|
+
decidedVia?: string;
|
|
230
267
|
}): Promise<void>;
|
|
231
268
|
}
|
|
232
269
|
|
|
@@ -754,6 +791,12 @@ declare class InlineAgentRunner implements AgentRunner {
|
|
|
754
791
|
* shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
|
|
755
792
|
* cancel settles the wait too, by rejecting it.
|
|
756
793
|
*/
|
|
794
|
+
/**
|
|
795
|
+
* {@link park} for an approval, bounded by the policy's time to live when it has one: the timer
|
|
796
|
+
* settles the wait as `expired` — the same Decision the durable runner builds from its signal
|
|
797
|
+
* timeout — and a decision that arrives first disarms it.
|
|
798
|
+
*/
|
|
799
|
+
private parkApproval;
|
|
757
800
|
private park;
|
|
758
801
|
private topLevelHooks;
|
|
759
802
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DynamicModule, OnModuleInit, Type, OnApplicationBootstrap } from '@nestjs/common';
|
|
2
|
-
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-
|
|
3
|
-
export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, i as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-
|
|
2
|
+
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-CUyVPsa1.js';
|
|
3
|
+
export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, i as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-CUyVPsa1.js';
|
|
4
4
|
import { AgentRunner, AgentStore, QuotaStore, AttachmentStagingStore, Actor, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, StagedAttachment, QuotaView, AgentApprovalPort, ActorResolver, MessageAttachment, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
|
|
5
5
|
export * from '@dudousxd/nestjs-agent-core';
|
|
6
6
|
import { Request } from 'express';
|
|
@@ -80,8 +80,19 @@ declare class AgentService {
|
|
|
80
80
|
* ownership is derived from.
|
|
81
81
|
*/
|
|
82
82
|
subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
85
|
+
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
86
|
+
* thread, and `via` names the surface the decision came through (`'web'`, `'slack'`, …) — both
|
|
87
|
+
* persisted with the call.
|
|
88
|
+
*/
|
|
89
|
+
approve(actor: Actor, toolCallId: string, opts?: {
|
|
90
|
+
remember?: boolean;
|
|
91
|
+
via?: string;
|
|
92
|
+
}): Promise<void>;
|
|
93
|
+
reject(actor: Actor, toolCallId: string, reason?: string, opts?: {
|
|
94
|
+
via?: string;
|
|
95
|
+
}): Promise<void>;
|
|
85
96
|
/**
|
|
86
97
|
* Answer a parked question set. An omitted question takes the request's own pre-picked default —
|
|
87
98
|
* resolved by the LOOP against the request it already holds, so "the user just submitted" and
|
|
@@ -193,6 +204,29 @@ declare class AgentService {
|
|
|
193
204
|
* adapter can resolve+signal a decision WITHOUT this check — it is authorized upstream instead.
|
|
194
205
|
*/
|
|
195
206
|
private assertOwnsToolCall;
|
|
207
|
+
/**
|
|
208
|
+
* Check a reply against the questions it answers before it is signalled: every submitted value a
|
|
209
|
+
* question's own rules accept (a typed input's type, bounds and pattern; a pick from the offered
|
|
210
|
+
* options), and every `required` question answered — by the reply, or by the defaults an omitted
|
|
211
|
+
* question falls back to. A reply the loop would have to drop parts of is refused here instead, so
|
|
212
|
+
* the person who typed it hears why. Needs the store to hand back the call's recorded questions;
|
|
213
|
+
* without that seam the loop's own filtering is the only check.
|
|
214
|
+
*/
|
|
215
|
+
private assertAnswersFit;
|
|
216
|
+
/**
|
|
217
|
+
* Authorization seam for approve/reject. The call's recorded approver decides who may settle it:
|
|
218
|
+
* the requester (the default, and every call recorded before approvers existed) is the thread's
|
|
219
|
+
* own actor — the ownership check this always was; any other approver goes through the policy's
|
|
220
|
+
* `canDecide`, which by default asks whether the actor holds that role.
|
|
221
|
+
*/
|
|
222
|
+
private assertMayDecide;
|
|
223
|
+
/**
|
|
224
|
+
* Refuse a decision on a request that has already lapsed — recorded `expired`, or past its
|
|
225
|
+
* `expiresAt` with the run's own timer about to fire. Signalling it anyway would race the timeout:
|
|
226
|
+
* a durable runtime buffers a signal nobody is waiting on, and a late approval must not be what a
|
|
227
|
+
* later replay of the run reads back.
|
|
228
|
+
*/
|
|
229
|
+
private assertNotExpired;
|
|
196
230
|
/**
|
|
197
231
|
* Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
|
|
198
232
|
* never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
|
|
@@ -223,10 +257,13 @@ declare class AgentApprovalPortAdapter implements AgentApprovalPort {
|
|
|
223
257
|
constructor(agent: AgentService);
|
|
224
258
|
approve(toolCallId: string, opts?: {
|
|
225
259
|
executedByRef?: string;
|
|
260
|
+
remember?: boolean;
|
|
261
|
+
decidedVia?: string;
|
|
226
262
|
}): Promise<void>;
|
|
227
263
|
reject(toolCallId: string, opts?: {
|
|
228
264
|
executedByRef?: string;
|
|
229
265
|
reason?: string;
|
|
266
|
+
decidedVia?: string;
|
|
230
267
|
}): Promise<void>;
|
|
231
268
|
}
|
|
232
269
|
|
|
@@ -754,6 +791,12 @@ declare class InlineAgentRunner implements AgentRunner {
|
|
|
754
791
|
* shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
|
|
755
792
|
* cancel settles the wait too, by rejecting it.
|
|
756
793
|
*/
|
|
794
|
+
/**
|
|
795
|
+
* {@link park} for an approval, bounded by the policy's time to live when it has one: the timer
|
|
796
|
+
* settles the wait as `expired` — the same Decision the durable runner builds from its signal
|
|
797
|
+
* timeout — and a decision that arrives first disarms it.
|
|
798
|
+
*/
|
|
799
|
+
private parkApproval;
|
|
757
800
|
private park;
|
|
758
801
|
private topLevelHooks;
|
|
759
802
|
/**
|
package/dist/index.js
CHANGED
|
@@ -122,6 +122,9 @@ var AgentDepsFactory2 = class {
|
|
|
122
122
|
store: this.store,
|
|
123
123
|
sink: this.sink,
|
|
124
124
|
rolesPolicy: this.rolesPolicy,
|
|
125
|
+
...this.options.approvalPolicy !== void 0 ? {
|
|
126
|
+
approvalPolicy: this.options.approvalPolicy
|
|
127
|
+
} : {},
|
|
125
128
|
registry: this.registry,
|
|
126
129
|
promptContributors: this.promptContributors,
|
|
127
130
|
systemPrompt: definition?.systemPrompt ?? "You are a helpful assistant.",
|
|
@@ -269,8 +272,8 @@ AgentDepsFactory2 = _ts_decorate([
|
|
|
269
272
|
], AgentDepsFactory2);
|
|
270
273
|
|
|
271
274
|
// src/agent.service.ts
|
|
272
|
-
import { AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE2 } from "@dudousxd/nestjs-agent-core";
|
|
273
|
-
import { BadRequestException, ForbiddenException, Inject as Inject2, Injectable as Injectable2, NotFoundException, NotImplementedException, Optional as Optional2 } from "@nestjs/common";
|
|
275
|
+
import { AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE2, REQUESTER_APPROVER, mayDecideApproval, readElicitationQuestions, validateElicitationAnswer } from "@dudousxd/nestjs-agent-core";
|
|
276
|
+
import { BadRequestException, ForbiddenException, GoneException, Inject as Inject2, Injectable as Injectable2, NotFoundException, NotImplementedException, Optional as Optional2 } from "@nestjs/common";
|
|
274
277
|
|
|
275
278
|
// src/agent-deps.ts
|
|
276
279
|
function utcDay(date = /* @__PURE__ */ new Date()) {
|
|
@@ -423,18 +426,35 @@ var AgentService = class {
|
|
|
423
426
|
await this.assertOwnsActiveStream(actor, runId);
|
|
424
427
|
return this.subscribe(runId);
|
|
425
428
|
}
|
|
426
|
-
|
|
427
|
-
|
|
429
|
+
/**
|
|
430
|
+
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
431
|
+
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
432
|
+
* thread, and `via` names the surface the decision came through (`'web'`, `'slack'`, …) — both
|
|
433
|
+
* persisted with the call.
|
|
434
|
+
*/
|
|
435
|
+
async approve(actor, toolCallId2, opts = {}) {
|
|
436
|
+
await this.assertMayDecide(actor, toolCallId2);
|
|
428
437
|
return this.signalToolCall(toolCallId2, {
|
|
429
|
-
approved: true
|
|
438
|
+
approved: true,
|
|
439
|
+
executedByRef: actor.id,
|
|
440
|
+
...opts.remember === true ? {
|
|
441
|
+
remember: true
|
|
442
|
+
} : {},
|
|
443
|
+
...opts.via !== void 0 ? {
|
|
444
|
+
decidedVia: opts.via
|
|
445
|
+
} : {}
|
|
430
446
|
});
|
|
431
447
|
}
|
|
432
|
-
async reject(actor, toolCallId2, reason) {
|
|
433
|
-
await this.
|
|
448
|
+
async reject(actor, toolCallId2, reason, opts = {}) {
|
|
449
|
+
await this.assertMayDecide(actor, toolCallId2);
|
|
434
450
|
return this.signalToolCall(toolCallId2, {
|
|
435
451
|
approved: false,
|
|
452
|
+
executedByRef: actor.id,
|
|
436
453
|
...reason !== void 0 ? {
|
|
437
454
|
reason
|
|
455
|
+
} : {},
|
|
456
|
+
...opts.via !== void 0 ? {
|
|
457
|
+
decidedVia: opts.via
|
|
438
458
|
} : {}
|
|
439
459
|
});
|
|
440
460
|
}
|
|
@@ -446,6 +466,7 @@ var AgentService = class {
|
|
|
446
466
|
*/
|
|
447
467
|
async answer(actor, toolCallId2, answers2 = {}) {
|
|
448
468
|
await this.assertOwnsToolCall(actor, toolCallId2);
|
|
469
|
+
await this.assertAnswersFit(toolCallId2, answers2);
|
|
449
470
|
const reply = {
|
|
450
471
|
answers: answers2,
|
|
451
472
|
answeredByRef: actor.id
|
|
@@ -475,6 +496,7 @@ var AgentService = class {
|
|
|
475
496
|
* not the thread's own actor).
|
|
476
497
|
*/
|
|
477
498
|
async signalToolCall(toolCallId2, reply) {
|
|
499
|
+
await this.assertNotExpired(toolCallId2);
|
|
478
500
|
const runId = await this.resolveRunForToolCall(toolCallId2);
|
|
479
501
|
return this.runner.signal(runId, toolCallId2, reply);
|
|
480
502
|
}
|
|
@@ -713,6 +735,68 @@ var AgentService = class {
|
|
|
713
735
|
}
|
|
714
736
|
}
|
|
715
737
|
/**
|
|
738
|
+
* Check a reply against the questions it answers before it is signalled: every submitted value a
|
|
739
|
+
* question's own rules accept (a typed input's type, bounds and pattern; a pick from the offered
|
|
740
|
+
* options), and every `required` question answered — by the reply, or by the defaults an omitted
|
|
741
|
+
* question falls back to. A reply the loop would have to drop parts of is refused here instead, so
|
|
742
|
+
* the person who typed it hears why. Needs the store to hand back the call's recorded questions;
|
|
743
|
+
* without that seam the loop's own filtering is the only check.
|
|
744
|
+
*/
|
|
745
|
+
async assertAnswersFit(toolCallId2, answers2) {
|
|
746
|
+
if (this.store.toolCallInput === void 0) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
const questions = readElicitationQuestions(await this.store.toolCallInput(toolCallId2));
|
|
750
|
+
for (const question of questions) {
|
|
751
|
+
const submitted = Object.hasOwn(answers2, question.id) ? answers2[question.id] : void 0;
|
|
752
|
+
const problem = validateElicitationAnswer(question, submitted ?? question.defaults ?? []);
|
|
753
|
+
if (problem !== null) {
|
|
754
|
+
throw new BadRequestException(`answers["${question.id}"] ${problem}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Authorization seam for approve/reject. The call's recorded approver decides who may settle it:
|
|
760
|
+
* the requester (the default, and every call recorded before approvers existed) is the thread's
|
|
761
|
+
* own actor — the ownership check this always was; any other approver goes through the policy's
|
|
762
|
+
* `canDecide`, which by default asks whether the actor holds that role.
|
|
763
|
+
*/
|
|
764
|
+
async assertMayDecide(actor, toolCallId2) {
|
|
765
|
+
const approval = await this.store.toolCallApproval?.(toolCallId2);
|
|
766
|
+
const approver = approval?.approver ?? REQUESTER_APPROVER;
|
|
767
|
+
if (approver === REQUESTER_APPROVER) {
|
|
768
|
+
return this.assertOwnsToolCall(actor, toolCallId2);
|
|
769
|
+
}
|
|
770
|
+
const requesterRef = await this.store.ownerOfToolCall(toolCallId2);
|
|
771
|
+
if (requesterRef === null) {
|
|
772
|
+
throw new NotFoundException(`tool call ${toolCallId2} not found`);
|
|
773
|
+
}
|
|
774
|
+
const allowed = await mayDecideApproval(this.deps.forAgent().approvalPolicy, actor, {
|
|
775
|
+
toolCallId: toolCallId2,
|
|
776
|
+
approver,
|
|
777
|
+
requesterRef
|
|
778
|
+
});
|
|
779
|
+
if (!allowed) {
|
|
780
|
+
throw new ForbiddenException(`this approval is for ${approver}`);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Refuse a decision on a request that has already lapsed — recorded `expired`, or past its
|
|
785
|
+
* `expiresAt` with the run's own timer about to fire. Signalling it anyway would race the timeout:
|
|
786
|
+
* a durable runtime buffers a signal nobody is waiting on, and a late approval must not be what a
|
|
787
|
+
* later replay of the run reads back.
|
|
788
|
+
*/
|
|
789
|
+
async assertNotExpired(toolCallId2) {
|
|
790
|
+
const approval = await this.store.toolCallApproval?.(toolCallId2);
|
|
791
|
+
if (approval === null || approval === void 0) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const lapsed = approval.status === "expired" || approval.status === "pending_approval" && approval.expiresAt !== null && Date.parse(approval.expiresAt) <= Date.now();
|
|
795
|
+
if (lapsed) {
|
|
796
|
+
throw new GoneException(`the approval request for tool call ${toolCallId2} has expired`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
716
800
|
* Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
|
|
717
801
|
* never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
|
|
718
802
|
* call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
|
|
@@ -790,6 +874,12 @@ var AgentApprovalPortAdapter = class {
|
|
|
790
874
|
approved: true,
|
|
791
875
|
...opts?.executedByRef !== void 0 ? {
|
|
792
876
|
executedByRef: opts.executedByRef
|
|
877
|
+
} : {},
|
|
878
|
+
...opts?.remember === true ? {
|
|
879
|
+
remember: true
|
|
880
|
+
} : {},
|
|
881
|
+
...opts?.decidedVia !== void 0 ? {
|
|
882
|
+
decidedVia: opts.decidedVia
|
|
793
883
|
} : {}
|
|
794
884
|
});
|
|
795
885
|
}
|
|
@@ -801,6 +891,9 @@ var AgentApprovalPortAdapter = class {
|
|
|
801
891
|
} : {},
|
|
802
892
|
...opts?.reason !== void 0 ? {
|
|
803
893
|
reason: opts.reason
|
|
894
|
+
} : {},
|
|
895
|
+
...opts?.decidedVia !== void 0 ? {
|
|
896
|
+
decidedVia: opts.decidedVia
|
|
804
897
|
} : {}
|
|
805
898
|
});
|
|
806
899
|
}
|
|
@@ -1720,6 +1813,8 @@ var MAX_ANSWERED_QUESTIONS = 100;
|
|
|
1720
1813
|
var MAX_VALUES_PER_QUESTION = 100;
|
|
1721
1814
|
var MAX_ANSWER_VALUE_LENGTH = 4096;
|
|
1722
1815
|
var MAX_REJECT_REASON_LENGTH = 4096;
|
|
1816
|
+
var MAX_VIA_LENGTH = 64;
|
|
1817
|
+
var DEFAULT_VIA = "web";
|
|
1723
1818
|
function toolCallId(claimed) {
|
|
1724
1819
|
if (typeof claimed !== "string" || claimed.length === 0) {
|
|
1725
1820
|
throw new BadRequestException4("toolCallId must be a non-empty string");
|
|
@@ -1740,6 +1835,26 @@ function rejectReason(claimed) {
|
|
|
1740
1835
|
return claimed;
|
|
1741
1836
|
}
|
|
1742
1837
|
__name(rejectReason, "rejectReason");
|
|
1838
|
+
function remember(claimed) {
|
|
1839
|
+
if (claimed === void 0) {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
if (typeof claimed !== "boolean") {
|
|
1843
|
+
throw new BadRequestException4("remember must be a boolean");
|
|
1844
|
+
}
|
|
1845
|
+
return claimed;
|
|
1846
|
+
}
|
|
1847
|
+
__name(remember, "remember");
|
|
1848
|
+
function via(claimed) {
|
|
1849
|
+
if (claimed === void 0) {
|
|
1850
|
+
return DEFAULT_VIA;
|
|
1851
|
+
}
|
|
1852
|
+
if (typeof claimed !== "string" || claimed.length === 0 || claimed.length > MAX_VIA_LENGTH) {
|
|
1853
|
+
throw new BadRequestException4(`via must be a string of 1-${MAX_VIA_LENGTH} characters`);
|
|
1854
|
+
}
|
|
1855
|
+
return claimed;
|
|
1856
|
+
}
|
|
1857
|
+
__name(via, "via");
|
|
1743
1858
|
function answers(claimed) {
|
|
1744
1859
|
if (claimed === void 0) {
|
|
1745
1860
|
return void 0;
|
|
@@ -1784,14 +1899,19 @@ var ToolCallController = class {
|
|
|
1784
1899
|
}
|
|
1785
1900
|
async approve(req, body) {
|
|
1786
1901
|
const actor = await this.actorResolver.resolve(req);
|
|
1787
|
-
await this.agent.approve(actor, toolCallId(body.toolCallId)
|
|
1902
|
+
await this.agent.approve(actor, toolCallId(body.toolCallId), {
|
|
1903
|
+
remember: remember(body.remember),
|
|
1904
|
+
via: via(body.via)
|
|
1905
|
+
});
|
|
1788
1906
|
return {
|
|
1789
1907
|
ok: true
|
|
1790
1908
|
};
|
|
1791
1909
|
}
|
|
1792
1910
|
async reject(req, body) {
|
|
1793
1911
|
const actor = await this.actorResolver.resolve(req);
|
|
1794
|
-
await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason)
|
|
1912
|
+
await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason), {
|
|
1913
|
+
via: via(body.via)
|
|
1914
|
+
});
|
|
1795
1915
|
return {
|
|
1796
1916
|
ok: true
|
|
1797
1917
|
};
|
|
@@ -1857,7 +1977,7 @@ _ts_decorate11([
|
|
|
1857
1977
|
_ts_metadata11("design:type", Function),
|
|
1858
1978
|
_ts_metadata11("design:paramtypes", [
|
|
1859
1979
|
typeof Request === "undefined" ? Object : Request,
|
|
1860
|
-
|
|
1980
|
+
Object
|
|
1861
1981
|
]),
|
|
1862
1982
|
_ts_metadata11("design:returntype", Promise)
|
|
1863
1983
|
], ToolCallController.prototype, "skip", null);
|
|
@@ -2723,6 +2843,36 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
2723
2843
|
* shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
|
|
2724
2844
|
* cancel settles the wait too, by rejecting it.
|
|
2725
2845
|
*/
|
|
2846
|
+
/**
|
|
2847
|
+
* {@link park} for an approval, bounded by the policy's time to live when it has one: the timer
|
|
2848
|
+
* settles the wait as `expired` — the same Decision the durable runner builds from its signal
|
|
2849
|
+
* timeout — and a decision that arrives first disarms it.
|
|
2850
|
+
*/
|
|
2851
|
+
parkApproval(runId, toolCallId2, timeoutMs) {
|
|
2852
|
+
const waiting = this.park(runId, toolCallId2);
|
|
2853
|
+
if (timeoutMs === void 0) {
|
|
2854
|
+
return waiting;
|
|
2855
|
+
}
|
|
2856
|
+
const key = `${runId}:${toolCallId2}`;
|
|
2857
|
+
let timer;
|
|
2858
|
+
const lapse = new Promise((resolve) => {
|
|
2859
|
+
timer = setTimeout(() => {
|
|
2860
|
+
const entry = this.pending.get(key);
|
|
2861
|
+
if (entry !== void 0) {
|
|
2862
|
+
this.pending.delete(key);
|
|
2863
|
+
resolve({
|
|
2864
|
+
approved: false,
|
|
2865
|
+
expired: true
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
}, timeoutMs);
|
|
2869
|
+
timer.unref?.();
|
|
2870
|
+
});
|
|
2871
|
+
return Promise.race([
|
|
2872
|
+
waiting,
|
|
2873
|
+
lapse
|
|
2874
|
+
]).finally(() => clearTimeout(timer));
|
|
2875
|
+
}
|
|
2726
2876
|
park(runId, toolCallId2) {
|
|
2727
2877
|
return new Promise((resolve, reject) => {
|
|
2728
2878
|
this.pending.set(`${runId}:${toolCallId2}`, {
|
|
@@ -2736,7 +2886,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
2736
2886
|
return {
|
|
2737
2887
|
runId,
|
|
2738
2888
|
openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
|
|
2739
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
2889
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
2740
2890
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
2741
2891
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
2742
2892
|
parallel: settleAll,
|
|
@@ -2787,7 +2937,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
2787
2937
|
const hooks = {
|
|
2788
2938
|
runId,
|
|
2789
2939
|
openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
|
|
2790
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
2940
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
2791
2941
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
2792
2942
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
2793
2943
|
parallel: settleAll,
|
|
@@ -2891,7 +3041,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
2891
3041
|
runId,
|
|
2892
3042
|
// Forward into the top-level stream; the top-level run owns end/fail on that shared sink.
|
|
2893
3043
|
openSink: /* @__PURE__ */ __name(async () => childSinkWriter(await deps.sink.open(sinkRunId)), "openSink"),
|
|
2894
|
-
awaitApproval: /* @__PURE__ */ __name((call) => this.
|
|
3044
|
+
awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
|
|
2895
3045
|
awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
|
|
2896
3046
|
step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
|
|
2897
3047
|
parallel: settleAll,
|