@dudousxd/nestjs-agent 1.36.0 → 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 +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { DynamicModule, Type, OnApplicationBootstrap, OnModuleInit, ConflictException, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
-
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, d as AgentProtocolAdapter, A as AgentDepsFactory, e as AgentSkillsOptions, C as ChatQueueService } from './chat-queue.service-
|
|
3
|
-
export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-
|
|
2
|
+
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, d as AgentProtocolAdapter, A as AgentDepsFactory, e as AgentSkillsOptions, C as ChatQueueService } from './chat-queue.service-fopUN313.cjs';
|
|
3
|
+
export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-fopUN313.cjs';
|
|
4
4
|
import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
|
|
5
|
-
import { ActorResolver, AttachmentStagingStore, AgentApprovalPort, Actor, AgentStore, StagedAttachment, MessageAttachment, ModelCatalog, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MessageFeedbackValue, MessageFeedback, ModelCatalogView, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply, ChatQueueState, TokenStreamSink, SinkWriter, ActionProposalOutcome, ActionProposal } from '@dudousxd/nestjs-agent-core';
|
|
5
|
+
import { ActorResolver, AttachmentStagingStore, AgentApprovalPort, Actor, AgentStore, StagedAttachment, MessageAttachment, ModelCatalog, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MessageFeedbackValue, MessageFeedback, ModelCatalogView, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply, ChatQueueState, TokenStreamSink, SinkWriter, SubscribeOptions, ActionProposalOutcome, ActionProposal } from '@dudousxd/nestjs-agent-core';
|
|
6
6
|
export * from '@dudousxd/nestjs-agent-core';
|
|
7
7
|
export { FunctionalToolDefinition } from '@dudousxd/nestjs-agent-core';
|
|
8
8
|
import { A2uiOptions } from '@dudousxd/nestjs-agent-core/a2ui';
|
|
9
9
|
import { Request, Response } from 'express';
|
|
10
|
-
import { A as AgentService, a as ActionProposalService } from './index-
|
|
11
|
-
export { b as A2uiAdapterOptions, c as A2uiRunHandler, d as AttachmentLimits, C as ChatParams, e as ChatSendMode, f as ChatSendResult, D as DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, g as DEFAULT_MAX_ATTACHMENT_BYTES, M as MAX_ATTACHMENTS_PER_MESSAGE, N as NO_USER_MESSAGE_CODE, Q as QueuedSend, T as ThreadDefaultAgentReader, h as ThreadModelReader, i as a2uiAdapter, j as attachmentLimits } from './index-
|
|
10
|
+
import { A as AgentService, a as ActionProposalService } from './index-CJdTomOT.cjs';
|
|
11
|
+
export { b as A2uiAdapterOptions, c as A2uiRunHandler, d as AttachmentLimits, C as ChatParams, e as ChatSendMode, f as ChatSendResult, D as DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, g as DEFAULT_MAX_ATTACHMENT_BYTES, M as MAX_ATTACHMENTS_PER_MESSAGE, N as NO_USER_MESSAGE_CODE, Q as QueuedSend, T as ThreadDefaultAgentReader, h as ThreadModelReader, i as a2uiAdapter, j as attachmentLimits } from './index-CJdTomOT.cjs';
|
|
12
12
|
import { AgentGenui } from './genui/index.cjs';
|
|
13
13
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
14
14
|
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
|
@@ -870,7 +870,8 @@ declare const AGENT_SERVICE: unique symbol;
|
|
|
870
870
|
|
|
871
871
|
/**
|
|
872
872
|
* The default `TokenStreamSink`: buffers chunks per run in-process so a reconnecting subscriber
|
|
873
|
-
* replays everything so far, then follows live.
|
|
873
|
+
* replays everything so far, then follows live. A run's buffer is dropped {@link ENDED_GRACE_MS}
|
|
874
|
+
* after it ends, or {@link LIVE_TTL_MS} after its last write if it never does.
|
|
874
875
|
*
|
|
875
876
|
* Single-replica only. Because the buffer lives in one process's memory, a resume/reconnect that
|
|
876
877
|
* lands on a different replica than the one running the turn finds an empty buffer — the stream
|
|
@@ -879,10 +880,21 @@ declare const AGENT_SERVICE: unique symbol;
|
|
|
879
880
|
*/
|
|
880
881
|
declare class InProcessTokenStreamSink implements TokenStreamSink {
|
|
881
882
|
private readonly runs;
|
|
883
|
+
/** Subscribers of a run nothing has opened yet, woken by its `open()`. */
|
|
884
|
+
private readonly waiting;
|
|
882
885
|
private buffer;
|
|
886
|
+
private expireAfter;
|
|
887
|
+
/** Forget the run, and let anyone still following it finish as if it had ended. */
|
|
888
|
+
private drop;
|
|
883
889
|
private wake;
|
|
890
|
+
private finish;
|
|
891
|
+
/**
|
|
892
|
+
* The run's writer. Each call finds the run's buffer afresh, so a run that went quiet past the
|
|
893
|
+
* live TTL (parked on a person) and then writes again starts a new buffer its next subscriber
|
|
894
|
+
* follows, rather than writing into one nothing reads.
|
|
895
|
+
*/
|
|
884
896
|
open(runId: string): SinkWriter;
|
|
885
|
-
subscribe(runId: string): AsyncIterable<Uint8Array>;
|
|
897
|
+
subscribe(runId: string, options?: SubscribeOptions): AsyncIterable<Uint8Array>;
|
|
886
898
|
close(runId: string): void;
|
|
887
899
|
}
|
|
888
900
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { DynamicModule, Type, OnApplicationBootstrap, OnModuleInit, ConflictException, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
-
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, d as AgentProtocolAdapter, A as AgentDepsFactory, e as AgentSkillsOptions, C as ChatQueueService } from './chat-queue.service-
|
|
3
|
-
export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-
|
|
2
|
+
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, d as AgentProtocolAdapter, A as AgentDepsFactory, e as AgentSkillsOptions, C as ChatQueueService } from './chat-queue.service-YjiKVTKE.js';
|
|
3
|
+
export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-YjiKVTKE.js';
|
|
4
4
|
import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
|
|
5
|
-
import { ActorResolver, AttachmentStagingStore, AgentApprovalPort, Actor, AgentStore, StagedAttachment, MessageAttachment, ModelCatalog, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MessageFeedbackValue, MessageFeedback, ModelCatalogView, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply, ChatQueueState, TokenStreamSink, SinkWriter, ActionProposalOutcome, ActionProposal } from '@dudousxd/nestjs-agent-core';
|
|
5
|
+
import { ActorResolver, AttachmentStagingStore, AgentApprovalPort, Actor, AgentStore, StagedAttachment, MessageAttachment, ModelCatalog, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MessageFeedbackValue, MessageFeedback, ModelCatalogView, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply, ChatQueueState, TokenStreamSink, SinkWriter, SubscribeOptions, ActionProposalOutcome, ActionProposal } from '@dudousxd/nestjs-agent-core';
|
|
6
6
|
export * from '@dudousxd/nestjs-agent-core';
|
|
7
7
|
export { FunctionalToolDefinition } from '@dudousxd/nestjs-agent-core';
|
|
8
8
|
import { A2uiOptions } from '@dudousxd/nestjs-agent-core/a2ui';
|
|
9
9
|
import { Request, Response } from 'express';
|
|
10
|
-
import { A as AgentService, a as ActionProposalService } from './index-
|
|
11
|
-
export { b as A2uiAdapterOptions, c as A2uiRunHandler, d as AttachmentLimits, C as ChatParams, e as ChatSendMode, f as ChatSendResult, D as DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, g as DEFAULT_MAX_ATTACHMENT_BYTES, M as MAX_ATTACHMENTS_PER_MESSAGE, N as NO_USER_MESSAGE_CODE, Q as QueuedSend, T as ThreadDefaultAgentReader, h as ThreadModelReader, i as a2uiAdapter, j as attachmentLimits } from './index-
|
|
10
|
+
import { A as AgentService, a as ActionProposalService } from './index-D1xRrnCq.js';
|
|
11
|
+
export { b as A2uiAdapterOptions, c as A2uiRunHandler, d as AttachmentLimits, C as ChatParams, e as ChatSendMode, f as ChatSendResult, D as DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, g as DEFAULT_MAX_ATTACHMENT_BYTES, M as MAX_ATTACHMENTS_PER_MESSAGE, N as NO_USER_MESSAGE_CODE, Q as QueuedSend, T as ThreadDefaultAgentReader, h as ThreadModelReader, i as a2uiAdapter, j as attachmentLimits } from './index-D1xRrnCq.js';
|
|
12
12
|
import { AgentGenui } from './genui/index.js';
|
|
13
13
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
14
14
|
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
|
@@ -870,7 +870,8 @@ declare const AGENT_SERVICE: unique symbol;
|
|
|
870
870
|
|
|
871
871
|
/**
|
|
872
872
|
* The default `TokenStreamSink`: buffers chunks per run in-process so a reconnecting subscriber
|
|
873
|
-
* replays everything so far, then follows live.
|
|
873
|
+
* replays everything so far, then follows live. A run's buffer is dropped {@link ENDED_GRACE_MS}
|
|
874
|
+
* after it ends, or {@link LIVE_TTL_MS} after its last write if it never does.
|
|
874
875
|
*
|
|
875
876
|
* Single-replica only. Because the buffer lives in one process's memory, a resume/reconnect that
|
|
876
877
|
* lands on a different replica than the one running the turn finds an empty buffer — the stream
|
|
@@ -879,10 +880,21 @@ declare const AGENT_SERVICE: unique symbol;
|
|
|
879
880
|
*/
|
|
880
881
|
declare class InProcessTokenStreamSink implements TokenStreamSink {
|
|
881
882
|
private readonly runs;
|
|
883
|
+
/** Subscribers of a run nothing has opened yet, woken by its `open()`. */
|
|
884
|
+
private readonly waiting;
|
|
882
885
|
private buffer;
|
|
886
|
+
private expireAfter;
|
|
887
|
+
/** Forget the run, and let anyone still following it finish as if it had ended. */
|
|
888
|
+
private drop;
|
|
883
889
|
private wake;
|
|
890
|
+
private finish;
|
|
891
|
+
/**
|
|
892
|
+
* The run's writer. Each call finds the run's buffer afresh, so a run that went quiet past the
|
|
893
|
+
* live TTL (parked on a person) and then writes again starts a new buffer its next subscriber
|
|
894
|
+
* follows, rather than writing into one nothing reads.
|
|
895
|
+
*/
|
|
884
896
|
open(runId: string): SinkWriter;
|
|
885
|
-
subscribe(runId: string): AsyncIterable<Uint8Array>;
|
|
897
|
+
subscribe(runId: string, options?: SubscribeOptions): AsyncIterable<Uint8Array>;
|
|
886
898
|
close(runId: string): void;
|
|
887
899
|
}
|
|
888
900
|
|
package/dist/index.js
CHANGED
|
@@ -1584,6 +1584,26 @@ var RunNotActiveException = class extends ConflictException {
|
|
|
1584
1584
|
}
|
|
1585
1585
|
};
|
|
1586
1586
|
|
|
1587
|
+
// src/thread-reads.ts
|
|
1588
|
+
function threadReads(store, threadId, separately) {
|
|
1589
|
+
const projecting = store;
|
|
1590
|
+
if (typeof projecting.threadSettings !== "function") {
|
|
1591
|
+
return separately;
|
|
1592
|
+
}
|
|
1593
|
+
let settings;
|
|
1594
|
+
const read = /* @__PURE__ */ __name(() => {
|
|
1595
|
+
settings ??= projecting.threadSettings?.(threadId) ?? Promise.resolve(null);
|
|
1596
|
+
return settings;
|
|
1597
|
+
}, "read");
|
|
1598
|
+
return {
|
|
1599
|
+
owner: /* @__PURE__ */ __name(async () => (await read())?.actorRef ?? null, "owner"),
|
|
1600
|
+
defaultAgent: /* @__PURE__ */ __name(async () => (await read())?.defaultAgent ?? null, "defaultAgent"),
|
|
1601
|
+
persona: /* @__PURE__ */ __name(async () => (await read())?.persona ?? null, "persona"),
|
|
1602
|
+
model: /* @__PURE__ */ __name(async () => (await read())?.model ?? null, "model")
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
__name(threadReads, "threadReads");
|
|
1606
|
+
|
|
1587
1607
|
// src/agent.service.ts
|
|
1588
1608
|
function _ts_decorate5(decorators, target, key, desc) {
|
|
1589
1609
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -1847,7 +1867,7 @@ var AgentService2 = class {
|
|
|
1847
1867
|
* else the thread's pinned one, else none. An agent the catalog locks to one model runs on it
|
|
1848
1868
|
* whatever was sent or pinned, and a send naming another model is refused.
|
|
1849
1869
|
*/
|
|
1850
|
-
async resolveModel(actor, agent, requested, threadId) {
|
|
1870
|
+
async resolveModel(actor, agent, requested, threadId, reads) {
|
|
1851
1871
|
const locked = this.models !== void 0 ? (await this.models.list({
|
|
1852
1872
|
actor,
|
|
1853
1873
|
agent
|
|
@@ -1858,7 +1878,7 @@ var AgentService2 = class {
|
|
|
1858
1878
|
}
|
|
1859
1879
|
return locked.model;
|
|
1860
1880
|
}
|
|
1861
|
-
const model = requested ?? (threadId !== void 0 ? await this.threadModel(threadId) : null);
|
|
1881
|
+
const model = requested ?? (threadId !== void 0 ? await (reads?.model() ?? this.threadModel(threadId)) : null);
|
|
1862
1882
|
if (model === null || model === void 0) {
|
|
1863
1883
|
return void 0;
|
|
1864
1884
|
}
|
|
@@ -1918,10 +1938,11 @@ var AgentService2 = class {
|
|
|
1918
1938
|
if ("proposalDecision" in decision) return decision;
|
|
1919
1939
|
}
|
|
1920
1940
|
await this.assertWithinQuota(params.actor);
|
|
1921
|
-
const
|
|
1941
|
+
const reads = params.threadId !== void 0 ? this.threadReads(params.threadId) : void 0;
|
|
1942
|
+
const target = this.deps.resolveAgent(await this.resolveAgentName(params.agentName, params.threadId, reads));
|
|
1922
1943
|
const agentName = target.agentName;
|
|
1923
|
-
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId);
|
|
1924
|
-
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
|
|
1944
|
+
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId, reads);
|
|
1945
|
+
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId, reads);
|
|
1925
1946
|
const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
|
|
1926
1947
|
let threadId = params.threadId;
|
|
1927
1948
|
if (threadId === void 0) {
|
|
@@ -1945,9 +1966,9 @@ var AgentService2 = class {
|
|
|
1945
1966
|
});
|
|
1946
1967
|
threadId = created.id;
|
|
1947
1968
|
} else {
|
|
1948
|
-
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId);
|
|
1969
|
+
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId, reads);
|
|
1949
1970
|
if (params.personaId !== void 0) {
|
|
1950
|
-
await this.pinThreadPersona(threadId, params.personaId);
|
|
1971
|
+
await this.pinThreadPersona(threadId, params.personaId, reads);
|
|
1951
1972
|
}
|
|
1952
1973
|
}
|
|
1953
1974
|
const input = {
|
|
@@ -2273,8 +2294,8 @@ var AgentService2 = class {
|
|
|
2273
2294
|
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
2274
2295
|
* actor's answer, tool arguments and tool results.
|
|
2275
2296
|
*/
|
|
2276
|
-
subscribe(runId) {
|
|
2277
|
-
return this.deps.forAgent().sink.subscribe(runId);
|
|
2297
|
+
subscribe(runId, options) {
|
|
2298
|
+
return this.deps.forAgent().sink.subscribe(runId, options);
|
|
2278
2299
|
}
|
|
2279
2300
|
/**
|
|
2280
2301
|
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
@@ -2282,9 +2303,9 @@ var AgentService2 = class {
|
|
|
2282
2303
|
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
2283
2304
|
* ownership is derived from.
|
|
2284
2305
|
*/
|
|
2285
|
-
async subscribeAs(actor, runId) {
|
|
2306
|
+
async subscribeAs(actor, runId, options) {
|
|
2286
2307
|
await this.assertOwnsActiveStream(actor, runId);
|
|
2287
|
-
return this.subscribe(runId);
|
|
2308
|
+
return this.subscribe(runId, options);
|
|
2288
2309
|
}
|
|
2289
2310
|
/**
|
|
2290
2311
|
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
@@ -2491,7 +2512,7 @@ var AgentService2 = class {
|
|
|
2491
2512
|
* The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
|
|
2492
2513
|
* pinned persona is read only when the send names none AND the agent has personas to pick from.
|
|
2493
2514
|
*/
|
|
2494
|
-
async resolvePersona(agentName, requested, threadId) {
|
|
2515
|
+
async resolvePersona(agentName, requested, threadId, reads) {
|
|
2495
2516
|
const needsThread = requested === void 0 && threadId !== void 0 && this.deps.personaCatalog(agentName).length > 0;
|
|
2496
2517
|
return this.deps.resolvePersona({
|
|
2497
2518
|
agentName,
|
|
@@ -2499,16 +2520,16 @@ var AgentService2 = class {
|
|
|
2499
2520
|
requested
|
|
2500
2521
|
} : {},
|
|
2501
2522
|
...needsThread ? {
|
|
2502
|
-
threadPersona: await threadPersona(this.store, threadId)
|
|
2523
|
+
threadPersona: await (reads?.persona() ?? threadPersona(this.store, threadId))
|
|
2503
2524
|
} : {}
|
|
2504
2525
|
});
|
|
2505
2526
|
}
|
|
2506
2527
|
/** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
|
|
2507
|
-
async pinThreadPersona(threadId, persona) {
|
|
2528
|
+
async pinThreadPersona(threadId, persona, reads) {
|
|
2508
2529
|
if (this.store.updateThread === void 0) {
|
|
2509
2530
|
return;
|
|
2510
2531
|
}
|
|
2511
|
-
if (await threadPersona(this.store, threadId) !== persona) {
|
|
2532
|
+
if (await (reads?.persona() ?? threadPersona(this.store, threadId)) !== persona) {
|
|
2512
2533
|
await this.store.updateThread(threadId, {
|
|
2513
2534
|
persona
|
|
2514
2535
|
});
|
|
@@ -2535,18 +2556,27 @@ var AgentService2 = class {
|
|
|
2535
2556
|
* workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
|
|
2536
2557
|
* the full read.
|
|
2537
2558
|
*/
|
|
2538
|
-
async resolveAgentName(agentName, threadId) {
|
|
2559
|
+
async resolveAgentName(agentName, threadId, reads) {
|
|
2539
2560
|
if (agentName !== void 0) {
|
|
2540
2561
|
return agentName;
|
|
2541
2562
|
}
|
|
2542
2563
|
if (threadId !== void 0) {
|
|
2543
|
-
const threadDefault = await this.threadDefaultAgent(threadId);
|
|
2564
|
+
const threadDefault = await (reads?.defaultAgent() ?? this.threadDefaultAgent(threadId));
|
|
2544
2565
|
if (threadDefault !== null) {
|
|
2545
2566
|
return threadDefault;
|
|
2546
2567
|
}
|
|
2547
2568
|
}
|
|
2548
2569
|
return this.deps.defaultAgentName();
|
|
2549
2570
|
}
|
|
2571
|
+
/** Everything a send reads off `threadId`, in one read where the store allows it. */
|
|
2572
|
+
threadReads(threadId) {
|
|
2573
|
+
return threadReads(this.store, threadId, {
|
|
2574
|
+
owner: /* @__PURE__ */ __name(() => this.store.ownerOfThread(threadId), "owner"),
|
|
2575
|
+
defaultAgent: /* @__PURE__ */ __name(() => this.threadDefaultAgent(threadId), "defaultAgent"),
|
|
2576
|
+
persona: /* @__PURE__ */ __name(() => threadPersona(this.store, threadId), "persona"),
|
|
2577
|
+
model: /* @__PURE__ */ __name(() => this.threadModel(threadId), "model")
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2550
2580
|
async threadDefaultAgent(threadId) {
|
|
2551
2581
|
const projecting = this.store;
|
|
2552
2582
|
if (typeof projecting.defaultAgentForThread === "function") {
|
|
@@ -2716,8 +2746,8 @@ var AgentService2 = class {
|
|
|
2716
2746
|
* Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
|
|
2717
2747
|
* thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
|
|
2718
2748
|
*/
|
|
2719
|
-
async assertOwnsThread(actor, threadId) {
|
|
2720
|
-
const owner = await this.store.ownerOfThread(threadId);
|
|
2749
|
+
async assertOwnsThread(actor, threadId, reads) {
|
|
2750
|
+
const owner = await (reads?.owner() ?? this.store.ownerOfThread(threadId));
|
|
2721
2751
|
if (owner === null) {
|
|
2722
2752
|
throw new NotFoundException2(`thread ${threadId} not found`);
|
|
2723
2753
|
}
|
|
@@ -3157,6 +3187,18 @@ AttachmentsController = _ts_decorate8([
|
|
|
3157
3187
|
import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError, streamFailure } from "@dudousxd/nestjs-agent-core";
|
|
3158
3188
|
import { validateUiCapabilities } from "@dudousxd/nestjs-agent-core/genui";
|
|
3159
3189
|
import { BadRequestException as BadRequestException4, Body, Controller as Controller3, Get as Get3, Inject as Inject8, Param, Post as Post2, Query, Req as Req2, Res } from "@nestjs/common";
|
|
3190
|
+
|
|
3191
|
+
// src/controller/reader-gone.ts
|
|
3192
|
+
function readerGone(res) {
|
|
3193
|
+
const controller = new AbortController();
|
|
3194
|
+
res.once("close", () => controller.abort());
|
|
3195
|
+
return {
|
|
3196
|
+
signal: controller.signal
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
__name(readerGone, "readerGone");
|
|
3200
|
+
|
|
3201
|
+
// src/controller/chat.controller.ts
|
|
3160
3202
|
function _ts_decorate9(decorators, target, key, desc) {
|
|
3161
3203
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3162
3204
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -3290,7 +3332,8 @@ var ChatController = class {
|
|
|
3290
3332
|
return;
|
|
3291
3333
|
}
|
|
3292
3334
|
const { runId, threadId } = result;
|
|
3293
|
-
|
|
3335
|
+
const gone = readerGone(res);
|
|
3336
|
+
await this.pipe(res, runId, this.agent.subscribe(runId, gone), threadId, 0, gone.signal);
|
|
3294
3337
|
}
|
|
3295
3338
|
/**
|
|
3296
3339
|
* Attach to a run's stream: every buffered frame, then live ones. `?after=<seq>` (or the SSE
|
|
@@ -3300,7 +3343,8 @@ var ChatController = class {
|
|
|
3300
3343
|
async stream(req, runId, res, after) {
|
|
3301
3344
|
const actor = await this.actorResolver.resolve(req);
|
|
3302
3345
|
const cursor = parseCursor(after) ?? parseCursor(req.headers["last-event-id"]) ?? 0;
|
|
3303
|
-
|
|
3346
|
+
const gone = readerGone(res);
|
|
3347
|
+
await this.pipe(res, runId, await this.agent.subscribeAs(actor, runId, gone), void 0, cursor, gone.signal);
|
|
3304
3348
|
}
|
|
3305
3349
|
async cancel(req, runId) {
|
|
3306
3350
|
const actor = await this.actorResolver.resolve(req);
|
|
@@ -3316,8 +3360,11 @@ var ChatController = class {
|
|
|
3316
3360
|
* and on any later `GET …/stream`, whichever replica serves it. A reconnecting client passes the
|
|
3317
3361
|
* last one it saw as `?after=`; frames at or below it are read off the sink but not re-sent.
|
|
3318
3362
|
* `meta`, `done` and `error` carry no id: `meta` is re-sent on every attach, the terminals once.
|
|
3363
|
+
*
|
|
3364
|
+
* A reader that disconnects aborts `gone`, which the sink honours by ending the subscription even
|
|
3365
|
+
* while the run is silent (parked on a person, or never writing again).
|
|
3319
3366
|
*/
|
|
3320
|
-
async pipe(res, runId, events, threadId, after = 0) {
|
|
3367
|
+
async pipe(res, runId, events, threadId, after = 0, gone) {
|
|
3321
3368
|
res.setHeader("Content-Type", "text/event-stream");
|
|
3322
3369
|
res.setHeader("Cache-Control", "no-cache");
|
|
3323
3370
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -3346,6 +3393,9 @@ data: ${line}
|
|
|
3346
3393
|
}, "forward");
|
|
3347
3394
|
try {
|
|
3348
3395
|
for await (const chunk of events) {
|
|
3396
|
+
if (gone?.aborted) {
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3349
3399
|
buffer += decoder.decode(chunk, {
|
|
3350
3400
|
stream: true
|
|
3351
3401
|
});
|
|
@@ -3359,11 +3409,17 @@ data: ${line}
|
|
|
3359
3409
|
newline = buffer.indexOf("\n");
|
|
3360
3410
|
}
|
|
3361
3411
|
}
|
|
3412
|
+
if (gone?.aborted) {
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3362
3415
|
if (buffer.length > 0) {
|
|
3363
3416
|
forward(buffer);
|
|
3364
3417
|
}
|
|
3365
3418
|
res.write("event: done\ndata: {}\n\n");
|
|
3366
3419
|
} catch (error) {
|
|
3420
|
+
if (gone?.aborted) {
|
|
3421
|
+
return;
|
|
3422
|
+
}
|
|
3367
3423
|
const payload = error instanceof AgentStreamError ? {
|
|
3368
3424
|
code: error.code,
|
|
3369
3425
|
message: error.message
|
|
@@ -5293,49 +5349,121 @@ SkillDiscoveryService = _ts_decorate22([
|
|
|
5293
5349
|
|
|
5294
5350
|
// src/in-process-sink.ts
|
|
5295
5351
|
import { AgentStreamError as AgentStreamError2 } from "@dudousxd/nestjs-agent-core";
|
|
5352
|
+
var LIVE_TTL_MS = 36e5;
|
|
5353
|
+
var ENDED_GRACE_MS = 6e4;
|
|
5296
5354
|
var InProcessTokenStreamSink = class {
|
|
5297
5355
|
static {
|
|
5298
5356
|
__name(this, "InProcessTokenStreamSink");
|
|
5299
5357
|
}
|
|
5300
5358
|
runs = /* @__PURE__ */ new Map();
|
|
5359
|
+
/** Subscribers of a run nothing has opened yet, woken by its `open()`. */
|
|
5360
|
+
waiting = /* @__PURE__ */ new Map();
|
|
5301
5361
|
buffer(runId) {
|
|
5302
5362
|
let buf = this.runs.get(runId);
|
|
5303
5363
|
if (buf === void 0) {
|
|
5304
5364
|
buf = {
|
|
5305
5365
|
chunks: [],
|
|
5306
5366
|
ended: false,
|
|
5307
|
-
notify: /* @__PURE__ */ new Set()
|
|
5367
|
+
notify: /* @__PURE__ */ new Set(),
|
|
5368
|
+
lastWriteAt: Date.now(),
|
|
5369
|
+
expiry: void 0
|
|
5308
5370
|
};
|
|
5309
5371
|
this.runs.set(runId, buf);
|
|
5372
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS);
|
|
5373
|
+
const waiters = this.waiting.get(runId);
|
|
5374
|
+
this.waiting.delete(runId);
|
|
5375
|
+
for (const resolve2 of waiters ?? []) {
|
|
5376
|
+
resolve2();
|
|
5377
|
+
}
|
|
5310
5378
|
}
|
|
5311
5379
|
return buf;
|
|
5312
5380
|
}
|
|
5381
|
+
expireAfter(runId, buf, ms) {
|
|
5382
|
+
if (buf.expiry !== void 0) {
|
|
5383
|
+
clearTimeout(buf.expiry);
|
|
5384
|
+
}
|
|
5385
|
+
buf.expiry = setTimeout(() => {
|
|
5386
|
+
buf.expiry = void 0;
|
|
5387
|
+
const idle = Date.now() - buf.lastWriteAt;
|
|
5388
|
+
if (!buf.ended && idle < LIVE_TTL_MS) {
|
|
5389
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS - idle);
|
|
5390
|
+
return;
|
|
5391
|
+
}
|
|
5392
|
+
this.drop(runId, buf);
|
|
5393
|
+
}, ms);
|
|
5394
|
+
buf.expiry.unref?.();
|
|
5395
|
+
}
|
|
5396
|
+
/** Forget the run, and let anyone still following it finish as if it had ended. */
|
|
5397
|
+
drop(runId, buf) {
|
|
5398
|
+
if (buf.expiry !== void 0) {
|
|
5399
|
+
clearTimeout(buf.expiry);
|
|
5400
|
+
buf.expiry = void 0;
|
|
5401
|
+
}
|
|
5402
|
+
if (this.runs.get(runId) === buf) {
|
|
5403
|
+
this.runs.delete(runId);
|
|
5404
|
+
}
|
|
5405
|
+
buf.ended = true;
|
|
5406
|
+
this.wake(buf);
|
|
5407
|
+
}
|
|
5313
5408
|
wake(buf) {
|
|
5314
5409
|
for (const resolve2 of buf.notify) {
|
|
5315
5410
|
resolve2();
|
|
5316
5411
|
}
|
|
5317
5412
|
buf.notify.clear();
|
|
5318
5413
|
}
|
|
5414
|
+
finish(runId, buf) {
|
|
5415
|
+
buf.ended = true;
|
|
5416
|
+
this.wake(buf);
|
|
5417
|
+
this.expireAfter(runId, buf, ENDED_GRACE_MS);
|
|
5418
|
+
}
|
|
5419
|
+
/**
|
|
5420
|
+
* The run's writer. Each call finds the run's buffer afresh, so a run that went quiet past the
|
|
5421
|
+
* live TTL (parked on a person) and then writes again starts a new buffer its next subscriber
|
|
5422
|
+
* follows, rather than writing into one nothing reads.
|
|
5423
|
+
*/
|
|
5319
5424
|
open(runId) {
|
|
5320
|
-
|
|
5425
|
+
this.buffer(runId);
|
|
5321
5426
|
return {
|
|
5322
5427
|
write: /* @__PURE__ */ __name((chunk) => {
|
|
5428
|
+
const buf = this.buffer(runId);
|
|
5323
5429
|
buf.chunks.push(chunk);
|
|
5430
|
+
buf.lastWriteAt = Date.now();
|
|
5324
5431
|
this.wake(buf);
|
|
5325
5432
|
}, "write"),
|
|
5326
5433
|
end: /* @__PURE__ */ __name(() => {
|
|
5327
|
-
|
|
5328
|
-
this.wake(buf);
|
|
5434
|
+
this.finish(runId, this.buffer(runId));
|
|
5329
5435
|
}, "end"),
|
|
5330
5436
|
fail: /* @__PURE__ */ __name((error) => {
|
|
5437
|
+
const buf = this.buffer(runId);
|
|
5331
5438
|
buf.failure = error;
|
|
5332
|
-
|
|
5333
|
-
this.wake(buf);
|
|
5439
|
+
this.finish(runId, buf);
|
|
5334
5440
|
}, "fail")
|
|
5335
5441
|
};
|
|
5336
5442
|
}
|
|
5337
|
-
async *subscribe(runId) {
|
|
5338
|
-
const
|
|
5443
|
+
async *subscribe(runId, options = {}) {
|
|
5444
|
+
const { signal } = options;
|
|
5445
|
+
let buf = this.runs.get(runId);
|
|
5446
|
+
while (buf === void 0) {
|
|
5447
|
+
const waiters = this.waiting.get(runId) ?? /* @__PURE__ */ new Set();
|
|
5448
|
+
this.waiting.set(runId, waiters);
|
|
5449
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
5450
|
+
}, "resolve");
|
|
5451
|
+
const opened = new Promise((r) => {
|
|
5452
|
+
resolve2 = r;
|
|
5453
|
+
});
|
|
5454
|
+
waiters.add(resolve2);
|
|
5455
|
+
try {
|
|
5456
|
+
if (!await untilAborted(opened, signal)) {
|
|
5457
|
+
return;
|
|
5458
|
+
}
|
|
5459
|
+
} finally {
|
|
5460
|
+
waiters.delete(resolve2);
|
|
5461
|
+
if (waiters.size === 0 && this.waiting.get(runId) === waiters) {
|
|
5462
|
+
this.waiting.delete(runId);
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
buf = this.runs.get(runId);
|
|
5466
|
+
}
|
|
5339
5467
|
let index = 0;
|
|
5340
5468
|
while (true) {
|
|
5341
5469
|
while (index < buf.chunks.length) {
|
|
@@ -5351,13 +5479,56 @@ var InProcessTokenStreamSink = class {
|
|
|
5351
5479
|
}
|
|
5352
5480
|
return;
|
|
5353
5481
|
}
|
|
5354
|
-
|
|
5482
|
+
const current = buf;
|
|
5483
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
5484
|
+
}, "resolve");
|
|
5485
|
+
const woken = new Promise((r) => {
|
|
5486
|
+
resolve2 = r;
|
|
5487
|
+
});
|
|
5488
|
+
current.notify.add(resolve2);
|
|
5489
|
+
try {
|
|
5490
|
+
if (!await untilAborted(woken, signal)) {
|
|
5491
|
+
return;
|
|
5492
|
+
}
|
|
5493
|
+
} finally {
|
|
5494
|
+
current.notify.delete(resolve2);
|
|
5495
|
+
}
|
|
5355
5496
|
}
|
|
5356
5497
|
}
|
|
5357
5498
|
close(runId) {
|
|
5499
|
+
const buf = this.runs.get(runId);
|
|
5500
|
+
if (buf?.expiry !== void 0) {
|
|
5501
|
+
clearTimeout(buf.expiry);
|
|
5502
|
+
}
|
|
5358
5503
|
this.runs.delete(runId);
|
|
5359
5504
|
}
|
|
5360
5505
|
};
|
|
5506
|
+
async function untilAborted(promise, signal) {
|
|
5507
|
+
if (signal === void 0) {
|
|
5508
|
+
await promise;
|
|
5509
|
+
return true;
|
|
5510
|
+
}
|
|
5511
|
+
if (signal.aborted) {
|
|
5512
|
+
return false;
|
|
5513
|
+
}
|
|
5514
|
+
let onAbort = /* @__PURE__ */ __name(() => {
|
|
5515
|
+
}, "onAbort");
|
|
5516
|
+
const aborted = new Promise((resolve2) => {
|
|
5517
|
+
onAbort = /* @__PURE__ */ __name(() => resolve2(false), "onAbort");
|
|
5518
|
+
signal.addEventListener("abort", onAbort, {
|
|
5519
|
+
once: true
|
|
5520
|
+
});
|
|
5521
|
+
});
|
|
5522
|
+
try {
|
|
5523
|
+
return await Promise.race([
|
|
5524
|
+
promise.then(() => true),
|
|
5525
|
+
aborted
|
|
5526
|
+
]);
|
|
5527
|
+
} finally {
|
|
5528
|
+
signal.removeEventListener("abort", onAbort);
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
__name(untilAborted, "untilAborted");
|
|
5361
5532
|
|
|
5362
5533
|
// src/ledger-quota-provider.ts
|
|
5363
5534
|
import { exhaustedWindow, quotaPeriodRange, quotaWarning } from "@dudousxd/nestjs-agent-core";
|
|
@@ -5376,13 +5547,22 @@ var LedgerQuotaProvider = class {
|
|
|
5376
5547
|
async report(query) {
|
|
5377
5548
|
const now = query.now ?? /* @__PURE__ */ new Date();
|
|
5378
5549
|
const actorRef = query.actor.id;
|
|
5379
|
-
const
|
|
5380
|
-
|
|
5381
|
-
];
|
|
5382
|
-
if (this.store.
|
|
5383
|
-
const
|
|
5384
|
-
|
|
5385
|
-
|
|
5550
|
+
const day = quotaPeriodRange("day", now);
|
|
5551
|
+
const month = quotaPeriodRange("month", now);
|
|
5552
|
+
const windows = [];
|
|
5553
|
+
if (this.store.usageWindows !== void 0) {
|
|
5554
|
+
const [dayUsed, monthUsed] = await this.store.usageWindows(actorRef, [
|
|
5555
|
+
day,
|
|
5556
|
+
month
|
|
5557
|
+
]);
|
|
5558
|
+
windows.push(this.usedWindow("day", dayUsed, day.resetsAt));
|
|
5559
|
+
windows.push(this.usedWindow("month", monthUsed, month.resetsAt));
|
|
5560
|
+
} else {
|
|
5561
|
+
windows.push(this.usedWindow("day", await this.store.quotaToday(actorRef, day.fromDay), day.resetsAt));
|
|
5562
|
+
if (this.store.usageBetween !== void 0) {
|
|
5563
|
+
const used = await this.store.usageBetween(actorRef, month.fromDay, month.toDay);
|
|
5564
|
+
windows.push(this.usedWindow("month", used, month.resetsAt));
|
|
5565
|
+
}
|
|
5386
5566
|
}
|
|
5387
5567
|
const blocked = exhaustedWindow(windows);
|
|
5388
5568
|
const warning = quotaWarning(windows);
|
|
@@ -5396,10 +5576,8 @@ var LedgerQuotaProvider = class {
|
|
|
5396
5576
|
} : {}
|
|
5397
5577
|
};
|
|
5398
5578
|
}
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
const used = await this.store.quotaToday(actorRef, range.fromDay);
|
|
5402
|
-
return this.window("day", used.usedTokens, this.usd(used), range.resetsAt);
|
|
5579
|
+
usedWindow(period, used, resetsAt) {
|
|
5580
|
+
return this.window(period, used?.usedTokens ?? 0, used === void 0 ? 0 : this.usd(used), resetsAt);
|
|
5403
5581
|
}
|
|
5404
5582
|
/** The window's USD: every priced row, or (with `countEstimatedCost: false`) reported cost only. */
|
|
5405
5583
|
usd(used) {
|
|
@@ -7244,7 +7422,7 @@ var AgUiRunHandler = class {
|
|
|
7244
7422
|
seq: 0
|
|
7245
7423
|
};
|
|
7246
7424
|
try {
|
|
7247
|
-
const frames = agUiFramesFromNdjson(this.agent.subscribe(runId));
|
|
7425
|
+
const frames = agUiFramesFromNdjson(this.agent.subscribe(runId, readerGone(res)));
|
|
7248
7426
|
for await (const event of agUiEvents(frames, {
|
|
7249
7427
|
...options,
|
|
7250
7428
|
cursor
|
|
@@ -7575,7 +7753,7 @@ var A2uiRunHandler = class {
|
|
|
7575
7753
|
`);
|
|
7576
7754
|
}, "write");
|
|
7577
7755
|
try {
|
|
7578
|
-
const frames = agUiFramesFromNdjson2(this.agent.subscribe(runId));
|
|
7756
|
+
const frames = agUiFramesFromNdjson2(this.agent.subscribe(runId, readerGone(res)));
|
|
7579
7757
|
for await (const event of agUiEvents2(frames, {
|
|
7580
7758
|
threadId: options.threadId ?? runId,
|
|
7581
7759
|
runId,
|