@dudousxd/nestjs-agent-core 0.33.0 → 0.34.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 +3 -1
- package/dist/genui/index.d.cts +1 -1
- package/dist/genui/index.d.ts +1 -1
- package/dist/guardrails/index.d.cts +2 -2
- package/dist/guardrails/index.d.ts +2 -2
- package/dist/index.cjs +234 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -5
- package/dist/index.d.ts +77 -5
- package/dist/index.js +222 -5
- package/dist/index.js.map +1 -1
- package/dist/{processors-DvZDW5zS.d.cts → processors-Bm2DniQ8.d.cts} +1 -1
- package/dist/{processors-C1jir8eB.d.ts → processors-CtrfAA0B.d.ts} +1 -1
- package/dist/{tool-CMTuoJ2v.d.cts → tool-C128TFWw.d.cts} +82 -1
- package/dist/{tool-CMTuoJ2v.d.ts → tool-C128TFWw.d.ts} +82 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,9 @@ import type { ModelProvider, AgentStore, ToolSpec, RolesPolicy } from '@dudousxd
|
|
|
35
35
|
- `MemoryRecord` / `MemoryProvider` / `offerMemories` / `writeMemory` — what the assistant concluded about a person or an organisation, carried across turns and threads. Scoped by the SAME opaque tokens and the same `ScopeResolver` skills use, so a deployment has one answer to "which scopes does this actor have". A memory is a keyed fact: `{ key, text, scope, origin, updatedAt }`, and the key is what makes a conflict mechanically detectable — two memories sharing a key at different scopes are one question answered twice, and `resolveMemoryDigest` lets the narrower win. Where it does, the entry's `overrides` carries the beaten **text** and its **author**, not merely its scope (a skill's `shadows`): the model is following one procedure either way, but a memory is a VALUE, and an agent that knew only that a wider one existed could tell the user nothing except which it picked. **Not retrieval:** a passage is a document someone authored and can fix at its source, a memory is the agent's own inference about someone who never saw it written — hence `MemoryOrigin` on every record, a block that tells the model these are its own fallible notes, and `forget` being REQUIRED on the provider while `write` is optional. Every provider method and every multi-argument export here takes ONE named object (`ListMemoriesInput`, `StoreMemoryInput`, `ResolveMemoryDigestInput`, …): `key`, `text` and `scope` are all strings, and transposed positional arguments would compile clean and write a fact whose key is its value. `memoryWriteVerdict` carries the same four rules as `skillWriteVerdict`; rule three (nothing but a human may write above its own scope, whatever elevation a host grants) is enforced by SHAPE as well as by check, since `rememberToolDefinition()` takes no scope parameter. `memoryForgetVerdict` is narrower still and takes no `elevated` flag: deleting what the assistant believes about YOU needs nobody's permission. What enters the system prompt is one line per memory bounded by `maxMemories` (`DEFAULT_MAX_MEMORIES`), each capped at `maxFactChars` (`DEFAULT_MAX_FACT_CHARS`) when it is WRITTEN — so the block's ceiling is the product of two numbers an operator set, and there is no body/catalog split because a fact that cannot be stated in a line is a document. **The prompt budget is bounded; the store is not.** Once the applicable set outgrows the block, WHICH memories it carries is a decision, and making it by scope starves the widest scopes first — one person's twentieth note would end every chance their organisation's facts had, leaving only a non-zero `omitted` behind. So a provider MAY implement `search({ scopes, query, limit, ctx })` and the block is filled by relevance to the turn instead; omit it and every turn is served by `list`, selecting narrowest-then-newest as before. Scope remains a hard FILTER that gates before ranking (a record returned outside `scopes` is dropped, so a host's filter bug costs throughput rather than privacy), and `search` must return every record sharing a returned key or precedence inverts. `MemoryRecord.pinned` is the categorical always-on marker — present whatever the turn is about, spending the same budget, never set by the agent (`StoreMemoryInput` has no such field, and the `remember` tool has no such parameter), with `MemoryDigest.pinnedOmitted` naming the one omission that is a misconfiguration rather than a budget. `buildMemoryBlock` frames entries by `origin.author`: what the agent CONCLUDED is hedged ("your own notes … prefer what the user says now"), what a person STATED is not, because telling a model to prefer the user over an organisation's published policy hands any user an override of it by assertion. A `partial` block says so, so the model does not read an absence as evidence. The loop spends ONE checkpoint (`memory:digest`) holding the whole digest — the search included, since a ranking is the most re-derivable decision here — which is both what the block is rendered from and what a later `remember` call is authorized against; the write itself happens inside the ordinary `tool:<callId>` checkpoint, which is what makes it idempotent under replay. The query is the user's own turn text and nothing else: the only thing available before the first model call, already a journaled input to the run, and it fails at a turn with no topic — which is what `pinned` is for.
|
|
36
36
|
- `AgentStore.setMessageToolResults(messageId, results)` — a message's tool CALLS are known when it is appended and their outputs are not, so the loop settles them afterwards with one write of the turn's complete result list (its synthetic `retrieve` / `structured_output` calls included). Both halves live on the message because that is where a thread reader pairs them; a call whose output only ever reaches the `agent_tool_call` table renders as a tool still running. Required, not optional — a store that silently declines it breaks a client with nothing logged.
|
|
37
37
|
- `AttachmentStagingStore.list(input)` / `AgentStore.referencedMediaIds(actorRef, mediaIds)` — the two halves of attachment housekeeping, split the way ownership is. `stage()` writes bytes before any message exists, so an upload the user never sent leaves media nothing points at; the HOST can enumerate that media (it stored it) but cannot see a transcript, and this library sees every transcript but never holds bytes. `list` returns `StagedAttachment` metadata (`mediaId`, `name`, `contentType`, `sizeBytes`, `createdAt` — no `url`, since `resolve` mints those per turn precisely so they can be short-lived); `referencedMediaIds` answers which of a set of ids a message that still exists carries, scoped to one actor. The answer is DERIVED from the surviving message rows on every call, never latched: `truncateFrom` deletes messages — regenerating a turn does exactly that — so a reference disappears, and a flag set at send time would pin the bytes for ever. Both are optional; a caller that cannot get an answer must collect nothing rather than read silence as "unreferenced". `AgentService.collectableAttachments` composes them, and deletes nothing.
|
|
38
|
-
- `agentFailureCode(error)` — the stream error code for a failed run (`cancelled`, `quota_exceeded`, `output_rejected`, `structured_output_invalid`, else `run_failed`), so a control doing its job never reads as the model breaking.
|
|
38
|
+
- `agentFailureCode(error)` — the stream error code for a failed run (`cancelled`, `quota_exceeded`, `output_rejected`, `structured_output_invalid`, `replay_diverged` for the durable runtime refusing a checkpoint position, `model_no_output` for a model call that produced nothing, else `run_failed`), so a control doing its job never reads as the model breaking. `streamFailure(error)` is the `{ code, message }` a runner closes the stream with: for a crash the message is `RUN_FAILED_MESSAGE` in production and the raw text elsewhere (`exposeStreamErrorDetails` decides it outright) — the error itself belongs in the log and on the run row.
|
|
39
|
+
- `settleDanglingToolCalls(messages, outcomes?)` / `danglingToolCallIds(messages)` — give every tool call in a history a result. A turn that dies mid-step leaves an assistant message asking for tools and answered by nothing, which a provider refuses, so every later turn on the thread fails too. The loop settles them inside `load:thread` (no new position; a replay reads the recorded payload) from the calls' own rows where the store implements the optional `AgentStore.toolCallOutcomes` — a tool that DID run hands the model its real output, so it is not run again — and otherwise with `UNFINISHED_TOOL_CALL`. `AgentStore.failUnsettledToolCalls(runId, error)` (optional) is what a failing run calls so no approval card waits on it; `settleDeadRun(store, { runId, threadId?, failure? })` is the same for a caller with no journal left to write to.
|
|
40
|
+
- `AiToolCtx.idempotencyKey` (`<runId>:<toolCallId>`) / `AiToolCtx.toolCallId` — the same for every execution of one call, so a tool re-run after a worker died between its side effect and its checkpoint (or by a transient retry) can be made to land on the first attempt. `toolCallContext(ctx, toolCallId)` builds it for a runner's own dispatched step.
|
|
39
41
|
- `RunCancelledError` / `AgentLoopHooks.cancelled()` — real cancellation. The loop asks `hooks.cancelled()` at the points where stopping is safe and cheap (between steps, before the next model call, before a turn's tools are dispatched), ALWAYS from inside a checkpoint, so the answer is journaled and a cancel arriving between two replays can never change a branch a replayed position already took; the positions themselves sit behind `hooks.patched('agent:cancellation')`, so a run already in flight keeps its recorded shape. A tool already executing is never interrupted — there is no un-executing a side effect, and abandoning a dispatched step leaves a journal holding a dispatch whose result never lands. Observing a cancel throws `RunCancelledError`, which unwinds through the path a suspend already uses; the RUNNER settles it, recording the run `cancelled` (`AgentStore.recordRunEnd`'s third terminal, never `failed`) and ending the stream on a `{ kind: 'cancelled' }` frame rather than failing it, so a user pressing Stop is not in anybody's error rate.
|
|
40
42
|
- `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.
|
|
41
43
|
- `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.
|
package/dist/genui/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as Catalog, J as JsonSchema, G as GenuiValidation, b as GenuiIssue } from '../catalog-CrzetM3_.cjs';
|
|
2
2
|
export { A as AjvLike, c as COMPONENT_NAME, d as CatalogOptions, C as ComponentDefinition, e as JsonSchemaValidator, P as PropsSchema, f as ajvValidator, g as builtinJsonSchemaValidator, h as defineCatalog, i as defineComponent, j as formatIssues, k as isStandardSchema, t as toJsonSchema, l as toSnakeCase, m as toolNameFor, v as validateProps, n as validatePropsSync } from '../catalog-CrzetM3_.cjs';
|
|
3
3
|
import { StandardSchemaV1, StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { A as Actor, T as ToolSpec, a as ToolHandler, b as ToolPresentation } from '../tool-
|
|
4
|
+
import { A as Actor, T as ToolSpec, a as ToolHandler, b as ToolPresentation } from '../tool-C128TFWw.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* One node of a composed UI: a catalog component, its props, and — for components declared with
|
package/dist/genui/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as Catalog, J as JsonSchema, G as GenuiValidation, b as GenuiIssue } from '../catalog-CrzetM3_.js';
|
|
2
2
|
export { A as AjvLike, c as COMPONENT_NAME, d as CatalogOptions, C as ComponentDefinition, e as JsonSchemaValidator, P as PropsSchema, f as ajvValidator, g as builtinJsonSchemaValidator, h as defineCatalog, i as defineComponent, j as formatIssues, k as isStandardSchema, t as toJsonSchema, l as toSnakeCase, m as toolNameFor, v as validateProps, n as validatePropsSync } from '../catalog-CrzetM3_.js';
|
|
3
3
|
import { StandardSchemaV1, StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { A as Actor, T as ToolSpec, a as ToolHandler, b as ToolPresentation } from '../tool-
|
|
4
|
+
import { A as Actor, T as ToolSpec, a as ToolHandler, b as ToolPresentation } from '../tool-C128TFWw.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* One node of a composed UI: a catalog component, its props, and — for components declared with
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InputProcessor, O as OutputProcessor } from '../processors-
|
|
2
|
-
import { A as Actor, a as ToolHandler } from '../tool-
|
|
1
|
+
import { I as InputProcessor, O as OutputProcessor } from '../processors-Bm2DniQ8.cjs';
|
|
2
|
+
import { A as Actor, a as ToolHandler } from '../tool-C128TFWw.cjs';
|
|
3
3
|
import '@standard-schema/spec';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InputProcessor, O as OutputProcessor } from '../processors-
|
|
2
|
-
import { A as Actor, a as ToolHandler } from '../tool-
|
|
1
|
+
import { I as InputProcessor, O as OutputProcessor } from '../processors-CtrfAA0B.js';
|
|
2
|
+
import { A as Actor, a as ToolHandler } from '../tool-C128TFWw.js';
|
|
3
3
|
import '@standard-schema/spec';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -76,6 +76,11 @@ __export(src_exports, {
|
|
|
76
76
|
REMEMBER_TOOL_DESCRIPTION: () => REMEMBER_TOOL_DESCRIPTION,
|
|
77
77
|
REMEMBER_TOOL_NAME: () => REMEMBER_TOOL_NAME,
|
|
78
78
|
REQUESTER_APPROVER: () => REQUESTER_APPROVER,
|
|
79
|
+
RUN_ENDED_BEFORE_TOOL_CALL: () => RUN_ENDED_BEFORE_TOOL_CALL,
|
|
80
|
+
RUN_FAILED_MESSAGE: () => RUN_FAILED_MESSAGE,
|
|
81
|
+
RUN_NOT_ACTIVE_CODE: () => RUN_NOT_ACTIVE_CODE,
|
|
82
|
+
RUN_NOT_ACTIVE_MESSAGE: () => RUN_NOT_ACTIVE_MESSAGE,
|
|
83
|
+
RUN_NO_LONGER_RUNNING: () => RUN_NO_LONGER_RUNNING,
|
|
79
84
|
RunCancelledError: () => RunCancelledError,
|
|
80
85
|
SKILL_TOOL_DESCRIPTION: () => SKILL_TOOL_DESCRIPTION,
|
|
81
86
|
SKILL_TOOL_NAME: () => SKILL_TOOL_NAME,
|
|
@@ -86,6 +91,7 @@ __export(src_exports, {
|
|
|
86
91
|
ToolInputInvalidError: () => ToolInputInvalidError,
|
|
87
92
|
ToolNotFoundError: () => ToolNotFoundError,
|
|
88
93
|
ToolRegistry: () => ToolRegistry,
|
|
94
|
+
UNFINISHED_TOOL_CALL: () => UNFINISHED_TOOL_CALL,
|
|
89
95
|
actorScope: () => actorScope,
|
|
90
96
|
agentDiagnosticKey: () => agentDiagnosticKey,
|
|
91
97
|
agentFailureCode: () => agentFailureCode,
|
|
@@ -103,6 +109,7 @@ __export(src_exports, {
|
|
|
103
109
|
createIncrementalGate: () => createIncrementalGate,
|
|
104
110
|
createNoopEmitUi: () => createNoopEmitUi,
|
|
105
111
|
createUiCollector: () => createUiCollector,
|
|
112
|
+
danglingToolCallIds: () => danglingToolCallIds,
|
|
106
113
|
dayBoundsUtc: () => dayBoundsUtc,
|
|
107
114
|
decodeStreamEvent: () => decodeStreamEvent,
|
|
108
115
|
defaultCanDecide: () => defaultCanDecide,
|
|
@@ -114,6 +121,7 @@ __export(src_exports, {
|
|
|
114
121
|
estimateCost: () => estimateCost,
|
|
115
122
|
estimateMessageTokens: () => estimateMessageTokens,
|
|
116
123
|
exhaustedWindow: () => exhaustedWindow,
|
|
124
|
+
exposeStreamErrorDetails: () => exposeStreamErrorDetails,
|
|
117
125
|
extractJson: () => extractJson,
|
|
118
126
|
filterToolsByAllowList: () => filterToolsByAllowList,
|
|
119
127
|
filterToolsByCanUse: () => filterToolsByCanUse,
|
|
@@ -176,6 +184,8 @@ __export(src_exports, {
|
|
|
176
184
|
runOutputProcessors: () => runOutputProcessors,
|
|
177
185
|
seedModelPrices: () => seedModelPrices,
|
|
178
186
|
settleAll: () => settleAll,
|
|
187
|
+
settleDanglingToolCalls: () => settleDanglingToolCalls,
|
|
188
|
+
settleDeadRun: () => settleDeadRun,
|
|
179
189
|
settleElicitation: () => settleElicitation,
|
|
180
190
|
settleUnsettledDelegation: () => settleUnsettledDelegation,
|
|
181
191
|
skillInputSchema: () => skillInputSchema,
|
|
@@ -184,9 +194,11 @@ __export(src_exports, {
|
|
|
184
194
|
stampToolKinds: () => stampToolKinds,
|
|
185
195
|
staticModelCatalog: () => staticModelCatalog,
|
|
186
196
|
staticSkillProvider: () => staticSkillProvider,
|
|
197
|
+
streamFailure: () => streamFailure,
|
|
187
198
|
summarizeWithModel: () => summarizeWithModel,
|
|
188
199
|
tenantScope: () => tenantScope,
|
|
189
200
|
toolCallApprovalFromRow: () => toolCallApprovalFromRow,
|
|
201
|
+
toolCallContext: () => toolCallContext,
|
|
190
202
|
traceLlmTurn: () => traceLlmTurn,
|
|
191
203
|
traceToolExecution: () => traceToolExecution,
|
|
192
204
|
truncateDetailContent: () => truncateDetailContent,
|
|
@@ -2553,6 +2565,123 @@ function isControlFlowSignal(error) {
|
|
|
2553
2565
|
}
|
|
2554
2566
|
__name(isControlFlowSignal, "isControlFlowSignal");
|
|
2555
2567
|
|
|
2568
|
+
// src/dangling-tool-calls.ts
|
|
2569
|
+
var UNFINISHED_TOOL_CALL = "This tool call was never completed: the turn it belonged to ended unexpectedly before it was settled. Do not assume it ran, and do not assume it did not \u2014 check the current state with a read tool if one exists, tell the person plainly what is and is not confirmed, and do it again only if they still want it.";
|
|
2570
|
+
var RUN_ENDED_BEFORE_TOOL_CALL = "the run ended before this tool call was settled";
|
|
2571
|
+
var DECLINED = "The person was asked to approve this action and declined it. Nothing ran and nothing changed.";
|
|
2572
|
+
var EXPIRED = "This action needed approval, and the request expired before anyone decided. Nothing ran.";
|
|
2573
|
+
function danglingToolCallIds(messages) {
|
|
2574
|
+
const ids = [];
|
|
2575
|
+
for (const message of messages) {
|
|
2576
|
+
const calls = message.toolCalls ?? [];
|
|
2577
|
+
if (message.role !== "assistant" || calls.length === 0) {
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
const settled = new Set((message.toolResults ?? []).map((result) => result.id));
|
|
2581
|
+
for (const call of calls) {
|
|
2582
|
+
if (!settled.has(call.id)) {
|
|
2583
|
+
ids.push(call.id);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
return ids;
|
|
2588
|
+
}
|
|
2589
|
+
__name(danglingToolCallIds, "danglingToolCallIds");
|
|
2590
|
+
function resultFor(call, outcome) {
|
|
2591
|
+
const base = {
|
|
2592
|
+
id: call.id,
|
|
2593
|
+
name: call.name
|
|
2594
|
+
};
|
|
2595
|
+
switch (outcome?.status) {
|
|
2596
|
+
case "executed":
|
|
2597
|
+
case "auto_executed":
|
|
2598
|
+
return {
|
|
2599
|
+
...base,
|
|
2600
|
+
output: outcome.output ?? null
|
|
2601
|
+
};
|
|
2602
|
+
case "failed":
|
|
2603
|
+
return {
|
|
2604
|
+
...base,
|
|
2605
|
+
output: null,
|
|
2606
|
+
error: outcome.error ?? UNFINISHED_TOOL_CALL
|
|
2607
|
+
};
|
|
2608
|
+
case "rejected":
|
|
2609
|
+
return {
|
|
2610
|
+
...base,
|
|
2611
|
+
output: {
|
|
2612
|
+
rejected: true
|
|
2613
|
+
},
|
|
2614
|
+
denied: true,
|
|
2615
|
+
error: DECLINED
|
|
2616
|
+
};
|
|
2617
|
+
case "expired":
|
|
2618
|
+
return {
|
|
2619
|
+
...base,
|
|
2620
|
+
output: {
|
|
2621
|
+
rejected: true,
|
|
2622
|
+
expired: true
|
|
2623
|
+
},
|
|
2624
|
+
denied: true,
|
|
2625
|
+
expired: true,
|
|
2626
|
+
error: EXPIRED
|
|
2627
|
+
};
|
|
2628
|
+
default:
|
|
2629
|
+
return {
|
|
2630
|
+
...base,
|
|
2631
|
+
output: null,
|
|
2632
|
+
error: UNFINISHED_TOOL_CALL
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
__name(resultFor, "resultFor");
|
|
2637
|
+
function settleDanglingToolCalls(messages, outcomes = []) {
|
|
2638
|
+
const known = new Map(outcomes.map((outcome) => [
|
|
2639
|
+
outcome.id,
|
|
2640
|
+
outcome
|
|
2641
|
+
]));
|
|
2642
|
+
return messages.map((message) => {
|
|
2643
|
+
const calls = message.toolCalls ?? [];
|
|
2644
|
+
if (message.role !== "assistant" || calls.length === 0) {
|
|
2645
|
+
return message;
|
|
2646
|
+
}
|
|
2647
|
+
const results = message.toolResults ?? [];
|
|
2648
|
+
const settled = new Set(results.map((result) => result.id));
|
|
2649
|
+
const missing = calls.filter((call) => !settled.has(call.id));
|
|
2650
|
+
if (missing.length === 0) {
|
|
2651
|
+
return message;
|
|
2652
|
+
}
|
|
2653
|
+
return {
|
|
2654
|
+
...message,
|
|
2655
|
+
toolResults: [
|
|
2656
|
+
...results,
|
|
2657
|
+
...missing.map((call) => resultFor(call, known.get(call.id)))
|
|
2658
|
+
]
|
|
2659
|
+
};
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
__name(settleDanglingToolCalls, "settleDanglingToolCalls");
|
|
2663
|
+
|
|
2664
|
+
// src/dead-run.ts
|
|
2665
|
+
var RUN_NOT_ACTIVE_CODE = "run_not_active";
|
|
2666
|
+
var RUN_NOT_ACTIVE_MESSAGE = "This request is no longer waiting for an answer: the turn it belonged to has ended. Send the message again.";
|
|
2667
|
+
var RUN_NO_LONGER_RUNNING = "the run is no longer running";
|
|
2668
|
+
async function settleDeadRun(store, input) {
|
|
2669
|
+
const failure = input.failure;
|
|
2670
|
+
if (failure !== void 0) {
|
|
2671
|
+
await Promise.resolve(store.recordRunEnd?.({
|
|
2672
|
+
runId: input.runId,
|
|
2673
|
+
status: "failed",
|
|
2674
|
+
errorCode: failure.code,
|
|
2675
|
+
errorMessage: failure.message
|
|
2676
|
+
})).catch(() => void 0);
|
|
2677
|
+
}
|
|
2678
|
+
await Promise.resolve(store.failUnsettledToolCalls?.(input.runId, RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
2679
|
+
if (input.threadId !== void 0) {
|
|
2680
|
+
await releaseThreadRun(store, input.threadId, input.runId).catch(() => void 0);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
__name(settleDeadRun, "settleDeadRun");
|
|
2684
|
+
|
|
2556
2685
|
// src/delegation.ts
|
|
2557
2686
|
function normalizeDelegation(entry) {
|
|
2558
2687
|
return typeof entry === "string" ? {
|
|
@@ -3017,9 +3146,46 @@ function agentFailureCode(error) {
|
|
|
3017
3146
|
if (error instanceof StructuredOutputError) {
|
|
3018
3147
|
return "structured_output_invalid";
|
|
3019
3148
|
}
|
|
3020
|
-
|
|
3149
|
+
if (isReplayIntegrityError(error)) {
|
|
3150
|
+
return "replay_diverged";
|
|
3151
|
+
}
|
|
3152
|
+
return isNoOutputError(error) ? "model_no_output" : "run_failed";
|
|
3021
3153
|
}
|
|
3022
3154
|
__name(agentFailureCode, "agentFailureCode");
|
|
3155
|
+
function isNoOutputError(error) {
|
|
3156
|
+
if (!(error instanceof Error)) {
|
|
3157
|
+
return false;
|
|
3158
|
+
}
|
|
3159
|
+
return error.name === "AI_NoOutputGeneratedError" || error.name === "NoOutputGeneratedError" || error.message.startsWith("No output generated");
|
|
3160
|
+
}
|
|
3161
|
+
__name(isNoOutputError, "isNoOutputError");
|
|
3162
|
+
var RUN_FAILED_MESSAGE = "The assistant could not finish this answer. Please try again.";
|
|
3163
|
+
var streamErrorDetails;
|
|
3164
|
+
function exposeStreamErrorDetails(expose) {
|
|
3165
|
+
streamErrorDetails = expose;
|
|
3166
|
+
}
|
|
3167
|
+
__name(exposeStreamErrorDetails, "exposeStreamErrorDetails");
|
|
3168
|
+
var WORDED_CODES = /* @__PURE__ */ new Set([
|
|
3169
|
+
"quota_exceeded",
|
|
3170
|
+
"output_rejected",
|
|
3171
|
+
"structured_output_invalid"
|
|
3172
|
+
]);
|
|
3173
|
+
function streamFailure(error) {
|
|
3174
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3175
|
+
const code = agentFailureCode(error);
|
|
3176
|
+
if (WORDED_CODES.has(code)) {
|
|
3177
|
+
return {
|
|
3178
|
+
code,
|
|
3179
|
+
message: detail
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
const exposed = streamErrorDetails ?? process.env.NODE_ENV !== "production";
|
|
3183
|
+
return {
|
|
3184
|
+
code,
|
|
3185
|
+
message: exposed ? detail : RUN_FAILED_MESSAGE
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
__name(streamFailure, "streamFailure");
|
|
3023
3189
|
var MAX_DELEGATION_DEPTH = 5;
|
|
3024
3190
|
var DEFAULT_MAX_AGENT_APPEARANCES = 1;
|
|
3025
3191
|
function delegationRefusal(args) {
|
|
@@ -3211,9 +3377,9 @@ function splitHistory(policy, input, messages) {
|
|
|
3211
3377
|
}
|
|
3212
3378
|
__name(splitHistory, "splitHistory");
|
|
3213
3379
|
async function loadSelectedHistory(deps, input, hooks) {
|
|
3214
|
-
|
|
3380
|
+
const recorded = await hooks.step("load:thread", async () => {
|
|
3215
3381
|
const thread = await readThreadForTurn(deps, input.threadId);
|
|
3216
|
-
const { keep, drop } = splitHistory(deps.historyPolicy, input, toModelMessages(thread.messages));
|
|
3382
|
+
const { keep, drop } = splitHistory(deps.historyPolicy, input, await settleHistory(deps, toModelMessages(thread.messages)));
|
|
3217
3383
|
return {
|
|
3218
3384
|
messages: keep,
|
|
3219
3385
|
dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
|
|
@@ -3221,8 +3387,21 @@ async function loadSelectedHistory(deps, input, hooks) {
|
|
|
3221
3387
|
hasAssistantMessage: thread.hasAssistantMessage
|
|
3222
3388
|
};
|
|
3223
3389
|
});
|
|
3390
|
+
return {
|
|
3391
|
+
...recorded,
|
|
3392
|
+
messages: settleDanglingToolCalls(recorded.messages)
|
|
3393
|
+
};
|
|
3224
3394
|
}
|
|
3225
3395
|
__name(loadSelectedHistory, "loadSelectedHistory");
|
|
3396
|
+
async function settleHistory(deps, messages) {
|
|
3397
|
+
const dangling = danglingToolCallIds(messages);
|
|
3398
|
+
if (dangling.length === 0) {
|
|
3399
|
+
return messages;
|
|
3400
|
+
}
|
|
3401
|
+
const outcomes = await Promise.resolve(deps.store.toolCallOutcomes?.(dangling) ?? []).catch(() => []);
|
|
3402
|
+
return settleDanglingToolCalls(messages, outcomes);
|
|
3403
|
+
}
|
|
3404
|
+
__name(settleHistory, "settleHistory");
|
|
3226
3405
|
async function readThreadForTurn(deps, threadId) {
|
|
3227
3406
|
const windowing = deps.store;
|
|
3228
3407
|
if (typeof windowing.loadThreadForTurn === "function") {
|
|
@@ -3262,7 +3441,7 @@ __name(turnMessageLimit, "turnMessageLimit");
|
|
|
3262
3441
|
async function loadWholeThread(deps, input, hooks) {
|
|
3263
3442
|
const thread = await hooks.step("load:thread", () => deps.store.getThread(input.threadId));
|
|
3264
3443
|
const stored = thread?.messages ?? [];
|
|
3265
|
-
const { keep, drop } = splitHistory(deps.historyPolicy, input, toModelMessages(stored));
|
|
3444
|
+
const { keep, drop } = splitHistory(deps.historyPolicy, input, settleDanglingToolCalls(toModelMessages(stored)));
|
|
3266
3445
|
return {
|
|
3267
3446
|
messages: keep,
|
|
3268
3447
|
dropped: drop,
|
|
@@ -3445,6 +3624,14 @@ async function awaitElicitation(hooks, request, ctx) {
|
|
|
3445
3624
|
}, ctx));
|
|
3446
3625
|
}
|
|
3447
3626
|
__name(awaitElicitation, "awaitElicitation");
|
|
3627
|
+
function toolCallContext(ctx, toolCallId) {
|
|
3628
|
+
return {
|
|
3629
|
+
...ctx,
|
|
3630
|
+
toolCallId,
|
|
3631
|
+
idempotencyKey: `${ctx.runId}:${toolCallId}`
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3634
|
+
__name(toolCallContext, "toolCallContext");
|
|
3448
3635
|
function toolContext(deps, input, hooks) {
|
|
3449
3636
|
return {
|
|
3450
3637
|
actor: input.actor,
|
|
@@ -3884,7 +4071,7 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3884
4071
|
}, () => invokeWithTransientRetry(() => {
|
|
3885
4072
|
ui2.restart();
|
|
3886
4073
|
return deps.registry.invoke(call.name, call.input, {
|
|
3887
|
-
...ctx,
|
|
4074
|
+
...toolCallContext(ctx, call.id),
|
|
3888
4075
|
emitUi: ui2.emit
|
|
3889
4076
|
}, deps.rolesPolicy);
|
|
3890
4077
|
}, deps.toolTransientRetry ?? {}, {
|
|
@@ -5482,6 +5669,36 @@ var InMemoryAgentStore = class {
|
|
|
5482
5669
|
} : {}
|
|
5483
5670
|
});
|
|
5484
5671
|
}
|
|
5672
|
+
async toolCallOutcomes(toolCallIds) {
|
|
5673
|
+
const outcomes = [];
|
|
5674
|
+
for (const id of toolCallIds) {
|
|
5675
|
+
const row = this.toolCalls.get(id);
|
|
5676
|
+
if (row !== void 0) {
|
|
5677
|
+
outcomes.push({
|
|
5678
|
+
id,
|
|
5679
|
+
status: row.status,
|
|
5680
|
+
...row.output !== void 0 ? {
|
|
5681
|
+
output: row.output
|
|
5682
|
+
} : {},
|
|
5683
|
+
...row.error !== void 0 ? {
|
|
5684
|
+
error: row.error
|
|
5685
|
+
} : {}
|
|
5686
|
+
});
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
return outcomes;
|
|
5690
|
+
}
|
|
5691
|
+
async failUnsettledToolCalls(runId, error) {
|
|
5692
|
+
let settled = 0;
|
|
5693
|
+
for (const row of this.toolCalls.values()) {
|
|
5694
|
+
if (row.runId === runId && row.status === "pending_approval") {
|
|
5695
|
+
row.status = "failed";
|
|
5696
|
+
row.error = error;
|
|
5697
|
+
settled += 1;
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
return settled;
|
|
5701
|
+
}
|
|
5485
5702
|
async updateToolCall(input) {
|
|
5486
5703
|
const row = this.toolCalls.get(input.toolCallId);
|
|
5487
5704
|
if (row === void 0) {
|
|
@@ -5809,6 +6026,11 @@ var InMemoryAgentStore = class {
|
|
|
5809
6026
|
REMEMBER_TOOL_DESCRIPTION,
|
|
5810
6027
|
REMEMBER_TOOL_NAME,
|
|
5811
6028
|
REQUESTER_APPROVER,
|
|
6029
|
+
RUN_ENDED_BEFORE_TOOL_CALL,
|
|
6030
|
+
RUN_FAILED_MESSAGE,
|
|
6031
|
+
RUN_NOT_ACTIVE_CODE,
|
|
6032
|
+
RUN_NOT_ACTIVE_MESSAGE,
|
|
6033
|
+
RUN_NO_LONGER_RUNNING,
|
|
5812
6034
|
RunCancelledError,
|
|
5813
6035
|
SKILL_TOOL_DESCRIPTION,
|
|
5814
6036
|
SKILL_TOOL_NAME,
|
|
@@ -5819,6 +6041,7 @@ var InMemoryAgentStore = class {
|
|
|
5819
6041
|
ToolInputInvalidError,
|
|
5820
6042
|
ToolNotFoundError,
|
|
5821
6043
|
ToolRegistry,
|
|
6044
|
+
UNFINISHED_TOOL_CALL,
|
|
5822
6045
|
actorScope,
|
|
5823
6046
|
agentDiagnosticKey,
|
|
5824
6047
|
agentFailureCode,
|
|
@@ -5836,6 +6059,7 @@ var InMemoryAgentStore = class {
|
|
|
5836
6059
|
createIncrementalGate,
|
|
5837
6060
|
createNoopEmitUi,
|
|
5838
6061
|
createUiCollector,
|
|
6062
|
+
danglingToolCallIds,
|
|
5839
6063
|
dayBoundsUtc,
|
|
5840
6064
|
decodeStreamEvent,
|
|
5841
6065
|
defaultCanDecide,
|
|
@@ -5847,6 +6071,7 @@ var InMemoryAgentStore = class {
|
|
|
5847
6071
|
estimateCost,
|
|
5848
6072
|
estimateMessageTokens,
|
|
5849
6073
|
exhaustedWindow,
|
|
6074
|
+
exposeStreamErrorDetails,
|
|
5850
6075
|
extractJson,
|
|
5851
6076
|
filterToolsByAllowList,
|
|
5852
6077
|
filterToolsByCanUse,
|
|
@@ -5909,6 +6134,8 @@ var InMemoryAgentStore = class {
|
|
|
5909
6134
|
runOutputProcessors,
|
|
5910
6135
|
seedModelPrices,
|
|
5911
6136
|
settleAll,
|
|
6137
|
+
settleDanglingToolCalls,
|
|
6138
|
+
settleDeadRun,
|
|
5912
6139
|
settleElicitation,
|
|
5913
6140
|
settleUnsettledDelegation,
|
|
5914
6141
|
skillInputSchema,
|
|
@@ -5917,9 +6144,11 @@ var InMemoryAgentStore = class {
|
|
|
5917
6144
|
stampToolKinds,
|
|
5918
6145
|
staticModelCatalog,
|
|
5919
6146
|
staticSkillProvider,
|
|
6147
|
+
streamFailure,
|
|
5920
6148
|
summarizeWithModel,
|
|
5921
6149
|
tenantScope,
|
|
5922
6150
|
toolCallApprovalFromRow,
|
|
6151
|
+
toolCallContext,
|
|
5923
6152
|
traceLlmTurn,
|
|
5924
6153
|
traceToolExecution,
|
|
5925
6154
|
truncateDetailContent,
|