@dudousxd/nestjs-agent 1.36.1 → 1.37.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 +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 +250 -50
- 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 +250 -50
- package/dist/durable/index.js.map +1 -1
- package/dist/{index-NF93YDF4.d.cts → index-4caNqEQ-.d.cts} +29 -6
- package/dist/{index-DpS9P7so.d.ts → index-yC_Ra3CE.d.ts} +29 -6
- package/dist/index.cjs +244 -47
- 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 +244 -47
- 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 +3 -3
package/dist/durable/index.cjs
CHANGED
|
@@ -585,10 +585,11 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
585
585
|
* `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
|
|
586
586
|
* `cancelled` once it has settled.
|
|
587
587
|
*
|
|
588
|
-
* Costs a run read at each of the loop's observation points (two per step)
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
588
|
+
* Costs a run read at each of the loop's observation points (two per step): the run's own row
|
|
589
|
+
* where the gateway can read it alone, else its detail, which carries every checkpoint of the run.
|
|
590
|
+
* That row is one the control plane already has, against a turn that is otherwise making model
|
|
591
|
+
* calls, and it buys the property that matters: the answer comes from a place BOTH the canceller
|
|
592
|
+
* and every pod that might replay this run can see.
|
|
592
593
|
*
|
|
593
594
|
* Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
|
|
594
595
|
* unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
|
|
@@ -596,8 +597,10 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
596
597
|
* can never disagree about what was seen here.
|
|
597
598
|
*/
|
|
598
599
|
async isCancelled(runId) {
|
|
600
|
+
const runs = this.runs;
|
|
599
601
|
try {
|
|
600
|
-
const
|
|
602
|
+
const run = typeof runs?.getRun === "function" ? await runs.getRun(runId) : (await runs?.getRunDetail(runId))?.run;
|
|
603
|
+
const status = run?.status;
|
|
601
604
|
return status === "cancelled" || status === "cancelling";
|
|
602
605
|
} catch {
|
|
603
606
|
return false;
|
|
@@ -909,49 +912,121 @@ var import_common3 = require("@nestjs/common");
|
|
|
909
912
|
|
|
910
913
|
// src/in-process-sink.ts
|
|
911
914
|
var import_nestjs_agent_core5 = require("@dudousxd/nestjs-agent-core");
|
|
915
|
+
var LIVE_TTL_MS = 36e5;
|
|
916
|
+
var ENDED_GRACE_MS = 6e4;
|
|
912
917
|
var InProcessTokenStreamSink = class {
|
|
913
918
|
static {
|
|
914
919
|
__name(this, "InProcessTokenStreamSink");
|
|
915
920
|
}
|
|
916
921
|
runs = /* @__PURE__ */ new Map();
|
|
922
|
+
/** Subscribers of a run nothing has opened yet, woken by its `open()`. */
|
|
923
|
+
waiting = /* @__PURE__ */ new Map();
|
|
917
924
|
buffer(runId) {
|
|
918
925
|
let buf = this.runs.get(runId);
|
|
919
926
|
if (buf === void 0) {
|
|
920
927
|
buf = {
|
|
921
928
|
chunks: [],
|
|
922
929
|
ended: false,
|
|
923
|
-
notify: /* @__PURE__ */ new Set()
|
|
930
|
+
notify: /* @__PURE__ */ new Set(),
|
|
931
|
+
lastWriteAt: Date.now(),
|
|
932
|
+
expiry: void 0
|
|
924
933
|
};
|
|
925
934
|
this.runs.set(runId, buf);
|
|
935
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS);
|
|
936
|
+
const waiters = this.waiting.get(runId);
|
|
937
|
+
this.waiting.delete(runId);
|
|
938
|
+
for (const resolve2 of waiters ?? []) {
|
|
939
|
+
resolve2();
|
|
940
|
+
}
|
|
926
941
|
}
|
|
927
942
|
return buf;
|
|
928
943
|
}
|
|
944
|
+
expireAfter(runId, buf, ms) {
|
|
945
|
+
if (buf.expiry !== void 0) {
|
|
946
|
+
clearTimeout(buf.expiry);
|
|
947
|
+
}
|
|
948
|
+
buf.expiry = setTimeout(() => {
|
|
949
|
+
buf.expiry = void 0;
|
|
950
|
+
const idle = Date.now() - buf.lastWriteAt;
|
|
951
|
+
if (!buf.ended && idle < LIVE_TTL_MS) {
|
|
952
|
+
this.expireAfter(runId, buf, LIVE_TTL_MS - idle);
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
this.drop(runId, buf);
|
|
956
|
+
}, ms);
|
|
957
|
+
buf.expiry.unref?.();
|
|
958
|
+
}
|
|
959
|
+
/** Forget the run, and let anyone still following it finish as if it had ended. */
|
|
960
|
+
drop(runId, buf) {
|
|
961
|
+
if (buf.expiry !== void 0) {
|
|
962
|
+
clearTimeout(buf.expiry);
|
|
963
|
+
buf.expiry = void 0;
|
|
964
|
+
}
|
|
965
|
+
if (this.runs.get(runId) === buf) {
|
|
966
|
+
this.runs.delete(runId);
|
|
967
|
+
}
|
|
968
|
+
buf.ended = true;
|
|
969
|
+
this.wake(buf);
|
|
970
|
+
}
|
|
929
971
|
wake(buf) {
|
|
930
972
|
for (const resolve2 of buf.notify) {
|
|
931
973
|
resolve2();
|
|
932
974
|
}
|
|
933
975
|
buf.notify.clear();
|
|
934
976
|
}
|
|
977
|
+
finish(runId, buf) {
|
|
978
|
+
buf.ended = true;
|
|
979
|
+
this.wake(buf);
|
|
980
|
+
this.expireAfter(runId, buf, ENDED_GRACE_MS);
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* The run's writer. Each call finds the run's buffer afresh, so a run that went quiet past the
|
|
984
|
+
* live TTL (parked on a person) and then writes again starts a new buffer its next subscriber
|
|
985
|
+
* follows, rather than writing into one nothing reads.
|
|
986
|
+
*/
|
|
935
987
|
open(runId) {
|
|
936
|
-
|
|
988
|
+
this.buffer(runId);
|
|
937
989
|
return {
|
|
938
990
|
write: /* @__PURE__ */ __name((chunk) => {
|
|
991
|
+
const buf = this.buffer(runId);
|
|
939
992
|
buf.chunks.push(chunk);
|
|
993
|
+
buf.lastWriteAt = Date.now();
|
|
940
994
|
this.wake(buf);
|
|
941
995
|
}, "write"),
|
|
942
996
|
end: /* @__PURE__ */ __name(() => {
|
|
943
|
-
|
|
944
|
-
this.wake(buf);
|
|
997
|
+
this.finish(runId, this.buffer(runId));
|
|
945
998
|
}, "end"),
|
|
946
999
|
fail: /* @__PURE__ */ __name((error) => {
|
|
1000
|
+
const buf = this.buffer(runId);
|
|
947
1001
|
buf.failure = error;
|
|
948
|
-
|
|
949
|
-
this.wake(buf);
|
|
1002
|
+
this.finish(runId, buf);
|
|
950
1003
|
}, "fail")
|
|
951
1004
|
};
|
|
952
1005
|
}
|
|
953
|
-
async *subscribe(runId) {
|
|
954
|
-
const
|
|
1006
|
+
async *subscribe(runId, options = {}) {
|
|
1007
|
+
const { signal } = options;
|
|
1008
|
+
let buf = this.runs.get(runId);
|
|
1009
|
+
while (buf === void 0) {
|
|
1010
|
+
const waiters = this.waiting.get(runId) ?? /* @__PURE__ */ new Set();
|
|
1011
|
+
this.waiting.set(runId, waiters);
|
|
1012
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
1013
|
+
}, "resolve");
|
|
1014
|
+
const opened = new Promise((r) => {
|
|
1015
|
+
resolve2 = r;
|
|
1016
|
+
});
|
|
1017
|
+
waiters.add(resolve2);
|
|
1018
|
+
try {
|
|
1019
|
+
if (!await untilAborted(opened, signal)) {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
} finally {
|
|
1023
|
+
waiters.delete(resolve2);
|
|
1024
|
+
if (waiters.size === 0 && this.waiting.get(runId) === waiters) {
|
|
1025
|
+
this.waiting.delete(runId);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
buf = this.runs.get(runId);
|
|
1029
|
+
}
|
|
955
1030
|
let index = 0;
|
|
956
1031
|
while (true) {
|
|
957
1032
|
while (index < buf.chunks.length) {
|
|
@@ -967,13 +1042,56 @@ var InProcessTokenStreamSink = class {
|
|
|
967
1042
|
}
|
|
968
1043
|
return;
|
|
969
1044
|
}
|
|
970
|
-
|
|
1045
|
+
const current = buf;
|
|
1046
|
+
let resolve2 = /* @__PURE__ */ __name(() => {
|
|
1047
|
+
}, "resolve");
|
|
1048
|
+
const woken = new Promise((r) => {
|
|
1049
|
+
resolve2 = r;
|
|
1050
|
+
});
|
|
1051
|
+
current.notify.add(resolve2);
|
|
1052
|
+
try {
|
|
1053
|
+
if (!await untilAborted(woken, signal)) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
} finally {
|
|
1057
|
+
current.notify.delete(resolve2);
|
|
1058
|
+
}
|
|
971
1059
|
}
|
|
972
1060
|
}
|
|
973
1061
|
close(runId) {
|
|
1062
|
+
const buf = this.runs.get(runId);
|
|
1063
|
+
if (buf?.expiry !== void 0) {
|
|
1064
|
+
clearTimeout(buf.expiry);
|
|
1065
|
+
}
|
|
974
1066
|
this.runs.delete(runId);
|
|
975
1067
|
}
|
|
976
1068
|
};
|
|
1069
|
+
async function untilAborted(promise, signal) {
|
|
1070
|
+
if (signal === void 0) {
|
|
1071
|
+
await promise;
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
if (signal.aborted) {
|
|
1075
|
+
return false;
|
|
1076
|
+
}
|
|
1077
|
+
let onAbort = /* @__PURE__ */ __name(() => {
|
|
1078
|
+
}, "onAbort");
|
|
1079
|
+
const aborted = new Promise((resolve2) => {
|
|
1080
|
+
onAbort = /* @__PURE__ */ __name(() => resolve2(false), "onAbort");
|
|
1081
|
+
signal.addEventListener("abort", onAbort, {
|
|
1082
|
+
once: true
|
|
1083
|
+
});
|
|
1084
|
+
});
|
|
1085
|
+
try {
|
|
1086
|
+
return await Promise.race([
|
|
1087
|
+
promise.then(() => true),
|
|
1088
|
+
aborted
|
|
1089
|
+
]);
|
|
1090
|
+
} finally {
|
|
1091
|
+
signal.removeEventListener("abort", onAbort);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
__name(untilAborted, "untilAborted");
|
|
977
1095
|
|
|
978
1096
|
// src/durable/durable-agent-runner.ts
|
|
979
1097
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
@@ -2849,6 +2967,26 @@ var RunNotActiveException = class extends import_common9.ConflictException {
|
|
|
2849
2967
|
}
|
|
2850
2968
|
};
|
|
2851
2969
|
|
|
2970
|
+
// src/thread-reads.ts
|
|
2971
|
+
function threadReads(store, threadId, separately) {
|
|
2972
|
+
const projecting = store;
|
|
2973
|
+
if (typeof projecting.threadSettings !== "function") {
|
|
2974
|
+
return separately;
|
|
2975
|
+
}
|
|
2976
|
+
let settings;
|
|
2977
|
+
const read = /* @__PURE__ */ __name(() => {
|
|
2978
|
+
settings ??= projecting.threadSettings?.(threadId) ?? Promise.resolve(null);
|
|
2979
|
+
return settings;
|
|
2980
|
+
}, "read");
|
|
2981
|
+
return {
|
|
2982
|
+
owner: /* @__PURE__ */ __name(async () => (await read())?.actorRef ?? null, "owner"),
|
|
2983
|
+
defaultAgent: /* @__PURE__ */ __name(async () => (await read())?.defaultAgent ?? null, "defaultAgent"),
|
|
2984
|
+
persona: /* @__PURE__ */ __name(async () => (await read())?.persona ?? null, "persona"),
|
|
2985
|
+
model: /* @__PURE__ */ __name(async () => (await read())?.model ?? null, "model")
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
__name(threadReads, "threadReads");
|
|
2989
|
+
|
|
2852
2990
|
// src/agent.service.ts
|
|
2853
2991
|
function _ts_decorate9(decorators, target, key, desc) {
|
|
2854
2992
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3112,7 +3250,7 @@ var AgentService = class {
|
|
|
3112
3250
|
* else the thread's pinned one, else none. An agent the catalog locks to one model runs on it
|
|
3113
3251
|
* whatever was sent or pinned, and a send naming another model is refused.
|
|
3114
3252
|
*/
|
|
3115
|
-
async resolveModel(actor, agent, requested, threadId) {
|
|
3253
|
+
async resolveModel(actor, agent, requested, threadId, reads) {
|
|
3116
3254
|
const locked = this.models !== void 0 ? (await this.models.list({
|
|
3117
3255
|
actor,
|
|
3118
3256
|
agent
|
|
@@ -3123,7 +3261,7 @@ var AgentService = class {
|
|
|
3123
3261
|
}
|
|
3124
3262
|
return locked.model;
|
|
3125
3263
|
}
|
|
3126
|
-
const model = requested ?? (threadId !== void 0 ? await this.threadModel(threadId) : null);
|
|
3264
|
+
const model = requested ?? (threadId !== void 0 ? await (reads?.model() ?? this.threadModel(threadId)) : null);
|
|
3127
3265
|
if (model === null || model === void 0) {
|
|
3128
3266
|
return void 0;
|
|
3129
3267
|
}
|
|
@@ -3183,10 +3321,11 @@ var AgentService = class {
|
|
|
3183
3321
|
if ("proposalDecision" in decision) return decision;
|
|
3184
3322
|
}
|
|
3185
3323
|
await this.assertWithinQuota(params.actor);
|
|
3186
|
-
const
|
|
3324
|
+
const reads = params.threadId !== void 0 ? this.threadReads(params.threadId) : void 0;
|
|
3325
|
+
const target = this.deps.resolveAgent(await this.resolveAgentName(params.agentName, params.threadId, reads));
|
|
3187
3326
|
const agentName = target.agentName;
|
|
3188
|
-
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId);
|
|
3189
|
-
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
|
|
3327
|
+
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId, reads);
|
|
3328
|
+
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId, reads);
|
|
3190
3329
|
const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
|
|
3191
3330
|
let threadId = params.threadId;
|
|
3192
3331
|
if (threadId === void 0) {
|
|
@@ -3210,9 +3349,9 @@ var AgentService = class {
|
|
|
3210
3349
|
});
|
|
3211
3350
|
threadId = created.id;
|
|
3212
3351
|
} else {
|
|
3213
|
-
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId);
|
|
3352
|
+
if (params.authorized !== true) await this.assertOwnsThread(params.actor, threadId, reads);
|
|
3214
3353
|
if (params.personaId !== void 0) {
|
|
3215
|
-
await this.pinThreadPersona(threadId, params.personaId);
|
|
3354
|
+
await this.pinThreadPersona(threadId, params.personaId, reads);
|
|
3216
3355
|
}
|
|
3217
3356
|
}
|
|
3218
3357
|
const input = {
|
|
@@ -3538,8 +3677,8 @@ var AgentService = class {
|
|
|
3538
3677
|
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
3539
3678
|
* actor's answer, tool arguments and tool results.
|
|
3540
3679
|
*/
|
|
3541
|
-
subscribe(runId) {
|
|
3542
|
-
return this.deps.forAgent().sink.subscribe(runId);
|
|
3680
|
+
subscribe(runId, options) {
|
|
3681
|
+
return this.deps.forAgent().sink.subscribe(runId, options);
|
|
3543
3682
|
}
|
|
3544
3683
|
/**
|
|
3545
3684
|
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
@@ -3547,9 +3686,9 @@ var AgentService = class {
|
|
|
3547
3686
|
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
3548
3687
|
* ownership is derived from.
|
|
3549
3688
|
*/
|
|
3550
|
-
async subscribeAs(actor, runId) {
|
|
3689
|
+
async subscribeAs(actor, runId, options) {
|
|
3551
3690
|
await this.assertOwnsActiveStream(actor, runId);
|
|
3552
|
-
return this.subscribe(runId);
|
|
3691
|
+
return this.subscribe(runId, options);
|
|
3553
3692
|
}
|
|
3554
3693
|
/**
|
|
3555
3694
|
* Approve a parked action call as `actor`. Who may is the call's recorded approver's business
|
|
@@ -3702,9 +3841,28 @@ var AgentService = class {
|
|
|
3702
3841
|
await this.assertOwnsThread(actor, threadId);
|
|
3703
3842
|
return this.store.softDeleteThread(threadId);
|
|
3704
3843
|
}
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3844
|
+
/**
|
|
3845
|
+
* Copy a thread up to `fromMessageId` (inclusive) into a new one. By default only the thread's
|
|
3846
|
+
* owner may, and the copy is theirs. With `params.authorized`, the host has vouched for `actor`
|
|
3847
|
+
* (a share link it checked) and the copy is `actor`'s — see {@link ForkThreadParams}.
|
|
3848
|
+
*/
|
|
3849
|
+
async forkThread(actor, threadId, fromMessageId, params = {}) {
|
|
3850
|
+
if (params.authorized === true) {
|
|
3851
|
+
if (await this.store.ownerOfThread(threadId) === null) {
|
|
3852
|
+
throw new import_common10.NotFoundException(`thread ${threadId} not found`);
|
|
3853
|
+
}
|
|
3854
|
+
} else {
|
|
3855
|
+
await this.assertOwnsThread(actor, threadId);
|
|
3856
|
+
}
|
|
3857
|
+
const options = {
|
|
3858
|
+
...params.authorized === true ? {
|
|
3859
|
+
actorRef: actor.id
|
|
3860
|
+
} : {},
|
|
3861
|
+
...params.tenantRef !== void 0 ? {
|
|
3862
|
+
tenantRef: params.tenantRef
|
|
3863
|
+
} : {}
|
|
3864
|
+
};
|
|
3865
|
+
return this.store.forkThread(threadId, fromMessageId, options);
|
|
3708
3866
|
}
|
|
3709
3867
|
/** Kept for source compatibility — a thin wrapper over the more general `updateThread`. */
|
|
3710
3868
|
async renameThread(actor, threadId, title) {
|
|
@@ -3756,7 +3914,7 @@ var AgentService = class {
|
|
|
3756
3914
|
* The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
|
|
3757
3915
|
* pinned persona is read only when the send names none AND the agent has personas to pick from.
|
|
3758
3916
|
*/
|
|
3759
|
-
async resolvePersona(agentName, requested, threadId) {
|
|
3917
|
+
async resolvePersona(agentName, requested, threadId, reads) {
|
|
3760
3918
|
const needsThread = requested === void 0 && threadId !== void 0 && this.deps.personaCatalog(agentName).length > 0;
|
|
3761
3919
|
return this.deps.resolvePersona({
|
|
3762
3920
|
agentName,
|
|
@@ -3764,16 +3922,16 @@ var AgentService = class {
|
|
|
3764
3922
|
requested
|
|
3765
3923
|
} : {},
|
|
3766
3924
|
...needsThread ? {
|
|
3767
|
-
threadPersona: await threadPersona(this.store, threadId)
|
|
3925
|
+
threadPersona: await (reads?.persona() ?? threadPersona(this.store, threadId))
|
|
3768
3926
|
} : {}
|
|
3769
3927
|
});
|
|
3770
3928
|
}
|
|
3771
3929
|
/** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
|
|
3772
|
-
async pinThreadPersona(threadId, persona) {
|
|
3930
|
+
async pinThreadPersona(threadId, persona, reads) {
|
|
3773
3931
|
if (this.store.updateThread === void 0) {
|
|
3774
3932
|
return;
|
|
3775
3933
|
}
|
|
3776
|
-
if (await threadPersona(this.store, threadId) !== persona) {
|
|
3934
|
+
if (await (reads?.persona() ?? threadPersona(this.store, threadId)) !== persona) {
|
|
3777
3935
|
await this.store.updateThread(threadId, {
|
|
3778
3936
|
persona
|
|
3779
3937
|
});
|
|
@@ -3800,18 +3958,27 @@ var AgentService = class {
|
|
|
3800
3958
|
* workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
|
|
3801
3959
|
* the full read.
|
|
3802
3960
|
*/
|
|
3803
|
-
async resolveAgentName(agentName, threadId) {
|
|
3961
|
+
async resolveAgentName(agentName, threadId, reads) {
|
|
3804
3962
|
if (agentName !== void 0) {
|
|
3805
3963
|
return agentName;
|
|
3806
3964
|
}
|
|
3807
3965
|
if (threadId !== void 0) {
|
|
3808
|
-
const threadDefault = await this.threadDefaultAgent(threadId);
|
|
3966
|
+
const threadDefault = await (reads?.defaultAgent() ?? this.threadDefaultAgent(threadId));
|
|
3809
3967
|
if (threadDefault !== null) {
|
|
3810
3968
|
return threadDefault;
|
|
3811
3969
|
}
|
|
3812
3970
|
}
|
|
3813
3971
|
return this.deps.defaultAgentName();
|
|
3814
3972
|
}
|
|
3973
|
+
/** Everything a send reads off `threadId`, in one read where the store allows it. */
|
|
3974
|
+
threadReads(threadId) {
|
|
3975
|
+
return threadReads(this.store, threadId, {
|
|
3976
|
+
owner: /* @__PURE__ */ __name(() => this.store.ownerOfThread(threadId), "owner"),
|
|
3977
|
+
defaultAgent: /* @__PURE__ */ __name(() => this.threadDefaultAgent(threadId), "defaultAgent"),
|
|
3978
|
+
persona: /* @__PURE__ */ __name(() => threadPersona(this.store, threadId), "persona"),
|
|
3979
|
+
model: /* @__PURE__ */ __name(() => this.threadModel(threadId), "model")
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3815
3982
|
async threadDefaultAgent(threadId) {
|
|
3816
3983
|
const projecting = this.store;
|
|
3817
3984
|
if (typeof projecting.defaultAgentForThread === "function") {
|
|
@@ -3981,8 +4148,8 @@ var AgentService = class {
|
|
|
3981
4148
|
* Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
|
|
3982
4149
|
* thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
|
|
3983
4150
|
*/
|
|
3984
|
-
async assertOwnsThread(actor, threadId) {
|
|
3985
|
-
const owner = await this.store.ownerOfThread(threadId);
|
|
4151
|
+
async assertOwnsThread(actor, threadId, reads) {
|
|
4152
|
+
const owner = await (reads?.owner() ?? this.store.ownerOfThread(threadId));
|
|
3986
4153
|
if (owner === null) {
|
|
3987
4154
|
throw new import_common10.NotFoundException(`thread ${threadId} not found`);
|
|
3988
4155
|
}
|
|
@@ -4422,6 +4589,18 @@ AttachmentsController = _ts_decorate12([
|
|
|
4422
4589
|
var import_nestjs_agent_core19 = require("@dudousxd/nestjs-agent-core");
|
|
4423
4590
|
var import_genui3 = require("@dudousxd/nestjs-agent-core/genui");
|
|
4424
4591
|
var import_common14 = require("@nestjs/common");
|
|
4592
|
+
|
|
4593
|
+
// src/controller/reader-gone.ts
|
|
4594
|
+
function readerGone(res) {
|
|
4595
|
+
const controller = new AbortController();
|
|
4596
|
+
res.once("close", () => controller.abort());
|
|
4597
|
+
return {
|
|
4598
|
+
signal: controller.signal
|
|
4599
|
+
};
|
|
4600
|
+
}
|
|
4601
|
+
__name(readerGone, "readerGone");
|
|
4602
|
+
|
|
4603
|
+
// src/controller/chat.controller.ts
|
|
4425
4604
|
function _ts_decorate13(decorators, target, key, desc) {
|
|
4426
4605
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4427
4606
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -4555,7 +4734,8 @@ var ChatController = class {
|
|
|
4555
4734
|
return;
|
|
4556
4735
|
}
|
|
4557
4736
|
const { runId, threadId } = result;
|
|
4558
|
-
|
|
4737
|
+
const gone = readerGone(res);
|
|
4738
|
+
await this.pipe(res, runId, this.agent.subscribe(runId, gone), threadId, 0, gone.signal);
|
|
4559
4739
|
}
|
|
4560
4740
|
/**
|
|
4561
4741
|
* Attach to a run's stream: every buffered frame, then live ones. `?after=<seq>` (or the SSE
|
|
@@ -4565,7 +4745,8 @@ var ChatController = class {
|
|
|
4565
4745
|
async stream(req, runId, res, after) {
|
|
4566
4746
|
const actor = await this.actorResolver.resolve(req);
|
|
4567
4747
|
const cursor = parseCursor(after) ?? parseCursor(req.headers["last-event-id"]) ?? 0;
|
|
4568
|
-
|
|
4748
|
+
const gone = readerGone(res);
|
|
4749
|
+
await this.pipe(res, runId, await this.agent.subscribeAs(actor, runId, gone), void 0, cursor, gone.signal);
|
|
4569
4750
|
}
|
|
4570
4751
|
async cancel(req, runId) {
|
|
4571
4752
|
const actor = await this.actorResolver.resolve(req);
|
|
@@ -4581,8 +4762,11 @@ var ChatController = class {
|
|
|
4581
4762
|
* and on any later `GET …/stream`, whichever replica serves it. A reconnecting client passes the
|
|
4582
4763
|
* last one it saw as `?after=`; frames at or below it are read off the sink but not re-sent.
|
|
4583
4764
|
* `meta`, `done` and `error` carry no id: `meta` is re-sent on every attach, the terminals once.
|
|
4765
|
+
*
|
|
4766
|
+
* A reader that disconnects aborts `gone`, which the sink honours by ending the subscription even
|
|
4767
|
+
* while the run is silent (parked on a person, or never writing again).
|
|
4584
4768
|
*/
|
|
4585
|
-
async pipe(res, runId, events, threadId, after = 0) {
|
|
4769
|
+
async pipe(res, runId, events, threadId, after = 0, gone) {
|
|
4586
4770
|
res.setHeader("Content-Type", "text/event-stream");
|
|
4587
4771
|
res.setHeader("Cache-Control", "no-cache");
|
|
4588
4772
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -4611,6 +4795,9 @@ data: ${line}
|
|
|
4611
4795
|
}, "forward");
|
|
4612
4796
|
try {
|
|
4613
4797
|
for await (const chunk of events) {
|
|
4798
|
+
if (gone?.aborted) {
|
|
4799
|
+
return;
|
|
4800
|
+
}
|
|
4614
4801
|
buffer += decoder.decode(chunk, {
|
|
4615
4802
|
stream: true
|
|
4616
4803
|
});
|
|
@@ -4624,11 +4811,17 @@ data: ${line}
|
|
|
4624
4811
|
newline = buffer.indexOf("\n");
|
|
4625
4812
|
}
|
|
4626
4813
|
}
|
|
4814
|
+
if (gone?.aborted) {
|
|
4815
|
+
return;
|
|
4816
|
+
}
|
|
4627
4817
|
if (buffer.length > 0) {
|
|
4628
4818
|
forward(buffer);
|
|
4629
4819
|
}
|
|
4630
4820
|
res.write("event: done\ndata: {}\n\n");
|
|
4631
4821
|
} catch (error) {
|
|
4822
|
+
if (gone?.aborted) {
|
|
4823
|
+
return;
|
|
4824
|
+
}
|
|
4632
4825
|
const payload = error instanceof import_nestjs_agent_core19.AgentStreamError ? {
|
|
4633
4826
|
code: error.code,
|
|
4634
4827
|
message: error.message
|
|
@@ -6529,13 +6722,22 @@ var LedgerQuotaProvider = class {
|
|
|
6529
6722
|
async report(query) {
|
|
6530
6723
|
const now = query.now ?? /* @__PURE__ */ new Date();
|
|
6531
6724
|
const actorRef = query.actor.id;
|
|
6532
|
-
const
|
|
6533
|
-
|
|
6534
|
-
];
|
|
6535
|
-
if (this.store.
|
|
6536
|
-
const
|
|
6537
|
-
|
|
6538
|
-
|
|
6725
|
+
const day = (0, import_nestjs_agent_core34.quotaPeriodRange)("day", now);
|
|
6726
|
+
const month = (0, import_nestjs_agent_core34.quotaPeriodRange)("month", now);
|
|
6727
|
+
const windows = [];
|
|
6728
|
+
if (this.store.usageWindows !== void 0) {
|
|
6729
|
+
const [dayUsed, monthUsed] = await this.store.usageWindows(actorRef, [
|
|
6730
|
+
day,
|
|
6731
|
+
month
|
|
6732
|
+
]);
|
|
6733
|
+
windows.push(this.usedWindow("day", dayUsed, day.resetsAt));
|
|
6734
|
+
windows.push(this.usedWindow("month", monthUsed, month.resetsAt));
|
|
6735
|
+
} else {
|
|
6736
|
+
windows.push(this.usedWindow("day", await this.store.quotaToday(actorRef, day.fromDay), day.resetsAt));
|
|
6737
|
+
if (this.store.usageBetween !== void 0) {
|
|
6738
|
+
const used = await this.store.usageBetween(actorRef, month.fromDay, month.toDay);
|
|
6739
|
+
windows.push(this.usedWindow("month", used, month.resetsAt));
|
|
6740
|
+
}
|
|
6539
6741
|
}
|
|
6540
6742
|
const blocked = (0, import_nestjs_agent_core34.exhaustedWindow)(windows);
|
|
6541
6743
|
const warning = (0, import_nestjs_agent_core34.quotaWarning)(windows);
|
|
@@ -6549,10 +6751,8 @@ var LedgerQuotaProvider = class {
|
|
|
6549
6751
|
} : {}
|
|
6550
6752
|
};
|
|
6551
6753
|
}
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
const used = await this.store.quotaToday(actorRef, range.fromDay);
|
|
6555
|
-
return this.window("day", used.usedTokens, this.usd(used), range.resetsAt);
|
|
6754
|
+
usedWindow(period, used, resetsAt) {
|
|
6755
|
+
return this.window(period, used?.usedTokens ?? 0, used === void 0 ? 0 : this.usd(used), resetsAt);
|
|
6556
6756
|
}
|
|
6557
6757
|
/** The window's USD: every priced row, or (with `countEstimatedCost: false`) reported cost only. */
|
|
6558
6758
|
usd(used) {
|