@schlenkr/ragents 0.1.6 → 0.1.8
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/apps/server/src/chat-handler.ts +2 -0
- package/apps/server/src/config.ts +6 -0
- package/apps/server/src/host-api.json +1 -0
- package/apps/server/src/layout.ts +0 -15
- package/apps/server/src/plugin-support/agent-tool.ts +2 -3
- package/apps/server/src/plugin-support/plugin-folder.ts +2 -1
- package/apps/server/src/provider.ts +34 -11
- package/apps/server/src/ragents/actor-chat-history.ts +1 -0
- package/apps/server/src/ragents/engine.ts +23 -24
- package/apps/server/src/ragents/journal-chat-events.ts +2 -0
- package/apps/server/src/ragents/run-owner.ts +3 -2
- package/apps/server/src/ragents/session.ts +3 -3
- package/apps/server/src/run-preparation.ts +14 -18
- package/apps/server/src/run-transfer.ts +65 -4
- package/apps/server/src/settings.ts +2 -4
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-B9qYBARS.js → highlighted-body-KPVGNVTW-xXXhTDbh.js} +1 -1
- package/apps/web/dist/assets/{main-BCjvD2Hh.js → main-C7JeZFjs.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-CvYYFK_n.js → mermaid-HWGCJPDP-DfsU5ofC.js} +69 -69
- package/apps/web/dist/assets/{runPanel-CG7kRMJh.js → runPanel-ZT6TPDLM.js} +1 -1
- package/apps/web/dist/help/guide-clients.html +13 -4
- package/apps/web/dist/help/guide-clients.md +14 -4
- package/apps/web/dist/help/guide-distributed.html +8 -3
- package/apps/web/dist/help/guide-distributed.md +8 -3
- package/apps/web/dist/help/guide-runtime.html +18 -12
- package/apps/web/dist/help/guide-runtime.md +19 -12
- package/apps/web/dist/help/mini-app.css +23 -3
- package/apps/web/dist/host-web.json +4 -4
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/run-panel.html +2 -2
- package/apps/web/src/SessionList.tsx +16 -8
- package/apps/web/src/StartTiles.tsx +1 -1
- package/apps/web/src/api.ts +2 -0
- package/apps/web/src/panel/PanelPage.tsx +1 -1
- package/apps/web/src/panel/RunLine.tsx +4 -2
- package/apps/web/src/panel/contract.ts +2 -0
- package/bundles/ragents.activity/ragents-bundle.json +1 -1
- package/bundles/ragents.actor-programs/ragents-bundle.json +1 -1
- package/bundles/ragents.ask/ragents-bundle.json +1 -1
- package/bundles/ragents.browser/ragents-bundle.json +1 -1
- package/bundles/ragents.documents/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
- package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
- package/bundles/ragents.orchestration/ragents-bundle.json +1 -1
- package/bundles/ragents.overseer/ragents-bundle.json +1 -1
- package/bundles/ragents.processes/ragents-bundle.json +1 -1
- package/bundles/ragents.product/ragents-bundle.json +1 -1
- package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
- package/bundles/ragents.reference/ragents-bundle.json +2 -2
- package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
- package/bundles/ragents.todo/ragents-bundle.json +1 -1
- package/bundles/ragents.transcript/ragents-bundle.json +1 -1
- package/bundles/ragents.watch/ragents-bundle.json +1 -1
- package/bundles/ragents.workspace/ragents-bundle.json +1 -1
- package/package.json +2 -2
- package/packages/agent/dist/core/agent-settings.d.ts +2 -2
- package/packages/agent/dist/core/compaction/compaction.d.ts +16 -50
- package/packages/agent/dist/core/context-log.d.ts +25 -0
- package/packages/agent/dist/core/messages.d.ts +3 -10
- package/packages/agent/dist/core/tool-definition.d.ts +17 -0
- package/packages/agent/dist/core/tools/bash.d.ts +2 -7
- package/packages/agent/dist/core/tools/edit.d.ts +1 -1
- package/packages/agent/dist/core/tools/read.d.ts +1 -1
- package/packages/agent/dist/core/tools/write.d.ts +1 -1
- package/packages/agent/dist/index.d.ts +7 -8
- package/packages/agent/dist/loop/agent.d.ts +2 -6
- package/packages/agent/package.json +1 -1
- package/packages/agent/src/core/agent-settings.ts +2 -2
- package/packages/agent/src/core/compaction/compaction.ts +49 -175
- package/packages/agent/src/core/context-log.ts +49 -0
- package/packages/agent/src/core/messages.ts +5 -11
- package/packages/agent/src/core/tool-definition.ts +29 -0
- package/packages/agent/src/core/tools/bash.ts +2 -9
- package/packages/agent/src/core/tools/edit.ts +2 -11
- package/packages/agent/src/core/tools/read.ts +3 -18
- package/packages/agent/src/core/tools/write.ts +1 -5
- package/packages/agent/src/index.ts +28 -14
- package/packages/agent/src/loop/agent-loop.ts +1 -2
- package/packages/agent/src/loop/agent.ts +8 -2
- package/packages/ai/dist/api/ai-sdk.d.ts +2 -0
- package/packages/ai/dist/types.d.ts +2 -0
- package/packages/ai/src/api/ai-sdk-messages.ts +15 -10
- package/packages/ai/src/api/ai-sdk.ts +11 -0
- package/packages/ai/src/api/transform-messages.ts +1 -1
- package/packages/ai/src/types.ts +2 -0
- package/packages/ai/src/utils/retry.ts +14 -0
- package/packages/ragents/src/agents/actor-input.ts +15 -0
- package/packages/ragents/src/agents/model-context.ts +360 -0
- package/packages/ragents/src/agents/plugins.ts +5 -0
- package/packages/ragents/src/agents/scheduler.ts +3 -7
- package/packages/ragents/src/agents/tools.ts +2 -2
- package/packages/ragents/src/agents/toolset.ts +47 -20
- package/packages/ragents/src/agents/workspaces.ts +0 -6
- package/packages/ragents/src/domain/event-semantics.ts +51 -0
- package/packages/ragents/src/domain/event-validation.ts +166 -0
- package/packages/ragents/src/domain/events.ts +72 -0
- package/packages/ragents/src/domain/model.ts +4 -2
- package/packages/ragents/src/domain/projection.ts +9 -0
- package/packages/ragents/src/domain/tool-result-text.ts +9 -0
- package/packages/ragents/src/drivers/agent-hooks.ts +25 -38
- package/packages/ragents/src/drivers/agent-runtime.ts +118 -933
- package/packages/ragents/src/drivers/agent-turn.ts +735 -0
- package/packages/ragents/src/drivers/agent.ts +9 -7
- package/packages/ragents/src/drivers/skill-preload.ts +53 -63
- package/packages/ragents/src/drivers/types.ts +21 -5
- package/packages/ragents/src/index.ts +8 -6
- package/packages/ragents/src/plugin-host.ts +60 -13
- package/packages/ragents/src/plugin-types.ts +7 -2
- package/packages/ragents/src/runtime/decisions/actors.ts +10 -0
- package/packages/ragents/src/runtime/decisions/turns.ts +66 -7
- package/packages/ragents/src/runtime/journal-storage.ts +7 -4
- package/packages/ragents/src/runtime/journal.ts +35 -0
- package/packages/ragents/src/runtime/orchestration.ts +140 -9
- package/packages/workspace-executor/src/sandbox-tools.ts +10 -11
- package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
- package/scripts/agent/journal.ts +5 -3
- package/scripts/package/publish-package.ts +4 -3
- package/packages/agent/dist/core/agent-session-runtime.d.ts +0 -33
- package/packages/agent/dist/core/agent-session-services.d.ts +0 -35
- package/packages/agent/dist/core/agent-session.d.ts +0 -234
- package/packages/agent/dist/core/auth-guidance.d.ts +0 -4
- package/packages/agent/dist/core/defaults.d.ts +0 -3
- package/packages/agent/dist/core/extensions/index.d.ts +0 -11
- package/packages/agent/dist/core/extensions/loader.d.ts +0 -12
- package/packages/agent/dist/core/extensions/runner.d.ts +0 -39
- package/packages/agent/dist/core/extensions/types.d.ts +0 -156
- package/packages/agent/dist/core/extensions/wrapper.d.ts +0 -12
- package/packages/agent/dist/core/resource-loader.d.ts +0 -39
- package/packages/agent/dist/core/sdk.d.ts +0 -38
- package/packages/agent/dist/core/session-manager.d.ts +0 -189
- package/packages/agent/dist/core/source-info.d.ts +0 -16
- package/packages/agent/dist/core/system-prompt.d.ts +0 -21
- package/packages/agent/src/core/agent-session-runtime.ts +0 -94
- package/packages/agent/src/core/agent-session-services.ts +0 -74
- package/packages/agent/src/core/agent-session.ts +0 -1108
- package/packages/agent/src/core/auth-guidance.ts +0 -14
- package/packages/agent/src/core/defaults.ts +0 -3
- package/packages/agent/src/core/extensions/index.ts +0 -38
- package/packages/agent/src/core/extensions/loader.ts +0 -125
- package/packages/agent/src/core/extensions/runner.ts +0 -211
- package/packages/agent/src/core/extensions/types.ts +0 -197
- package/packages/agent/src/core/extensions/wrapper.ts +0 -24
- package/packages/agent/src/core/resource-loader.ts +0 -142
- package/packages/agent/src/core/sdk.ts +0 -123
- package/packages/agent/src/core/session-manager.ts +0 -743
- package/packages/agent/src/core/source-info.ts +0 -28
- package/packages/agent/src/core/system-prompt.ts +0 -108
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
activeContextEntries,
|
|
5
|
+
Agent,
|
|
6
|
+
type AgentEvent,
|
|
7
|
+
type AgentLoopTurnUpdate,
|
|
8
|
+
type AgentMessage,
|
|
9
|
+
type AgentSettings,
|
|
10
|
+
type AgentTool,
|
|
11
|
+
calculateContextTokens,
|
|
12
|
+
compact,
|
|
13
|
+
contextMessages,
|
|
14
|
+
convertToLlm,
|
|
15
|
+
EMPTY_RESPONSE_NUDGE,
|
|
16
|
+
estimateContextTokens,
|
|
17
|
+
formatSkillsForPrompt,
|
|
18
|
+
isRunFailure,
|
|
19
|
+
type ModelRuntime,
|
|
20
|
+
prepareCompaction,
|
|
21
|
+
shouldCompact,
|
|
22
|
+
type Skill,
|
|
23
|
+
type ThinkingLevel,
|
|
24
|
+
} from "@ragents/agent";
|
|
25
|
+
import {
|
|
26
|
+
type AssistantMessage,
|
|
27
|
+
type ImageContent,
|
|
28
|
+
isContextOverflow,
|
|
29
|
+
isRetryableAssistantError,
|
|
30
|
+
type Model,
|
|
31
|
+
type TextContent,
|
|
32
|
+
type ToolCall,
|
|
33
|
+
type ToolResultMessage,
|
|
34
|
+
type UserMessage,
|
|
35
|
+
} from "@ragents/ai";
|
|
36
|
+
|
|
37
|
+
import { ignoredFieldsNotice } from "../agents/toolset.ts";
|
|
38
|
+
import type { RunFunction } from "../agents/tools.ts";
|
|
39
|
+
import { assertJsonValue, type JsonValue } from "../domain/json.ts";
|
|
40
|
+
import { addUsage, emptyUsage, type TurnUsage } from "../domain/model.ts";
|
|
41
|
+
import type { CompletedModelStep } from "../runtime/decisions/turns.ts";
|
|
42
|
+
import type { AgentHook } from "./agent-hooks.ts";
|
|
43
|
+
import { prepareInputAttachments } from "./attachments.ts";
|
|
44
|
+
import type { SkillPreload } from "./skill-preload.ts";
|
|
45
|
+
import type { TurnRequest, TurnResult } from "./types.ts";
|
|
46
|
+
|
|
47
|
+
type AgentUsage = { input: number; output: number; cacheRead: number; cacheWrite: number; cost?: { total?: number } };
|
|
48
|
+
|
|
49
|
+
export type AgentTurnOptions = {
|
|
50
|
+
request: TurnRequest<"agent">;
|
|
51
|
+
signal: AbortSignal;
|
|
52
|
+
modelRuntime: ModelRuntime;
|
|
53
|
+
model: Model<any>;
|
|
54
|
+
thinkingLevel: ThinkingLevel;
|
|
55
|
+
skills: readonly Skill[];
|
|
56
|
+
hooks: readonly AgentHook[];
|
|
57
|
+
preload: SkillPreload | null;
|
|
58
|
+
settings: AgentSettings;
|
|
59
|
+
track: <T>(operation: PromiseLike<T> | T) => Promise<T>;
|
|
60
|
+
onDiagnostic: (message: string) => void;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const errorMessage = (error: unknown) => (error instanceof Error ? error.message : String(error));
|
|
64
|
+
|
|
65
|
+
const usageOf = (usage: AgentUsage | undefined): TurnUsage => ({
|
|
66
|
+
inputTokens: usage?.input ?? 0,
|
|
67
|
+
outputTokens: usage?.output ?? 0,
|
|
68
|
+
cacheReadTokens: usage?.cacheRead ?? 0,
|
|
69
|
+
cacheWriteTokens: usage?.cacheWrite ?? 0,
|
|
70
|
+
costUsd: usage?.cost?.total ?? 0,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const asJson = (value: unknown) => {
|
|
74
|
+
try {
|
|
75
|
+
return JSON.stringify(value ?? {});
|
|
76
|
+
} catch {
|
|
77
|
+
return String(value);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const asJsonValue = (value: unknown): JsonValue => JSON.parse(asJson(value)) as JsonValue;
|
|
82
|
+
|
|
83
|
+
const asText = (value: unknown): string => {
|
|
84
|
+
if (typeof value === "string")
|
|
85
|
+
return value;
|
|
86
|
+
|
|
87
|
+
const content = (value as { content?: unknown })?.content;
|
|
88
|
+
|
|
89
|
+
if (Array.isArray(content)) {
|
|
90
|
+
const texts = content
|
|
91
|
+
.filter((entry): entry is { type: string; text: string } => (entry as { type?: string })?.type === "text")
|
|
92
|
+
.map((entry) => entry.text);
|
|
93
|
+
|
|
94
|
+
if (texts.length > 0)
|
|
95
|
+
return texts.join("\n");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return asJson(value);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const lastAssistantOf = (messages: readonly AgentMessage[]): AssistantMessage | undefined =>
|
|
102
|
+
[...messages].reverse().find((message): message is AssistantMessage => message.role === "assistant");
|
|
103
|
+
|
|
104
|
+
/** A retry continues behind every failed step at the end, since the journal keeps each of them. */
|
|
105
|
+
const withoutTrailingFailures = (messages: readonly AgentMessage[]) => {
|
|
106
|
+
const failed = (message: AgentMessage | undefined) =>
|
|
107
|
+
message?.role === "assistant" && (message.stopReason === "error" || message.stopReason === "aborted");
|
|
108
|
+
let end = messages.length;
|
|
109
|
+
|
|
110
|
+
while (failed(messages[end - 1]))
|
|
111
|
+
end--;
|
|
112
|
+
|
|
113
|
+
return messages.slice(0, end);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** One turn of an LLM actor on the agent loop; its context is always the projection of the journal. */
|
|
117
|
+
export class AgentTurn {
|
|
118
|
+
readonly #options: AgentTurnOptions;
|
|
119
|
+
readonly #request: TurnRequest<"agent">;
|
|
120
|
+
readonly #inputIds = new WeakMap<object, string | null>();
|
|
121
|
+
readonly #unexecutedManagedCalls = new Map<string, { name: string; input: JsonValue }>();
|
|
122
|
+
readonly #directToolCalls = new Map<string, { name: string; input: unknown }>();
|
|
123
|
+
#activeManagedNames = new Set<string>();
|
|
124
|
+
#agent: Agent | null = null;
|
|
125
|
+
#active = true;
|
|
126
|
+
#usage: TurnUsage = emptyUsage();
|
|
127
|
+
#failure: string | null = null;
|
|
128
|
+
#modelFailure: string | null = null;
|
|
129
|
+
#pendingText = "";
|
|
130
|
+
#systemPrompt = "";
|
|
131
|
+
#preloadedSkills = "";
|
|
132
|
+
#contextKey: string | undefined;
|
|
133
|
+
#lastAssistant: AssistantMessage | undefined;
|
|
134
|
+
#retryAttempt = 0;
|
|
135
|
+
#overflowRecoveryAttempted = false;
|
|
136
|
+
#retryAbort: AbortController | null = null;
|
|
137
|
+
#compactionAbort: AbortController | null = null;
|
|
138
|
+
#settled: Promise<void> = Promise.resolve();
|
|
139
|
+
|
|
140
|
+
constructor(options: AgentTurnOptions) {
|
|
141
|
+
this.#options = options;
|
|
142
|
+
this.#request = options.request;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Runs the turn up to its last model answer; `onAccepted` marks the point from which an abort reaches the loop. */
|
|
146
|
+
async run(onAccepted: () => void): Promise<void> {
|
|
147
|
+
const { request, signal, model } = this.#options;
|
|
148
|
+
const tools = this.#toolsOf(request);
|
|
149
|
+
this.#setSystemPrompt(request.systemPrompt, tools);
|
|
150
|
+
await this.#refreshTools(tools);
|
|
151
|
+
const prepared = await prepareInputAttachments(request, model.input);
|
|
152
|
+
|
|
153
|
+
if (signal.aborted)
|
|
154
|
+
return;
|
|
155
|
+
|
|
156
|
+
if (!(await this.#options.modelRuntime.checkAuth(model.provider)))
|
|
157
|
+
throw new Error(`Für den Anbieter ${model.provider} ist kein Schlüssel konfiguriert; er kommt aus der Konfiguration des Profils.`);
|
|
158
|
+
|
|
159
|
+
const context = request.modelContext();
|
|
160
|
+
await this.#checkCompaction(lastAssistantOf(context.messages), false);
|
|
161
|
+
this.#preloadedSkills = this.#options.preload
|
|
162
|
+
? (await this.#options.track(this.#options.preload({ prompt: prepared.prompt, skills: this.#options.skills, model, signal }))) ?? ""
|
|
163
|
+
: "";
|
|
164
|
+
|
|
165
|
+
if (signal.aborted)
|
|
166
|
+
return;
|
|
167
|
+
|
|
168
|
+
const prompt: UserMessage = {
|
|
169
|
+
role: "user",
|
|
170
|
+
content: [{ type: "text", text: prepared.prompt }, ...prepared.attachments],
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
};
|
|
173
|
+
this.#inputIds.set(prompt, request.input.id);
|
|
174
|
+
const agent = this.#agentFor(request.modelContext().messages, tools);
|
|
175
|
+
this.#agent = agent;
|
|
176
|
+
const settled = Promise.withResolvers<void>();
|
|
177
|
+
this.#settled = settled.promise;
|
|
178
|
+
onAccepted();
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await agent.prompt(prompt);
|
|
182
|
+
|
|
183
|
+
while (await this.#afterRun())
|
|
184
|
+
await agent.continue();
|
|
185
|
+
} finally {
|
|
186
|
+
settled.resolve();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Stops the loop, a waiting retry and a running compaction, and waits until the run has settled. */
|
|
191
|
+
async abort(): Promise<void> {
|
|
192
|
+
this.#retryAbort?.abort();
|
|
193
|
+
this.#compactionAbort?.abort();
|
|
194
|
+
this.#agent?.abort();
|
|
195
|
+
await this.#settled;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
result(): TurnResult {
|
|
199
|
+
return { failure: this.#failure ?? this.#modelFailure, usage: this.#usage };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
abortedResult(): TurnResult {
|
|
203
|
+
return { failure: this.#failure ?? "The agent turn was aborted.", usage: this.#usage };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
addUsage(usage: TurnUsage) {
|
|
207
|
+
this.#usage = addUsage(this.#usage, usage);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Ends the turn: no further journal writes, and an aborted turn keeps the text it already streamed. */
|
|
211
|
+
deactivate() {
|
|
212
|
+
if (!this.#active)
|
|
213
|
+
return;
|
|
214
|
+
|
|
215
|
+
this.#active = false;
|
|
216
|
+
this.#unexecutedManagedCalls.clear();
|
|
217
|
+
this.#directToolCalls.clear();
|
|
218
|
+
|
|
219
|
+
if (this.#options.signal.aborted)
|
|
220
|
+
this.#recordInterruptedOutput();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#agentFor(messages: readonly AgentMessage[], tools: AgentTool[]): Agent {
|
|
224
|
+
const { modelRuntime, model, thinkingLevel, settings, request } = this.#options;
|
|
225
|
+
const agent: Agent = new Agent({
|
|
226
|
+
initialState: { systemPrompt: this.#currentSystemPrompt(), model, thinkingLevel, tools, messages: [...messages] },
|
|
227
|
+
convertToLlm,
|
|
228
|
+
streamFn: (streamModel, context, options) => {
|
|
229
|
+
const maxRetries = options?.maxRetries ?? settings.providerRequest.maxRetries;
|
|
230
|
+
|
|
231
|
+
return modelRuntime.streamSimple(streamModel, context, {
|
|
232
|
+
...options,
|
|
233
|
+
timeoutMs: options?.timeoutMs ?? settings.providerRequest.timeoutMs,
|
|
234
|
+
...(maxRetries === undefined ? {} : { maxRetries }),
|
|
235
|
+
maxRetryDelayMs: options?.maxRetryDelayMs ?? settings.providerRequest.maxRetryDelayMs,
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
sessionId: `${request.runId}.${request.agentId}`,
|
|
239
|
+
transformContext: (_messages, signal) => this.#contextForModelCall(signal),
|
|
240
|
+
afterToolCall: async ({ toolCall, result, isError }) => this.#afterToolCall(toolCall.name, result.content, isError),
|
|
241
|
+
prepareNextTurnWithContext: async (turn): Promise<AgentLoopTurnUpdate> => {
|
|
242
|
+
await this.#refreshTools(agent.state.tools);
|
|
243
|
+
return { context: { ...turn.context, systemPrompt: this.#currentSystemPrompt(), tools: agent.state.tools.slice() } };
|
|
244
|
+
},
|
|
245
|
+
steeringSource: () => this.#steering(),
|
|
246
|
+
formatUnknownToolError: (name) => this.#active && !this.#options.signal.aborted
|
|
247
|
+
? `Die Funktion ${name} ist kein natives Werkzeug. Suche ihren Vertrag mit typescript_api und rufe sie in typescript_eval über context.functions auf.`
|
|
248
|
+
: undefined,
|
|
249
|
+
maxRetryDelayMs: settings.providerRequest.maxRetryDelayMs,
|
|
250
|
+
});
|
|
251
|
+
agent.subscribe((event: AgentEvent) => this.#handleEvent(event));
|
|
252
|
+
|
|
253
|
+
return agent;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#currentSystemPrompt() {
|
|
257
|
+
return this.#preloadedSkills ? `${this.#systemPrompt}\n\n${this.#preloadedSkills}` : this.#systemPrompt;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#setSystemPrompt(prompt: string, tools: readonly AgentTool[]) {
|
|
261
|
+
const skills = tools.some((tool) => tool.name === "read") ? formatSkillsForPrompt(this.#options.skills) : "";
|
|
262
|
+
this.#systemPrompt = `${prompt}${skills}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async #refreshTools(tools: AgentTool[]) {
|
|
266
|
+
const request = this.#request;
|
|
267
|
+
|
|
268
|
+
if (!request.refreshTools)
|
|
269
|
+
return;
|
|
270
|
+
|
|
271
|
+
this.#options.signal.throwIfAborted();
|
|
272
|
+
const snapshot = await this.#options.track(request.refreshTools());
|
|
273
|
+
this.#options.signal.throwIfAborted();
|
|
274
|
+
|
|
275
|
+
if (!this.#active)
|
|
276
|
+
throw new Error("The agent turn ended while refreshing tools.");
|
|
277
|
+
|
|
278
|
+
Object.assign(request, snapshot);
|
|
279
|
+
tools.splice(0, tools.length, ...this.#toolsOf(request));
|
|
280
|
+
this.#setSystemPrompt(snapshot.systemPrompt, tools);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
#toolsOf(request: TurnRequest<"agent">): AgentTool[] {
|
|
284
|
+
const names = new Set<string>();
|
|
285
|
+
const allowedNames = request.allowedToolNames === null ? null : new Set(request.allowedToolNames);
|
|
286
|
+
|
|
287
|
+
if (allowedNames && allowedNames.size !== request.allowedToolNames?.length)
|
|
288
|
+
throw new Error(`agent ${request.agentId} received duplicate allowed tool names.`);
|
|
289
|
+
|
|
290
|
+
if (allowedNames) {
|
|
291
|
+
const outside = request.tools
|
|
292
|
+
.map((tool) => tool.name)
|
|
293
|
+
.filter((name) => !["typescript_api", "typescript_eval"].includes(name) && !allowedNames.has(name));
|
|
294
|
+
|
|
295
|
+
if (outside.length > 0)
|
|
296
|
+
throw new Error(`Der Agent erhielt Werkzeuge außerhalb seiner Freigabeliste: ${[...new Set(outside)].join(", ")}.`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const tools = request.tools.map((entry) => {
|
|
300
|
+
if (names.has(entry.name))
|
|
301
|
+
throw new Error(`RAgents supplied duplicate agent tool ${entry.name}.`);
|
|
302
|
+
|
|
303
|
+
names.add(entry.name);
|
|
304
|
+
|
|
305
|
+
return this.#toolOf(entry);
|
|
306
|
+
});
|
|
307
|
+
this.#activeManagedNames = names;
|
|
308
|
+
|
|
309
|
+
return tools;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
#toolOf(entry: RunFunction): AgentTool {
|
|
313
|
+
return {
|
|
314
|
+
name: entry.name,
|
|
315
|
+
label: entry.label,
|
|
316
|
+
description: [entry.description, entry.longDescription].filter(Boolean).join("\n\n"),
|
|
317
|
+
parameters: entry.schema,
|
|
318
|
+
executionMode: entry.executionMode ?? (entry.name.startsWith("agent_") ? "sequential" : "parallel"),
|
|
319
|
+
execute: async (toolCallId: string, params: unknown, toolSignal: AbortSignal | undefined) => {
|
|
320
|
+
if (!this.#active)
|
|
321
|
+
throw new Error(`agent tool ${entry.name} was called outside an RAgents turn.`);
|
|
322
|
+
|
|
323
|
+
if (this.#options.signal.aborted || toolSignal?.aborted)
|
|
324
|
+
throw new Error(`agent tool ${entry.name} was aborted.`);
|
|
325
|
+
|
|
326
|
+
assertJsonValue(params, `agent tool ${entry.name} input`);
|
|
327
|
+
this.#unexecutedManagedCalls.delete(toolCallId);
|
|
328
|
+
const call = await this.#request.invoke(toolCallId, entry.name, params, this.#contextKey);
|
|
329
|
+
const text = typeof call.output === "string" ? call.output : asJson(call.output);
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
content: [{
|
|
333
|
+
type: "text" as const,
|
|
334
|
+
text: call.ignoredFields.length === 0 ? text : `${ignoredFieldsNotice(entry, call.ignoredFields)}\n\n${text}`,
|
|
335
|
+
}],
|
|
336
|
+
details: {},
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** The projection of the journal plus the hidden notes of the hooks for this one call. */
|
|
343
|
+
async #contextForModelCall(signal: AbortSignal | undefined): Promise<AgentMessage[]> {
|
|
344
|
+
const context = this.#request.modelContext();
|
|
345
|
+
this.#contextKey = context.key;
|
|
346
|
+
const notes: AgentMessage[] = [];
|
|
347
|
+
|
|
348
|
+
for (const hook of this.#options.hooks) {
|
|
349
|
+
if (!hook.beforeModelCall)
|
|
350
|
+
continue;
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const note = await this.#options.track(hook.beforeModelCall({
|
|
354
|
+
signal,
|
|
355
|
+
modelReadsImages: this.#options.model.input.includes("image"),
|
|
356
|
+
kept: this.#request.hookState.kept(hook.id),
|
|
357
|
+
keep: (value) => {
|
|
358
|
+
if (!this.#active)
|
|
359
|
+
throw new Error(`The hook ${hook.id} is stale: its agent turn has ended.`);
|
|
360
|
+
|
|
361
|
+
this.#request.hookState.keep(hook.id, value);
|
|
362
|
+
},
|
|
363
|
+
}));
|
|
364
|
+
|
|
365
|
+
if (note !== undefined)
|
|
366
|
+
notes.push({ role: "custom", customType: hook.id, content: note, display: false, timestamp: Date.now() });
|
|
367
|
+
} catch (error) {
|
|
368
|
+
this.#hookFailed(hook.id, "beforeModelCall", error);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return [...context.messages, ...notes];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async #afterToolCall(toolName: string, original: (TextContent | ImageContent)[], isError: boolean) {
|
|
376
|
+
let content = original;
|
|
377
|
+
let error = isError;
|
|
378
|
+
let replaced = false;
|
|
379
|
+
|
|
380
|
+
for (const hook of this.#options.hooks) {
|
|
381
|
+
if (!hook.afterToolCall)
|
|
382
|
+
continue;
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const replacement = await this.#options.track(hook.afterToolCall(
|
|
386
|
+
{ toolName, isError: error },
|
|
387
|
+
{ signal: this.#agent?.signal, modelReadsImages: this.#options.model.input.includes("image") },
|
|
388
|
+
));
|
|
389
|
+
|
|
390
|
+
if (!replacement)
|
|
391
|
+
continue;
|
|
392
|
+
|
|
393
|
+
content = replacement.content.map((part) => ({ ...part }));
|
|
394
|
+
error = replacement.isError ?? error;
|
|
395
|
+
replaced = true;
|
|
396
|
+
} catch (failure) {
|
|
397
|
+
this.#hookFailed(hook.id, "afterToolCall", failure);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return replaced ? { content, isError: error } : undefined;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
#hookFailed(id: string, phase: string, error: unknown) {
|
|
405
|
+
this.#failure ??= `agent hook ${id} failed during ${phase}: ${errorMessage(error)}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async #steering(): Promise<UserMessage[]> {
|
|
409
|
+
if (!this.#active || this.#options.signal.aborted)
|
|
410
|
+
return [];
|
|
411
|
+
|
|
412
|
+
const messages: UserMessage[] = [];
|
|
413
|
+
|
|
414
|
+
for (const steered of this.#request.claimSteering()) {
|
|
415
|
+
const prepared = await prepareInputAttachments(
|
|
416
|
+
{ ...this.#request, attachments: steered.attachments, prompt: steered.prompt },
|
|
417
|
+
this.#options.model.input,
|
|
418
|
+
);
|
|
419
|
+
const message: UserMessage = { role: "user", content: [{ type: "text", text: prepared.prompt }, ...prepared.attachments], timestamp: Date.now() };
|
|
420
|
+
this.#inputIds.set(message, steered.input.id);
|
|
421
|
+
messages.push(message);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return messages;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
#handleEvent(event: AgentEvent) {
|
|
428
|
+
if (!this.#active)
|
|
429
|
+
return;
|
|
430
|
+
|
|
431
|
+
const request = this.#request;
|
|
432
|
+
|
|
433
|
+
if (event.type === "message_update") {
|
|
434
|
+
if (this.#options.signal.aborted)
|
|
435
|
+
return;
|
|
436
|
+
|
|
437
|
+
const delta = event.assistantMessageEvent;
|
|
438
|
+
|
|
439
|
+
if (delta.type === "text_delta" && delta.delta) {
|
|
440
|
+
this.#pendingText += delta.delta;
|
|
441
|
+
request.publish({ kind: "text", delta: delta.delta });
|
|
442
|
+
} else if (delta.type === "thinking_delta" && delta.delta)
|
|
443
|
+
request.publish({ kind: "thinking", delta: delta.delta });
|
|
444
|
+
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (event.type === "message_start") {
|
|
449
|
+
if (event.message.role === "user")
|
|
450
|
+
this.#overflowRecoveryAttempted = false;
|
|
451
|
+
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (event.type === "tool_execution_start") {
|
|
456
|
+
const { toolCallId: id, toolName: name, args } = event;
|
|
457
|
+
request.publish({ kind: "tool", id, name, arguments: asJson(args) });
|
|
458
|
+
|
|
459
|
+
if (this.#activeManagedNames.has(name)) {
|
|
460
|
+
this.#unexecutedManagedCalls.set(id, { name, input: asJsonValue(args) });
|
|
461
|
+
} else {
|
|
462
|
+
assertJsonValue(args, `agent tool ${name} input`);
|
|
463
|
+
this.#directToolCalls.set(id, { name, input: args });
|
|
464
|
+
request.recordTool?.({ kind: "started", id, name, input: args });
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (event.type === "tool_execution_end") {
|
|
471
|
+
const id = event.toolCallId;
|
|
472
|
+
request.publish({ kind: "tool-result", id, result: asText(event.result), isError: event.isError });
|
|
473
|
+
const unexecuted = this.#unexecutedManagedCalls.get(id);
|
|
474
|
+
|
|
475
|
+
if (unexecuted) {
|
|
476
|
+
this.#unexecutedManagedCalls.delete(id);
|
|
477
|
+
|
|
478
|
+
if (event.isError) {
|
|
479
|
+
request.recordTool?.({ kind: "started", id, name: unexecuted.name, input: unexecuted.input });
|
|
480
|
+
request.recordTool?.({ kind: "failed", id, name: unexecuted.name, error: asText(event.result) });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const direct = this.#directToolCalls.get(id);
|
|
485
|
+
|
|
486
|
+
if (direct) {
|
|
487
|
+
this.#directToolCalls.delete(id);
|
|
488
|
+
const output = asText(event.result);
|
|
489
|
+
request.recordTool?.(event.isError
|
|
490
|
+
? { kind: "failed", id, name: direct.name, error: output }
|
|
491
|
+
: { kind: "completed", id, name: direct.name, output });
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (event.type !== "message_end")
|
|
498
|
+
return;
|
|
499
|
+
|
|
500
|
+
const message = event.message;
|
|
501
|
+
|
|
502
|
+
if (message.role === "user") {
|
|
503
|
+
const inputId = this.#inputIds.get(message);
|
|
504
|
+
|
|
505
|
+
if (inputId === undefined && message.content !== EMPTY_RESPONSE_NUDGE)
|
|
506
|
+
throw new Error("The agent loop added a user message that no input and no nudge explains.");
|
|
507
|
+
|
|
508
|
+
request.recordContext({ kind: "input", inputId: inputId ?? null, content: message.content });
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (message.role === "toolResult") {
|
|
513
|
+
const result = message as ToolResultMessage;
|
|
514
|
+
request.recordContext({ kind: "tool-result", toolCallId: result.toolCallId, toolName: result.toolName, isError: result.isError, content: result.content });
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (message.role !== "assistant")
|
|
519
|
+
return;
|
|
520
|
+
|
|
521
|
+
this.#usage = addUsage(this.#usage, usageOf(message.usage));
|
|
522
|
+
|
|
523
|
+
if (this.#options.signal.aborted || message.stopReason === "aborted") {
|
|
524
|
+
this.#recordInterruptedOutput();
|
|
525
|
+
this.#failure ??= "The agent turn was aborted.";
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (isRunFailure(message)) {
|
|
530
|
+
this.#failure ??= message.errorMessage ?? "The agent loop failed without a message.";
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
this.#pendingText = "";
|
|
535
|
+
this.#uniqueToolCallIds(message);
|
|
536
|
+
const { role: _role, ...step } = message;
|
|
537
|
+
request.recordContext({ kind: "step", step: step as unknown as CompletedModelStep });
|
|
538
|
+
this.#lastAssistant = message;
|
|
539
|
+
|
|
540
|
+
if (message.stopReason !== "error") {
|
|
541
|
+
this.#overflowRecoveryAttempted = false;
|
|
542
|
+
this.#retryAttempt = 0;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// A retry or a compaction continues after a provider error, so only the last answer decides.
|
|
546
|
+
this.#modelFailure = message.stopReason === "error"
|
|
547
|
+
? message.errorMessage ?? "The model provider returned an error."
|
|
548
|
+
: null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** The journal keys a tool call by its ID; a model that reuses one gets a suffixed ID before the loop runs the call. */
|
|
552
|
+
#uniqueToolCallIds(message: AssistantMessage) {
|
|
553
|
+
const calls = message.content.filter((block): block is ToolCall => block.type === "toolCall");
|
|
554
|
+
|
|
555
|
+
if (calls.length === 0)
|
|
556
|
+
return;
|
|
557
|
+
|
|
558
|
+
const used = new Set(this.#request.modelContext().messages.flatMap((entry) => entry.role === "assistant"
|
|
559
|
+
? entry.content.flatMap((block) => block.type === "toolCall" ? [block.id] : [])
|
|
560
|
+
: []));
|
|
561
|
+
|
|
562
|
+
for (const call of calls) {
|
|
563
|
+
let id = call.id;
|
|
564
|
+
|
|
565
|
+
for (let suffix = 2; used.has(id); suffix++)
|
|
566
|
+
id = `${call.id}-${suffix}`;
|
|
567
|
+
|
|
568
|
+
call.id = id;
|
|
569
|
+
used.add(id);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
#recordInterruptedOutput() {
|
|
574
|
+
if (!this.#pendingText.trim())
|
|
575
|
+
return;
|
|
576
|
+
|
|
577
|
+
this.#request.emit({ kind: "assistant-interrupted", text: this.#pendingText });
|
|
578
|
+
this.#pendingText = "";
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async #afterRun(): Promise<boolean> {
|
|
582
|
+
const message = this.#lastAssistant;
|
|
583
|
+
this.#lastAssistant = undefined;
|
|
584
|
+
|
|
585
|
+
if (!message || !this.#active || this.#options.signal.aborted)
|
|
586
|
+
return false;
|
|
587
|
+
|
|
588
|
+
if (this.#isRetryable(message) && (await this.#prepareRetry()))
|
|
589
|
+
return true;
|
|
590
|
+
|
|
591
|
+
if (this.#options.signal.aborted)
|
|
592
|
+
return false;
|
|
593
|
+
|
|
594
|
+
if (message.stopReason === "error")
|
|
595
|
+
this.#retryAttempt = 0;
|
|
596
|
+
|
|
597
|
+
return this.#checkCompaction(message);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#isRetryable(message: AssistantMessage) {
|
|
601
|
+
return !isContextOverflow(message, this.#options.model.contextWindow ?? 0) && isRetryableAssistantError(message);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async #prepareRetry(): Promise<boolean> {
|
|
605
|
+
const settings = this.#options.settings.retry;
|
|
606
|
+
|
|
607
|
+
if (!settings.enabled || this.#retryAttempt >= settings.maxRetries)
|
|
608
|
+
return false;
|
|
609
|
+
|
|
610
|
+
this.#retryAttempt++;
|
|
611
|
+
const agent = this.#agent!;
|
|
612
|
+
agent.state.messages = withoutTrailingFailures(agent.state.messages);
|
|
613
|
+
this.#retryAbort = new AbortController();
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
await sleep(settings.baseDelayMs * 2 ** (this.#retryAttempt - 1), undefined, {
|
|
617
|
+
signal: AbortSignal.any([this.#retryAbort.signal, this.#options.signal]),
|
|
618
|
+
});
|
|
619
|
+
} catch {
|
|
620
|
+
this.#retryAttempt = 0;
|
|
621
|
+
return false;
|
|
622
|
+
} finally {
|
|
623
|
+
this.#retryAbort = null;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** After a context overflow compacts and retries once; over the threshold compacts without a retry. */
|
|
630
|
+
async #checkCompaction(assistant: AssistantMessage | undefined, skipAborted = true): Promise<boolean> {
|
|
631
|
+
const settings = this.#options.settings.compaction;
|
|
632
|
+
const { model } = this.#options;
|
|
633
|
+
|
|
634
|
+
if (!assistant || !settings.enabled || (skipAborted && assistant.stopReason === "aborted"))
|
|
635
|
+
return false;
|
|
636
|
+
|
|
637
|
+
const contextWindow = model.contextWindow ?? 0;
|
|
638
|
+
const sameModel = assistant.provider === model.provider && assistant.model === model.id;
|
|
639
|
+
const log = this.#request.modelContext().entries;
|
|
640
|
+
const compactionIndex = log.findLastIndex((entry) => entry.kind === "compaction");
|
|
641
|
+
const assistantIndex = log.findLastIndex((entry) => entry.kind === "message" && entry.message.role === "assistant");
|
|
642
|
+
|
|
643
|
+
if (compactionIndex >= 0 && assistantIndex < compactionIndex)
|
|
644
|
+
return false;
|
|
645
|
+
|
|
646
|
+
if (sameModel && isContextOverflow(assistant, contextWindow)) {
|
|
647
|
+
if (assistant.stopReason === "stop")
|
|
648
|
+
return this.#compact(false);
|
|
649
|
+
|
|
650
|
+
if (this.#overflowRecoveryAttempted)
|
|
651
|
+
return false;
|
|
652
|
+
|
|
653
|
+
this.#overflowRecoveryAttempted = true;
|
|
654
|
+
return this.#compact(true);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const direct = assistant.usage ? calculateContextTokens(assistant.usage) : 0;
|
|
658
|
+
let contextTokens = direct;
|
|
659
|
+
|
|
660
|
+
if (assistant.stopReason === "error" || direct === 0) {
|
|
661
|
+
const active = activeContextEntries(log);
|
|
662
|
+
const estimate = estimateContextTokens(contextMessages(log));
|
|
663
|
+
|
|
664
|
+
if (estimate.lastUsageIndex === null)
|
|
665
|
+
return false;
|
|
666
|
+
|
|
667
|
+
const usageEntry = active[estimate.lastUsageIndex];
|
|
668
|
+
|
|
669
|
+
if (compactionIndex >= 0 && (!usageEntry || log.indexOf(usageEntry) < compactionIndex))
|
|
670
|
+
return false;
|
|
671
|
+
|
|
672
|
+
contextTokens = estimate.tokens;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return shouldCompact(contextTokens, contextWindow, settings) ? this.#compact(false) : false;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
async #compact(retry: boolean): Promise<boolean> {
|
|
679
|
+
const { model, modelRuntime, thinkingLevel, settings } = this.#options;
|
|
680
|
+
const preparation = prepareCompaction([...this.#request.modelContext().entries], settings.compaction);
|
|
681
|
+
|
|
682
|
+
if (!preparation)
|
|
683
|
+
return false;
|
|
684
|
+
|
|
685
|
+
const abort = new AbortController();
|
|
686
|
+
this.#compactionAbort = abort;
|
|
687
|
+
|
|
688
|
+
try {
|
|
689
|
+
const auth = await modelRuntime.getAuth(model).catch(() => undefined);
|
|
690
|
+
const headers = auth?.auth.headers
|
|
691
|
+
? Object.fromEntries(Object.entries(auth.auth.headers).filter((entry): entry is [string, string] => entry[1] !== null))
|
|
692
|
+
: undefined;
|
|
693
|
+
const result = await this.#options.track(compact(
|
|
694
|
+
preparation,
|
|
695
|
+
model,
|
|
696
|
+
auth?.auth.apiKey,
|
|
697
|
+
headers,
|
|
698
|
+
undefined,
|
|
699
|
+
AbortSignal.any([abort.signal, this.#options.signal]),
|
|
700
|
+
thinkingLevel,
|
|
701
|
+
(streamModel, context, options) => modelRuntime.streamSimple(streamModel, context, options),
|
|
702
|
+
auth?.env,
|
|
703
|
+
));
|
|
704
|
+
|
|
705
|
+
if (abort.signal.aborted || this.#options.signal.aborted || !this.#active)
|
|
706
|
+
return false;
|
|
707
|
+
|
|
708
|
+
this.#request.recordContext({
|
|
709
|
+
kind: "compaction",
|
|
710
|
+
compaction: {
|
|
711
|
+
summary: result.summary,
|
|
712
|
+
firstKeptEventId: result.firstKeptId,
|
|
713
|
+
tokensBefore: result.tokensBefore,
|
|
714
|
+
provider: model.provider,
|
|
715
|
+
model: model.id,
|
|
716
|
+
readFiles: result.readFiles,
|
|
717
|
+
modifiedFiles: result.modifiedFiles,
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
const messages = this.#request.modelContext().messages;
|
|
721
|
+
|
|
722
|
+
if (this.#agent)
|
|
723
|
+
this.#agent.state.messages = retry ? withoutTrailingFailures(messages) : [...messages];
|
|
724
|
+
|
|
725
|
+
return retry;
|
|
726
|
+
} catch (error) {
|
|
727
|
+
if (!abort.signal.aborted && !this.#options.signal.aborted)
|
|
728
|
+
this.#options.onDiagnostic(`Die Verdichtung des Modellkontexts ist gescheitert: ${errorMessage(error)}`);
|
|
729
|
+
return false;
|
|
730
|
+
} finally {
|
|
731
|
+
if (this.#compactionAbort === abort)
|
|
732
|
+
this.#compactionAbort = null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|