@dudousxd/nestjs-agent-core 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/guardrails/index.d.cts +1 -1
- package/dist/guardrails/index.d.ts +1 -1
- package/dist/index.cjs +138 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -179
- package/dist/index.d.ts +47 -179
- package/dist/index.js +136 -11
- package/dist/index.js.map +1 -1
- package/dist/{tool-CL9oEytW.d.cts → tool-Q2rmGeaG.d.cts} +295 -1
- package/dist/{tool-CL9oEytW.d.ts → tool-Q2rmGeaG.d.ts} +295 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ import type { ModelProvider, AgentStore, ToolSpec, RolesPolicy } from '@dudousxd
|
|
|
36
36
|
- `isControlFlowSignal(error)` / `isReplayIntegrityError(error)` — recognize a durable suspend and a checkpoint refusal without importing the durable packages (a `Symbol.for` marker and a class name, respectively). Rethrow both untouched from any `catch` in a workflow body.
|
|
37
37
|
- `settleAll(tasks)` / `SettledTask<T>` — the implementation behind the optional `AgentLoopHooks.parallel`, which is how a turn's `read` tool calls run concurrently. It invokes every task synchronously, in list order, before awaiting any of them (so a runner that takes checkpoint positions on the call keeps them in call order), and resolves only once all of them have settled (so a runner that unwinds a turn by throwing never abandons a sibling mid-dispatch). A runner that assigns positions anywhere else simply omits the hook and the loop stays sequential.
|
|
38
38
|
- `AgentStreamEvent` / `encodeStreamEvent` / `decodeStreamEvent` — the live-stream vocabulary (text, reasoning, tool calls with optional `parentId` nesting, `elicitation`, `approval-requested` with `approver`/`expiresAt`, server-pushed `ui` components, `title`, `cancelled`). It is also the contract a runner that is not this loop writes to get the React client for free: see [docs/stream-protocol.md](../../docs/stream-protocol.md). `AgentUiComponent` and `AgentApprovalRequest` are the payload types.
|
|
39
|
+
- `observeTurnFrames` / `withTurnFrames` — what a model turn streamed that its result does not carry: the model's thinking (`reasoning` text, and `reasoningMs` summed over each burst of consecutive reasoning frames) and pushed `ui` components (first-seen order, last props per id). The loop and the dispatched `llm` step wrap the provider's sink with it inside the model checkpoint, so every provider that streams the vocabulary gets reasoning persisted (`StoredMessage.reasoning` / `reasoningMs` / `ui`) and a replay reads the journaled numbers back. A provider may set `ModelTurnResult.reasoning` / `reasoningMs` / `ui` itself; its values win. The step's `reasoningMs` also rides the `step-finish` frame.
|
|
40
|
+
- `ToolPresentation` / `ToolResultView` / `ToolCatalogEntry` — how a person-facing surface talks about a tool without naming it (`ToolSpec.presentation`, set by `@AiTool({ presentation })`; never shown to the model). `ToolRegistry.visibleSpecs(actor, policy, allowList)` returns the whole specs behind the same gates `definitionsFor` applies, which is what `GET /agent/tools` serves.
|
|
39
41
|
- `runAgentLoop(deps, input, hooks)` — the loop; the NestJS package drives it from both runners
|
|
40
42
|
|
|
41
43
|
## Guardrails — `@dudousxd/nestjs-agent-core/guardrails`
|
package/dist/index.cjs
CHANGED
|
@@ -119,6 +119,7 @@ __export(src_exports, {
|
|
|
119
119
|
memoryWriteVerdict: () => memoryWriteVerdict,
|
|
120
120
|
normalizeDelegation: () => normalizeDelegation,
|
|
121
121
|
normalizeElicitationReply: () => normalizeElicitationReply,
|
|
122
|
+
observeTurnFrames: () => observeTurnFrames,
|
|
122
123
|
offerMemories: () => offerMemories,
|
|
123
124
|
offerSkills: () => offerSkills,
|
|
124
125
|
publishAgentDelegated: () => publishAgentDelegated,
|
|
@@ -168,6 +169,7 @@ __export(src_exports, {
|
|
|
168
169
|
withMemoryTool: () => withMemoryTool,
|
|
169
170
|
withSkillTool: () => withSkillTool,
|
|
170
171
|
withToolTimeout: () => withToolTimeout,
|
|
172
|
+
withTurnFrames: () => withTurnFrames,
|
|
171
173
|
writeMemory: () => writeMemory
|
|
172
174
|
});
|
|
173
175
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -229,6 +231,105 @@ function decodeStreamEvent(line) {
|
|
|
229
231
|
}
|
|
230
232
|
__name(decodeStreamEvent, "decodeStreamEvent");
|
|
231
233
|
|
|
234
|
+
// src/turn-frames.ts
|
|
235
|
+
function observeTurnFrames(writer, now = Date.now) {
|
|
236
|
+
const decoder2 = new TextDecoder();
|
|
237
|
+
let pending = "";
|
|
238
|
+
let reasoning = "";
|
|
239
|
+
let reasoningMs = 0;
|
|
240
|
+
let burstStartedAt;
|
|
241
|
+
const ui = /* @__PURE__ */ new Map();
|
|
242
|
+
function closeBurst() {
|
|
243
|
+
if (burstStartedAt !== void 0) {
|
|
244
|
+
reasoningMs += Math.max(0, now() - burstStartedAt);
|
|
245
|
+
burstStartedAt = void 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
__name(closeBurst, "closeBurst");
|
|
249
|
+
function observeLine(line) {
|
|
250
|
+
if (line.trim().length === 0) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const event = decodeStreamEvent(line);
|
|
254
|
+
if (event === null) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (event.kind === "reasoning") {
|
|
258
|
+
burstStartedAt ??= now();
|
|
259
|
+
reasoning += event.text;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
closeBurst();
|
|
263
|
+
if (event.kind === "ui") {
|
|
264
|
+
ui.set(event.id, {
|
|
265
|
+
id: event.id,
|
|
266
|
+
component: event.component,
|
|
267
|
+
props: event.props,
|
|
268
|
+
...event.version !== void 0 ? {
|
|
269
|
+
version: event.version
|
|
270
|
+
} : {}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
__name(observeLine, "observeLine");
|
|
275
|
+
function observe(chunk) {
|
|
276
|
+
pending += decoder2.decode(chunk, {
|
|
277
|
+
stream: true
|
|
278
|
+
});
|
|
279
|
+
let newline = pending.indexOf("\n");
|
|
280
|
+
while (newline !== -1) {
|
|
281
|
+
observeLine(pending.slice(0, newline));
|
|
282
|
+
pending = pending.slice(newline + 1);
|
|
283
|
+
newline = pending.indexOf("\n");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
__name(observe, "observe");
|
|
287
|
+
return {
|
|
288
|
+
writer: {
|
|
289
|
+
write(chunk) {
|
|
290
|
+
observe(chunk);
|
|
291
|
+
return writer.write(chunk);
|
|
292
|
+
},
|
|
293
|
+
end: /* @__PURE__ */ __name(() => writer.end(), "end"),
|
|
294
|
+
fail: /* @__PURE__ */ __name((error) => writer.fail(error), "fail")
|
|
295
|
+
},
|
|
296
|
+
summary() {
|
|
297
|
+
if (pending.length > 0) {
|
|
298
|
+
observeLine(pending);
|
|
299
|
+
pending = "";
|
|
300
|
+
}
|
|
301
|
+
closeBurst();
|
|
302
|
+
return {
|
|
303
|
+
...reasoning.length > 0 ? {
|
|
304
|
+
reasoning,
|
|
305
|
+
reasoningMs: Math.round(reasoningMs)
|
|
306
|
+
} : {},
|
|
307
|
+
...ui.size > 0 ? {
|
|
308
|
+
ui: [
|
|
309
|
+
...ui.values()
|
|
310
|
+
]
|
|
311
|
+
} : {}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
__name(observeTurnFrames, "observeTurnFrames");
|
|
317
|
+
function withTurnFrames(result, frames) {
|
|
318
|
+
return {
|
|
319
|
+
...result,
|
|
320
|
+
...result.reasoning === void 0 && frames.reasoning !== void 0 ? {
|
|
321
|
+
reasoning: frames.reasoning
|
|
322
|
+
} : {},
|
|
323
|
+
...result.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
|
|
324
|
+
reasoningMs: frames.reasoningMs
|
|
325
|
+
} : {},
|
|
326
|
+
...result.ui === void 0 && frames.ui !== void 0 ? {
|
|
327
|
+
ui: frames.ui
|
|
328
|
+
} : {}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
__name(withTurnFrames, "withTurnFrames");
|
|
332
|
+
|
|
232
333
|
// src/spi/pricing-store.ts
|
|
233
334
|
async function seedModelPrices(store, prices) {
|
|
234
335
|
for (const price of prices) {
|
|
@@ -1949,6 +2050,19 @@ var ToolRegistry = class {
|
|
|
1949
2050
|
* tool the allow-list has already excluded is a call whose answer nothing reads.
|
|
1950
2051
|
*/
|
|
1951
2052
|
async definitionsFor(actor, policy, allowedTools) {
|
|
2053
|
+
return (await this.visibleSpecs(actor, policy, allowedTools)).map((spec) => ({
|
|
2054
|
+
name: spec.name,
|
|
2055
|
+
kind: spec.kind,
|
|
2056
|
+
description: spec.description,
|
|
2057
|
+
inputSchema: spec.inputSchema
|
|
2058
|
+
}));
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
|
|
2062
|
+
* enabled, role, `canUse`) in the same order. For a surface that lists what an actor can reach
|
|
2063
|
+
* (`GET <base>/tools`), which must never disagree with what the model is actually shown.
|
|
2064
|
+
*/
|
|
2065
|
+
async visibleSpecs(actor, policy, allowedTools) {
|
|
1952
2066
|
const pinnedNames = new Set(filterToolsByAllowList(this.allSpecs(), allowedTools).map((spec) => spec.name));
|
|
1953
2067
|
const pinned = [
|
|
1954
2068
|
...this.entries.values()
|
|
@@ -1957,12 +2071,7 @@ var ToolRegistry = class {
|
|
|
1957
2071
|
const allowedByRole = new Set((await filterToolsByRole(live.map((entry) => entry.spec), actor, policy)).map((spec) => spec.name));
|
|
1958
2072
|
const roleScoped = live.filter((entry) => allowedByRole.has(entry.spec.name));
|
|
1959
2073
|
const actorScoped = await filterToolsByCanUse(roleScoped, actor);
|
|
1960
|
-
return actorScoped.map(({ spec }) =>
|
|
1961
|
-
name: spec.name,
|
|
1962
|
-
kind: spec.kind,
|
|
1963
|
-
description: spec.description,
|
|
1964
|
-
inputSchema: spec.inputSchema
|
|
1965
|
-
}));
|
|
2074
|
+
return actorScoped.map(({ spec }) => spec);
|
|
1966
2075
|
}
|
|
1967
2076
|
/**
|
|
1968
2077
|
* Run a tool. Re-checks that the tool is enabled and that the role allows it (defense-in-depth —
|
|
@@ -3534,12 +3643,13 @@ ${block}`;
|
|
|
3534
3643
|
writer
|
|
3535
3644
|
}) : void 0;
|
|
3536
3645
|
const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
|
|
3537
|
-
const
|
|
3646
|
+
const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
|
|
3647
|
+
const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
|
|
3538
3648
|
system: prompt.system,
|
|
3539
3649
|
messages: prompt.messages,
|
|
3540
3650
|
tools,
|
|
3541
|
-
sink:
|
|
3542
|
-
})), deps);
|
|
3651
|
+
sink: frames.writer
|
|
3652
|
+
})), frames.summary()), deps);
|
|
3543
3653
|
if (incremental !== void 0) {
|
|
3544
3654
|
await incremental.settled();
|
|
3545
3655
|
const refusal = incremental.rejection();
|
|
@@ -3727,6 +3837,15 @@ ${block}`;
|
|
|
3727
3837
|
...input.agentName !== void 0 ? {
|
|
3728
3838
|
agentName: input.agentName
|
|
3729
3839
|
} : {},
|
|
3840
|
+
...turn.reasoning !== void 0 ? {
|
|
3841
|
+
reasoning: turn.reasoning
|
|
3842
|
+
} : {},
|
|
3843
|
+
...turn.reasoningMs !== void 0 ? {
|
|
3844
|
+
reasoningMs: turn.reasoningMs
|
|
3845
|
+
} : {},
|
|
3846
|
+
...turn.ui !== void 0 && turn.ui.length > 0 ? {
|
|
3847
|
+
ui: turn.ui
|
|
3848
|
+
} : {},
|
|
3730
3849
|
...messageCalls.length > 0 ? {
|
|
3731
3850
|
toolCalls: messageCalls
|
|
3732
3851
|
} : {},
|
|
@@ -3784,7 +3903,10 @@ ${block}`;
|
|
|
3784
3903
|
await writer.write(encodeStreamEvent({
|
|
3785
3904
|
kind: "step-finish",
|
|
3786
3905
|
usage: turn.usage,
|
|
3787
|
-
costUsd
|
|
3906
|
+
costUsd,
|
|
3907
|
+
...turn.reasoningMs !== void 0 ? {
|
|
3908
|
+
reasoningMs: turn.reasoningMs
|
|
3909
|
+
} : {}
|
|
3788
3910
|
}));
|
|
3789
3911
|
});
|
|
3790
3912
|
break;
|
|
@@ -3837,7 +3959,10 @@ ${block}`;
|
|
|
3837
3959
|
await writer.write(encodeStreamEvent({
|
|
3838
3960
|
kind: "step-finish",
|
|
3839
3961
|
usage: turn.usage,
|
|
3840
|
-
costUsd
|
|
3962
|
+
costUsd,
|
|
3963
|
+
...turn.reasoningMs !== void 0 ? {
|
|
3964
|
+
reasoningMs: turn.reasoningMs
|
|
3965
|
+
} : {}
|
|
3841
3966
|
}));
|
|
3842
3967
|
});
|
|
3843
3968
|
}
|
|
@@ -3992,6 +4117,7 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
3992
4117
|
memoryWriteVerdict,
|
|
3993
4118
|
normalizeDelegation,
|
|
3994
4119
|
normalizeElicitationReply,
|
|
4120
|
+
observeTurnFrames,
|
|
3995
4121
|
offerMemories,
|
|
3996
4122
|
offerSkills,
|
|
3997
4123
|
publishAgentDelegated,
|
|
@@ -4041,6 +4167,7 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4041
4167
|
withMemoryTool,
|
|
4042
4168
|
withSkillTool,
|
|
4043
4169
|
withToolTimeout,
|
|
4170
|
+
withTurnFrames,
|
|
4044
4171
|
writeMemory
|
|
4045
4172
|
});
|
|
4046
4173
|
//# sourceMappingURL=index.cjs.map
|