@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
|
@@ -10,12 +10,13 @@ export type {
|
|
|
10
10
|
AgentRuntimeContext,
|
|
11
11
|
AgentRuntimeDiagnostic,
|
|
12
12
|
AgentRuntimeManagerOptions,
|
|
13
|
-
AgentSessionStore,
|
|
14
13
|
} from "./agent-runtime.ts";
|
|
15
|
-
export { AgentRuntimeManager
|
|
14
|
+
export { AgentRuntimeManager } from "./agent-runtime.ts";
|
|
15
|
+
export { agentHookOf, type AgentHook } from "./agent-hooks.ts";
|
|
16
16
|
export {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
createSkillPreload,
|
|
18
|
+
skillPreloadHookName,
|
|
19
|
+
type SkillPreload,
|
|
19
20
|
type SkillPreloadConfiguration,
|
|
20
21
|
type SkillPreloadOptions,
|
|
21
22
|
type SkillCatalogEntry,
|
|
@@ -23,17 +24,18 @@ export {
|
|
|
23
24
|
type SkillSelector,
|
|
24
25
|
} from "./skill-preload.ts";
|
|
25
26
|
|
|
26
|
-
export type
|
|
27
|
+
export type AgentLoopDriverOptions = Omit<AgentRuntimeManagerOptions, "modelRuntime"> & {
|
|
27
28
|
modelRuntime?: ModelRuntime | Promise<ModelRuntime>;
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
/** The driver of LLM actors: the agent loop on the model context the journal holds. */
|
|
32
|
+
export class AgentLoopDriver implements AgentDriver<"agent"> {
|
|
31
33
|
readonly kind = "agent" as const;
|
|
32
34
|
readonly supportsPlainLlm = true;
|
|
33
35
|
readonly #modelRuntime: Promise<ModelRuntime>;
|
|
34
36
|
readonly #runtimeManager: AgentRuntimeManager;
|
|
35
37
|
|
|
36
|
-
constructor(options:
|
|
38
|
+
constructor(options: AgentLoopDriverOptions = {}) {
|
|
37
39
|
this.#modelRuntime = Promise.resolve(options.modelRuntime ?? ModelRuntime.create());
|
|
38
40
|
this.#runtimeManager = new AgentRuntimeManager({ ...options, modelRuntime: this.#modelRuntime });
|
|
39
41
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
|
-
import { stripFrontmatter, type
|
|
3
|
+
import { stripFrontmatter, type ModelRuntime, type Skill } from "@ragents/agent";
|
|
4
4
|
|
|
5
5
|
import type { TurnUsage } from "../domain/model.ts";
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const skillPreloadHookName = "ragents-skill-preload";
|
|
8
8
|
|
|
9
9
|
export type SkillCatalogEntry = {
|
|
10
10
|
name: string;
|
|
@@ -130,64 +130,54 @@ const preloadedSkills = async (skills: readonly Skill[]): Promise<string> => {
|
|
|
130
130
|
].join("\n\n");
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
export
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
systemPrompt: `${event.systemPrompt}\n\n${await preloadedSkills(selected)}`,
|
|
185
|
-
};
|
|
186
|
-
} catch (error) {
|
|
187
|
-
options.onDiagnostic?.(
|
|
188
|
-
`Agent skill preloading fell back to the skill catalog: ${error instanceof Error ? error.message : String(error)}`,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
},
|
|
193
|
-
});
|
|
133
|
+
export type SkillPreloadRequest = {
|
|
134
|
+
prompt: string;
|
|
135
|
+
skills: readonly Skill[];
|
|
136
|
+
model: ReturnType<ModelRuntime["getModels"]>[number];
|
|
137
|
+
signal: AbortSignal | undefined;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/** Selects skills for the task of a turn and returns their bodies as a section of this turn's system prompt, or undefined. */
|
|
141
|
+
export type SkillPreload = (request: SkillPreloadRequest) => Promise<string | undefined>;
|
|
142
|
+
|
|
143
|
+
export const createSkillPreload = (options: SkillPreloadOptions): SkillPreload => async ({ prompt, skills, model, signal }) => {
|
|
144
|
+
try {
|
|
145
|
+
if (skills.length === 0)
|
|
146
|
+
return undefined;
|
|
147
|
+
|
|
148
|
+
const explicit = explicitSkillNames(prompt, skills);
|
|
149
|
+
let names: readonly string[] | null = explicit.length > 0 ? explicit : null;
|
|
150
|
+
let candidates = skills;
|
|
151
|
+
|
|
152
|
+
if (explicit.length === 0) {
|
|
153
|
+
candidates = skills.filter((skill) => !skill.disableModelInvocation);
|
|
154
|
+
const catalog = candidates.map(({ name, description }) => ({ name, description }));
|
|
155
|
+
|
|
156
|
+
if (catalog.length === 0)
|
|
157
|
+
return undefined;
|
|
158
|
+
|
|
159
|
+
const selector = options.selector ?? agentSelector(
|
|
160
|
+
options.modelRuntime,
|
|
161
|
+
model,
|
|
162
|
+
options.maxSelectionTokens ?? 100,
|
|
163
|
+
options.onUsage,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
names = await selector({ prompt, skills: catalog, signal });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!names || names.length === 0)
|
|
170
|
+
return undefined;
|
|
171
|
+
|
|
172
|
+
const selected = names
|
|
173
|
+
.map((name) => candidates.find((skill) => skill.name === name))
|
|
174
|
+
.filter((skill): skill is Skill => skill !== undefined);
|
|
175
|
+
|
|
176
|
+
return selected.length === 0 ? undefined : await preloadedSkills(selected);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
options.onDiagnostic?.(
|
|
179
|
+
`Agent skill preloading fell back to the skill catalog: ${error instanceof Error ? error.message : String(error)}`,
|
|
180
|
+
);
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import type { ImageContent, TextContent, UserContent } from "@ragents/ai";
|
|
2
|
+
|
|
1
3
|
import type { AgentDriverKind, ModelSelection } from "../domain/driver.ts";
|
|
4
|
+
import type { EventPayloads } from "../domain/events.ts";
|
|
5
|
+
import type { CompletedModelStep } from "../runtime/decisions/turns.ts";
|
|
6
|
+
import type { ModelContext } from "../agents/model-context.ts";
|
|
2
7
|
import type { JsonValue } from "../domain/json.ts";
|
|
3
8
|
import type { TurnUsage } from "../domain/model.ts";
|
|
4
9
|
import type { DeliveredInput } from "../agents/delivery.ts";
|
|
@@ -24,20 +29,33 @@ type TurnDriverFacts = {
|
|
|
24
29
|
agent: {
|
|
25
30
|
driverKind: "agent";
|
|
26
31
|
selection: ModelSelection;
|
|
27
|
-
forkOf: string | null;
|
|
28
32
|
/** `modelContext` names the model context the result enters, see `ToolScope.modelContext`; without it file tools track no seen state. */
|
|
29
33
|
invoke: (toolCallId: string, name: string, input: JsonValue, modelContext?: string) => Promise<ToolInvocation>;
|
|
30
34
|
/** Claims the pending inputs that may join this turn now, in journal order; empty once the turn ends or aborts. */
|
|
31
35
|
claimSteering: () => readonly SteeredInput[];
|
|
36
|
+
/** The model context of this agent as the journal holds it now. */
|
|
37
|
+
modelContext: () => ModelContext;
|
|
38
|
+
/** Records what enters the model context; only a recorded entry is context. */
|
|
39
|
+
recordContext: (entry: ModelContextRecord) => void;
|
|
40
|
+
/** What a hook kept for this agent, in the journal as plugin state of the actor. */
|
|
41
|
+
hookState: {
|
|
42
|
+
kept: (hookId: string) => JsonValue | undefined;
|
|
43
|
+
keep: (hookId: string, value: JsonValue) => void;
|
|
44
|
+
};
|
|
32
45
|
};
|
|
33
46
|
};
|
|
34
47
|
|
|
35
48
|
export type DriverEvent =
|
|
36
|
-
| { kind: "assistant"; text: string }
|
|
37
49
|
| { kind: "assistant-interrupted"; text: string }
|
|
38
|
-
| { kind: "reasoning"; text: string }
|
|
39
50
|
| { kind: "runtime"; text: string };
|
|
40
51
|
|
|
52
|
+
/** What a driver writes into the model context of its agent, in the order the model sees it. */
|
|
53
|
+
export type ModelContextRecord =
|
|
54
|
+
| { kind: "input"; inputId: string | null; content: string | readonly UserContent[] }
|
|
55
|
+
| { kind: "step"; step: CompletedModelStep }
|
|
56
|
+
| { kind: "tool-result"; toolCallId: string; toolName: string; isError: boolean; content: readonly (TextContent | ImageContent)[] }
|
|
57
|
+
| { kind: "compaction"; compaction: Omit<EventPayloads["context.compacted"], "turnId"> };
|
|
58
|
+
|
|
41
59
|
export type DriverToolEvent =
|
|
42
60
|
| { kind: "started"; id: string; name: string; input: JsonValue }
|
|
43
61
|
| { kind: "completed"; id: string; name: string; output: JsonValue }
|
|
@@ -60,8 +78,6 @@ export type TurnRequest<Kind extends AutomatedDriverKind = AutomatedDriverKind>
|
|
|
60
78
|
systemPrompt: string;
|
|
61
79
|
/** The working directory of the workspace tools, as the model sees it; it may lie on another machine. */
|
|
62
80
|
workspace: string;
|
|
63
|
-
/** The folder on this machine for the driver's own work, resolved only when a driver needs it. */
|
|
64
|
-
runtimeDirectory: () => Promise<string>;
|
|
65
81
|
/** Stores an attached file under attachments/ of the workspace, where the workspace tools read it; returns its name there. */
|
|
66
82
|
storeAttachment: (name: string, content: Uint8Array) => Promise<string>;
|
|
67
83
|
tools: readonly RunFunction[];
|
|
@@ -27,6 +27,7 @@ export {
|
|
|
27
27
|
PluginHost,
|
|
28
28
|
ProfileContributionRegistry,
|
|
29
29
|
PromptContributionRegistry,
|
|
30
|
+
RunConditionRegistry,
|
|
30
31
|
SessionMetadataContributionRegistry,
|
|
31
32
|
SkillContributionRegistry,
|
|
32
33
|
StartEntryContributionRegistry,
|
|
@@ -91,18 +92,19 @@ export { TurnToolset, schemaComplaints, type ToolInvocation } from "./agents/too
|
|
|
91
92
|
export { claimTurn, workingActorFrom, type ClaimedTurn } from "./agents/turn.ts";
|
|
92
93
|
export { FixedWorkspaces, type Workspaces } from "./agents/workspaces.ts";
|
|
93
94
|
export {
|
|
94
|
-
|
|
95
|
+
AgentLoopDriver,
|
|
95
96
|
AgentRuntimeManager,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
type
|
|
97
|
+
agentHookOf,
|
|
98
|
+
createSkillPreload,
|
|
99
|
+
skillPreloadHookName,
|
|
100
|
+
type AgentHook,
|
|
101
|
+
type AgentLoopDriverOptions,
|
|
100
102
|
type AgentRuntimeContext,
|
|
101
103
|
type AgentRuntimeDiagnostic,
|
|
102
104
|
type AgentRuntimeManagerOptions,
|
|
105
|
+
type SkillPreload,
|
|
103
106
|
type SkillPreloadConfiguration,
|
|
104
107
|
type SkillPreloadOptions,
|
|
105
|
-
type AgentSessionStore,
|
|
106
108
|
type SkillCatalogEntry,
|
|
107
109
|
type SkillSelectionRequest,
|
|
108
110
|
type SkillSelector,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
3
|
-
import type
|
|
4
|
-
import { agentHookExtension } from "./drivers/agent-hooks.ts";
|
|
3
|
+
import { agentHookOf, type AgentHook } from "./drivers/agent-hooks.ts";
|
|
5
4
|
import { Value } from "typebox/value";
|
|
6
5
|
import { canStartEntry, defaultHttpRights, isAccessRight, unrestrictedAccess, type AccessContext } from "./access.ts";
|
|
7
6
|
import { DomainError } from "./runtime/domain-error.ts";
|
|
@@ -42,6 +41,7 @@ import type {
|
|
|
42
41
|
RAgentsPlugin,
|
|
43
42
|
ServiceToken,
|
|
44
43
|
RegisteredStartOption,
|
|
44
|
+
RunCondition,
|
|
45
45
|
SessionLifecycleContribution,
|
|
46
46
|
SessionMetadata,
|
|
47
47
|
SessionStartedContext,
|
|
@@ -87,6 +87,24 @@ class ContributionRegistry<T extends { id: string }> {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Die Laufbedingung je Plugin; ein Plugin ohne gilt in jedem Run. */
|
|
91
|
+
export class RunConditionRegistry {
|
|
92
|
+
readonly #conditions = new Map<string, RunCondition>();
|
|
93
|
+
|
|
94
|
+
register(owner: string, condition: RunCondition): void {
|
|
95
|
+
if (this.#conditions.has(owner)) throw new Error(`Plugin ${owner} hat bereits eine Laufbedingung`);
|
|
96
|
+
this.#conditions.set(owner, condition);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
has(owner: string): boolean {
|
|
100
|
+
return this.#conditions.has(owner);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
applies(owner: string, runId: string): boolean {
|
|
104
|
+
return this.#conditions.get(owner)?.(runId) ?? true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
90
108
|
export class MethodContributionRegistry {
|
|
91
109
|
readonly #methods = new ContributionRegistry<{ id: string; contribution: MethodContribution }>("Methode");
|
|
92
110
|
|
|
@@ -220,6 +238,11 @@ export class OperationContributionRegistry {
|
|
|
220
238
|
|
|
221
239
|
export class AgentContributionRegistry {
|
|
222
240
|
readonly #contributions = new ContributionRegistry<AgentContribution>("Agent-Beitrag");
|
|
241
|
+
readonly #conditions: RunConditionRegistry;
|
|
242
|
+
|
|
243
|
+
constructor(conditions = new RunConditionRegistry()) {
|
|
244
|
+
this.#conditions = conditions;
|
|
245
|
+
}
|
|
223
246
|
|
|
224
247
|
register(owner: string, contributions: readonly AgentContribution[]): void {
|
|
225
248
|
for (const contribution of contributions) {
|
|
@@ -230,8 +253,10 @@ export class AgentContributionRegistry {
|
|
|
230
253
|
this.#contributions.register(owner, contributions);
|
|
231
254
|
}
|
|
232
255
|
|
|
233
|
-
resolve(context: AgentContributionContext): readonly
|
|
234
|
-
return this.#contributions.entries()
|
|
256
|
+
resolve(context: AgentContributionContext): readonly AgentHook[] {
|
|
257
|
+
return this.#contributions.entries()
|
|
258
|
+
.filter(({ owner }) => this.#conditions.applies(owner, context.runId))
|
|
259
|
+
.map(({ value }) => agentHookOf(value, context));
|
|
235
260
|
}
|
|
236
261
|
|
|
237
262
|
describe(): readonly PublicAgentHookContribution[] {
|
|
@@ -247,6 +272,11 @@ export class AgentContributionRegistry {
|
|
|
247
272
|
|
|
248
273
|
export class ToolContributionRegistry {
|
|
249
274
|
readonly #contributors: Owned<ToolContributor>[] = [];
|
|
275
|
+
readonly #conditions: RunConditionRegistry;
|
|
276
|
+
|
|
277
|
+
constructor(conditions = new RunConditionRegistry()) {
|
|
278
|
+
this.#conditions = conditions;
|
|
279
|
+
}
|
|
250
280
|
|
|
251
281
|
register(owner: string, contributors: readonly ToolContributor[]): void {
|
|
252
282
|
for (const contributor of contributors) {
|
|
@@ -279,7 +309,9 @@ export class ToolContributionRegistry {
|
|
|
279
309
|
}
|
|
280
310
|
|
|
281
311
|
entries(): readonly ToolContributor[] {
|
|
282
|
-
return this.#contributors.map(({ value }) =>
|
|
312
|
+
return this.#contributors.map(({ owner, value }) => this.#conditions.has(owner)
|
|
313
|
+
? { ...value, runCondition: (runId: string) => this.#conditions.applies(owner, runId) }
|
|
314
|
+
: value);
|
|
283
315
|
}
|
|
284
316
|
|
|
285
317
|
describe(): readonly PublicToolDescriptor[] {
|
|
@@ -296,6 +328,11 @@ export class ToolContributionRegistry {
|
|
|
296
328
|
|
|
297
329
|
export class PromptContributionRegistry {
|
|
298
330
|
readonly #prompts = new ContributionRegistry<PromptContribution>("Prompt-Beitrag");
|
|
331
|
+
readonly #conditions: RunConditionRegistry;
|
|
332
|
+
|
|
333
|
+
constructor(conditions = new RunConditionRegistry()) {
|
|
334
|
+
this.#conditions = conditions;
|
|
335
|
+
}
|
|
299
336
|
|
|
300
337
|
register(owner: string, prompts: readonly PromptContribution[]): void {
|
|
301
338
|
this.#prompts.register(owner, prompts);
|
|
@@ -331,10 +368,13 @@ export class PromptContributionRegistry {
|
|
|
331
368
|
})));
|
|
332
369
|
}
|
|
333
370
|
|
|
334
|
-
/** Die Beiträge, die für diesen Run anders lauten als im Schnappschuss; alles andere bleibt der gerenderte Text. */
|
|
371
|
+
/** Die Beiträge, die für diesen Run anders lauten als im Schnappschuss; ein leerer Text fehlt dort, alles andere bleibt der gerenderte Text. */
|
|
335
372
|
runOverrides(runId: string): ReadonlyMap<string, string> {
|
|
336
|
-
return new Map([...this.#prompts.entries()].flatMap(({ value }) =>
|
|
337
|
-
|
|
373
|
+
return new Map([...this.#prompts.entries()].flatMap(({ owner, value }) => {
|
|
374
|
+
if (!this.#conditions.applies(owner, runId)) return [[value.id, ""] as const];
|
|
375
|
+
const text = value.renderForRun?.(runId);
|
|
376
|
+
return text === undefined ? [] : [[value.id, text.trim()] as const];
|
|
377
|
+
}));
|
|
338
378
|
}
|
|
339
379
|
|
|
340
380
|
static handlebarsContext(context: PromptRenderContext): Record<string, unknown> {
|
|
@@ -344,6 +384,11 @@ export class PromptContributionRegistry {
|
|
|
344
384
|
|
|
345
385
|
export class SkillContributionRegistry {
|
|
346
386
|
readonly #skills = new ContributionRegistry<SkillContribution>("Skill-Beitrag");
|
|
387
|
+
readonly #conditions: RunConditionRegistry;
|
|
388
|
+
|
|
389
|
+
constructor(conditions = new RunConditionRegistry()) {
|
|
390
|
+
this.#conditions = conditions;
|
|
391
|
+
}
|
|
347
392
|
|
|
348
393
|
register(owner: string, skills: readonly SkillContribution[]): void {
|
|
349
394
|
for (const skill of skills) {
|
|
@@ -386,7 +431,7 @@ export class SkillContributionRegistry {
|
|
|
386
431
|
id: value.id,
|
|
387
432
|
owner,
|
|
388
433
|
audiences: value.audiences ?? allAudiences,
|
|
389
|
-
paths: await value.paths(context),
|
|
434
|
+
paths: context && !this.#conditions.applies(owner, context.runId) ? [] : await value.paths(context),
|
|
390
435
|
})));
|
|
391
436
|
}
|
|
392
437
|
}
|
|
@@ -1069,10 +1114,11 @@ export class PluginHost {
|
|
|
1069
1114
|
readonly channels = new ChannelContributionRegistry();
|
|
1070
1115
|
readonly http = new HttpContributionRegistry();
|
|
1071
1116
|
readonly operations = new OperationContributionRegistry();
|
|
1072
|
-
readonly
|
|
1073
|
-
readonly
|
|
1074
|
-
readonly
|
|
1075
|
-
readonly
|
|
1117
|
+
readonly runConditions = new RunConditionRegistry();
|
|
1118
|
+
readonly agentRuntime = new AgentContributionRegistry(this.runConditions);
|
|
1119
|
+
readonly tools = new ToolContributionRegistry(this.runConditions);
|
|
1120
|
+
readonly prompts = new PromptContributionRegistry(this.runConditions);
|
|
1121
|
+
readonly skills = new SkillContributionRegistry(this.runConditions);
|
|
1076
1122
|
readonly startEntries = new StartEntryContributionRegistry();
|
|
1077
1123
|
readonly profiles = new ProfileContributionRegistry();
|
|
1078
1124
|
readonly script = new ScriptContributionRegistry();
|
|
@@ -1122,6 +1168,7 @@ export class PluginHost {
|
|
|
1122
1168
|
agentRuntime: (...entries) => this.agentRuntime.register(manifest.id, entries),
|
|
1123
1169
|
profiles: (...entries) => this.profiles.register(manifest.id, entries),
|
|
1124
1170
|
prompts: (...entries) => this.prompts.register(manifest.id, entries),
|
|
1171
|
+
runCondition: (condition) => this.runConditions.register(manifest.id, condition),
|
|
1125
1172
|
provide: (token, service) => this.#services.provide(manifest.id, token, service),
|
|
1126
1173
|
service: (token) => this.#services.require(token),
|
|
1127
1174
|
optionalService: (token) => this.#services.optional(token),
|
|
@@ -277,10 +277,13 @@ export interface PromptContribution {
|
|
|
277
277
|
delivery?: "initial" | "on-demand";
|
|
278
278
|
requiresTools?: readonly string[];
|
|
279
279
|
render: (context: PromptRenderContext) => string | Promise<string>;
|
|
280
|
-
/** Hängt der Beitrag am einzelnen Run, ersetzt dieser Text den von `render
|
|
281
|
-
renderForRun?: (runId: string) => string;
|
|
280
|
+
/** Hängt der Beitrag am einzelnen Run, ersetzt dieser Text den von `render`, undefined behält ihn - synchron, damit der Turn ihn ohne Warten hat. */
|
|
281
|
+
renderForRun?: (runId: string) => string | undefined;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
/** Ob die laufbezogenen Beiträge eines Plugins in einem Run gelten; der Host fragt je Run, ein Wurf lässt dessen Turn mit dieser Ursache scheitern. */
|
|
285
|
+
export type RunCondition = (runId: string) => boolean;
|
|
286
|
+
|
|
284
287
|
export type ModelProviderConfig = Parameters<ModelRuntime["registerProvider"]>[1];
|
|
285
288
|
|
|
286
289
|
export interface ModelProviderRegistration {
|
|
@@ -420,6 +423,8 @@ export interface PluginRegistration {
|
|
|
420
423
|
agentRuntime: (...contributions: readonly AgentContribution[]) => void;
|
|
421
424
|
profiles: (...contributions: readonly ProfileContribution[]) => void;
|
|
422
425
|
prompts: (...contributions: readonly PromptContribution[]) => void;
|
|
426
|
+
/** Prompts, Funktionen, Skills und Agent-Hooks dieses Plugins gelten nur in Runs, für die die Bedingung zutrifft; höchstens eine je Plugin. */
|
|
427
|
+
runCondition: (condition: RunCondition) => void;
|
|
423
428
|
provide: <T>(token: ServiceToken<T>, service: T) => void;
|
|
424
429
|
service: <T>(token: ServiceToken<T>) => T;
|
|
425
430
|
optionalService: <T>(token: ServiceToken<T>) => T | undefined;
|
|
@@ -132,6 +132,16 @@ const forkSourceOf = (state: RunState, reference: string): string => {
|
|
|
132
132
|
if (source.execution.driver.kind !== "agent")
|
|
133
133
|
throw new DomainError("invalid-value", `Fork source ${reference} has no model context to fork.`, 400);
|
|
134
134
|
|
|
135
|
+
const finishedContext = [...state.turns.values()].some((turn) =>
|
|
136
|
+
turn.actorId === source.id && turn.status !== "running" && state.contextTurns.has(turn.id));
|
|
137
|
+
|
|
138
|
+
if (source.forkOf === null && !finishedContext)
|
|
139
|
+
throw new DomainError(
|
|
140
|
+
"fork-without-turn",
|
|
141
|
+
`Fork source ${reference} has no model context from a finished turn yet; a fork takes the context of finished turns only, never of the running one.`,
|
|
142
|
+
409,
|
|
143
|
+
);
|
|
144
|
+
|
|
135
145
|
return source.id;
|
|
136
146
|
};
|
|
137
147
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EventPayloads, ModelInputContent, ModelStepBlock, ModelToolResultPart, UncommittedEvent } from "../../domain/events.ts";
|
|
1
2
|
import type { JsonValue } from "../../domain/json.ts";
|
|
2
3
|
import { isPendingActorInput, type TurnUsage } from "../../domain/model.ts";
|
|
3
4
|
import { event, type Decision } from "../command.ts";
|
|
@@ -77,7 +78,7 @@ export const steerInputs =
|
|
|
77
78
|
};
|
|
78
79
|
|
|
79
80
|
const appendText = (
|
|
80
|
-
type: "model.output.
|
|
81
|
+
type: "model.output.interrupted" | "runtime.output.recorded",
|
|
81
82
|
actorId: string,
|
|
82
83
|
input: { turnId: string; text: string },
|
|
83
84
|
): Decision => (state, context) => {
|
|
@@ -86,18 +87,76 @@ const appendText = (
|
|
|
86
87
|
return [event(context, { type, payload: { turnId: input.turnId, text: clean(input.text, "text") } })];
|
|
87
88
|
};
|
|
88
89
|
|
|
89
|
-
export const appendModelOutput = (actorId: string, input: { turnId: string; text: string }): Decision =>
|
|
90
|
-
appendText("model.output.completed", actorId, input);
|
|
91
|
-
|
|
92
90
|
export const appendInterruptedModelOutput = (actorId: string, input: { turnId: string; text: string }): Decision =>
|
|
93
91
|
appendText("model.output.interrupted", actorId, input);
|
|
94
92
|
|
|
95
|
-
export const appendModelReasoning = (actorId: string, input: { turnId: string; text: string }): Decision =>
|
|
96
|
-
appendText("model.reasoning.completed", actorId, input);
|
|
97
|
-
|
|
98
93
|
export const appendRuntimeOutput = (actorId: string, input: { turnId: string; text: string }): Decision =>
|
|
99
94
|
appendText("runtime.output.recorded", actorId, input);
|
|
100
95
|
|
|
96
|
+
/** The input exactly as the model received it, at its place between the model steps. */
|
|
97
|
+
export const presentModelInput =
|
|
98
|
+
(actorId: string, input: { turnId: string; inputId: string | null; content: ModelInputContent }): Decision =>
|
|
99
|
+
(state, context) => {
|
|
100
|
+
runningTurn(state, context, actorId, input.turnId);
|
|
101
|
+
|
|
102
|
+
return [event(context, { type: "model.input.presented", payload: input })];
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type CompletedModelStep = Omit<EventPayloads["model.step.completed"], "turnId" | "content"> & {
|
|
106
|
+
content: readonly (
|
|
107
|
+
| { type: "text"; text: string; textSignature?: string }
|
|
108
|
+
| { type: "thinking"; thinking: string; thinkingSignature?: string; redacted?: boolean }
|
|
109
|
+
| Extract<ModelStepBlock, { type: "toolCall" }>
|
|
110
|
+
)[];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/** One command: the text and reasoning a step made visible as observation events, then the step itself, which does not repeat them. */
|
|
114
|
+
export const completeModelStep =
|
|
115
|
+
(actorId: string, input: { turnId: string; step: CompletedModelStep }): Decision =>
|
|
116
|
+
(state, context) => {
|
|
117
|
+
runningTurn(state, context, actorId, input.turnId);
|
|
118
|
+
const observed: UncommittedEvent[] = [];
|
|
119
|
+
const content = input.step.content.map((block): ModelStepBlock => {
|
|
120
|
+
if (block.type === "text" && block.text.trim()) {
|
|
121
|
+
observed.push(event(context, { type: "model.output.completed", payload: { turnId: input.turnId, text: block.text } }));
|
|
122
|
+
const { text: _text, ...rest } = block;
|
|
123
|
+
return rest;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (block.type === "thinking" && block.thinking.trim()) {
|
|
127
|
+
observed.push(event(context, { type: "model.reasoning.completed", payload: { turnId: input.turnId, text: block.thinking } }));
|
|
128
|
+
const { thinking: _thinking, ...rest } = block;
|
|
129
|
+
return rest;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { ...block };
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return [
|
|
136
|
+
...observed,
|
|
137
|
+
event(context, { type: "model.step.completed", payload: { turnId: input.turnId, ...input.step, content } }),
|
|
138
|
+
];
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const presentToolResult =
|
|
142
|
+
(
|
|
143
|
+
actorId: string,
|
|
144
|
+
input: { turnId: string; toolCallId: string; toolName: string; isError: boolean; content?: ModelToolResultPart[] },
|
|
145
|
+
): Decision =>
|
|
146
|
+
(state, context) => {
|
|
147
|
+
runningTurn(state, context, actorId, input.turnId);
|
|
148
|
+
|
|
149
|
+
return [event(context, { type: "model.tool-result.presented", payload: input })];
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const compactContext =
|
|
153
|
+
(actorId: string, input: EventPayloads["context.compacted"]): Decision =>
|
|
154
|
+
(state, context) => {
|
|
155
|
+
runningTurn(state, context, actorId, input.turnId);
|
|
156
|
+
|
|
157
|
+
return [event(context, { type: "context.compacted", payload: input })];
|
|
158
|
+
};
|
|
159
|
+
|
|
101
160
|
export const startToolCall =
|
|
102
161
|
(actorId: string, input: { turnId: string; toolCallId: string; name: string; input: JsonValue; ignoredFields?: string[] }): Decision =>
|
|
103
162
|
(state, context) => {
|
|
@@ -8,9 +8,9 @@ import { isRunId } from "../domain/portable-id.ts";
|
|
|
8
8
|
import { syncDirectory } from "./durable-fs.ts";
|
|
9
9
|
import type { CommandRecord, JournalCommand } from "./journal.ts";
|
|
10
10
|
|
|
11
|
-
/** Rises whenever an older stand would reject newly written lines; the
|
|
12
|
-
export const journalStorageVersion =
|
|
13
|
-
const readableJournalStorageVersions: readonly unknown[] = [
|
|
11
|
+
/** Rises whenever an older stand would reject newly written lines; the journal reads only this version. */
|
|
12
|
+
export const journalStorageVersion = 7;
|
|
13
|
+
const readableJournalStorageVersions: readonly unknown[] = [journalStorageVersion];
|
|
14
14
|
export const journalPayloadThresholdBytes = 4096;
|
|
15
15
|
|
|
16
16
|
type PayloadReference = { sha256: string; bytes: number };
|
|
@@ -161,7 +161,10 @@ export const parseJournalRecord = (line: string, runDirectory: string, location:
|
|
|
161
161
|
const record = objectOf(parsed, location);
|
|
162
162
|
|
|
163
163
|
if (!readableJournalStorageVersions.includes(record.formatVersion))
|
|
164
|
-
throw new Error(`${location} has unsupported journal format version ${String(record.formatVersion)}
|
|
164
|
+
throw new Error(`${location} has unsupported journal format version ${String(record.formatVersion)}.${
|
|
165
|
+
typeof record.formatVersion === "number" && record.formatVersion < journalStorageVersion
|
|
166
|
+
? " Journals before format 7 hold no model context and are not migrated."
|
|
167
|
+
: ""}`);
|
|
165
168
|
|
|
166
169
|
exactKeys(record, location, ["formatVersion", "runId", "command", "occurredAt", "events"]);
|
|
167
170
|
|
|
@@ -346,6 +346,12 @@ export class Journal {
|
|
|
346
346
|
return [...this.#loadFailures.values()];
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
/** Every locked run with its cause: journals rejected at load and runs isolated by a write failure. */
|
|
350
|
+
unavailableRuns(): readonly JournalLoadFailure[] {
|
|
351
|
+
return [...new Set([...this.#loadFailures.keys(), ...this.#writeFailures.keys()])]
|
|
352
|
+
.flatMap((runId) => this.failureOf(runId) ?? []);
|
|
353
|
+
}
|
|
354
|
+
|
|
349
355
|
failureOf(runId: string): JournalLoadFailure | null {
|
|
350
356
|
const failure = this.#loadFailures.get(runId);
|
|
351
357
|
const writeFailure = this.#writeFailures.get(runId);
|
|
@@ -369,6 +375,34 @@ export class Journal {
|
|
|
369
375
|
return [...(this.#runs.get(runId)?.events ?? [])];
|
|
370
376
|
}
|
|
371
377
|
|
|
378
|
+
/** The events after the given sequence; the sequence counts from 1 without gaps. */
|
|
379
|
+
eventsSince(runId: RunId, sequence: number): JournalEvent[] {
|
|
380
|
+
this.assertRunAvailable(runId);
|
|
381
|
+
return (this.#runs.get(runId)?.events ?? []).slice(sequence);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** The events from the newest back, without copying the journal; a caller stops as soon as it has what it needs. */
|
|
385
|
+
*recentEvents(runId: RunId): Generator<JournalEvent> {
|
|
386
|
+
this.assertRunAvailable(runId);
|
|
387
|
+
const events = this.#runs.get(runId)?.events ?? [];
|
|
388
|
+
|
|
389
|
+
for (let index = events.length - 1; index >= 0; index--)
|
|
390
|
+
yield events[index]!;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** A value read from the current state without cloning all of it; `read` must neither keep nor change the state. */
|
|
394
|
+
select<Value>(runId: RunId, read: (state: RunState) => Value): Value | null {
|
|
395
|
+
this.assertRunAvailable(runId);
|
|
396
|
+
const state = this.#runs.get(runId)?.state;
|
|
397
|
+
|
|
398
|
+
return state ? clone(read(state)) : null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
firstEventId(runId: RunId): string | undefined {
|
|
402
|
+
this.assertRunAvailable(runId);
|
|
403
|
+
return this.#runs.get(runId)?.events[0]?.eventId;
|
|
404
|
+
}
|
|
405
|
+
|
|
372
406
|
records(runId: RunId): readonly CommandRecord[] {
|
|
373
407
|
this.assertRunAvailable(runId);
|
|
374
408
|
return [...(this.#runs.get(runId)?.records ?? [])];
|
|
@@ -571,6 +605,7 @@ export class Journal {
|
|
|
571
605
|
const semantics = log ? {
|
|
572
606
|
events: new Map(log.semantics.events),
|
|
573
607
|
sourcedToolCalls: new Set(log.semantics.sourcedToolCalls),
|
|
608
|
+
presented: new Set(log.semantics.presented),
|
|
574
609
|
} : eventSemanticContext();
|
|
575
610
|
let state: RunDraft | null = log?.state ? forkProjection(log.state) : null;
|
|
576
611
|
|