@dudousxd/nestjs-agent 1.36.1 → 1.36.2
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 +2 -2
- package/dist/a2ui/index.cjs +11 -1
- package/dist/a2ui/index.cjs.map +1 -1
- package/dist/a2ui/index.d.cts +2 -2
- package/dist/a2ui/index.d.ts +2 -2
- package/dist/a2ui/index.js +11 -1
- package/dist/a2ui/index.js.map +1 -1
- package/dist/{chat-queue.service-CzXsKVb1.d.ts → chat-queue.service-YjiKVTKE.d.ts} +2 -2
- package/dist/{chat-queue.service-CF2fcJDx.d.cts → chat-queue.service-fopUN313.d.cts} +2 -2
- package/dist/durable/index.cjs +228 -47
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +6 -18
- package/dist/durable/index.d.ts +6 -18
- package/dist/durable/index.js +228 -47
- package/dist/durable/index.js.map +1 -1
- package/dist/{index-NF93YDF4.d.cts → index-CJdTomOT.d.cts} +6 -4
- package/dist/{index-DpS9P7so.d.ts → index-D1xRrnCq.d.ts} +6 -4
- package/dist/index.cjs +222 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -7
- package/dist/index.d.ts +19 -7
- package/dist/index.js +222 -44
- package/dist/index.js.map +1 -1
- package/dist/sink-redis/index.cjs +164 -18
- package/dist/sink-redis/index.cjs.map +1 -1
- package/dist/sink-redis/index.d.cts +36 -7
- package/dist/sink-redis/index.d.ts +36 -7
- package/dist/sink-redis/index.js +165 -19
- package/dist/sink-redis/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { A2uiStreamOptions, a2uiThreadReplay } from '@dudousxd/nestjs-agent-core/a2ui';
|
|
2
|
-
import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
|
|
2
|
+
import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, SubscribeOptions, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
|
|
3
3
|
import { Request, Response } from 'express';
|
|
4
4
|
import { UiCapabilities } from '@dudousxd/nestjs-agent-core/genui';
|
|
5
|
-
import { b as AgentModuleOptions, A as AgentDepsFactory, C as ChatQueueService, d as AgentProtocolAdapter } from './chat-queue.service-
|
|
5
|
+
import { b as AgentModuleOptions, A as AgentDepsFactory, C as ChatQueueService, d as AgentProtocolAdapter } from './chat-queue.service-fopUN313.cjs';
|
|
6
6
|
|
|
7
7
|
/** Default per-file size cap (20 MiB). */
|
|
8
8
|
declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
|
|
@@ -355,14 +355,14 @@ declare class AgentService {
|
|
|
355
355
|
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
356
356
|
* actor's answer, tool arguments and tool results.
|
|
357
357
|
*/
|
|
358
|
-
subscribe(runId: string): AsyncIterable<Uint8Array>;
|
|
358
|
+
subscribe(runId: string, options?: SubscribeOptions): AsyncIterable<Uint8Array>;
|
|
359
359
|
/**
|
|
360
360
|
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
361
361
|
* streaming this run may read it. A run that has already finished is reported missing rather than
|
|
362
362
|
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
363
363
|
* ownership is derived from.
|
|
364
364
|
*/
|
|
365
|
-
subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
|
|
365
|
+
subscribeAs(actor: Actor, runId: string, options?: SubscribeOptions): Promise<AsyncIterable<Uint8Array>>;
|
|
366
366
|
/**
|
|
367
367
|
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
368
368
|
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
@@ -460,6 +460,8 @@ declare class AgentService {
|
|
|
460
460
|
* the full read.
|
|
461
461
|
*/
|
|
462
462
|
private resolveAgentName;
|
|
463
|
+
/** Everything a send reads off `threadId`, in one read where the store allows it. */
|
|
464
|
+
private threadReads;
|
|
463
465
|
private threadDefaultAgent;
|
|
464
466
|
private toSummaryView;
|
|
465
467
|
private toDetailView;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { A2uiStreamOptions, a2uiThreadReplay } from '@dudousxd/nestjs-agent-core/a2ui';
|
|
2
|
-
import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
|
|
2
|
+
import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, SubscribeOptions, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
|
|
3
3
|
import { Request, Response } from 'express';
|
|
4
4
|
import { UiCapabilities } from '@dudousxd/nestjs-agent-core/genui';
|
|
5
|
-
import { b as AgentModuleOptions, A as AgentDepsFactory, C as ChatQueueService, d as AgentProtocolAdapter } from './chat-queue.service-
|
|
5
|
+
import { b as AgentModuleOptions, A as AgentDepsFactory, C as ChatQueueService, d as AgentProtocolAdapter } from './chat-queue.service-YjiKVTKE.js';
|
|
6
6
|
|
|
7
7
|
/** Default per-file size cap (20 MiB). */
|
|
8
8
|
declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
|
|
@@ -355,14 +355,14 @@ declare class AgentService {
|
|
|
355
355
|
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
356
356
|
* actor's answer, tool arguments and tool results.
|
|
357
357
|
*/
|
|
358
|
-
subscribe(runId: string): AsyncIterable<Uint8Array>;
|
|
358
|
+
subscribe(runId: string, options?: SubscribeOptions): AsyncIterable<Uint8Array>;
|
|
359
359
|
/**
|
|
360
360
|
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
361
361
|
* streaming this run may read it. A run that has already finished is reported missing rather than
|
|
362
362
|
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
363
363
|
* ownership is derived from.
|
|
364
364
|
*/
|
|
365
|
-
subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
|
|
365
|
+
subscribeAs(actor: Actor, runId: string, options?: SubscribeOptions): Promise<AsyncIterable<Uint8Array>>;
|
|
366
366
|
/**
|
|
367
367
|
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
368
368
|
* (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
|
|
@@ -460,6 +460,8 @@ declare class AgentService {
|
|
|
460
460
|
* the full read.
|
|
461
461
|
*/
|
|
462
462
|
private resolveAgentName;
|
|
463
|
+
/** Everything a send reads off `threadId`, in one read where the store allows it. */
|
|
464
|
+
private threadReads;
|
|
463
465
|
private threadDefaultAgent;
|
|
464
466
|
private toSummaryView;
|
|
465
467
|
private toDetailView;
|
package/dist/index.cjs
CHANGED
|
@@ -1676,6 +1676,26 @@ var RunNotActiveException = class extends import_common5.ConflictException {
|
|
|
1676
1676
|
}
|
|
1677
1677
|
};
|
|
1678
1678
|
|
|
1679
|
+
// src/thread-reads.ts
|
|
1680
|
+
function threadReads(store, threadId, separately) {
|
|
1681
|
+
const projecting = store;
|
|
1682
|
+
if (typeof projecting.threadSettings !== "function") {
|
|
1683
|
+
return separately;
|
|
1684
|
+
}
|
|
1685
|
+
let settings;
|
|
1686
|
+
const read = /* @__PURE__ */ __name(() => {
|
|
1687
|
+
settings ??= projecting.threadSettings?.(threadId) ?? Promise.resolve(null);
|
|
1688
|
+
return settings;
|
|
1689
|
+
}, "read");
|
|
1690
|
+
return {
|
|
1691
|
+
owner: /* @__PURE__ */ __name(async () => (await read())?.actorRef ?? null, "owner"),
|
|
1692
|
+
defaultAgent: /* @__PURE__ */ __name(async () => (await read())?.defaultAgent ?? null, "defaultAgent"),
|
|
1693
|
+
persona: /* @__PURE__ */ __name(async () => (await read())?.persona ?? null, "persona"),
|
|
1694
|
+
model: /* @__PURE__ */ __name(async () => (await read())?.model ?? null, "model")
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
__name(threadReads, "threadReads");
|
|
1698
|
+
|
|
1679
1699
|
// src/agent.service.ts
|
|
1680
1700
|
function _ts_decorate5(decorators, target, key, desc) {
|
|
1681
1701
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -1939,7 +1959,7 @@ var AgentService2 = class {
|
|
|
1939
1959
|
* else the thread's pinned one, else none. An agent the catalog locks to one model runs on it
|
|
1940
1960
|
* whatever was sent or pinned, and a send naming another model is refused.
|
|
1941
1961
|
*/
|
|
1942
|
-
async resolveModel(actor, agent, requested, threadId) {
|
|
1962
|
+
async resolveModel(actor, agent, requested, threadId, reads) {
|
|
1943
1963
|
const locked = this.models !== void 0 ? (await this.models.list({
|
|
1944
1964
|
actor,
|
|
1945
1965
|
agent
|
|
@@ -1950,7 +1970,7 @@ var AgentService2 = class {
|
|
|
1950
1970
|
}
|
|
1951
1971
|
return locked.model;
|
|
1952
1972
|
}
|
|
1953
|
-
const model = requested ?? (threadId !== void 0 ? await this.threadModel(threadId) : null);
|
|
1973
|
+
const model = requested ?? (threadId !== void 0 ? await (reads?.model() ?? this.threadModel(threadId)) : null);
|
|
1954
1974
|
if (model === null || model === void 0) {
|
|
1955
1975
|
return void 0;
|
|
1956
1976
|
}
|
|
@@ -2010,10 +2030,11 @@ var AgentService2 = class {
|
|
|
2010
2030
|
if ("proposalDecision" in decision) return decision;
|
|
2011
2031
|
}
|
|
2012
2032
|
await this.assertWithinQuota(params.actor);
|
|
2013
|
-
const
|
|
2033
|
+
const reads = params.threadId !== void 0 ? this.threadReads(params.threadId) : void 0;
|
|
2034
|
+
const target = this.deps.resolveAgent(await this.resolveAgentName(params.agentName, params.threadId, reads));
|
|
2014
2035
|
const agentName = target.agentName;
|
|
2015
|
-
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId);
|
|
2016
|
-
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
|
|
2036
|
+
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId, reads);
|
|
2037
|
+
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId, reads);
|
|
2017
2038
|
const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
|
|
2018
2039
|
let threadId = params.threadId;
|
|
2019
2040
|
if (threadId === void 0) {
|
|
@@ -2037,9 +2058,9 @@ var AgentService2 = class {
|
|
|
2037
2058
|
});
|
|
2038
2059
|
threadId = created.id;
|
|
2039
2060
|
} else {
|
|
2040
|
-
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId);
|
|
2061
|
+
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId, reads);
|
|
2041
2062
|
if (params.personaId !== void 0) {
|
|
2042
|
-
await this.pinThreadPersona(threadId, params.personaId);
|
|
2063
|
+
await this.pinThreadPersona(threadId, params.personaId, reads);
|
|
2043
2064
|
}
|
|
2044
2065
|
}
|
|
2045
2066
|
const input = {
|
|
@@ -2365,8 +2386,8 @@ var AgentService2 = class {
|
|
|
2365
2386
|
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
2366
2387
|
* actor's answer, tool arguments and tool results.
|
|
2367
2388
|
*/
|
|
2368
|
-
subscribe(runId) {
|
|
2369
|
-
return this.deps.forAgent().sink.subscribe(runId);
|
|
2389
|
+
subscribe(runId, options) {
|
|
2390
|
+
return this.deps.forAgent().sink.subscribe(runId, options);
|
|
2370
2391
|
}
|
|
2371
2392
|
/**
|
|
2372
2393
|
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
@@ -2374,9 +2395,9 @@ var AgentService2 = class {
|
|
|
2374
2395
|
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
2375
2396
|
* ownership is derived from.
|
|
2376
2397
|
*/
|
|
2377
|
-
async subscribeAs(actor, runId) {
|
|
2398
|
+
async subscribeAs(actor, runId, options) {
|
|
2378
2399
|
await this.assertOwnsActiveStream(actor, runId);
|
|
2379
|
-
return this.subscribe(runId);
|
|
2400
|
+
return this.subscribe(runId, options);
|
|
2380
2401
|
}
|
|
2381
2402
|
/**
|
|
2382
2403
|
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
@@ -2583,7 +2604,7 @@ var AgentService2 = class {
|
|
|
2583
2604
|
* The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
|
|
2584
2605
|
* pinned persona is read only when the send names none AND the agent has personas to pick from.
|
|
2585
2606
|
*/
|
|
2586
|
-
async resolvePersona(agentName, requested, threadId) {
|
|
2607
|
+
async resolvePersona(agentName, requested, threadId, reads) {
|
|
2587
2608
|
const needsThread = requested === void 0 && threadId !== void 0 && this.deps.personaCatalog(agentName).length > 0;
|
|
2588
2609
|
return this.deps.resolvePersona({
|
|
2589
2610
|
agentName,
|
|
@@ -2591,16 +2612,16 @@ var AgentService2 = class {
|
|
|
2591
2612
|
requested
|
|
2592
2613
|
} : {},
|
|
2593
2614
|
...needsThread ? {
|
|
2594
|
-
threadPersona: await threadPersona(this.store, threadId)
|
|
2615
|
+
threadPersona: await (reads?.persona() ?? threadPersona(this.store, threadId))
|
|
2595
2616
|
} : {}
|
|
2596
2617
|
});
|
|
2597
2618
|
}
|
|
2598
2619
|
/** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
|
|
2599
|
-
async pinThreadPersona(threadId, persona) {
|
|
2620
|
+
async pinThreadPersona(threadId, persona, reads) {
|
|
2600
2621
|
if (this.store.updateThread === void 0) {
|
|
2601
2622
|
return;
|
|
2602
2623
|
}
|
|
2603
|
-
if (await threadPersona(this.store, threadId) !== persona) {
|
|
2624
|
+
if (await (reads?.persona() ?? threadPersona(this.store, threadId)) !== persona) {
|
|
2604
2625
|
await this.store.updateThread(threadId, {
|
|
2605
2626
|
persona
|
|
2606
2627
|
});
|
|
@@ -2627,18 +2648,27 @@ var AgentService2 = class {
|
|
|
2627
2648
|
* workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
|
|
2628
2649
|
* the full read.
|
|
2629
2650
|
*/
|
|
2630
|
-
async resolveAgentName(agentName, threadId) {
|
|
2651
|
+
async resolveAgentName(agentName, threadId, reads) {
|
|
2631
2652
|
if (agentName !== void 0) {
|
|
2632
2653
|
return agentName;
|
|
2633
2654
|
}
|
|
2634
2655
|
if (threadId !== void 0) {
|
|
2635
|
-
const threadDefault = await this.threadDefaultAgent(threadId);
|
|
2656
|
+
const threadDefault = await (reads?.defaultAgent() ?? this.threadDefaultAgent(threadId));
|
|
2636
2657
|
if (threadDefault !== null) {
|
|
2637
2658
|
return threadDefault;
|
|
2638
2659
|
}
|
|
2639
2660
|
}
|
|
2640
2661
|
return this.deps.defaultAgentName();
|
|
2641
2662
|
}
|
|
2663
|
+
/** Everything a send reads off `threadId`, in one read where the store allows it. */
|
|
2664
|
+
threadReads(threadId) {
|
|
2665
|
+
return threadReads(this.store, threadId, {
|
|
2666
|
+
owner: /* @__PURE__ */ __name(() => this.store.ownerOfThread(threadId), "owner"),
|
|
2667
|
+
defaultAgent: /* @__PURE__ */ __name(() => this.threadDefaultAgent(threadId), "defaultAgent"),
|
|
2668
|
+
persona: /* @__PURE__ */ __name(() => threadPersona(this.store, threadId), "persona"),
|
|
2669
|
+
model: /* @__PURE__ */ __name(() => this.threadModel(threadId), "model")
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2642
2672
|
async threadDefaultAgent(threadId) {
|
|
2643
2673
|
const projecting = this.store;
|
|
2644
2674
|
if (typeof projecting.defaultAgentForThread === "function") {
|
|
@@ -2808,8 +2838,8 @@ var AgentService2 = class {
|
|
|
2808
2838
|
* Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
|
|
2809
2839
|
* thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
|
|
2810
2840
|
*/
|
|
2811
|
-
async assertOwnsThread(actor, threadId) {
|
|
2812
|
-
const owner = await this.store.ownerOfThread(threadId);
|
|
2841
|
+
async assertOwnsThread(actor, threadId, reads) {
|
|
2842
|
+
const owner = await (reads?.owner() ?? this.store.ownerOfThread(threadId));
|
|
2813
2843
|
if (owner === null) {
|
|
2814
2844
|
throw new import_common6.NotFoundException(`thread ${threadId} not found`);
|
|
2815
2845
|
}
|
|
@@ -3249,6 +3279,18 @@ AttachmentsController = _ts_decorate8([
|
|
|
3249
3279
|
var import_nestjs_agent_core12 = require("@dudousxd/nestjs-agent-core");
|
|
3250
3280
|
var import_genui2 = require("@dudousxd/nestjs-agent-core/genui");
|
|
3251
3281
|
var import_common10 = require("@nestjs/common");
|
|
3282
|
+
|
|
3283
|
+
// src/controller/reader-gone.ts
|
|
3284
|
+
function readerGone(res) {
|
|
3285
|
+
const controller = new AbortController();
|
|
3286
|
+
res.once("close", () => controller.abort());
|
|
3287
|
+
return {
|
|
3288
|
+
signal: controller.signal
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
__name(readerGone, "readerGone");
|
|
3292
|
+
|
|
3293
|
+
// src/controller/chat.controller.ts
|
|
3252
3294
|
function _ts_decorate9(decorators, target, key, desc) {
|
|
3253
3295
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3254
3296
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -3382,7 +3424,8 @@ var ChatController = class {
|
|
|
3382
3424
|
return;
|
|
3383
3425
|
}
|
|
3384
3426
|
const { runId, threadId } = result;
|
|
3385
|
-
|
|
3427
|
+
const gone = readerGone(res);
|
|
3428
|
+
await this.pipe(res, runId, this.agent.subscribe(runId, gone), threadId, 0, gone.signal);
|
|
3386
3429
|
}
|
|
3387
3430
|
/**
|
|
3388
3431
|
* Attach to a run's stream: every buffered frame, then live ones. `?after=<seq>` (or the SSE
|
|
@@ -3392,7 +3435,8 @@ var ChatController = class {
|
|
|
3392
3435
|
async stream(req, runId, res, after) {
|
|
3393
3436
|
const actor = await this.actorResolver.resolve(req);
|
|
3394
3437
|
const cursor = parseCursor(after) ?? parseCursor(req.headers["last-event-id"]) ?? 0;
|
|
3395
|
-
|
|
3438
|
+
const gone = readerGone(res);
|
|
3439
|
+
await this.pipe(res, runId, await this.agent.subscribeAs(actor, runId, gone), void 0, cursor, gone.signal);
|
|
3396
3440
|
}
|
|
3397
3441
|
async cancel(req, runId) {
|
|
3398
3442
|
const actor = await this.actorResolver.resolve(req);
|
|
@@ -3408,8 +3452,11 @@ var ChatController = class {
|
|
|
3408
3452
|
* and on any later `GET …/stream`, whichever replica serves it. A reconnecting client passes the
|
|
3409
3453
|
* last one it saw as `?after=`; frames at or below it are read off the sink but not re-sent.
|
|
3410
3454
|
* `meta`, `done` and `error` carry no id: `meta` is re-sent on every attach, the terminals once.
|
|
3455
|
+
*
|
|
3456
|
+
* A reader that disconnects aborts `gone`, which the sink honours by ending the subscription even
|
|
3457
|
+
* while the run is silent (parked on a person, or never writing again).
|
|
3411
3458
|
*/
|
|
3412
|
-
async pipe(res, runId, events, threadId, after = 0) {
|
|
3459
|
+
async pipe(res, runId, events, threadId, after = 0, gone) {
|
|
3413
3460
|
res.setHeader("Content-Type", "text/event-stream");
|
|
3414
3461
|
res.setHeader("Cache-Control", "no-cache");
|
|
3415
3462
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -3438,6 +3485,9 @@ data: ${line}
|
|
|
3438
3485
|
}, "forward");
|
|
3439
3486
|
try {
|
|
3440
3487
|
for await (const chunk of events) {
|
|
3488
|
+
if (gone?.aborted) {
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3441
3491
|
buffer += decoder.decode(chunk, {
|
|
3442
3492
|
stream: true
|
|
3443
3493
|
});
|
|
@@ -3451,11 +3501,17 @@ data: ${line}
|
|
|
3451
3501
|
newline = buffer.indexOf("\n");
|
|
3452
3502
|
}
|
|
3453
3503
|
}
|
|
3504
|
+
if (gone?.aborted) {
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3454
3507
|
if (buffer.length > 0) {
|
|
3455
3508
|
forward(buffer);
|
|
3456
3509
|
}
|
|
3457
3510
|
res.write("event: done\ndata: {}\n\n");
|
|
3458
3511
|
} catch (error) {
|
|
3512
|
+
if (gone?.aborted) {
|
|
3513
|
+
return;
|
|
3514
|
+
}
|
|
3459
3515
|
const payload = error instanceof import_nestjs_agent_core12.AgentStreamError ? {
|
|
3460
3516
|
code: error.code,
|
|
3461
3517
|
message: error.message
|
|
@@ -5385,49 +5441,121 @@ SkillDiscoveryService = _ts_decorate22([
|
|
|
5385
5441
|
|
|
5386
5442
|
// src/in-process-sink.ts
|
|
5387
5443
|
var import_nestjs_agent_core27 = require("@dudousxd/nestjs-agent-core");
|
|
5444
|
+
var LIVE_TTL_MS = 36e5;
|
|
5445
|
+
var ENDED_GRACE_MS = 6e4;
|
|
5388
5446
|
var InProcessTokenStreamSink = class {
|
|
5389
5447
|
static {
|
|
5390
5448
|
__name(this, "InProcessTokenStreamSink");
|
|
5391
5449
|
}
|
|
5392
5450
|
runs = /* @__PURE__ */ new Map();
|
|
5451
|
+
/** Subscribers of a run nothing has opened yet, woken by its `open()`. */
|
|
5452
|
+
waiting = /* @__PURE__ */ new Map();
|
|
5393
5453
|
buffer(runId) {
|
|
5394
5454
|
let buf = this.runs.get(runId);
|
|
5395
5455
|
if (buf === void 0) {
|
|
5396
5456
|
buf = {
|
|
5397
5457
|
chunks: [],
|
|
5398
5458
|
ended: false,
|
|
5399
|
-
notify: /* @__PURE__ */ new Set()
|
|
5459
|
+
notify: /* @__PURE__ */ new Set(),
|
|
5460
|
+
lastWriteAt: Date.now(),
|
|
5461
|
+
expiry: void 0
|
|
5400
5462
|
};
|
|
5401
5463
|
this.runs.set(runId, buf);
|
|
5464
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS);
|
|
5465
|
+
const waiters = this.waiting.get(runId);
|
|
5466
|
+
this.waiting.delete(runId);
|
|
5467
|
+
for (const resolve2 of waiters ?? []) {
|
|
5468
|
+
resolve2();
|
|
5469
|
+
}
|
|
5402
5470
|
}
|
|
5403
5471
|
return buf;
|
|
5404
5472
|
}
|
|
5473
|
+
expireAfter(runId, buf, ms) {
|
|
5474
|
+
if (buf.expiry !== void 0) {
|
|
5475
|
+
clearTimeout(buf.expiry);
|
|
5476
|
+
}
|
|
5477
|
+
buf.expiry = setTimeout(() => {
|
|
5478
|
+
buf.expiry = void 0;
|
|
5479
|
+
const idle = Date.now() - buf.lastWriteAt;
|
|
5480
|
+
if (!buf.ended && idle < LIVE_TTL_MS) {
|
|
5481
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS - idle);
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5484
|
+
this.drop(runId, buf);
|
|
5485
|
+
}, ms);
|
|
5486
|
+
buf.expiry.unref?.();
|
|
5487
|
+
}
|
|
5488
|
+
/** Forget the run, and let anyone still following it finish as if it had ended. */
|
|
5489
|
+
drop(runId, buf) {
|
|
5490
|
+
if (buf.expiry !== void 0) {
|
|
5491
|
+
clearTimeout(buf.expiry);
|
|
5492
|
+
buf.expiry = void 0;
|
|
5493
|
+
}
|
|
5494
|
+
if (this.runs.get(runId) === buf) {
|
|
5495
|
+
this.runs.delete(runId);
|
|
5496
|
+
}
|
|
5497
|
+
buf.ended = true;
|
|
5498
|
+
this.wake(buf);
|
|
5499
|
+
}
|
|
5405
5500
|
wake(buf) {
|
|
5406
5501
|
for (const resolve2 of buf.notify) {
|
|
5407
5502
|
resolve2();
|
|
5408
5503
|
}
|
|
5409
5504
|
buf.notify.clear();
|
|
5410
5505
|
}
|
|
5506
|
+
finish(runId, buf) {
|
|
5507
|
+
buf.ended = true;
|
|
5508
|
+
this.wake(buf);
|
|
5509
|
+
this.expireAfter(runId, buf, ENDED_GRACE_MS);
|
|
5510
|
+
}
|
|
5511
|
+
/**
|
|
5512
|
+
* The run's writer. Each call finds the run's buffer afresh, so a run that went quiet past the
|
|
5513
|
+
* live TTL (parked on a person) and then writes again starts a new buffer its next subscriber
|
|
5514
|
+
* follows, rather than writing into one nothing reads.
|
|
5515
|
+
*/
|
|
5411
5516
|
open(runId) {
|
|
5412
|
-
|
|
5517
|
+
this.buffer(runId);
|
|
5413
5518
|
return {
|
|
5414
5519
|
write: /* @__PURE__ */ __name((chunk) => {
|
|
5520
|
+
const buf = this.buffer(runId);
|
|
5415
5521
|
buf.chunks.push(chunk);
|
|
5522
|
+
buf.lastWriteAt = Date.now();
|
|
5416
5523
|
this.wake(buf);
|
|
5417
5524
|
}, "write"),
|
|
5418
5525
|
end: /* @__PURE__ */ __name(() => {
|
|
5419
|
-
|
|
5420
|
-
this.wake(buf);
|
|
5526
|
+
this.finish(runId, this.buffer(runId));
|
|
5421
5527
|
}, "end"),
|
|
5422
5528
|
fail: /* @__PURE__ */ __name((error) => {
|
|
5529
|
+
const buf = this.buffer(runId);
|
|
5423
5530
|
buf.failure = error;
|
|
5424
|
-
|
|
5425
|
-
this.wake(buf);
|
|
5531
|
+
this.finish(runId, buf);
|
|
5426
5532
|
}, "fail")
|
|
5427
5533
|
};
|
|
5428
5534
|
}
|
|
5429
|
-
async *subscribe(runId) {
|
|
5430
|
-
const
|
|
5535
|
+
async *subscribe(runId, options = {}) {
|
|
5536
|
+
const { signal } = options;
|
|
5537
|
+
let buf = this.runs.get(runId);
|
|
5538
|
+
while (buf === void 0) {
|
|
5539
|
+
const waiters = this.waiting.get(runId) ?? /* @__PURE__ */ new Set();
|
|
5540
|
+
this.waiting.set(runId, waiters);
|
|
5541
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
5542
|
+
}, "resolve");
|
|
5543
|
+
const opened = new Promise((r) => {
|
|
5544
|
+
resolve2 = r;
|
|
5545
|
+
});
|
|
5546
|
+
waiters.add(resolve2);
|
|
5547
|
+
try {
|
|
5548
|
+
if (!await untilAborted(opened, signal)) {
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5551
|
+
} finally {
|
|
5552
|
+
waiters.delete(resolve2);
|
|
5553
|
+
if (waiters.size === 0 && this.waiting.get(runId) === waiters) {
|
|
5554
|
+
this.waiting.delete(runId);
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
buf = this.runs.get(runId);
|
|
5558
|
+
}
|
|
5431
5559
|
let index = 0;
|
|
5432
5560
|
while (true) {
|
|
5433
5561
|
while (index < buf.chunks.length) {
|
|
@@ -5443,13 +5571,56 @@ var InProcessTokenStreamSink = class {
|
|
|
5443
5571
|
}
|
|
5444
5572
|
return;
|
|
5445
5573
|
}
|
|
5446
|
-
|
|
5574
|
+
const current = buf;
|
|
5575
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
5576
|
+
}, "resolve");
|
|
5577
|
+
const woken = new Promise((r) => {
|
|
5578
|
+
resolve2 = r;
|
|
5579
|
+
});
|
|
5580
|
+
current.notify.add(resolve2);
|
|
5581
|
+
try {
|
|
5582
|
+
if (!await untilAborted(woken, signal)) {
|
|
5583
|
+
return;
|
|
5584
|
+
}
|
|
5585
|
+
} finally {
|
|
5586
|
+
current.notify.delete(resolve2);
|
|
5587
|
+
}
|
|
5447
5588
|
}
|
|
5448
5589
|
}
|
|
5449
5590
|
close(runId) {
|
|
5591
|
+
const buf = this.runs.get(runId);
|
|
5592
|
+
if (buf?.expiry !== void 0) {
|
|
5593
|
+
clearTimeout(buf.expiry);
|
|
5594
|
+
}
|
|
5450
5595
|
this.runs.delete(runId);
|
|
5451
5596
|
}
|
|
5452
5597
|
};
|
|
5598
|
+
async function untilAborted(promise, signal) {
|
|
5599
|
+
if (signal === void 0) {
|
|
5600
|
+
await promise;
|
|
5601
|
+
return true;
|
|
5602
|
+
}
|
|
5603
|
+
if (signal.aborted) {
|
|
5604
|
+
return false;
|
|
5605
|
+
}
|
|
5606
|
+
let onAbort = /* @__PURE__ */ __name(() => {
|
|
5607
|
+
}, "onAbort");
|
|
5608
|
+
const aborted = new Promise((resolve2) => {
|
|
5609
|
+
onAbort = /* @__PURE__ */ __name(() => resolve2(false), "onAbort");
|
|
5610
|
+
signal.addEventListener("abort", onAbort, {
|
|
5611
|
+
once: true
|
|
5612
|
+
});
|
|
5613
|
+
});
|
|
5614
|
+
try {
|
|
5615
|
+
return await Promise.race([
|
|
5616
|
+
promise.then(() => true),
|
|
5617
|
+
aborted
|
|
5618
|
+
]);
|
|
5619
|
+
} finally {
|
|
5620
|
+
signal.removeEventListener("abort", onAbort);
|
|
5621
|
+
}
|
|
5622
|
+
}
|
|
5623
|
+
__name(untilAborted, "untilAborted");
|
|
5453
5624
|
|
|
5454
5625
|
// src/ledger-quota-provider.ts
|
|
5455
5626
|
var import_nestjs_agent_core28 = require("@dudousxd/nestjs-agent-core");
|
|
@@ -5468,13 +5639,22 @@ var LedgerQuotaProvider = class {
|
|
|
5468
5639
|
async report(query) {
|
|
5469
5640
|
const now = query.now ?? /* @__PURE__ */ new Date();
|
|
5470
5641
|
const actorRef = query.actor.id;
|
|
5471
|
-
const
|
|
5472
|
-
|
|
5473
|
-
];
|
|
5474
|
-
if (this.store.
|
|
5475
|
-
const
|
|
5476
|
-
|
|
5477
|
-
|
|
5642
|
+
const day = (0, import_nestjs_agent_core28.quotaPeriodRange)("day", now);
|
|
5643
|
+
const month = (0, import_nestjs_agent_core28.quotaPeriodRange)("month", now);
|
|
5644
|
+
const windows = [];
|
|
5645
|
+
if (this.store.usageWindows !== void 0) {
|
|
5646
|
+
const [dayUsed, monthUsed] = await this.store.usageWindows(actorRef, [
|
|
5647
|
+
day,
|
|
5648
|
+
month
|
|
5649
|
+
]);
|
|
5650
|
+
windows.push(this.usedWindow("day", dayUsed, day.resetsAt));
|
|
5651
|
+
windows.push(this.usedWindow("month", monthUsed, month.resetsAt));
|
|
5652
|
+
} else {
|
|
5653
|
+
windows.push(this.usedWindow("day", await this.store.quotaToday(actorRef, day.fromDay), day.resetsAt));
|
|
5654
|
+
if (this.store.usageBetween !== void 0) {
|
|
5655
|
+
const used = await this.store.usageBetween(actorRef, month.fromDay, month.toDay);
|
|
5656
|
+
windows.push(this.usedWindow("month", used, month.resetsAt));
|
|
5657
|
+
}
|
|
5478
5658
|
}
|
|
5479
5659
|
const blocked = (0, import_nestjs_agent_core28.exhaustedWindow)(windows);
|
|
5480
5660
|
const warning = (0, import_nestjs_agent_core28.quotaWarning)(windows);
|
|
@@ -5488,10 +5668,8 @@ var LedgerQuotaProvider = class {
|
|
|
5488
5668
|
} : {}
|
|
5489
5669
|
};
|
|
5490
5670
|
}
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
const used = await this.store.quotaToday(actorRef, range.fromDay);
|
|
5494
|
-
return this.window("day", used.usedTokens, this.usd(used), range.resetsAt);
|
|
5671
|
+
usedWindow(period, used, resetsAt) {
|
|
5672
|
+
return this.window(period, used?.usedTokens ?? 0, used === void 0 ? 0 : this.usd(used), resetsAt);
|
|
5495
5673
|
}
|
|
5496
5674
|
/** The window's USD: every priced row, or (with `countEstimatedCost: false`) reported cost only. */
|
|
5497
5675
|
usd(used) {
|
|
@@ -7336,7 +7514,7 @@ var AgUiRunHandler = class {
|
|
|
7336
7514
|
seq: 0
|
|
7337
7515
|
};
|
|
7338
7516
|
try {
|
|
7339
|
-
const frames = (0, import_ag_ui.agUiFramesFromNdjson)(this.agent.subscribe(runId));
|
|
7517
|
+
const frames = (0, import_ag_ui.agUiFramesFromNdjson)(this.agent.subscribe(runId, readerGone(res)));
|
|
7340
7518
|
for await (const event of (0, import_ag_ui.agUiEvents)(frames, {
|
|
7341
7519
|
...options,
|
|
7342
7520
|
cursor
|
|
@@ -7667,7 +7845,7 @@ var A2uiRunHandler = class {
|
|
|
7667
7845
|
`);
|
|
7668
7846
|
}, "write");
|
|
7669
7847
|
try {
|
|
7670
|
-
const frames = (0, import_ag_ui2.agUiFramesFromNdjson)(this.agent.subscribe(runId));
|
|
7848
|
+
const frames = (0, import_ag_ui2.agUiFramesFromNdjson)(this.agent.subscribe(runId, readerGone(res)));
|
|
7671
7849
|
for await (const event of (0, import_ag_ui2.agUiEvents)(frames, {
|
|
7672
7850
|
threadId: options.threadId ?? runId,
|
|
7673
7851
|
runId,
|