@schlenkr/ragents 0.1.6 → 0.1.7
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/provider.ts +34 -11
- package/apps/server/src/ragents/actor-chat-history.ts +1 -0
- package/apps/server/src/ragents/engine.ts +5 -13
- 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-CFhTJEmH.js} +1 -1
- package/apps/web/dist/assets/{main-BCjvD2Hh.js → main-DlzW4t4c.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-CvYYFK_n.js → mermaid-HWGCJPDP-kxkB991i.js} +69 -69
- package/apps/web/dist/assets/{runPanel-CG7kRMJh.js → runPanel-BJEFQ__w.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 +3 -3
- 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/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/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 +7 -6
- package/packages/ragents/src/plugin-host.ts +3 -4
- 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/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[];
|
|
@@ -91,18 +91,19 @@ export { TurnToolset, schemaComplaints, type ToolInvocation } from "./agents/too
|
|
|
91
91
|
export { claimTurn, workingActorFrom, type ClaimedTurn } from "./agents/turn.ts";
|
|
92
92
|
export { FixedWorkspaces, type Workspaces } from "./agents/workspaces.ts";
|
|
93
93
|
export {
|
|
94
|
-
|
|
94
|
+
AgentLoopDriver,
|
|
95
95
|
AgentRuntimeManager,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
type
|
|
96
|
+
agentHookOf,
|
|
97
|
+
createSkillPreload,
|
|
98
|
+
skillPreloadHookName,
|
|
99
|
+
type AgentHook,
|
|
100
|
+
type AgentLoopDriverOptions,
|
|
100
101
|
type AgentRuntimeContext,
|
|
101
102
|
type AgentRuntimeDiagnostic,
|
|
102
103
|
type AgentRuntimeManagerOptions,
|
|
104
|
+
type SkillPreload,
|
|
103
105
|
type SkillPreloadConfiguration,
|
|
104
106
|
type SkillPreloadOptions,
|
|
105
|
-
type AgentSessionStore,
|
|
106
107
|
type SkillCatalogEntry,
|
|
107
108
|
type SkillSelectionRequest,
|
|
108
109
|
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";
|
|
@@ -230,8 +229,8 @@ export class AgentContributionRegistry {
|
|
|
230
229
|
this.#contributions.register(owner, contributions);
|
|
231
230
|
}
|
|
232
231
|
|
|
233
|
-
resolve(context: AgentContributionContext): readonly
|
|
234
|
-
return this.#contributions.entries().map(({ value }) =>
|
|
232
|
+
resolve(context: AgentContributionContext): readonly AgentHook[] {
|
|
233
|
+
return this.#contributions.entries().map(({ value }) => agentHookOf(value, context));
|
|
235
234
|
}
|
|
236
235
|
|
|
237
236
|
describe(): readonly PublicAgentHookContribution[] {
|
|
@@ -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
|
|