@dudousxd/nestjs-agent 1.16.1 → 1.17.1
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 +15 -0
- package/dist/durable/index.cjs +1686 -1616
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -0
- package/dist/durable/index.d.ts +1 -0
- package/dist/durable/index.js +1012 -942
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +452 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +73 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/durable/index.js
CHANGED
|
@@ -2,10 +2,10 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/durable/agent-run.workflow.ts
|
|
5
|
-
import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_STORE
|
|
5
|
+
import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_STORE, RUN_ENDED_BEFORE_TOOL_CALL, RunCancelledError, agentFailureCode, encodeStreamEvent as encodeStreamEvent2, isReplayIntegrityError, publishAgentRunFailed, releaseThreadRun, runAgentLoop, settleAll, settleDeadRun, settleUnsettledDelegation, streamFailure } from "@dudousxd/nestjs-agent-core";
|
|
6
6
|
import { RUN_GATEWAY, Workflow } from "@dudousxd/nestjs-durable";
|
|
7
7
|
import { isWorkflowControlFlowSignal } from "@dudousxd/nestjs-durable-core";
|
|
8
|
-
import { Inject as
|
|
8
|
+
import { Inject as Inject2, Injectable as Injectable2, Logger, Optional } from "@nestjs/common";
|
|
9
9
|
|
|
10
10
|
// src/agent-deps.ts
|
|
11
11
|
function utcDay(date = /* @__PURE__ */ new Date()) {
|
|
@@ -23,9 +23,26 @@ function childSinkWriter(writer) {
|
|
|
23
23
|
}
|
|
24
24
|
__name(childSinkWriter, "childSinkWriter");
|
|
25
25
|
|
|
26
|
-
// src/queue/chat-queue.
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// src/queue/chat-queue.token.ts
|
|
27
|
+
var AGENT_CHAT_QUEUE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:chat-queue");
|
|
28
|
+
|
|
29
|
+
// src/durable/agent-run.steps.ts
|
|
30
|
+
import { AGENT_DEPS_FACTORY, createFrameBuffer, createUiCollector, encodeStreamEvent, invokeWithTransientRetry, observeTurnFrames, publishAgentToolRetry, stampToolKinds, toolCallContext, traceLlmTurn, traceToolExecution, withAskTool, withMemoryTool, withSkillTool, withToolTimeout, withTurnFrames, wrapToolStepOutput } from "@dudousxd/nestjs-agent-core";
|
|
31
|
+
import { Step } from "@dudousxd/nestjs-durable";
|
|
32
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
33
|
+
|
|
34
|
+
// src/durable/outside-workflow-ctx.ts
|
|
35
|
+
import { runInWorkflowCtx } from "@dudousxd/nestjs-durable-core";
|
|
36
|
+
function outsideWorkflowCtx(fn) {
|
|
37
|
+
return runInWorkflowCtx(void 0, fn);
|
|
38
|
+
}
|
|
39
|
+
__name(outsideWorkflowCtx, "outsideWorkflowCtx");
|
|
40
|
+
function stepOf(ctx) {
|
|
41
|
+
return (name, fn, ...rest) => ctx.localStep(name, (...args) => outsideWorkflowCtx(() => fn(...args)), ...rest);
|
|
42
|
+
}
|
|
43
|
+
__name(stepOf, "stepOf");
|
|
44
|
+
|
|
45
|
+
// src/durable/agent-run.steps.ts
|
|
29
46
|
function _ts_decorate(decorators, target, key, desc) {
|
|
30
47
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
31
48
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -52,729 +69,289 @@ function _ts_param(paramIndex, decorator) {
|
|
|
52
69
|
};
|
|
53
70
|
}
|
|
54
71
|
__name(_ts_param, "_ts_param");
|
|
55
|
-
var
|
|
56
|
-
var ChatQueueService = class _ChatQueueService {
|
|
72
|
+
var AgentRunSteps = class {
|
|
57
73
|
static {
|
|
58
|
-
__name(this, "
|
|
74
|
+
__name(this, "AgentRunSteps");
|
|
75
|
+
}
|
|
76
|
+
factory;
|
|
77
|
+
constructor(factory) {
|
|
78
|
+
this.factory = factory;
|
|
59
79
|
}
|
|
60
|
-
store;
|
|
61
|
-
sink;
|
|
62
|
-
quotaProvider;
|
|
63
|
-
options;
|
|
64
|
-
staging;
|
|
65
|
-
logger = new Logger(_ChatQueueService.name);
|
|
66
80
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
81
|
+
* Retryable: every side effect (persist/quota/stream markers) happens loop-side under
|
|
82
|
+
* `hooks.step`, so a durable retry only re-runs the model call and re-streams its tokens — never
|
|
83
|
+
* double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
|
|
84
|
+
* across however many turns/retries it takes.
|
|
69
85
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.store = store;
|
|
73
|
-
this.sink = sink;
|
|
74
|
-
this.quotaProvider = quotaProvider;
|
|
75
|
-
this.options = options;
|
|
76
|
-
this.staging = staging;
|
|
77
|
-
}
|
|
78
|
-
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
79
|
-
get supported() {
|
|
80
|
-
return isChatQueueStore(this.store);
|
|
86
|
+
llm(input) {
|
|
87
|
+
return outsideWorkflowCtx(() => this.runLlm(input));
|
|
81
88
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
async runLlm(input) {
|
|
90
|
+
const deps = this.factory.forAgent(input.agentName);
|
|
91
|
+
const tools = withMemoryTool({
|
|
92
|
+
tools: withSkillTool({
|
|
93
|
+
tools: withAskTool({
|
|
94
|
+
tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList, {
|
|
95
|
+
...input.threadId !== void 0 ? {
|
|
96
|
+
threadId: input.threadId
|
|
97
|
+
} : {},
|
|
98
|
+
...input.agentName !== void 0 ? {
|
|
99
|
+
agentName: input.agentName
|
|
100
|
+
} : {}
|
|
101
|
+
}),
|
|
102
|
+
ask: deps.ask
|
|
103
|
+
}),
|
|
104
|
+
enabled: deps.skills !== void 0
|
|
105
|
+
}),
|
|
106
|
+
enabled: deps.memory?.provider.write !== void 0
|
|
107
|
+
});
|
|
108
|
+
const buffer = input.bufferOutput === true ? createFrameBuffer() : void 0;
|
|
109
|
+
let writer;
|
|
110
|
+
if (buffer !== void 0) {
|
|
111
|
+
writer = buffer.writer;
|
|
112
|
+
} else {
|
|
113
|
+
const opened = await deps.sink.open(input.sinkRunId);
|
|
114
|
+
writer = input.childSink ? childSinkWriter(opened) : opened;
|
|
86
115
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
const frames = observeTurnFrames(writer);
|
|
117
|
+
const turn = withTurnFrames(await traceLlmTurn(input.runId, input.step, () => deps.model.runTurn({
|
|
118
|
+
system: input.system,
|
|
119
|
+
messages: input.messages,
|
|
120
|
+
tools,
|
|
121
|
+
sink: frames.writer,
|
|
122
|
+
...input.model !== void 0 ? {
|
|
123
|
+
model: input.model
|
|
124
|
+
} : {}
|
|
125
|
+
})), frames.summary());
|
|
126
|
+
const stamped = stampToolKinds(turn, deps);
|
|
127
|
+
return buffer === void 0 ? stamped : {
|
|
128
|
+
...stamped,
|
|
129
|
+
bufferedFrames: buffer.frames()
|
|
98
130
|
};
|
|
99
131
|
}
|
|
100
132
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
133
|
+
* NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
|
|
134
|
+
* above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
|
|
135
|
+
* invocation (a DB deadlock, a lock-wait timeout — see `isTransientToolError`): the server rolled
|
|
136
|
+
* that work back, so re-invoking it is safe, and doing it here (inside this one step, never a new
|
|
137
|
+
* checkpoint) is exactly how the non-dispatched loop path handles it too. The timeout is applied
|
|
138
|
+
* HERE, handler-side, via `withToolTimeout` — never as a durable dispatch `timeoutMs`, which
|
|
139
|
+
* bounds engine liveness (retryable) rather than the tool's own business outcome — and per
|
|
140
|
+
* ATTEMPT, so a retried invocation gets its own fresh timeout window.
|
|
103
141
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
if (typeof runner.isRunActive !== "function") {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
return await runner.isRunActive(holder);
|
|
113
|
-
} catch {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
142
|
+
tool(input) {
|
|
143
|
+
return outsideWorkflowCtx(() => this.runTool(input));
|
|
116
144
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
async holder(threadId, runner) {
|
|
122
|
-
const holder = await this.queueStore().activeRunForThread(threadId);
|
|
123
|
-
if (holder === null) {
|
|
124
|
-
return {
|
|
125
|
-
live: null,
|
|
126
|
-
stale: null
|
|
127
|
-
};
|
|
145
|
+
async runTool(input) {
|
|
146
|
+
const deps = this.factory.forAgent(input.ctx.agentName);
|
|
147
|
+
if (deps.registry.spec(input.toolName)?.kind === "action" && input.toolType !== "action") {
|
|
148
|
+
throw new Error(`tool "${input.toolName}" is an action tool but was dispatched without approval: the dispatching process resolved its kind against a registry that does not have it`);
|
|
128
149
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
const sinkRunId = input.sinkRunId;
|
|
151
|
+
let opened;
|
|
152
|
+
const ui = createUiCollector(input.toolCallId, sinkRunId === void 0 ? void 0 : async (event) => {
|
|
153
|
+
opened ??= Promise.resolve(deps.sink.open(sinkRunId)).then((writer) => input.childSink === true ? childSinkWriter(writer) : writer);
|
|
154
|
+
await (await opened).write(encodeStreamEvent(event));
|
|
155
|
+
});
|
|
156
|
+
const ctx = {
|
|
157
|
+
...toolCallContext(input.ctx, input.toolCallId),
|
|
158
|
+
emitUi: ui.emit
|
|
159
|
+
};
|
|
160
|
+
const localClassify = deps.toolTransientRetry !== false ? deps.toolTransientRetry?.classify : void 0;
|
|
161
|
+
const transientRetry = input.transientRetry === false ? false : {
|
|
162
|
+
attempts: input.transientRetry.attempts,
|
|
163
|
+
backoffMs: input.transientRetry.backoffMs,
|
|
164
|
+
...localClassify !== void 0 ? {
|
|
165
|
+
classify: localClassify
|
|
166
|
+
} : {}
|
|
135
167
|
};
|
|
168
|
+
const output = await traceToolExecution(input.ctx.runId, {
|
|
169
|
+
toolCallId: input.toolCallId,
|
|
170
|
+
toolName: input.toolName,
|
|
171
|
+
toolType: input.toolType
|
|
172
|
+
}, () => invokeWithTransientRetry(() => {
|
|
173
|
+
ui.restart();
|
|
174
|
+
const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
|
|
175
|
+
return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
|
|
176
|
+
}, transientRetry, {
|
|
177
|
+
onRetry: /* @__PURE__ */ __name((attempt, error) => {
|
|
178
|
+
publishAgentToolRetry({
|
|
179
|
+
toolName: input.toolName,
|
|
180
|
+
toolCallId: input.toolCallId,
|
|
181
|
+
attempt,
|
|
182
|
+
message: error instanceof Error ? error.message : String(error)
|
|
183
|
+
});
|
|
184
|
+
}, "onRetry")
|
|
185
|
+
}));
|
|
186
|
+
return input.collectUi === true ? wrapToolStepOutput(output, ui.components()) : output;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
_ts_decorate([
|
|
190
|
+
Step({
|
|
191
|
+
retries: 3
|
|
192
|
+
}),
|
|
193
|
+
_ts_metadata("design:type", Function),
|
|
194
|
+
_ts_metadata("design:paramtypes", [
|
|
195
|
+
typeof DispatchedLlmInput === "undefined" ? Object : DispatchedLlmInput
|
|
196
|
+
]),
|
|
197
|
+
_ts_metadata("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
198
|
+
], AgentRunSteps.prototype, "llm", null);
|
|
199
|
+
_ts_decorate([
|
|
200
|
+
Step(),
|
|
201
|
+
_ts_metadata("design:type", Function),
|
|
202
|
+
_ts_metadata("design:paramtypes", [
|
|
203
|
+
typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
|
|
204
|
+
]),
|
|
205
|
+
_ts_metadata("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
206
|
+
], AgentRunSteps.prototype, "tool", null);
|
|
207
|
+
AgentRunSteps = _ts_decorate([
|
|
208
|
+
Injectable(),
|
|
209
|
+
_ts_param(0, Inject(AGENT_DEPS_FACTORY)),
|
|
210
|
+
_ts_metadata("design:type", Function),
|
|
211
|
+
_ts_metadata("design:paramtypes", [
|
|
212
|
+
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
|
|
213
|
+
])
|
|
214
|
+
], AgentRunSteps);
|
|
215
|
+
|
|
216
|
+
// src/durable/agent-run.workflow.ts
|
|
217
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
218
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
219
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
220
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
221
|
+
} else {
|
|
222
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
223
|
+
if (d = decorators[i]) {
|
|
224
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
229
|
+
}
|
|
230
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
231
|
+
function _ts_metadata2(metadataKey, metadataValue) {
|
|
232
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
233
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
237
|
+
function _ts_param2(paramIndex, decorator) {
|
|
238
|
+
return function(target, key) {
|
|
239
|
+
decorator(target, key, paramIndex);
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
__name(_ts_param2, "_ts_param");
|
|
243
|
+
var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
244
|
+
static {
|
|
245
|
+
__name(this, "AgentRunWorkflow");
|
|
246
|
+
}
|
|
247
|
+
factory;
|
|
248
|
+
store;
|
|
249
|
+
runs;
|
|
250
|
+
queue;
|
|
251
|
+
logger = new Logger(_AgentRunWorkflow.name);
|
|
252
|
+
constructor(factory, store, runs, queue) {
|
|
253
|
+
this.factory = factory;
|
|
254
|
+
this.store = store;
|
|
255
|
+
this.runs = runs;
|
|
256
|
+
this.queue = queue;
|
|
136
257
|
}
|
|
137
258
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* the queue
|
|
141
|
-
* the stream learns what comes next from the `queue` frame, and so the thread is already free
|
|
142
|
-
* when the client sees the run end.
|
|
259
|
+
* Move the thread past this settling turn — to the next queued message, started here as a
|
|
260
|
+
* fire-and-forget `ctx.startChild` under the message's own id, or to a paused/empty queue — and
|
|
261
|
+
* answer the `queue` frame to write before the turn's terminal.
|
|
143
262
|
*
|
|
144
|
-
*
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return {};
|
|
149
|
-
}
|
|
150
|
-
const store = this.store;
|
|
151
|
-
const { threadId, outcome } = args;
|
|
152
|
-
if (await store.activeRunForThread(threadId) !== args.runId) {
|
|
153
|
-
return {};
|
|
154
|
-
}
|
|
155
|
-
let holding = args.runId;
|
|
156
|
-
for (let attempt = 0; attempt < MAX_DRAIN_ATTEMPTS; attempt += 1) {
|
|
157
|
-
const items = await store.listQueue(threadId);
|
|
158
|
-
const head = items[0];
|
|
159
|
-
if (head === void 0) {
|
|
160
|
-
if (holding !== null) {
|
|
161
|
-
await store.releaseActiveStream(threadId, holding);
|
|
162
|
-
holding = null;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
return {};
|
|
166
|
-
}
|
|
167
|
-
let pause = await store.queuePause(threadId);
|
|
168
|
-
if (pause === null) {
|
|
169
|
-
const reason = pauseReasonFor(outcome, head);
|
|
170
|
-
if (reason !== null) {
|
|
171
|
-
pause = {
|
|
172
|
-
reason,
|
|
173
|
-
...outcome === "failed" && args.error !== void 0 ? {
|
|
174
|
-
message: args.error
|
|
175
|
-
} : {},
|
|
176
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
177
|
-
};
|
|
178
|
-
await store.setQueuePause(threadId, pause);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (pause === null) {
|
|
182
|
-
const blocked = await this.quotaBlock(head.actor);
|
|
183
|
-
if (blocked !== null) {
|
|
184
|
-
pause = {
|
|
185
|
-
reason: "quota_exceeded",
|
|
186
|
-
message: blocked,
|
|
187
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
188
|
-
};
|
|
189
|
-
await store.setQueuePause(threadId, pause);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (pause !== null) {
|
|
193
|
-
if (holding !== null) {
|
|
194
|
-
await store.releaseActiveStream(threadId, holding);
|
|
195
|
-
}
|
|
196
|
-
return {
|
|
197
|
-
frame: queueFrame(items, pause)
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
const claimed = await store.claimActiveStream(threadId, head.id, holding !== null ? {
|
|
201
|
-
replacing: holding
|
|
202
|
-
} : {});
|
|
203
|
-
if (!claimed) {
|
|
204
|
-
return {};
|
|
205
|
-
}
|
|
206
|
-
holding = head.id;
|
|
207
|
-
if (!await store.removeQueuedMessage(head.id)) {
|
|
208
|
-
await store.releaseActiveStream(threadId, head.id);
|
|
209
|
-
holding = null;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const next = {
|
|
213
|
-
runId: head.id,
|
|
214
|
-
messageId: head.id,
|
|
215
|
-
input: await this.inputFor(head)
|
|
216
|
-
};
|
|
217
|
-
return {
|
|
218
|
-
next,
|
|
219
|
-
frame: queueFrame(items.slice(1), null, {
|
|
220
|
-
messageId: head.id,
|
|
221
|
-
runId: head.id
|
|
222
|
-
})
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
if (holding !== null) {
|
|
226
|
-
await store.releaseActiveStream(threadId, holding);
|
|
227
|
-
}
|
|
228
|
-
return {};
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* {@link plan}, then start what it picked with `start`. What an in-process settle (the inline
|
|
232
|
-
* runner, a durable cancel issued outside a workflow) calls; answers the frame to write.
|
|
233
|
-
*/
|
|
234
|
-
async handoff(args, start) {
|
|
235
|
-
const plan = await this.plan(args);
|
|
236
|
-
if (plan.next === void 0) {
|
|
237
|
-
return plan.frame;
|
|
238
|
-
}
|
|
239
|
-
return await this.launch(args.threadId, plan.next, start) ? plan.frame : this.pausedFrame(args.threadId);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Start the head of an IDLE thread's queue — after a send queued behind a run that turned out to
|
|
243
|
-
* be gone, after a resume, after a stale holder. Does nothing while a live run holds the thread
|
|
244
|
-
* (its settle drains) or while the queue is paused. Answers the started run's id, if any.
|
|
263
|
+
* Journaled: the decision (which message, claimed and popped) is one `localStep`, so a replay
|
|
264
|
+
* reads it back instead of popping a second message, and the start is the runtime's own
|
|
265
|
+
* replay-safe spawn. Gated by `ctx.patched`, so a run recorded before the queue existed replays
|
|
266
|
+
* against the history it has. A no-op for a sub-agent's run and on a store without a queue.
|
|
245
267
|
*/
|
|
246
|
-
async
|
|
247
|
-
|
|
268
|
+
async advanceQueue(ctx, input, outcome, error) {
|
|
269
|
+
const queue = this.queue;
|
|
270
|
+
if (queue === void 0 || !queue.supported || input.sinkRunId !== void 0 || input.deliverTo !== void 0) {
|
|
248
271
|
return void 0;
|
|
249
272
|
}
|
|
250
|
-
|
|
251
|
-
if (live !== null) {
|
|
273
|
+
if (!await ctx.patched("agent:chat-queue")) {
|
|
252
274
|
return void 0;
|
|
253
275
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
276
|
+
const plan = await stepOf(ctx)(`queue:${outcome}`, async () => {
|
|
277
|
+
try {
|
|
278
|
+
const settled = outcome === "completed" && await this.isCancelled(ctx.runId) ? "cancelled" : outcome;
|
|
279
|
+
return await queue.plan({
|
|
280
|
+
threadId: input.threadId,
|
|
281
|
+
runId: ctx.runId,
|
|
282
|
+
outcome: settled,
|
|
283
|
+
...error !== void 0 ? {
|
|
284
|
+
error
|
|
285
|
+
} : {}
|
|
286
|
+
});
|
|
287
|
+
} catch {
|
|
288
|
+
return {};
|
|
289
|
+
}
|
|
261
290
|
});
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
const next = plan.next;
|
|
292
|
+
if (next === void 0) {
|
|
293
|
+
return plan.frame;
|
|
264
294
|
}
|
|
265
|
-
|
|
266
|
-
runId
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
295
|
+
try {
|
|
296
|
+
await ctx.startChild(_AgentRunWorkflow, next.input, next.runId);
|
|
297
|
+
return plan.frame;
|
|
298
|
+
} catch (failure) {
|
|
299
|
+
if (isWorkflowControlFlowSignal(failure)) {
|
|
300
|
+
throw failure;
|
|
301
|
+
}
|
|
302
|
+
const reason = failure instanceof Error ? failure.message : String(failure);
|
|
303
|
+
return stepOf(ctx)("queue:restore", async () => {
|
|
304
|
+
await queue.restore(input.threadId, next, "start_failed", reason);
|
|
305
|
+
return queue.pausedFrame(input.threadId);
|
|
306
|
+
});
|
|
270
307
|
}
|
|
271
|
-
return started ? plan.next.runId : void 0;
|
|
272
308
|
}
|
|
273
309
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
310
|
+
* Has someone asked this run to stop? Read from the runtime's own run status, which is where
|
|
311
|
+
* `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
|
|
312
|
+
* `cancelled` once it has settled.
|
|
313
|
+
*
|
|
314
|
+
* Costs a run read at each of the loop's observation points (two per step). That is a row the
|
|
315
|
+
* control plane already has, against a turn that is otherwise making model calls, and it buys the
|
|
316
|
+
* property that matters: the answer comes from a place BOTH the canceller and every pod that might
|
|
317
|
+
* replay this run can see.
|
|
318
|
+
*
|
|
319
|
+
* Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
|
|
320
|
+
* unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
|
|
321
|
+
* because a status read timed out. The loop journals that answer like any other, so two processes
|
|
322
|
+
* can never disagree about what was seen here.
|
|
277
323
|
*/
|
|
278
|
-
async
|
|
279
|
-
this.starting.add(next.runId);
|
|
324
|
+
async isCancelled(runId) {
|
|
280
325
|
try {
|
|
281
|
-
|
|
282
|
-
return
|
|
283
|
-
} catch
|
|
284
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
-
this.logger.error(`queued message ${next.messageId} could not start: ${message}`);
|
|
286
|
-
await this.restore(threadId, next, "start_failed", message);
|
|
326
|
+
const status = (await this.runs?.getRunDetail(runId))?.run.status;
|
|
327
|
+
return status === "cancelled" || status === "cancelling";
|
|
328
|
+
} catch {
|
|
287
329
|
return false;
|
|
288
|
-
} finally {
|
|
289
|
-
this.starting.delete(next.runId);
|
|
290
330
|
}
|
|
291
331
|
}
|
|
292
|
-
/** Put a turn that never started back at the head of its queue, paused, and free the thread. */
|
|
293
|
-
async restore(threadId, next, reason, message) {
|
|
294
|
-
const store = this.queueStore();
|
|
295
|
-
const { input } = next;
|
|
296
|
-
await store.enqueueMessage({
|
|
297
|
-
threadId,
|
|
298
|
-
actor: input.actor,
|
|
299
|
-
content: input.userText,
|
|
300
|
-
at: "head",
|
|
301
|
-
...input.attachments !== void 0 ? {
|
|
302
|
-
attachments: input.attachments
|
|
303
|
-
} : {},
|
|
304
|
-
...input.agentName !== void 0 ? {
|
|
305
|
-
agentName: input.agentName
|
|
306
|
-
} : {},
|
|
307
|
-
...input.model !== void 0 ? {
|
|
308
|
-
model: input.model
|
|
309
|
-
} : {},
|
|
310
|
-
...input.pageContext !== void 0 ? {
|
|
311
|
-
pageContext: input.pageContext
|
|
312
|
-
} : {}
|
|
313
|
-
});
|
|
314
|
-
await store.setQueuePause(threadId, {
|
|
315
|
-
reason,
|
|
316
|
-
message,
|
|
317
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
318
|
-
});
|
|
319
|
-
await store.releaseActiveStream(threadId, next.runId);
|
|
320
|
-
}
|
|
321
|
-
/** A `queue` frame with the thread's current state. */
|
|
322
|
-
async pausedFrame(threadId) {
|
|
323
|
-
return {
|
|
324
|
-
kind: "queue",
|
|
325
|
-
queue: await this.state(threadId)
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
332
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
333
|
+
* Settle the delegation a DETACHED run was started for, when that run ends without an answer.
|
|
334
|
+
*
|
|
335
|
+
* The loop delivers its own success (`deliver:detached`) but cannot catch its own crash, and the
|
|
336
|
+
* runtime settles the run row without knowing anything delegated it. Left alone, the card in the
|
|
337
|
+
* calling conversation says "started" for ever — the one state a reader can neither wait on nor
|
|
338
|
+
* act on. A no-op for every other run: only a detached one carries a delivery address.
|
|
332
339
|
*/
|
|
333
|
-
async
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
await this.write(holder, {
|
|
338
|
-
kind: "queue",
|
|
339
|
-
queue: state
|
|
340
|
-
}).catch((error) => {
|
|
341
|
-
this.logger.warn(`could not publish the queue of thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
342
|
-
});
|
|
340
|
+
async settleDetachedParent(ctx, input, outcome) {
|
|
341
|
+
const delivery = input.deliverTo;
|
|
342
|
+
if (delivery === void 0) {
|
|
343
|
+
return;
|
|
343
344
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
const { blocked } = await this.quotaProvider.report({
|
|
357
|
-
actor
|
|
358
|
-
});
|
|
359
|
-
if (blocked === void 0) {
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
return blocked.reason ?? `The ${blocked.period === "day" ? "daily" : "monthly"} quota is used up`;
|
|
363
|
-
} catch {
|
|
364
|
-
return null;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* The turn a queued message starts: everything it was queued with, today's date, and its
|
|
369
|
-
* attachments' urls minted afresh — the ones stored at enqueue time may have expired while it
|
|
370
|
-
* waited. An attachment the staging store no longer resolves keeps its stored url.
|
|
371
|
-
*/
|
|
372
|
-
async inputFor(message) {
|
|
373
|
-
const attachments = await this.freshAttachments(message.actor, message.attachments ?? []);
|
|
374
|
-
return {
|
|
375
|
-
threadId: message.threadId,
|
|
376
|
-
actor: message.actor,
|
|
377
|
-
userText: message.content,
|
|
378
|
-
day: utcDay(),
|
|
379
|
-
...message.agentName !== void 0 ? {
|
|
380
|
-
agentName: message.agentName
|
|
381
|
-
} : {},
|
|
382
|
-
...attachments.length > 0 ? {
|
|
383
|
-
attachments
|
|
384
|
-
} : {},
|
|
385
|
-
...message.pageContext !== void 0 ? {
|
|
386
|
-
pageContext: message.pageContext
|
|
387
|
-
} : {},
|
|
388
|
-
...message.model !== void 0 ? {
|
|
389
|
-
model: message.model
|
|
390
|
-
} : {}
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
async freshAttachments(actor, attachments) {
|
|
394
|
-
const staging = this.staging;
|
|
395
|
-
if (attachments.length === 0 || staging === void 0 || typeof staging.resolve !== "function") {
|
|
396
|
-
return attachments;
|
|
397
|
-
}
|
|
398
|
-
return Promise.all(attachments.map(async (attachment) => {
|
|
399
|
-
try {
|
|
400
|
-
return await staging.resolve({
|
|
401
|
-
mediaId: attachment.mediaId,
|
|
402
|
-
actor
|
|
403
|
-
}) ?? attachment;
|
|
404
|
-
} catch {
|
|
405
|
-
return attachment;
|
|
406
|
-
}
|
|
407
|
-
}));
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
ChatQueueService = _ts_decorate([
|
|
411
|
-
Injectable(),
|
|
412
|
-
_ts_param(0, Inject(AGENT_STORE)),
|
|
413
|
-
_ts_param(1, Inject(AGENT_SINK)),
|
|
414
|
-
_ts_param(2, Optional()),
|
|
415
|
-
_ts_param(2, Inject(AGENT_QUOTA_PROVIDER)),
|
|
416
|
-
_ts_param(3, Optional()),
|
|
417
|
-
_ts_param(3, Inject(AGENT_OPTIONS)),
|
|
418
|
-
_ts_param(4, Optional()),
|
|
419
|
-
_ts_param(4, Inject(AGENT_ATTACHMENT_STAGING)),
|
|
420
|
-
_ts_metadata("design:type", Function),
|
|
421
|
-
_ts_metadata("design:paramtypes", [
|
|
422
|
-
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
423
|
-
typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
|
|
424
|
-
typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
|
|
425
|
-
typeof Pick === "undefined" ? Object : Pick,
|
|
426
|
-
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
|
|
427
|
-
])
|
|
428
|
-
], ChatQueueService);
|
|
429
|
-
function pauseReasonFor(outcome, head) {
|
|
430
|
-
if (outcome === "failed") {
|
|
431
|
-
return "run_failed";
|
|
432
|
-
}
|
|
433
|
-
if (outcome === "cancelled" && head.interrupt !== true) {
|
|
434
|
-
return "cancelled";
|
|
435
|
-
}
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
__name(pauseReasonFor, "pauseReasonFor");
|
|
439
|
-
function queueFrame(items, paused, started) {
|
|
440
|
-
return {
|
|
441
|
-
kind: "queue",
|
|
442
|
-
queue: {
|
|
443
|
-
items: items.map(queuedMessageView),
|
|
444
|
-
paused
|
|
445
|
-
},
|
|
446
|
-
...started !== void 0 ? {
|
|
447
|
-
started
|
|
448
|
-
} : {}
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
__name(queueFrame, "queueFrame");
|
|
452
|
-
|
|
453
|
-
// src/durable/agent-run.steps.ts
|
|
454
|
-
import { AGENT_DEPS_FACTORY, createFrameBuffer, createUiCollector, encodeStreamEvent as encodeStreamEvent2, invokeWithTransientRetry, observeTurnFrames, publishAgentToolRetry, stampToolKinds, traceLlmTurn, traceToolExecution, withAskTool, withMemoryTool, withSkillTool, withToolTimeout, withTurnFrames, wrapToolStepOutput } from "@dudousxd/nestjs-agent-core";
|
|
455
|
-
import { Step } from "@dudousxd/nestjs-durable";
|
|
456
|
-
import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
|
|
457
|
-
|
|
458
|
-
// src/durable/outside-workflow-ctx.ts
|
|
459
|
-
import { runInWorkflowCtx } from "@dudousxd/nestjs-durable-core";
|
|
460
|
-
function outsideWorkflowCtx(fn) {
|
|
461
|
-
return runInWorkflowCtx(void 0, fn);
|
|
462
|
-
}
|
|
463
|
-
__name(outsideWorkflowCtx, "outsideWorkflowCtx");
|
|
464
|
-
function stepOf(ctx) {
|
|
465
|
-
return (name, fn, ...rest) => ctx.localStep(name, (...args) => outsideWorkflowCtx(() => fn(...args)), ...rest);
|
|
466
|
-
}
|
|
467
|
-
__name(stepOf, "stepOf");
|
|
468
|
-
|
|
469
|
-
// src/durable/agent-run.steps.ts
|
|
470
|
-
function _ts_decorate2(decorators, target, key, desc) {
|
|
471
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
472
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
473
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
474
|
-
} else {
|
|
475
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
476
|
-
if (d = decorators[i]) {
|
|
477
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
482
|
-
}
|
|
483
|
-
__name(_ts_decorate2, "_ts_decorate");
|
|
484
|
-
function _ts_metadata2(metadataKey, metadataValue) {
|
|
485
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
486
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
__name(_ts_metadata2, "_ts_metadata");
|
|
490
|
-
function _ts_param2(paramIndex, decorator) {
|
|
491
|
-
return function(target, key) {
|
|
492
|
-
decorator(target, key, paramIndex);
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
__name(_ts_param2, "_ts_param");
|
|
496
|
-
var AgentRunSteps = class {
|
|
497
|
-
static {
|
|
498
|
-
__name(this, "AgentRunSteps");
|
|
499
|
-
}
|
|
500
|
-
factory;
|
|
501
|
-
constructor(factory) {
|
|
502
|
-
this.factory = factory;
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* Retryable: every side effect (persist/quota/stream markers) happens loop-side under
|
|
506
|
-
* `hooks.step`, so a durable retry only re-runs the model call and re-streams its tokens — never
|
|
507
|
-
* double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
|
|
508
|
-
* across however many turns/retries it takes.
|
|
509
|
-
*/
|
|
510
|
-
llm(input) {
|
|
511
|
-
return outsideWorkflowCtx(() => this.runLlm(input));
|
|
512
|
-
}
|
|
513
|
-
async runLlm(input) {
|
|
514
|
-
const deps = this.factory.forAgent(input.agentName);
|
|
515
|
-
const tools = withMemoryTool({
|
|
516
|
-
tools: withSkillTool({
|
|
517
|
-
tools: withAskTool({
|
|
518
|
-
tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList, {
|
|
519
|
-
...input.threadId !== void 0 ? {
|
|
520
|
-
threadId: input.threadId
|
|
521
|
-
} : {},
|
|
522
|
-
...input.agentName !== void 0 ? {
|
|
523
|
-
agentName: input.agentName
|
|
524
|
-
} : {}
|
|
525
|
-
}),
|
|
526
|
-
ask: deps.ask
|
|
527
|
-
}),
|
|
528
|
-
enabled: deps.skills !== void 0
|
|
529
|
-
}),
|
|
530
|
-
enabled: deps.memory?.provider.write !== void 0
|
|
531
|
-
});
|
|
532
|
-
const buffer = input.bufferOutput === true ? createFrameBuffer() : void 0;
|
|
533
|
-
let writer;
|
|
534
|
-
if (buffer !== void 0) {
|
|
535
|
-
writer = buffer.writer;
|
|
536
|
-
} else {
|
|
537
|
-
const opened = await deps.sink.open(input.sinkRunId);
|
|
538
|
-
writer = input.childSink ? childSinkWriter(opened) : opened;
|
|
539
|
-
}
|
|
540
|
-
const frames = observeTurnFrames(writer);
|
|
541
|
-
const turn = withTurnFrames(await traceLlmTurn(input.runId, input.step, () => deps.model.runTurn({
|
|
542
|
-
system: input.system,
|
|
543
|
-
messages: input.messages,
|
|
544
|
-
tools,
|
|
545
|
-
sink: frames.writer,
|
|
546
|
-
...input.model !== void 0 ? {
|
|
547
|
-
model: input.model
|
|
548
|
-
} : {}
|
|
549
|
-
})), frames.summary());
|
|
550
|
-
const stamped = stampToolKinds(turn, deps);
|
|
551
|
-
return buffer === void 0 ? stamped : {
|
|
552
|
-
...stamped,
|
|
553
|
-
bufferedFrames: buffer.frames()
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
|
|
558
|
-
* above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
|
|
559
|
-
* invocation (a DB deadlock, a lock-wait timeout — see `isTransientToolError`): the server rolled
|
|
560
|
-
* that work back, so re-invoking it is safe, and doing it here (inside this one step, never a new
|
|
561
|
-
* checkpoint) is exactly how the non-dispatched loop path handles it too. The timeout is applied
|
|
562
|
-
* HERE, handler-side, via `withToolTimeout` — never as a durable dispatch `timeoutMs`, which
|
|
563
|
-
* bounds engine liveness (retryable) rather than the tool's own business outcome — and per
|
|
564
|
-
* ATTEMPT, so a retried invocation gets its own fresh timeout window.
|
|
565
|
-
*/
|
|
566
|
-
tool(input) {
|
|
567
|
-
return outsideWorkflowCtx(() => this.runTool(input));
|
|
568
|
-
}
|
|
569
|
-
async runTool(input) {
|
|
570
|
-
const deps = this.factory.forAgent(input.ctx.agentName);
|
|
571
|
-
if (deps.registry.spec(input.toolName)?.kind === "action" && input.toolType !== "action") {
|
|
572
|
-
throw new Error(`tool "${input.toolName}" is an action tool but was dispatched without approval: the dispatching process resolved its kind against a registry that does not have it`);
|
|
573
|
-
}
|
|
574
|
-
const sinkRunId = input.sinkRunId;
|
|
575
|
-
let opened;
|
|
576
|
-
const ui = createUiCollector(input.toolCallId, sinkRunId === void 0 ? void 0 : async (event) => {
|
|
577
|
-
opened ??= Promise.resolve(deps.sink.open(sinkRunId)).then((writer) => input.childSink === true ? childSinkWriter(writer) : writer);
|
|
578
|
-
await (await opened).write(encodeStreamEvent2(event));
|
|
579
|
-
});
|
|
580
|
-
const ctx = {
|
|
581
|
-
...input.ctx,
|
|
582
|
-
emitUi: ui.emit
|
|
583
|
-
};
|
|
584
|
-
const localClassify = deps.toolTransientRetry !== false ? deps.toolTransientRetry?.classify : void 0;
|
|
585
|
-
const transientRetry = input.transientRetry === false ? false : {
|
|
586
|
-
attempts: input.transientRetry.attempts,
|
|
587
|
-
backoffMs: input.transientRetry.backoffMs,
|
|
588
|
-
...localClassify !== void 0 ? {
|
|
589
|
-
classify: localClassify
|
|
590
|
-
} : {}
|
|
591
|
-
};
|
|
592
|
-
const output = await traceToolExecution(input.ctx.runId, {
|
|
593
|
-
toolCallId: input.toolCallId,
|
|
594
|
-
toolName: input.toolName,
|
|
595
|
-
toolType: input.toolType
|
|
596
|
-
}, () => invokeWithTransientRetry(() => {
|
|
597
|
-
ui.restart();
|
|
598
|
-
const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
|
|
599
|
-
return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
|
|
600
|
-
}, transientRetry, {
|
|
601
|
-
onRetry: /* @__PURE__ */ __name((attempt, error) => {
|
|
602
|
-
publishAgentToolRetry({
|
|
603
|
-
toolName: input.toolName,
|
|
604
|
-
toolCallId: input.toolCallId,
|
|
605
|
-
attempt,
|
|
606
|
-
message: error instanceof Error ? error.message : String(error)
|
|
607
|
-
});
|
|
608
|
-
}, "onRetry")
|
|
609
|
-
}));
|
|
610
|
-
return input.collectUi === true ? wrapToolStepOutput(output, ui.components()) : output;
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
_ts_decorate2([
|
|
614
|
-
Step({
|
|
615
|
-
retries: 3
|
|
616
|
-
}),
|
|
617
|
-
_ts_metadata2("design:type", Function),
|
|
618
|
-
_ts_metadata2("design:paramtypes", [
|
|
619
|
-
typeof DispatchedLlmInput === "undefined" ? Object : DispatchedLlmInput
|
|
620
|
-
]),
|
|
621
|
-
_ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
622
|
-
], AgentRunSteps.prototype, "llm", null);
|
|
623
|
-
_ts_decorate2([
|
|
624
|
-
Step(),
|
|
625
|
-
_ts_metadata2("design:type", Function),
|
|
626
|
-
_ts_metadata2("design:paramtypes", [
|
|
627
|
-
typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
|
|
628
|
-
]),
|
|
629
|
-
_ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
630
|
-
], AgentRunSteps.prototype, "tool", null);
|
|
631
|
-
AgentRunSteps = _ts_decorate2([
|
|
632
|
-
Injectable2(),
|
|
633
|
-
_ts_param2(0, Inject2(AGENT_DEPS_FACTORY)),
|
|
634
|
-
_ts_metadata2("design:type", Function),
|
|
635
|
-
_ts_metadata2("design:paramtypes", [
|
|
636
|
-
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
|
|
637
|
-
])
|
|
638
|
-
], AgentRunSteps);
|
|
639
|
-
|
|
640
|
-
// src/durable/agent-run.workflow.ts
|
|
641
|
-
function _ts_decorate3(decorators, target, key, desc) {
|
|
642
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
643
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
644
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
645
|
-
} else {
|
|
646
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
647
|
-
if (d = decorators[i]) {
|
|
648
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
653
|
-
}
|
|
654
|
-
__name(_ts_decorate3, "_ts_decorate");
|
|
655
|
-
function _ts_metadata3(metadataKey, metadataValue) {
|
|
656
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
657
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
__name(_ts_metadata3, "_ts_metadata");
|
|
661
|
-
function _ts_param3(paramIndex, decorator) {
|
|
662
|
-
return function(target, key) {
|
|
663
|
-
decorator(target, key, paramIndex);
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
__name(_ts_param3, "_ts_param");
|
|
667
|
-
var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
668
|
-
static {
|
|
669
|
-
__name(this, "AgentRunWorkflow");
|
|
670
|
-
}
|
|
671
|
-
factory;
|
|
672
|
-
store;
|
|
673
|
-
runs;
|
|
674
|
-
queue;
|
|
675
|
-
constructor(factory, store, runs, queue) {
|
|
676
|
-
this.factory = factory;
|
|
677
|
-
this.store = store;
|
|
678
|
-
this.runs = runs;
|
|
679
|
-
this.queue = queue;
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Move the thread past this settling turn — to the next queued message, started here as a
|
|
683
|
-
* fire-and-forget `ctx.startChild` under the message's own id, or to a paused/empty queue — and
|
|
684
|
-
* answer the `queue` frame to write before the turn's terminal.
|
|
685
|
-
*
|
|
686
|
-
* Journaled: the decision (which message, claimed and popped) is one `localStep`, so a replay
|
|
687
|
-
* reads it back instead of popping a second message, and the start is the runtime's own
|
|
688
|
-
* replay-safe spawn. Gated by `ctx.patched`, so a run recorded before the queue existed replays
|
|
689
|
-
* against the history it has. A no-op for a sub-agent's run and on a store without a queue.
|
|
690
|
-
*/
|
|
691
|
-
async advanceQueue(ctx, input, outcome, error) {
|
|
692
|
-
const queue = this.queue;
|
|
693
|
-
if (queue === void 0 || !queue.supported || input.sinkRunId !== void 0 || input.deliverTo !== void 0) {
|
|
694
|
-
return void 0;
|
|
695
|
-
}
|
|
696
|
-
if (!await ctx.patched("agent:chat-queue")) {
|
|
697
|
-
return void 0;
|
|
698
|
-
}
|
|
699
|
-
const plan = await stepOf(ctx)(`queue:${outcome}`, async () => {
|
|
700
|
-
try {
|
|
701
|
-
const settled = outcome === "completed" && await this.isCancelled(ctx.runId) ? "cancelled" : outcome;
|
|
702
|
-
return await queue.plan({
|
|
703
|
-
threadId: input.threadId,
|
|
704
|
-
runId: ctx.runId,
|
|
705
|
-
outcome: settled,
|
|
706
|
-
...error !== void 0 ? {
|
|
707
|
-
error
|
|
708
|
-
} : {}
|
|
709
|
-
});
|
|
710
|
-
} catch {
|
|
711
|
-
return {};
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
const next = plan.next;
|
|
715
|
-
if (next === void 0) {
|
|
716
|
-
return plan.frame;
|
|
717
|
-
}
|
|
718
|
-
try {
|
|
719
|
-
await ctx.startChild(_AgentRunWorkflow, next.input, next.runId);
|
|
720
|
-
return plan.frame;
|
|
721
|
-
} catch (failure) {
|
|
722
|
-
if (isWorkflowControlFlowSignal(failure)) {
|
|
723
|
-
throw failure;
|
|
724
|
-
}
|
|
725
|
-
const reason = failure instanceof Error ? failure.message : String(failure);
|
|
726
|
-
return stepOf(ctx)("queue:restore", async () => {
|
|
727
|
-
await queue.restore(input.threadId, next, "start_failed", reason);
|
|
728
|
-
return queue.pausedFrame(input.threadId);
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Has someone asked this run to stop? Read from the runtime's own run status, which is where
|
|
734
|
-
* `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
|
|
735
|
-
* `cancelled` once it has settled.
|
|
736
|
-
*
|
|
737
|
-
* Costs a run read at each of the loop's observation points (two per step). That is a row the
|
|
738
|
-
* control plane already has, against a turn that is otherwise making model calls, and it buys the
|
|
739
|
-
* property that matters: the answer comes from a place BOTH the canceller and every pod that might
|
|
740
|
-
* replay this run can see.
|
|
741
|
-
*
|
|
742
|
-
* Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
|
|
743
|
-
* unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
|
|
744
|
-
* because a status read timed out. The loop journals that answer like any other, so two processes
|
|
745
|
-
* can never disagree about what was seen here.
|
|
746
|
-
*/
|
|
747
|
-
async isCancelled(runId) {
|
|
748
|
-
try {
|
|
749
|
-
const status = (await this.runs?.getRunDetail(runId))?.run.status;
|
|
750
|
-
return status === "cancelled" || status === "cancelling";
|
|
751
|
-
} catch {
|
|
752
|
-
return false;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* Settle the delegation a DETACHED run was started for, when that run ends without an answer.
|
|
757
|
-
*
|
|
758
|
-
* The loop delivers its own success (`deliver:detached`) but cannot catch its own crash, and the
|
|
759
|
-
* runtime settles the run row without knowing anything delegated it. Left alone, the card in the
|
|
760
|
-
* calling conversation says "started" for ever — the one state a reader can neither wait on nor
|
|
761
|
-
* act on. A no-op for every other run: only a detached one carries a delivery address.
|
|
762
|
-
*/
|
|
763
|
-
async settleDetachedParent(ctx, input, outcome) {
|
|
764
|
-
const delivery = input.deliverTo;
|
|
765
|
-
if (delivery === void 0) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
await stepOf(ctx)("deliver:detached:unsettled", () => settleUnsettledDelegation({
|
|
769
|
-
store: this.store,
|
|
770
|
-
delivery,
|
|
771
|
-
agent: input.agentName ?? "default",
|
|
772
|
-
runId: ctx.runId,
|
|
773
|
-
status: outcome.status,
|
|
774
|
-
...outcome.error !== void 0 ? {
|
|
775
|
-
error: outcome.error
|
|
776
|
-
} : {}
|
|
777
|
-
}));
|
|
345
|
+
await stepOf(ctx)("deliver:detached:unsettled", () => settleUnsettledDelegation({
|
|
346
|
+
store: this.store,
|
|
347
|
+
delivery,
|
|
348
|
+
agent: input.agentName ?? "default",
|
|
349
|
+
runId: ctx.runId,
|
|
350
|
+
status: outcome.status,
|
|
351
|
+
...outcome.error !== void 0 ? {
|
|
352
|
+
error: outcome.error
|
|
353
|
+
} : {}
|
|
354
|
+
}));
|
|
778
355
|
}
|
|
779
356
|
async run(ctx, input) {
|
|
780
357
|
const day = input.day ?? utcDay();
|
|
@@ -806,7 +383,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
806
383
|
end: /* @__PURE__ */ __name(async () => {
|
|
807
384
|
const frame = await this.advanceQueue(ctx, input, "completed");
|
|
808
385
|
if (frame !== void 0) {
|
|
809
|
-
await writer.write(
|
|
386
|
+
await writer.write(encodeStreamEvent2(frame));
|
|
810
387
|
}
|
|
811
388
|
await writer.end();
|
|
812
389
|
}, "end")
|
|
@@ -950,30 +527,42 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
950
527
|
throw error;
|
|
951
528
|
}
|
|
952
529
|
if (isReplayIntegrityError(error)) {
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
};
|
|
530
|
+
const detail = error.message;
|
|
531
|
+
const code2 = agentFailureCode(error);
|
|
532
|
+
this.logger.error(`agent run ${ctx.runId} failed (${code2}): ${detail}`);
|
|
957
533
|
publishAgentRunFailed({
|
|
958
534
|
runId: ctx.runId,
|
|
959
|
-
|
|
535
|
+
code: code2,
|
|
536
|
+
message: detail
|
|
960
537
|
});
|
|
961
|
-
await (
|
|
538
|
+
await settleDeadRun(this.store, {
|
|
539
|
+
runId: ctx.runId,
|
|
540
|
+
threadId: input.threadId,
|
|
541
|
+
failure: {
|
|
542
|
+
code: code2,
|
|
543
|
+
message: detail
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
await (await hooks.openSink()).fail(streamFailure(error));
|
|
962
547
|
throw error;
|
|
963
548
|
}
|
|
964
549
|
const message = error instanceof Error ? error.message : String(error);
|
|
965
550
|
const code = agentFailureCode(error);
|
|
551
|
+
this.logger.error(`agent run ${ctx.runId} failed (${code}): ${message}`);
|
|
966
552
|
publishAgentRunFailed({
|
|
967
553
|
runId: ctx.runId,
|
|
968
554
|
code,
|
|
969
555
|
message
|
|
970
556
|
});
|
|
971
|
-
await stepOf(ctx)("persist:run:fail", () =>
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
557
|
+
await stepOf(ctx)("persist:run:fail", async () => {
|
|
558
|
+
await this.store.recordRunEnd?.({
|
|
559
|
+
runId: ctx.runId,
|
|
560
|
+
status: "failed",
|
|
561
|
+
errorCode: code,
|
|
562
|
+
errorMessage: message
|
|
563
|
+
});
|
|
564
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(ctx.runId, RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
565
|
+
});
|
|
977
566
|
const queueFrame2 = await this.advanceQueue(ctx, input, "failed", message);
|
|
978
567
|
await stepOf(ctx)("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
|
|
979
568
|
await this.settleDetachedParent(ctx, input, {
|
|
@@ -982,29 +571,27 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
982
571
|
});
|
|
983
572
|
const writer = await hooks.openSink();
|
|
984
573
|
if (queueFrame2 !== void 0) {
|
|
985
|
-
await writer.write(
|
|
574
|
+
await writer.write(encodeStreamEvent2(queueFrame2));
|
|
986
575
|
}
|
|
987
|
-
await writer.fail(
|
|
988
|
-
code,
|
|
989
|
-
message
|
|
990
|
-
});
|
|
576
|
+
await writer.fail(streamFailure(error));
|
|
991
577
|
throw error;
|
|
992
578
|
}
|
|
993
579
|
}
|
|
994
580
|
};
|
|
995
|
-
AgentRunWorkflow =
|
|
996
|
-
|
|
581
|
+
AgentRunWorkflow = _ts_decorate2([
|
|
582
|
+
Injectable2(),
|
|
997
583
|
Workflow({
|
|
998
584
|
name: "agent.run",
|
|
999
585
|
version: "1"
|
|
1000
586
|
}),
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
587
|
+
_ts_param2(0, Inject2(AGENT_DEPS_FACTORY2)),
|
|
588
|
+
_ts_param2(1, Inject2(AGENT_STORE)),
|
|
589
|
+
_ts_param2(2, Optional()),
|
|
590
|
+
_ts_param2(2, Inject2(RUN_GATEWAY)),
|
|
591
|
+
_ts_param2(3, Optional()),
|
|
592
|
+
_ts_param2(3, Inject2(AGENT_CHAT_QUEUE)),
|
|
593
|
+
_ts_metadata2("design:type", Function),
|
|
594
|
+
_ts_metadata2("design:paramtypes", [
|
|
1008
595
|
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
|
|
1009
596
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
1010
597
|
typeof RunGateway === "undefined" ? Object : RunGateway,
|
|
@@ -1018,10 +605,10 @@ __name(isSignalTimeout, "isSignalTimeout");
|
|
|
1018
605
|
|
|
1019
606
|
// src/durable/durable-agent-runner.ts
|
|
1020
607
|
import { randomUUID } from "crypto";
|
|
1021
|
-
import { AGENT_SINK
|
|
608
|
+
import { AGENT_SINK, AGENT_STORE as AGENT_STORE2, encodeStreamEvent as encodeStreamEvent3, releaseThreadRun as releaseThreadRun2 } from "@dudousxd/nestjs-agent-core";
|
|
1022
609
|
import { RUN_GATEWAY as RUN_GATEWAY2, WorkflowService } from "@dudousxd/nestjs-durable";
|
|
1023
610
|
import { isWorkflowControlFlowSignal as isWorkflowControlFlowSignal2 } from "@dudousxd/nestjs-durable-core";
|
|
1024
|
-
import { Inject as
|
|
611
|
+
import { Inject as Inject3, Injectable as Injectable3, Logger as Logger2, Optional as Optional2 } from "@nestjs/common";
|
|
1025
612
|
|
|
1026
613
|
// src/in-process-sink.ts
|
|
1027
614
|
import { AgentStreamError } from "@dudousxd/nestjs-agent-core";
|
|
@@ -1092,7 +679,7 @@ var InProcessTokenStreamSink = class {
|
|
|
1092
679
|
};
|
|
1093
680
|
|
|
1094
681
|
// src/durable/durable-agent-runner.ts
|
|
1095
|
-
function
|
|
682
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
1096
683
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1097
684
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1098
685
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1105,19 +692,19 @@ function _ts_decorate4(decorators, target, key, desc) {
|
|
|
1105
692
|
}
|
|
1106
693
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1107
694
|
}
|
|
1108
|
-
__name(
|
|
1109
|
-
function
|
|
695
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
696
|
+
function _ts_metadata3(metadataKey, metadataValue) {
|
|
1110
697
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1111
698
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
1112
699
|
}
|
|
1113
700
|
}
|
|
1114
|
-
__name(
|
|
1115
|
-
function
|
|
701
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
702
|
+
function _ts_param3(paramIndex, decorator) {
|
|
1116
703
|
return function(target, key) {
|
|
1117
704
|
decorator(target, key, paramIndex);
|
|
1118
705
|
};
|
|
1119
706
|
}
|
|
1120
|
-
__name(
|
|
707
|
+
__name(_ts_param3, "_ts_param");
|
|
1121
708
|
function threadOfRun(input) {
|
|
1122
709
|
if (typeof input !== "object" || input === null || !("threadId" in input)) {
|
|
1123
710
|
return void 0;
|
|
@@ -1225,7 +812,7 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
1225
812
|
runId: nextRunId
|
|
1226
813
|
}));
|
|
1227
814
|
if (frame !== void 0) {
|
|
1228
|
-
await writer.write(
|
|
815
|
+
await writer.write(encodeStreamEvent3(frame));
|
|
1229
816
|
}
|
|
1230
817
|
} catch (error) {
|
|
1231
818
|
this.logger.error(`could not advance the queue of thread ${threadId} after cancelling ${runId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1233,7 +820,7 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
1233
820
|
}
|
|
1234
821
|
await releaseThreadRun2(this.store, threadId, runId);
|
|
1235
822
|
}
|
|
1236
|
-
await writer.write(
|
|
823
|
+
await writer.write(encodeStreamEvent3({
|
|
1237
824
|
kind: "cancelled"
|
|
1238
825
|
}));
|
|
1239
826
|
await writer.end();
|
|
@@ -1260,14 +847,15 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
1260
847
|
}
|
|
1261
848
|
}
|
|
1262
849
|
};
|
|
1263
|
-
DurableAgentRunner =
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
850
|
+
DurableAgentRunner = _ts_decorate3([
|
|
851
|
+
Injectable3(),
|
|
852
|
+
_ts_param3(1, Inject3(RUN_GATEWAY2)),
|
|
853
|
+
_ts_param3(2, Inject3(AGENT_STORE2)),
|
|
854
|
+
_ts_param3(3, Inject3(AGENT_SINK)),
|
|
855
|
+
_ts_param3(4, Optional2()),
|
|
856
|
+
_ts_param3(4, Inject3(AGENT_CHAT_QUEUE)),
|
|
857
|
+
_ts_metadata3("design:type", Function),
|
|
858
|
+
_ts_metadata3("design:paramtypes", [
|
|
1271
859
|
typeof WorkflowService === "undefined" ? Object : WorkflowService,
|
|
1272
860
|
typeof RunGateway === "undefined" ? Object : RunGateway,
|
|
1273
861
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
@@ -1279,7 +867,7 @@ DurableAgentRunner = _ts_decorate4([
|
|
|
1279
867
|
// src/durable/agent-durable.module.ts
|
|
1280
868
|
import { AGENT_DURABLE_RUNNER } from "@dudousxd/nestjs-agent-core";
|
|
1281
869
|
import { Global, Module } from "@nestjs/common";
|
|
1282
|
-
function
|
|
870
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
1283
871
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1284
872
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1285
873
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1292,7 +880,7 @@ function _ts_decorate5(decorators, target, key, desc) {
|
|
|
1292
880
|
}
|
|
1293
881
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1294
882
|
}
|
|
1295
|
-
__name(
|
|
883
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
1296
884
|
function runnerProviders() {
|
|
1297
885
|
return [
|
|
1298
886
|
DurableAgentRunner,
|
|
@@ -1308,7 +896,7 @@ var AgentDurableHttpModule = class AgentDurableHttpModule2 {
|
|
|
1308
896
|
__name(this, "AgentDurableHttpModule");
|
|
1309
897
|
}
|
|
1310
898
|
};
|
|
1311
|
-
AgentDurableHttpModule =
|
|
899
|
+
AgentDurableHttpModule = _ts_decorate4([
|
|
1312
900
|
Global(),
|
|
1313
901
|
Module({
|
|
1314
902
|
providers: [
|
|
@@ -1346,7 +934,7 @@ var AgentDurableModule = class _AgentDurableModule {
|
|
|
1346
934
|
};
|
|
1347
935
|
}
|
|
1348
936
|
};
|
|
1349
|
-
AgentDurableModule =
|
|
937
|
+
AgentDurableModule = _ts_decorate4([
|
|
1350
938
|
Global(),
|
|
1351
939
|
Module({
|
|
1352
940
|
providers: [
|
|
@@ -1364,14 +952,14 @@ AgentDurableModule = _ts_decorate5([
|
|
|
1364
952
|
|
|
1365
953
|
// src/agent.module.ts
|
|
1366
954
|
import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER13, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY7, AGENT_DURABLE_RUNNER as AGENT_DURABLE_RUNNER2, AGENT_MEMORY as AGENT_MEMORY3, AGENT_MODEL as AGENT_MODEL2, AGENT_MODEL_CATALOG as AGENT_MODEL_CATALOG3, AGENT_OPTIONS as AGENT_OPTIONS7, AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER3, AGENT_REGISTRY as AGENT_REGISTRY6, AGENT_ROLES_POLICY as AGENT_ROLES_POLICY2, AGENT_RUNNER as AGENT_RUNNER2, AGENT_SINK as AGENT_SINK4, AGENT_SKILLS as AGENT_SKILLS3, AGENT_SKILL_SOURCES as AGENT_SKILL_SOURCES2, AGENT_STORE as AGENT_STORE7, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry6, DefaultRolesPolicy, InMemoryAgentStore, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
|
|
1367
|
-
import { Global as Global2, Logger as
|
|
955
|
+
import { Global as Global2, Logger as Logger8, Module as Module2 } from "@nestjs/common";
|
|
1368
956
|
import { DiscoveryModule, ModulesContainer, RouterModule } from "@nestjs/core";
|
|
1369
957
|
|
|
1370
958
|
// src/agent-deps.factory.ts
|
|
1371
|
-
import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK as
|
|
1372
|
-
import { AGENT_OPTIONS
|
|
1373
|
-
import { Inject as
|
|
1374
|
-
function
|
|
959
|
+
import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK as AGENT_SINK2, AGENT_SKILLS, AGENT_STORE as AGENT_STORE3, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2, normalizeDelegation, summarizeWithModel, windowHistory } from "@dudousxd/nestjs-agent-core";
|
|
960
|
+
import { AGENT_OPTIONS } from "@dudousxd/nestjs-agent-core";
|
|
961
|
+
import { Inject as Inject4, Injectable as Injectable4, Optional as Optional3 } from "@nestjs/common";
|
|
962
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1375
963
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1376
964
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1377
965
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1384,19 +972,19 @@ function _ts_decorate6(decorators, target, key, desc) {
|
|
|
1384
972
|
}
|
|
1385
973
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1386
974
|
}
|
|
1387
|
-
__name(
|
|
1388
|
-
function
|
|
975
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
976
|
+
function _ts_metadata4(metadataKey, metadataValue) {
|
|
1389
977
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1390
978
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
1391
979
|
}
|
|
1392
980
|
}
|
|
1393
|
-
__name(
|
|
1394
|
-
function
|
|
981
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
982
|
+
function _ts_param4(paramIndex, decorator) {
|
|
1395
983
|
return function(target, key) {
|
|
1396
984
|
decorator(target, key, paramIndex);
|
|
1397
985
|
};
|
|
1398
986
|
}
|
|
1399
|
-
__name(
|
|
987
|
+
__name(_ts_param4, "_ts_param");
|
|
1400
988
|
function delegateToolName(args) {
|
|
1401
989
|
const slug = args.target.replace(/[^a-zA-Z0-9]+/g, "_");
|
|
1402
990
|
return args.detached === true ? `start_${slug}` : `ask_${slug}`;
|
|
@@ -1430,204 +1018,651 @@ var AgentDepsFactory2 = class {
|
|
|
1430
1018
|
this.skills = skills;
|
|
1431
1019
|
this.memory = memory;
|
|
1432
1020
|
}
|
|
1433
|
-
/** The turn's memory seam, shared with the read-back endpoint. Undefined → memory is not configured. */
|
|
1434
|
-
memoryConfig() {
|
|
1435
|
-
return this.memory;
|
|
1021
|
+
/** The turn's memory seam, shared with the read-back endpoint. Undefined → memory is not configured. */
|
|
1022
|
+
memoryConfig() {
|
|
1023
|
+
return this.memory;
|
|
1024
|
+
}
|
|
1025
|
+
/** The turn's skills seam, shared with the listing endpoint. Undefined → skills are not configured. */
|
|
1026
|
+
skillsConfig() {
|
|
1027
|
+
return this.skills;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* The agent a turn uses when the caller names none: the explicit `defaultAgent` option, else the
|
|
1031
|
+
* sole registered `@Agent` when there is exactly one, else `'default'` (a bare assistant).
|
|
1032
|
+
*/
|
|
1033
|
+
defaultAgentName() {
|
|
1034
|
+
if (this.options.defaultAgent !== void 0) {
|
|
1035
|
+
return this.options.defaultAgent;
|
|
1036
|
+
}
|
|
1037
|
+
const registered = this.agents.list();
|
|
1038
|
+
return registered.length === 1 ? registered[0]?.name ?? "default" : "default";
|
|
1039
|
+
}
|
|
1040
|
+
effectiveTools(definition) {
|
|
1041
|
+
if (definition === void 0) {
|
|
1042
|
+
return void 0;
|
|
1043
|
+
}
|
|
1044
|
+
const delegated = (definition.delegatesTo ?? []).map((edge) => {
|
|
1045
|
+
const { agent, detached } = normalizeDelegation(edge);
|
|
1046
|
+
return delegateToolName({
|
|
1047
|
+
target: agent,
|
|
1048
|
+
detached
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
if (definition.tools === void 0 && delegated.length === 0) {
|
|
1052
|
+
return void 0;
|
|
1053
|
+
}
|
|
1054
|
+
return [
|
|
1055
|
+
...definition.tools ?? [],
|
|
1056
|
+
...delegated
|
|
1057
|
+
];
|
|
1058
|
+
}
|
|
1059
|
+
forAgent(agentName) {
|
|
1060
|
+
const name = agentName ?? this.defaultAgentName();
|
|
1061
|
+
const definition = this.agents.get(name);
|
|
1062
|
+
const toolAllowList = this.effectiveTools(definition);
|
|
1063
|
+
const followUpsCount = this.followUpsCount();
|
|
1064
|
+
const retrieval = this.options.retrieval;
|
|
1065
|
+
const historyPolicy = this.historyPolicy(definition);
|
|
1066
|
+
const ask = definition?.ask ?? this.options.ask;
|
|
1067
|
+
return {
|
|
1068
|
+
model: this.model,
|
|
1069
|
+
store: this.store,
|
|
1070
|
+
sink: this.sink,
|
|
1071
|
+
rolesPolicy: this.rolesPolicy,
|
|
1072
|
+
...this.options.approvalPolicy !== void 0 ? {
|
|
1073
|
+
approvalPolicy: this.options.approvalPolicy
|
|
1074
|
+
} : {},
|
|
1075
|
+
registry: this.registry,
|
|
1076
|
+
promptContributors: this.promptContributors,
|
|
1077
|
+
systemPrompt: definition?.systemPrompt ?? this.options.systemPrompt ?? "You are a helpful assistant.",
|
|
1078
|
+
maxSteps: definition?.maxSteps ?? 8,
|
|
1079
|
+
...definition?.maxDelegationDepth !== void 0 ? {
|
|
1080
|
+
maxDelegationDepth: definition.maxDelegationDepth
|
|
1081
|
+
} : {},
|
|
1082
|
+
...definition?.maxAgentAppearances !== void 0 ? {
|
|
1083
|
+
maxAgentAppearances: definition.maxAgentAppearances
|
|
1084
|
+
} : {},
|
|
1085
|
+
...definition?.modelId !== void 0 ? {
|
|
1086
|
+
modelId: definition.modelId
|
|
1087
|
+
} : {},
|
|
1088
|
+
...this.pricingStore !== void 0 ? {
|
|
1089
|
+
pricingStore: this.pricingStore
|
|
1090
|
+
} : {},
|
|
1091
|
+
...historyPolicy !== void 0 ? {
|
|
1092
|
+
historyPolicy
|
|
1093
|
+
} : {},
|
|
1094
|
+
inputProcessors: this.options.inputProcessors ?? [],
|
|
1095
|
+
outputProcessors: this.options.outputProcessors ?? [],
|
|
1096
|
+
...definition?.outputSchema !== void 0 ? {
|
|
1097
|
+
outputSchema: definition.outputSchema
|
|
1098
|
+
} : {},
|
|
1099
|
+
...definition?.outputRepairAttempts !== void 0 ? {
|
|
1100
|
+
outputRepairAttempts: definition.outputRepairAttempts
|
|
1101
|
+
} : {},
|
|
1102
|
+
...definition?.intake !== void 0 ? {
|
|
1103
|
+
intake: definition.intake
|
|
1104
|
+
} : {},
|
|
1105
|
+
// Most specific wins, exactly as `historyPolicy` resolves: the agent's own `@Agent({ ask })`,
|
|
1106
|
+
// else the module-wide flag. Both are module config, so every process of a deployment agrees.
|
|
1107
|
+
...ask === true ? {
|
|
1108
|
+
ask: true
|
|
1109
|
+
} : {},
|
|
1110
|
+
// Module-wide, never per-agent: a skill is a procedure, not a persona, and an agent that could
|
|
1111
|
+
// opt out of one would make the catalog depend on which agent a turn happened to select — a
|
|
1112
|
+
// per-turn fact the worker re-deriving the tool list cannot see.
|
|
1113
|
+
...this.skills !== void 0 ? {
|
|
1114
|
+
skills: this.skills
|
|
1115
|
+
} : {},
|
|
1116
|
+
// Module-wide, never per-agent, for the same reason skills are: which memories a turn carries
|
|
1117
|
+
// cannot depend on which persona answered it, or the worker re-deriving a dispatched turn's
|
|
1118
|
+
// tool list would disagree with the loop about whether `remember` was offered.
|
|
1119
|
+
...this.memory !== void 0 ? {
|
|
1120
|
+
memory: this.memory
|
|
1121
|
+
} : {},
|
|
1122
|
+
...toolAllowList !== void 0 ? {
|
|
1123
|
+
toolAllowList
|
|
1124
|
+
} : {},
|
|
1125
|
+
...this.options.toolTimeoutMs !== void 0 ? {
|
|
1126
|
+
toolTimeoutMs: this.options.toolTimeoutMs
|
|
1127
|
+
} : {},
|
|
1128
|
+
...this.options.toolTransientRetry !== void 0 ? {
|
|
1129
|
+
toolTransientRetry: this.options.toolTransientRetry
|
|
1130
|
+
} : {},
|
|
1131
|
+
...followUpsCount !== void 0 ? {
|
|
1132
|
+
followUpsCount
|
|
1133
|
+
} : {},
|
|
1134
|
+
...retrieval?.mode === "inject" ? {
|
|
1135
|
+
retriever: retrieval.retriever,
|
|
1136
|
+
...retrieval.topK !== void 0 ? {
|
|
1137
|
+
retrievalTopK: retrieval.topK
|
|
1138
|
+
} : {}
|
|
1139
|
+
} : {}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
|
|
1144
|
+
* own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
|
|
1145
|
+
* `history` window. Undefined → unbounded, the behaviour of a module that configures none.
|
|
1146
|
+
*/
|
|
1147
|
+
historyPolicy(definition) {
|
|
1148
|
+
if (definition?.history !== void 0) {
|
|
1149
|
+
return this.window(definition.history);
|
|
1150
|
+
}
|
|
1151
|
+
if (this.options.historyPolicy !== void 0) {
|
|
1152
|
+
return this.options.historyPolicy;
|
|
1153
|
+
}
|
|
1154
|
+
return this.options.history === void 0 ? void 0 : this.window(this.options.history);
|
|
1155
|
+
}
|
|
1156
|
+
/** Build the built-in window policy from the declarative `{ maxMessages, maxTokens, summarize }`. */
|
|
1157
|
+
window(config) {
|
|
1158
|
+
return windowHistory({
|
|
1159
|
+
...config.maxMessages !== void 0 ? {
|
|
1160
|
+
maxMessages: config.maxMessages
|
|
1161
|
+
} : {},
|
|
1162
|
+
...config.maxTokens !== void 0 ? {
|
|
1163
|
+
maxTokens: config.maxTokens
|
|
1164
|
+
} : {},
|
|
1165
|
+
// The shared module model, not a per-agent one: an agent's `modelId` is an accounting label,
|
|
1166
|
+
// and only one provider is ever bound.
|
|
1167
|
+
...config.summarize === true ? {
|
|
1168
|
+
summarize: summarizeWithModel(this.model)
|
|
1169
|
+
} : {}
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
/** Normalize the `followUps` option (`true` → 3, `{ count }` → count) to a number, or undefined. */
|
|
1173
|
+
followUpsCount() {
|
|
1174
|
+
const followUps = this.options.followUps;
|
|
1175
|
+
if (followUps === true) {
|
|
1176
|
+
return 3;
|
|
1177
|
+
}
|
|
1178
|
+
if (typeof followUps === "object") {
|
|
1179
|
+
return followUps.count;
|
|
1180
|
+
}
|
|
1181
|
+
return void 0;
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
AgentDepsFactory2 = _ts_decorate5([
|
|
1185
|
+
Injectable4(),
|
|
1186
|
+
_ts_param4(0, Inject4(AGENT_OPTIONS)),
|
|
1187
|
+
_ts_param4(1, Inject4(AGENT_MODEL)),
|
|
1188
|
+
_ts_param4(2, Inject4(AGENT_STORE3)),
|
|
1189
|
+
_ts_param4(3, Inject4(AGENT_SINK2)),
|
|
1190
|
+
_ts_param4(4, Inject4(AGENT_ROLES_POLICY)),
|
|
1191
|
+
_ts_param4(5, Inject4(AGENT_TOOL_REGISTRY)),
|
|
1192
|
+
_ts_param4(6, Inject4(AGENT_REGISTRY)),
|
|
1193
|
+
_ts_param4(7, Inject4(AGENT_PROMPT_CONTRIBUTORS)),
|
|
1194
|
+
_ts_param4(8, Optional3()),
|
|
1195
|
+
_ts_param4(8, Inject4(AGENT_PRICING_STORE)),
|
|
1196
|
+
_ts_param4(9, Optional3()),
|
|
1197
|
+
_ts_param4(9, Inject4(AGENT_SKILLS)),
|
|
1198
|
+
_ts_param4(10, Optional3()),
|
|
1199
|
+
_ts_param4(10, Inject4(AGENT_MEMORY)),
|
|
1200
|
+
_ts_metadata4("design:type", Function),
|
|
1201
|
+
_ts_metadata4("design:paramtypes", [
|
|
1202
|
+
typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
|
|
1203
|
+
typeof ModelProvider === "undefined" ? Object : ModelProvider,
|
|
1204
|
+
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
1205
|
+
typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
|
|
1206
|
+
typeof RolesPolicy === "undefined" ? Object : RolesPolicy,
|
|
1207
|
+
typeof ToolRegistry2 === "undefined" ? Object : ToolRegistry2,
|
|
1208
|
+
typeof AgentRegistry2 === "undefined" ? Object : AgentRegistry2,
|
|
1209
|
+
Array,
|
|
1210
|
+
Object,
|
|
1211
|
+
Object,
|
|
1212
|
+
Object
|
|
1213
|
+
])
|
|
1214
|
+
], AgentDepsFactory2);
|
|
1215
|
+
|
|
1216
|
+
// src/agent.service.ts
|
|
1217
|
+
import { AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_MODEL_CATALOG, AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE5, REQUESTER_APPROVER, findCatalogModel, mayDecideApproval, readElicitationQuestions, settleDeadRun as settleDeadRun2, validateElicitationAnswer } from "@dudousxd/nestjs-agent-core";
|
|
1218
|
+
import { BadRequestException, ConflictException as ConflictException2, ForbiddenException, GoneException, HttpException, HttpStatus, Inject as Inject6, Injectable as Injectable6, NotFoundException, NotImplementedException as NotImplementedException2, Optional as Optional5 } from "@nestjs/common";
|
|
1219
|
+
|
|
1220
|
+
// src/queue/chat-queue.service.ts
|
|
1221
|
+
import { AGENT_ATTACHMENT_STAGING, AGENT_OPTIONS as AGENT_OPTIONS2, AGENT_QUOTA_PROVIDER, AGENT_SINK as AGENT_SINK3, AGENT_STORE as AGENT_STORE4, encodeStreamEvent as encodeStreamEvent4, isChatQueueStore, queuedMessageView } from "@dudousxd/nestjs-agent-core";
|
|
1222
|
+
import { Inject as Inject5, Injectable as Injectable5, Logger as Logger3, NotImplementedException, Optional as Optional4 } from "@nestjs/common";
|
|
1223
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1224
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1225
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1226
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1227
|
+
} else {
|
|
1228
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
1229
|
+
if (d = decorators[i]) {
|
|
1230
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1235
|
+
}
|
|
1236
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1237
|
+
function _ts_metadata5(metadataKey, metadataValue) {
|
|
1238
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1239
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1243
|
+
function _ts_param5(paramIndex, decorator) {
|
|
1244
|
+
return function(target, key) {
|
|
1245
|
+
decorator(target, key, paramIndex);
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
__name(_ts_param5, "_ts_param");
|
|
1249
|
+
var MAX_DRAIN_ATTEMPTS = 8;
|
|
1250
|
+
var ChatQueueService2 = class _ChatQueueService {
|
|
1251
|
+
static {
|
|
1252
|
+
__name(this, "ChatQueueService");
|
|
1253
|
+
}
|
|
1254
|
+
store;
|
|
1255
|
+
sink;
|
|
1256
|
+
quotaProvider;
|
|
1257
|
+
options;
|
|
1258
|
+
staging;
|
|
1259
|
+
logger = new Logger3(_ChatQueueService.name);
|
|
1260
|
+
/**
|
|
1261
|
+
* Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
|
|
1262
|
+
* alive whatever the runner says — it simply has not registered the run yet.
|
|
1263
|
+
*/
|
|
1264
|
+
starting = /* @__PURE__ */ new Set();
|
|
1265
|
+
constructor(store, sink, quotaProvider, options, staging) {
|
|
1266
|
+
this.store = store;
|
|
1267
|
+
this.sink = sink;
|
|
1268
|
+
this.quotaProvider = quotaProvider;
|
|
1269
|
+
this.options = options;
|
|
1270
|
+
this.staging = staging;
|
|
1271
|
+
}
|
|
1272
|
+
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
1273
|
+
get supported() {
|
|
1274
|
+
return isChatQueueStore(this.store);
|
|
1436
1275
|
}
|
|
1437
|
-
/** The
|
|
1438
|
-
|
|
1439
|
-
|
|
1276
|
+
/** The bound store as a queue store, or a 501 naming what is missing. */
|
|
1277
|
+
queueStore() {
|
|
1278
|
+
if (!isChatQueueStore(this.store)) {
|
|
1279
|
+
throw new NotImplementedException("Queueing messages requires an AgentStore that implements ChatQueueStore (enqueueMessage, listQueue, \u2026, claimActiveStream, releaseActiveStream); the bound store does not.");
|
|
1280
|
+
}
|
|
1281
|
+
return this.store;
|
|
1282
|
+
}
|
|
1283
|
+
async state(threadId) {
|
|
1284
|
+
const store = this.queueStore();
|
|
1285
|
+
const [items, paused] = await Promise.all([
|
|
1286
|
+
store.listQueue(threadId),
|
|
1287
|
+
store.queuePause(threadId)
|
|
1288
|
+
]);
|
|
1289
|
+
return {
|
|
1290
|
+
items: items.map(queuedMessageView),
|
|
1291
|
+
paused
|
|
1292
|
+
};
|
|
1440
1293
|
}
|
|
1441
1294
|
/**
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1295
|
+
* Whether `holder` is still running. A run this process is about to start is; otherwise the
|
|
1296
|
+
* runner decides, and a runner that cannot tell says yes.
|
|
1444
1297
|
*/
|
|
1445
|
-
|
|
1446
|
-
if (this.
|
|
1447
|
-
return
|
|
1298
|
+
async isAlive(holder, runner) {
|
|
1299
|
+
if (this.starting.has(holder)) {
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
if (typeof runner.isRunActive !== "function") {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
return await runner.isRunActive(holder);
|
|
1307
|
+
} catch {
|
|
1308
|
+
return true;
|
|
1448
1309
|
}
|
|
1449
|
-
const registered = this.agents.list();
|
|
1450
|
-
return registered.length === 1 ? registered[0]?.name ?? "default" : "default";
|
|
1451
1310
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1311
|
+
/**
|
|
1312
|
+
* The run holding `threadId`, or `null` when it is free — a holder the runner reports dead counts
|
|
1313
|
+
* as free, and is returned as `stale` so the caller can claim over it.
|
|
1314
|
+
*/
|
|
1315
|
+
async holder(threadId, runner) {
|
|
1316
|
+
const holder = await this.queueStore().activeRunForThread(threadId);
|
|
1317
|
+
if (holder === null) {
|
|
1318
|
+
return {
|
|
1319
|
+
live: null,
|
|
1320
|
+
stale: null
|
|
1321
|
+
};
|
|
1455
1322
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1323
|
+
return await this.isAlive(holder, runner) ? {
|
|
1324
|
+
live: holder,
|
|
1325
|
+
stale: null
|
|
1326
|
+
} : {
|
|
1327
|
+
live: null,
|
|
1328
|
+
stale: holder
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Decide what follows a TOP-LEVEL run that is settling on `threadId`, and move the thread there:
|
|
1333
|
+
* hand it to the next queued message (claimed and popped here, started by the caller), or pause
|
|
1334
|
+
* the queue, or release the thread. Call it BEFORE the run's terminal frame, so a client reading
|
|
1335
|
+
* the stream learns what comes next from the `queue` frame, and so the thread is already free
|
|
1336
|
+
* when the client sees the run end.
|
|
1337
|
+
*
|
|
1338
|
+
* A no-op returning `{}` on a store without a queue.
|
|
1339
|
+
*/
|
|
1340
|
+
async plan(args) {
|
|
1341
|
+
if (!isChatQueueStore(this.store)) {
|
|
1342
|
+
return {};
|
|
1465
1343
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1344
|
+
const store = this.store;
|
|
1345
|
+
const { threadId, outcome } = args;
|
|
1346
|
+
if (await store.activeRunForThread(threadId) !== args.runId) {
|
|
1347
|
+
return {};
|
|
1348
|
+
}
|
|
1349
|
+
let holding = args.runId;
|
|
1350
|
+
for (let attempt = 0; attempt < MAX_DRAIN_ATTEMPTS; attempt += 1) {
|
|
1351
|
+
const items = await store.listQueue(threadId);
|
|
1352
|
+
const head = items[0];
|
|
1353
|
+
if (head === void 0) {
|
|
1354
|
+
if (holding !== null) {
|
|
1355
|
+
await store.releaseActiveStream(threadId, holding);
|
|
1356
|
+
holding = null;
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
return {};
|
|
1360
|
+
}
|
|
1361
|
+
let pause = await store.queuePause(threadId);
|
|
1362
|
+
if (pause === null) {
|
|
1363
|
+
const reason = pauseReasonFor(outcome, head);
|
|
1364
|
+
if (reason !== null) {
|
|
1365
|
+
pause = {
|
|
1366
|
+
reason,
|
|
1367
|
+
...outcome === "failed" && args.error !== void 0 ? {
|
|
1368
|
+
message: args.error
|
|
1369
|
+
} : {},
|
|
1370
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1371
|
+
};
|
|
1372
|
+
await store.setQueuePause(threadId, pause);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
if (pause === null) {
|
|
1376
|
+
const blocked = await this.quotaBlock(head.actor);
|
|
1377
|
+
if (blocked !== null) {
|
|
1378
|
+
pause = {
|
|
1379
|
+
reason: "quota_exceeded",
|
|
1380
|
+
message: blocked,
|
|
1381
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1382
|
+
};
|
|
1383
|
+
await store.setQueuePause(threadId, pause);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
if (pause !== null) {
|
|
1387
|
+
if (holding !== null) {
|
|
1388
|
+
await store.releaseActiveStream(threadId, holding);
|
|
1389
|
+
}
|
|
1390
|
+
return {
|
|
1391
|
+
frame: queueFrame(items, pause)
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
const claimed = await store.claimActiveStream(threadId, head.id, holding !== null ? {
|
|
1395
|
+
replacing: holding
|
|
1396
|
+
} : {});
|
|
1397
|
+
if (!claimed) {
|
|
1398
|
+
return {};
|
|
1399
|
+
}
|
|
1400
|
+
holding = head.id;
|
|
1401
|
+
if (!await store.removeQueuedMessage(head.id)) {
|
|
1402
|
+
await store.releaseActiveStream(threadId, head.id);
|
|
1403
|
+
holding = null;
|
|
1404
|
+
continue;
|
|
1405
|
+
}
|
|
1406
|
+
const next = {
|
|
1407
|
+
runId: head.id,
|
|
1408
|
+
messageId: head.id,
|
|
1409
|
+
input: await this.inputFor(head)
|
|
1410
|
+
};
|
|
1411
|
+
return {
|
|
1412
|
+
next,
|
|
1413
|
+
frame: queueFrame(items.slice(1), null, {
|
|
1414
|
+
messageId: head.id,
|
|
1415
|
+
runId: head.id
|
|
1416
|
+
})
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
if (holding !== null) {
|
|
1420
|
+
await store.releaseActiveStream(threadId, holding);
|
|
1421
|
+
}
|
|
1422
|
+
return {};
|
|
1470
1423
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1424
|
+
/**
|
|
1425
|
+
* {@link plan}, then start what it picked with `start`. What an in-process settle (the inline
|
|
1426
|
+
* runner, a durable cancel issued outside a workflow) calls; answers the frame to write.
|
|
1427
|
+
*/
|
|
1428
|
+
async handoff(args, start) {
|
|
1429
|
+
const plan = await this.plan(args);
|
|
1430
|
+
if (plan.next === void 0) {
|
|
1431
|
+
return plan.frame;
|
|
1432
|
+
}
|
|
1433
|
+
return await this.launch(args.threadId, plan.next, start) ? plan.frame : this.pausedFrame(args.threadId);
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Start the head of an IDLE thread's queue — after a send queued behind a run that turned out to
|
|
1437
|
+
* be gone, after a resume, after a stale holder. Does nothing while a live run holds the thread
|
|
1438
|
+
* (its settle drains) or while the queue is paused. Answers the started run's id, if any.
|
|
1439
|
+
*/
|
|
1440
|
+
async kick(threadId, runner) {
|
|
1441
|
+
if (!isChatQueueStore(this.store)) {
|
|
1442
|
+
return void 0;
|
|
1443
|
+
}
|
|
1444
|
+
const { live, stale } = await this.holder(threadId, runner);
|
|
1445
|
+
if (live !== null) {
|
|
1446
|
+
return void 0;
|
|
1447
|
+
}
|
|
1448
|
+
if (stale !== null) {
|
|
1449
|
+
this.logger.warn(`thread ${threadId} was held by run ${stale}, which is no longer running`);
|
|
1450
|
+
}
|
|
1451
|
+
const plan = await this.plan({
|
|
1452
|
+
threadId,
|
|
1453
|
+
runId: stale,
|
|
1454
|
+
outcome: "completed"
|
|
1455
|
+
});
|
|
1456
|
+
if (plan.next === void 0) {
|
|
1457
|
+
return void 0;
|
|
1458
|
+
}
|
|
1459
|
+
const started = await this.launch(threadId, plan.next, (input, runId) => runner.start(input, {
|
|
1460
|
+
runId
|
|
1461
|
+
}));
|
|
1462
|
+
if (started && plan.frame !== void 0) {
|
|
1463
|
+
await this.write(plan.next.runId, plan.frame);
|
|
1464
|
+
}
|
|
1465
|
+
return started ? plan.next.runId : void 0;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Start a turn the thread is already claimed for. On failure the message goes back to the head of
|
|
1469
|
+
* the queue, the queue pauses (`start_failed`), and the claim is released — nothing is lost and
|
|
1470
|
+
* nothing is left holding the thread.
|
|
1471
|
+
*/
|
|
1472
|
+
async launch(threadId, next, start) {
|
|
1473
|
+
this.starting.add(next.runId);
|
|
1474
|
+
try {
|
|
1475
|
+
await start(next.input, next.runId);
|
|
1476
|
+
return true;
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1479
|
+
this.logger.error(`queued message ${next.messageId} could not start: ${message}`);
|
|
1480
|
+
await this.restore(threadId, next, "start_failed", message);
|
|
1481
|
+
return false;
|
|
1482
|
+
} finally {
|
|
1483
|
+
this.starting.delete(next.runId);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
/** Put a turn that never started back at the head of its queue, paused, and free the thread. */
|
|
1487
|
+
async restore(threadId, next, reason, message) {
|
|
1488
|
+
const store = this.queueStore();
|
|
1489
|
+
const { input } = next;
|
|
1490
|
+
await store.enqueueMessage({
|
|
1491
|
+
threadId,
|
|
1492
|
+
actor: input.actor,
|
|
1493
|
+
content: input.userText,
|
|
1494
|
+
at: "head",
|
|
1495
|
+
...input.attachments !== void 0 ? {
|
|
1496
|
+
attachments: input.attachments
|
|
1539
1497
|
} : {},
|
|
1540
|
-
...
|
|
1541
|
-
|
|
1498
|
+
...input.agentName !== void 0 ? {
|
|
1499
|
+
agentName: input.agentName
|
|
1542
1500
|
} : {},
|
|
1543
|
-
...
|
|
1544
|
-
|
|
1501
|
+
...input.model !== void 0 ? {
|
|
1502
|
+
model: input.model
|
|
1545
1503
|
} : {},
|
|
1546
|
-
...
|
|
1547
|
-
|
|
1548
|
-
...retrieval.topK !== void 0 ? {
|
|
1549
|
-
retrievalTopK: retrieval.topK
|
|
1550
|
-
} : {}
|
|
1504
|
+
...input.pageContext !== void 0 ? {
|
|
1505
|
+
pageContext: input.pageContext
|
|
1551
1506
|
} : {}
|
|
1507
|
+
});
|
|
1508
|
+
await store.setQueuePause(threadId, {
|
|
1509
|
+
reason,
|
|
1510
|
+
message,
|
|
1511
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1512
|
+
});
|
|
1513
|
+
await store.releaseActiveStream(threadId, next.runId);
|
|
1514
|
+
}
|
|
1515
|
+
/** A `queue` frame with the thread's current state. */
|
|
1516
|
+
async pausedFrame(threadId) {
|
|
1517
|
+
return {
|
|
1518
|
+
kind: "queue",
|
|
1519
|
+
queue: await this.state(threadId)
|
|
1552
1520
|
};
|
|
1553
1521
|
}
|
|
1554
1522
|
/**
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1523
|
+
* Tell whoever is watching the thread that its queue changed: a `queue` frame into the stream of
|
|
1524
|
+
* the run holding it. Best-effort — a thread nobody is streaming has no one to tell, and the next
|
|
1525
|
+
* read of the thread carries the queue anyway.
|
|
1558
1526
|
*/
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1527
|
+
async publish(threadId) {
|
|
1528
|
+
const state = await this.state(threadId);
|
|
1529
|
+
const holder = await this.queueStore().activeRunForThread(threadId);
|
|
1530
|
+
if (holder !== null) {
|
|
1531
|
+
await this.write(holder, {
|
|
1532
|
+
kind: "queue",
|
|
1533
|
+
queue: state
|
|
1534
|
+
}).catch((error) => {
|
|
1535
|
+
this.logger.warn(`could not publish the queue of thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1536
|
+
});
|
|
1562
1537
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1538
|
+
return state;
|
|
1539
|
+
}
|
|
1540
|
+
async write(runId, frame) {
|
|
1541
|
+
const writer = await this.sink.open(runId);
|
|
1542
|
+
await writer.write(encodeStreamEvent4(frame));
|
|
1543
|
+
}
|
|
1544
|
+
/** Why `actor` may not start a turn now (the quota message), or `null`. */
|
|
1545
|
+
async quotaBlock(actor) {
|
|
1546
|
+
if (this.options?.quota === void 0 || this.quotaProvider === void 0) {
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
try {
|
|
1550
|
+
const { blocked } = await this.quotaProvider.report({
|
|
1551
|
+
actor
|
|
1552
|
+
});
|
|
1553
|
+
if (blocked === void 0) {
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
return blocked.reason ?? `The ${blocked.period === "day" ? "daily" : "monthly"} quota is used up`;
|
|
1557
|
+
} catch {
|
|
1558
|
+
return null;
|
|
1565
1559
|
}
|
|
1566
|
-
return this.options.history === void 0 ? void 0 : this.window(this.options.history);
|
|
1567
1560
|
}
|
|
1568
|
-
/**
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1561
|
+
/**
|
|
1562
|
+
* The turn a queued message starts: everything it was queued with, today's date, and its
|
|
1563
|
+
* attachments' urls minted afresh — the ones stored at enqueue time may have expired while it
|
|
1564
|
+
* waited. An attachment the staging store no longer resolves keeps its stored url.
|
|
1565
|
+
*/
|
|
1566
|
+
async inputFor(message) {
|
|
1567
|
+
const attachments = await this.freshAttachments(message.actor, message.attachments ?? []);
|
|
1568
|
+
return {
|
|
1569
|
+
threadId: message.threadId,
|
|
1570
|
+
actor: message.actor,
|
|
1571
|
+
userText: message.content,
|
|
1572
|
+
day: utcDay(),
|
|
1573
|
+
...message.agentName !== void 0 ? {
|
|
1574
|
+
agentName: message.agentName
|
|
1573
1575
|
} : {},
|
|
1574
|
-
...
|
|
1575
|
-
|
|
1576
|
+
...attachments.length > 0 ? {
|
|
1577
|
+
attachments
|
|
1576
1578
|
} : {},
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1579
|
+
...message.pageContext !== void 0 ? {
|
|
1580
|
+
pageContext: message.pageContext
|
|
1581
|
+
} : {},
|
|
1582
|
+
...message.model !== void 0 ? {
|
|
1583
|
+
model: message.model
|
|
1581
1584
|
} : {}
|
|
1582
|
-
}
|
|
1585
|
+
};
|
|
1583
1586
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
return 3;
|
|
1589
|
-
}
|
|
1590
|
-
if (typeof followUps === "object") {
|
|
1591
|
-
return followUps.count;
|
|
1587
|
+
async freshAttachments(actor, attachments) {
|
|
1588
|
+
const staging = this.staging;
|
|
1589
|
+
if (attachments.length === 0 || staging === void 0 || typeof staging.resolve !== "function") {
|
|
1590
|
+
return attachments;
|
|
1592
1591
|
}
|
|
1593
|
-
return
|
|
1592
|
+
return Promise.all(attachments.map(async (attachment) => {
|
|
1593
|
+
try {
|
|
1594
|
+
return await staging.resolve({
|
|
1595
|
+
mediaId: attachment.mediaId,
|
|
1596
|
+
actor
|
|
1597
|
+
}) ?? attachment;
|
|
1598
|
+
} catch {
|
|
1599
|
+
return attachment;
|
|
1600
|
+
}
|
|
1601
|
+
}));
|
|
1594
1602
|
}
|
|
1595
1603
|
};
|
|
1596
|
-
|
|
1604
|
+
ChatQueueService2 = _ts_decorate6([
|
|
1597
1605
|
Injectable5(),
|
|
1598
|
-
_ts_param5(0, Inject5(
|
|
1599
|
-
_ts_param5(1, Inject5(
|
|
1600
|
-
_ts_param5(2,
|
|
1601
|
-
_ts_param5(
|
|
1602
|
-
_ts_param5(
|
|
1603
|
-
_ts_param5(
|
|
1604
|
-
_ts_param5(
|
|
1605
|
-
_ts_param5(
|
|
1606
|
-
_ts_param5(8, Optional4()),
|
|
1607
|
-
_ts_param5(8, Inject5(AGENT_PRICING_STORE)),
|
|
1608
|
-
_ts_param5(9, Optional4()),
|
|
1609
|
-
_ts_param5(9, Inject5(AGENT_SKILLS)),
|
|
1610
|
-
_ts_param5(10, Optional4()),
|
|
1611
|
-
_ts_param5(10, Inject5(AGENT_MEMORY)),
|
|
1606
|
+
_ts_param5(0, Inject5(AGENT_STORE4)),
|
|
1607
|
+
_ts_param5(1, Inject5(AGENT_SINK3)),
|
|
1608
|
+
_ts_param5(2, Optional4()),
|
|
1609
|
+
_ts_param5(2, Inject5(AGENT_QUOTA_PROVIDER)),
|
|
1610
|
+
_ts_param5(3, Optional4()),
|
|
1611
|
+
_ts_param5(3, Inject5(AGENT_OPTIONS2)),
|
|
1612
|
+
_ts_param5(4, Optional4()),
|
|
1613
|
+
_ts_param5(4, Inject5(AGENT_ATTACHMENT_STAGING)),
|
|
1612
1614
|
_ts_metadata5("design:type", Function),
|
|
1613
1615
|
_ts_metadata5("design:paramtypes", [
|
|
1614
|
-
typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
|
|
1615
|
-
typeof ModelProvider === "undefined" ? Object : ModelProvider,
|
|
1616
1616
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
1617
1617
|
typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
|
|
1618
|
-
typeof
|
|
1619
|
-
typeof
|
|
1620
|
-
typeof
|
|
1621
|
-
Array,
|
|
1622
|
-
Object,
|
|
1623
|
-
Object,
|
|
1624
|
-
Object
|
|
1618
|
+
typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
|
|
1619
|
+
typeof Pick === "undefined" ? Object : Pick,
|
|
1620
|
+
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
|
|
1625
1621
|
])
|
|
1626
|
-
],
|
|
1622
|
+
], ChatQueueService2);
|
|
1623
|
+
function pauseReasonFor(outcome, head) {
|
|
1624
|
+
if (outcome === "failed") {
|
|
1625
|
+
return "run_failed";
|
|
1626
|
+
}
|
|
1627
|
+
if (outcome === "cancelled" && head.interrupt !== true) {
|
|
1628
|
+
return "cancelled";
|
|
1629
|
+
}
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
__name(pauseReasonFor, "pauseReasonFor");
|
|
1633
|
+
function queueFrame(items, paused, started) {
|
|
1634
|
+
return {
|
|
1635
|
+
kind: "queue",
|
|
1636
|
+
queue: {
|
|
1637
|
+
items: items.map(queuedMessageView),
|
|
1638
|
+
paused
|
|
1639
|
+
},
|
|
1640
|
+
...started !== void 0 ? {
|
|
1641
|
+
started
|
|
1642
|
+
} : {}
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
__name(queueFrame, "queueFrame");
|
|
1646
|
+
|
|
1647
|
+
// src/run-not-active.exception.ts
|
|
1648
|
+
import { RUN_NOT_ACTIVE_CODE, RUN_NOT_ACTIVE_MESSAGE } from "@dudousxd/nestjs-agent-core";
|
|
1649
|
+
import { ConflictException } from "@nestjs/common";
|
|
1650
|
+
var RunNotActiveException = class extends ConflictException {
|
|
1651
|
+
static {
|
|
1652
|
+
__name(this, "RunNotActiveException");
|
|
1653
|
+
}
|
|
1654
|
+
runId;
|
|
1655
|
+
code = RUN_NOT_ACTIVE_CODE;
|
|
1656
|
+
constructor(runId) {
|
|
1657
|
+
super({
|
|
1658
|
+
statusCode: 409,
|
|
1659
|
+
code: RUN_NOT_ACTIVE_CODE,
|
|
1660
|
+
message: RUN_NOT_ACTIVE_MESSAGE
|
|
1661
|
+
}), this.runId = runId;
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1627
1664
|
|
|
1628
1665
|
// src/agent.service.ts
|
|
1629
|
-
import { AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_MODEL_CATALOG, AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE5, REQUESTER_APPROVER, findCatalogModel, mayDecideApproval, readElicitationQuestions, validateElicitationAnswer } from "@dudousxd/nestjs-agent-core";
|
|
1630
|
-
import { BadRequestException, ConflictException, ForbiddenException, GoneException, HttpException, HttpStatus, Inject as Inject6, Injectable as Injectable6, NotFoundException, NotImplementedException as NotImplementedException2, Optional as Optional5 } from "@nestjs/common";
|
|
1631
1666
|
function _ts_decorate7(decorators, target, key, desc) {
|
|
1632
1667
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1633
1668
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -1824,7 +1859,7 @@ var AgentService = class {
|
|
|
1824
1859
|
};
|
|
1825
1860
|
}
|
|
1826
1861
|
await this.requireQueue().publish(result.threadId);
|
|
1827
|
-
throw new
|
|
1862
|
+
throw new ConflictException2({
|
|
1828
1863
|
statusCode: 409,
|
|
1829
1864
|
code: "run_active",
|
|
1830
1865
|
message: `thread ${result.threadId} already has a turn running`
|
|
@@ -1884,6 +1919,11 @@ var AgentService = class {
|
|
|
1884
1919
|
}
|
|
1885
1920
|
const mode = params.mode ?? "auto";
|
|
1886
1921
|
const { live, stale } = await queue.holder(threadId, this.runner);
|
|
1922
|
+
if (stale !== null) {
|
|
1923
|
+
await settleDeadRun2(this.store, {
|
|
1924
|
+
runId: stale
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1887
1927
|
if (live === null && mode !== "queue") {
|
|
1888
1928
|
const runId = crypto.randomUUID();
|
|
1889
1929
|
if (await queue.queueStore().claimActiveStream(threadId, runId, stale !== null ? {
|
|
@@ -1896,7 +1936,7 @@ var AgentService = class {
|
|
|
1896
1936
|
}
|
|
1897
1937
|
}
|
|
1898
1938
|
if (params.regenerate === true) {
|
|
1899
|
-
throw new
|
|
1939
|
+
throw new ConflictException2({
|
|
1900
1940
|
statusCode: 409,
|
|
1901
1941
|
code: "run_active",
|
|
1902
1942
|
message: "cannot regenerate while a turn is running on this thread"
|
|
@@ -2244,8 +2284,29 @@ var AgentService = class {
|
|
|
2244
2284
|
async signalToolCall(toolCallId2, reply) {
|
|
2245
2285
|
await this.assertNotExpired(toolCallId2);
|
|
2246
2286
|
const runId = await this.resolveRunForToolCall(toolCallId2);
|
|
2287
|
+
await this.assertRunWaiting(runId);
|
|
2247
2288
|
return this.runner.signal(runId, toolCallId2, reply);
|
|
2248
2289
|
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Refuse a decision addressed at a run that is over (`409 run_not_active`). The runtime would take
|
|
2292
|
+
* the signal and buffer it for a run that never comes back — the person's "yes" is accepted, the
|
|
2293
|
+
* card says so, and nothing runs. Settles the calls the dead run left awaiting a decision on the
|
|
2294
|
+
* way out, so the card stops asking. A runner that cannot say whether a run is alive is taken at
|
|
2295
|
+
* its word that it is, and so is one that fails to answer.
|
|
2296
|
+
*/
|
|
2297
|
+
async assertRunWaiting(runId) {
|
|
2298
|
+
if (typeof this.runner.isRunActive !== "function") {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
const active = await this.runner.isRunActive(runId).catch(() => true);
|
|
2302
|
+
if (active) {
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
await settleDeadRun2(this.store, {
|
|
2306
|
+
runId
|
|
2307
|
+
});
|
|
2308
|
+
throw new RunNotActiveException(runId);
|
|
2309
|
+
}
|
|
2249
2310
|
async cancel(actor, runId) {
|
|
2250
2311
|
await this.assertOwnsActiveStream(actor, runId);
|
|
2251
2312
|
return this.runner.cancel(runId);
|
|
@@ -2638,7 +2699,7 @@ AgentService = _ts_decorate7([
|
|
|
2638
2699
|
typeof ModelCatalog === "undefined" ? Object : ModelCatalog,
|
|
2639
2700
|
typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
|
|
2640
2701
|
typeof Pick === "undefined" ? Object : Pick,
|
|
2641
|
-
typeof
|
|
2702
|
+
typeof ChatQueueService2 === "undefined" ? Object : ChatQueueService2
|
|
2642
2703
|
])
|
|
2643
2704
|
], AgentService);
|
|
2644
2705
|
|
|
@@ -2929,7 +2990,7 @@ AttachmentsController = _ts_decorate10([
|
|
|
2929
2990
|
], AttachmentsController);
|
|
2930
2991
|
|
|
2931
2992
|
// src/controller/chat.controller.ts
|
|
2932
|
-
import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError as AgentStreamError2 } from "@dudousxd/nestjs-agent-core";
|
|
2993
|
+
import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError as AgentStreamError2, streamFailure as streamFailure2 } from "@dudousxd/nestjs-agent-core";
|
|
2933
2994
|
import { BadRequestException as BadRequestException3, Body, Controller as Controller3, Get as Get3, Inject as Inject9, Param, Post as Post2, Query, Req as Req2, Res } from "@nestjs/common";
|
|
2934
2995
|
function _ts_decorate11(decorators, target, key, desc) {
|
|
2935
2996
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3125,10 +3186,7 @@ data: ${line}
|
|
|
3125
3186
|
const payload = error instanceof AgentStreamError2 ? {
|
|
3126
3187
|
code: error.code,
|
|
3127
3188
|
message: error.message
|
|
3128
|
-
} :
|
|
3129
|
-
code: "run_failed",
|
|
3130
|
-
message: error instanceof Error ? error.message : "stream error"
|
|
3131
|
-
};
|
|
3189
|
+
} : streamFailure2(error);
|
|
3132
3190
|
res.write(`event: error
|
|
3133
3191
|
data: ${JSON.stringify(payload)}
|
|
3134
3192
|
|
|
@@ -4453,7 +4511,7 @@ ToolsController = _ts_decorate21([
|
|
|
4453
4511
|
|
|
4454
4512
|
// src/discovery/agent-discovery.service.ts
|
|
4455
4513
|
import { AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS2, AGENT_REGISTRY as AGENT_REGISTRY4, AgentRegistry as AgentRegistry5 } from "@dudousxd/nestjs-agent-core";
|
|
4456
|
-
import { Inject as Inject20, Injectable as Injectable8, Logger as
|
|
4514
|
+
import { Inject as Inject20, Injectable as Injectable8, Logger as Logger4 } from "@nestjs/common";
|
|
4457
4515
|
import { DiscoveryService, MetadataScanner } from "@nestjs/core";
|
|
4458
4516
|
|
|
4459
4517
|
// src/decorator/agent.decorator.ts
|
|
@@ -4504,7 +4562,7 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
|
|
|
4504
4562
|
scanner;
|
|
4505
4563
|
registry;
|
|
4506
4564
|
contributors;
|
|
4507
|
-
logger = new
|
|
4565
|
+
logger = new Logger4(_AgentDiscoveryService.name);
|
|
4508
4566
|
constructor(discovery, scanner, registry, contributors) {
|
|
4509
4567
|
this.discovery = discovery;
|
|
4510
4568
|
this.scanner = scanner;
|
|
@@ -4637,7 +4695,7 @@ AgentDiscoveryService = _ts_decorate22([
|
|
|
4637
4695
|
|
|
4638
4696
|
// src/discovery/ai-tool-discovery.service.ts
|
|
4639
4697
|
import { AGENT_OPTIONS as AGENT_OPTIONS6, AGENT_REGISTRY as AGENT_REGISTRY5, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY2, normalizeDelegation as normalizeDelegation2 } from "@dudousxd/nestjs-agent-core";
|
|
4640
|
-
import { Inject as Inject21, Injectable as Injectable9, Logger as
|
|
4698
|
+
import { Inject as Inject21, Injectable as Injectable9, Logger as Logger5 } from "@nestjs/common";
|
|
4641
4699
|
import { DiscoveryService as DiscoveryService2 } from "@nestjs/core";
|
|
4642
4700
|
import { z } from "zod";
|
|
4643
4701
|
|
|
@@ -4691,7 +4749,7 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
|
|
|
4691
4749
|
registry;
|
|
4692
4750
|
agents;
|
|
4693
4751
|
options;
|
|
4694
|
-
logger = new
|
|
4752
|
+
logger = new Logger5(_AiToolDiscoveryService.name);
|
|
4695
4753
|
constructor(discovery, registry, agents, options) {
|
|
4696
4754
|
this.discovery = discovery;
|
|
4697
4755
|
this.registry = registry;
|
|
@@ -4842,7 +4900,7 @@ AiToolDiscoveryService = _ts_decorate23([
|
|
|
4842
4900
|
|
|
4843
4901
|
// src/discovery/skill-discovery.service.ts
|
|
4844
4902
|
import { AGENT_SKILL_SOURCES } from "@dudousxd/nestjs-agent-core";
|
|
4845
|
-
import { Inject as Inject22, Injectable as Injectable10, Logger as
|
|
4903
|
+
import { Inject as Inject22, Injectable as Injectable10, Logger as Logger6 } from "@nestjs/common";
|
|
4846
4904
|
import { DiscoveryService as DiscoveryService3 } from "@nestjs/core";
|
|
4847
4905
|
|
|
4848
4906
|
// src/decorator/skill.decorator.ts
|
|
@@ -4891,7 +4949,7 @@ var SkillDiscoveryService = class _SkillDiscoveryService {
|
|
|
4891
4949
|
}
|
|
4892
4950
|
discovery;
|
|
4893
4951
|
declared;
|
|
4894
|
-
logger = new
|
|
4952
|
+
logger = new Logger6(_SkillDiscoveryService.name);
|
|
4895
4953
|
constructor(discovery, declared) {
|
|
4896
4954
|
this.discovery = discovery;
|
|
4897
4955
|
this.declared = declared;
|
|
@@ -5001,8 +5059,8 @@ var LedgerQuotaProvider = class {
|
|
|
5001
5059
|
};
|
|
5002
5060
|
|
|
5003
5061
|
// src/runner/inline-agent-runner.ts
|
|
5004
|
-
import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY6, AGENT_STORE as AGENT_STORE6, RunCancelledError as RunCancelledError2, agentFailureCode as agentFailureCode2, encodeStreamEvent as encodeStreamEvent5, publishAgentRunFailed as publishAgentRunFailed2, releaseThreadRun as releaseThreadRun3, runAgentLoop as runAgentLoop2, settleAll as settleAll2, settleUnsettledDelegation as settleUnsettledDelegation2 } from "@dudousxd/nestjs-agent-core";
|
|
5005
|
-
import { Inject as Inject23, Injectable as Injectable11, Logger as
|
|
5062
|
+
import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY6, AGENT_STORE as AGENT_STORE6, RUN_ENDED_BEFORE_TOOL_CALL as RUN_ENDED_BEFORE_TOOL_CALL2, RunCancelledError as RunCancelledError2, agentFailureCode as agentFailureCode2, encodeStreamEvent as encodeStreamEvent5, publishAgentRunFailed as publishAgentRunFailed2, releaseThreadRun as releaseThreadRun3, runAgentLoop as runAgentLoop2, settleAll as settleAll2, settleUnsettledDelegation as settleUnsettledDelegation2, streamFailure as streamFailure3 } from "@dudousxd/nestjs-agent-core";
|
|
5063
|
+
import { Inject as Inject23, Injectable as Injectable11, Logger as Logger7, Optional as Optional10 } from "@nestjs/common";
|
|
5006
5064
|
function _ts_decorate25(decorators, target, key, desc) {
|
|
5007
5065
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5008
5066
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -5036,7 +5094,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
5036
5094
|
factory;
|
|
5037
5095
|
store;
|
|
5038
5096
|
queue;
|
|
5039
|
-
logger = new
|
|
5097
|
+
logger = new Logger7(_InlineAgentRunner.name);
|
|
5040
5098
|
pending = /* @__PURE__ */ new Map();
|
|
5041
5099
|
/**
|
|
5042
5100
|
* Runs someone has asked to stop. In-process, like `pending`, and for the same reason: this runner
|
|
@@ -5056,7 +5114,15 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
5056
5114
|
* that was running it is gone (a restart), and a thread it still holds is a stale claim.
|
|
5057
5115
|
*/
|
|
5058
5116
|
async isRunActive(runId) {
|
|
5059
|
-
|
|
5117
|
+
if (this.live.has(runId)) {
|
|
5118
|
+
return true;
|
|
5119
|
+
}
|
|
5120
|
+
for (const key of this.pending.keys()) {
|
|
5121
|
+
if (key.startsWith(`${runId}:`)) {
|
|
5122
|
+
return true;
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
return false;
|
|
5060
5126
|
}
|
|
5061
5127
|
async start(input, options = {}) {
|
|
5062
5128
|
const runId = options.runId ?? crypto.randomUUID();
|
|
@@ -5099,13 +5165,11 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
5099
5165
|
errorCode: code,
|
|
5100
5166
|
errorMessage: message
|
|
5101
5167
|
});
|
|
5168
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, RUN_ENDED_BEFORE_TOOL_CALL2)).catch(() => 0);
|
|
5102
5169
|
const writer = await deps.sink.open(runId);
|
|
5103
5170
|
await this.settleQueue(writer, input, runId, "failed", message);
|
|
5104
5171
|
await releaseThreadRun3(this.store, input.threadId, runId);
|
|
5105
|
-
await writer.fail(
|
|
5106
|
-
code,
|
|
5107
|
-
message
|
|
5108
|
-
});
|
|
5172
|
+
await writer.fail(streamFailure3(error));
|
|
5109
5173
|
}).finally(() => {
|
|
5110
5174
|
this.cancelled.delete(runId);
|
|
5111
5175
|
this.live.delete(runId);
|
|
@@ -5379,6 +5443,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
|
|
|
5379
5443
|
errorMessage: message
|
|
5380
5444
|
}
|
|
5381
5445
|
});
|
|
5446
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, RUN_ENDED_BEFORE_TOOL_CALL2)).catch(() => 0);
|
|
5382
5447
|
await settleUnsettledDelegation2({
|
|
5383
5448
|
store: this.store,
|
|
5384
5449
|
delivery: deliverTo,
|
|
@@ -5481,7 +5546,7 @@ InlineAgentRunner = _ts_decorate25([
|
|
|
5481
5546
|
_ts_metadata24("design:paramtypes", [
|
|
5482
5547
|
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
|
|
5483
5548
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
5484
|
-
typeof
|
|
5549
|
+
typeof ChatQueueService2 === "undefined" ? Object : ChatQueueService2
|
|
5485
5550
|
])
|
|
5486
5551
|
], InlineAgentRunner);
|
|
5487
5552
|
function isThreadTurn2(input) {
|
|
@@ -5543,7 +5608,7 @@ function _ts_decorate26(decorators, target, key, desc) {
|
|
|
5543
5608
|
}
|
|
5544
5609
|
__name(_ts_decorate26, "_ts_decorate");
|
|
5545
5610
|
var DEFAULT_PATH = "agent";
|
|
5546
|
-
var logger = new
|
|
5611
|
+
var logger = new Logger8("AgentModule");
|
|
5547
5612
|
function externalStoreWrapper(modules) {
|
|
5548
5613
|
for (const module of modules.values()) {
|
|
5549
5614
|
if (module.metatype === AgentModule) continue;
|
|
@@ -5696,7 +5761,11 @@ function sharedProviders(durable) {
|
|
|
5696
5761
|
SkillDiscoveryService,
|
|
5697
5762
|
// The thread message queue — every surface, since both runners and the durable workflow settle
|
|
5698
5763
|
// into it, and the chat routes enqueue into it.
|
|
5699
|
-
|
|
5764
|
+
ChatQueueService2,
|
|
5765
|
+
{
|
|
5766
|
+
provide: AGENT_CHAT_QUEUE,
|
|
5767
|
+
useExisting: ChatQueueService2
|
|
5768
|
+
},
|
|
5700
5769
|
InlineAgentRunner,
|
|
5701
5770
|
AgentService,
|
|
5702
5771
|
// Bound ALWAYS (durable or inline) — the console's cross-thread approvals inbox routes decisions
|
|
@@ -5758,7 +5827,8 @@ function exportsFor() {
|
|
|
5758
5827
|
AGENT_DEPS_FACTORY7,
|
|
5759
5828
|
AgentDepsFactory2,
|
|
5760
5829
|
AgentService,
|
|
5761
|
-
|
|
5830
|
+
ChatQueueService2,
|
|
5831
|
+
AGENT_CHAT_QUEUE,
|
|
5762
5832
|
InlineAgentRunner,
|
|
5763
5833
|
AGENT_APPROVAL_PORT
|
|
5764
5834
|
];
|