@rowan-agent/agent 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -10
- package/dist/index.d.ts +35 -9
- package/dist/index.js +281 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,14 +42,52 @@ The `Agent` class is the public facade. It drives the entire execution loop —
|
|
|
42
42
|
```ts
|
|
43
43
|
class Agent {
|
|
44
44
|
constructor(options: AgentOptions);
|
|
45
|
-
run(options?: RunOptions): Promise<
|
|
45
|
+
run(options?: RunOptions): Promise<RunResult>;
|
|
46
|
+
|
|
47
|
+
// User input / conversation continuation
|
|
48
|
+
appendUserMessage(input: string): void;
|
|
49
|
+
appendMessage(message: AgentMessage): void;
|
|
50
|
+
appendMessages(messages: AgentMessage[]): void;
|
|
51
|
+
runWithUserInput(input: string, options?: RunOptions): Promise<RunResult>;
|
|
52
|
+
runWithMessage(message: AgentMessage, options?: RunOptions): Promise<RunResult>;
|
|
53
|
+
resetInitialization(): void;
|
|
54
|
+
|
|
55
|
+
// Context and transcript
|
|
56
|
+
getContext(): AgentContext;
|
|
57
|
+
setContext(context: AgentContext): void;
|
|
58
|
+
updateContext(updater: (context: AgentContext) => AgentContext): void;
|
|
59
|
+
forkContext(overrides?: Partial<AgentContext>): AgentContext;
|
|
60
|
+
getMessages(): AgentMessage[];
|
|
61
|
+
setMessages(messages: AgentMessage[]): void;
|
|
62
|
+
clearMessages(): void;
|
|
63
|
+
getTranscript(): AgentMessage[];
|
|
64
|
+
replaceTranscript(messages: AgentMessage[]): void;
|
|
65
|
+
|
|
66
|
+
// Config access and shortcuts
|
|
67
|
+
getConfig(): AgentOptions;
|
|
68
|
+
setConfig(config: AgentOptions): void;
|
|
69
|
+
updateConfig(updater: (config: AgentOptions) => AgentOptions): void;
|
|
70
|
+
setSessionId(sessionId: string): void;
|
|
71
|
+
getSessionId(): string | undefined;
|
|
72
|
+
setModel(model: LlmModelRef): void;
|
|
73
|
+
setTools(tools: Tool[]): void;
|
|
74
|
+
setSkills(skills: Skill[]): void;
|
|
75
|
+
setPhases(phases: PhaseRegistry): void;
|
|
76
|
+
setCwd(cwd: string): void;
|
|
77
|
+
setStream(stream: StreamFn): void;
|
|
78
|
+
getModel(): LlmModelRef;
|
|
79
|
+
getTools(): Tool[];
|
|
80
|
+
getSkills(): Skill[];
|
|
81
|
+
getPhases(): PhaseRegistry | undefined;
|
|
82
|
+
getCwd(): string | undefined;
|
|
83
|
+
|
|
46
84
|
abort(): void;
|
|
47
85
|
subscribe(listener: AgentEventListener): () => void;
|
|
48
|
-
skill(name: string):
|
|
86
|
+
skill(name: string, additionalInstructions?: string): string;
|
|
49
87
|
phase(name: string): Promise<string>;
|
|
50
88
|
waitForIdle(): Promise<void>;
|
|
51
89
|
flushEvents(): Promise<void>;
|
|
52
|
-
readonly
|
|
90
|
+
readonly state: AgentStatus;
|
|
53
91
|
|
|
54
92
|
// Resource loading — replaces standalone loadSkills/loadPhases/loadExtensions
|
|
55
93
|
static loadSkills(targetPath: string): Promise<Skill[]>;
|
|
@@ -65,6 +103,7 @@ type AgentOptions = {
|
|
|
65
103
|
context: AgentContext;
|
|
66
104
|
model: LlmModelRef;
|
|
67
105
|
stream: StreamFn;
|
|
106
|
+
cwd?: string;
|
|
68
107
|
extensions?: LoadedExtension[];
|
|
69
108
|
sessionId?: string;
|
|
70
109
|
maxAttempts?: number;
|
|
@@ -78,18 +117,59 @@ type AgentOptions = {
|
|
|
78
117
|
};
|
|
79
118
|
```
|
|
80
119
|
|
|
81
|
-
###
|
|
120
|
+
### RunResult
|
|
82
121
|
|
|
83
122
|
```ts
|
|
84
|
-
type
|
|
85
|
-
status: AgentStatus;
|
|
86
|
-
outcome?: Outcome;
|
|
87
|
-
metrics?: LoopMetrics;
|
|
88
|
-
messages: AgentMessage[];
|
|
123
|
+
type RunResult = {
|
|
89
124
|
sessionId: string;
|
|
125
|
+
messages: AgentMessage[];
|
|
126
|
+
outcome: Outcome;
|
|
127
|
+
metrics: LoopMetrics;
|
|
90
128
|
};
|
|
91
129
|
```
|
|
92
130
|
|
|
131
|
+
### Conversation Continuation
|
|
132
|
+
|
|
133
|
+
For multi-turn use, append input to the agent's current transcript and run with the updated context. `runWithUserInput()` is the main convenience API; the lower-level append methods are useful when a UI or session layer controls message creation.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const first = await agent.runWithUserInput("summarize this repository");
|
|
137
|
+
|
|
138
|
+
agent.appendUserMessage("now focus on the CLI package");
|
|
139
|
+
const second = await agent.run();
|
|
140
|
+
|
|
141
|
+
await agent.runWithMessage(createMessage("user", "what changed since last turn?"));
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The agent uses `context.phases.entryPhaseId` only until the first successful run completes. Later turns start from the normalized `default` phase so long-lived agents do not repeat one-time entry work. Call `agent.resetInitialization()` when the next run should use `entryPhaseId` again.
|
|
145
|
+
|
|
146
|
+
Transcript helpers return snapshots, so callers can inspect or edit history without accidentally mutating the agent until they call a setter.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const messages = agent.getMessages();
|
|
150
|
+
agent.replaceTranscript(messages.slice(-6));
|
|
151
|
+
agent.clearMessages();
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Updating Config
|
|
155
|
+
|
|
156
|
+
`AgentOptions` can be replaced wholesale with `setConfig()`, or updated through focused shortcuts for common orchestration flows.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
agent.setSessionId("ses_known");
|
|
160
|
+
agent.setModel({ provider: "openai", id: "gpt-4.1" });
|
|
161
|
+
agent.setTools(createCoreTools({ root: process.cwd() }));
|
|
162
|
+
agent.setSkills(await Agent.loadSkills("./.rowan/skills"));
|
|
163
|
+
agent.setPhases(await Agent.loadPhases("./.rowan/phases"));
|
|
164
|
+
agent.setCwd(process.cwd());
|
|
165
|
+
agent.setStream(createDispatchStream());
|
|
166
|
+
|
|
167
|
+
agent.updateContext((context) => ({
|
|
168
|
+
...context,
|
|
169
|
+
systemPrompt: `${context.systemPrompt}\n\nPrefer concise answers.`,
|
|
170
|
+
}));
|
|
171
|
+
```
|
|
172
|
+
|
|
93
173
|
## AgentContext
|
|
94
174
|
|
|
95
175
|
The context snapshot that defines what the agent can see and do — the system prompt sets the role, messages form the conversation history, and tools/skills define the capability boundary.
|
|
@@ -264,7 +344,7 @@ Per iteration:
|
|
|
264
344
|
5. Transition, continue, or stop
|
|
265
345
|
```
|
|
266
346
|
|
|
267
|
-
**`entryPhaseId`** specifies which phase the loop enters
|
|
347
|
+
**`entryPhaseId`** specifies which phase the loop enters for an uninitialized Agent. When phases are loaded from `.rowan/phases/`, the first discovered phase becomes the entry. When none are configured, the Agent normalises to `"default"`. After a successful run, later turns start from `"default"` until `agent.resetInitialization()` is called. This field is an internal routing hint for the phase loop — it is **not** exposed to the LLM, since the agent does not need to know which phase is the entry point to make routing decisions.
|
|
268
348
|
|
|
269
349
|
### Example Phase Flow
|
|
270
350
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1357,7 +1357,8 @@ type AgentStatus = {
|
|
|
1357
1357
|
context: AgentContext;
|
|
1358
1358
|
model: LlmModelRef;
|
|
1359
1359
|
tools: Tool[];
|
|
1360
|
-
|
|
1360
|
+
initialized: boolean;
|
|
1361
|
+
running: boolean;
|
|
1361
1362
|
currentResult?: RunResult;
|
|
1362
1363
|
error?: string;
|
|
1363
1364
|
};
|
|
@@ -1376,6 +1377,37 @@ declare class Agent {
|
|
|
1376
1377
|
static loadExtensions(targetPath: string): Promise<LoadExtensionsResult>;
|
|
1377
1378
|
constructor(options: AgentOptions);
|
|
1378
1379
|
subscribe(listener: AgentEventListener): Unsubscribe;
|
|
1380
|
+
appendUserMessage(input: string): void;
|
|
1381
|
+
appendMessage(message: AgentMessage): void;
|
|
1382
|
+
appendMessages(messages: AgentMessage[]): void;
|
|
1383
|
+
runWithUserInput(input: string, options?: RunOptions): Promise<RunResult>;
|
|
1384
|
+
runWithMessage(message: AgentMessage, options?: RunOptions): Promise<RunResult>;
|
|
1385
|
+
resetInitialization(): void;
|
|
1386
|
+
getContext(): AgentContext;
|
|
1387
|
+
setContext(context: AgentContext): void;
|
|
1388
|
+
updateContext(updater: (context: AgentContext) => AgentContext): void;
|
|
1389
|
+
forkContext(overrides?: Partial<AgentContext>): AgentContext;
|
|
1390
|
+
getMessages(): AgentMessage[];
|
|
1391
|
+
setMessages(messages: AgentMessage[]): void;
|
|
1392
|
+
clearMessages(): void;
|
|
1393
|
+
getTranscript(): AgentMessage[];
|
|
1394
|
+
replaceTranscript(messages: AgentMessage[]): void;
|
|
1395
|
+
getConfig(): AgentOptions;
|
|
1396
|
+
setConfig(config: AgentOptions): void;
|
|
1397
|
+
updateConfig(updater: (config: AgentOptions) => AgentOptions): void;
|
|
1398
|
+
setSessionId(sessionId: string): void;
|
|
1399
|
+
getSessionId(): string | undefined;
|
|
1400
|
+
setModel(model: LlmModelRef): void;
|
|
1401
|
+
setTools(tools: Tool[]): void;
|
|
1402
|
+
setSkills(skills: Skill$1[]): void;
|
|
1403
|
+
setPhases(phases: PhaseRegistry): void;
|
|
1404
|
+
setCwd(cwd: string): void;
|
|
1405
|
+
setStream(stream: StreamFn): void;
|
|
1406
|
+
getModel(): LlmModelRef;
|
|
1407
|
+
getTools(): Tool[];
|
|
1408
|
+
getSkills(): Skill$1[];
|
|
1409
|
+
getPhases(): PhaseRegistry | undefined;
|
|
1410
|
+
getCwd(): string | undefined;
|
|
1379
1411
|
private emitToListeners;
|
|
1380
1412
|
flushEvents(): Promise<void>;
|
|
1381
1413
|
private processEvents;
|
|
@@ -1434,6 +1466,7 @@ declare class Agent {
|
|
|
1434
1466
|
waitForIdle(): Promise<void>;
|
|
1435
1467
|
private resolveRunConfig;
|
|
1436
1468
|
private createContextSnapshot;
|
|
1469
|
+
private cloneOptions;
|
|
1437
1470
|
}
|
|
1438
1471
|
|
|
1439
1472
|
declare function createId(prefix: string): string;
|
|
@@ -1714,13 +1747,6 @@ declare class EmptyResponseError extends Error {
|
|
|
1714
1747
|
readonly code = "empty_response";
|
|
1715
1748
|
constructor(message?: string);
|
|
1716
1749
|
}
|
|
1717
|
-
/** Error thrown when a routed phase needs a route tool call but the model did not emit one. */
|
|
1718
|
-
declare class MissingRouteToolCallError extends Error {
|
|
1719
|
-
readonly code = "missing_route_tool_call";
|
|
1720
|
-
readonly phase: string;
|
|
1721
|
-
readonly toolCallNames: string[];
|
|
1722
|
-
constructor(phase: string, toolCallNames: string[]);
|
|
1723
|
-
}
|
|
1724
1750
|
|
|
1725
1751
|
interface SystemPromptOptions {
|
|
1726
1752
|
/** Base system prompt. */
|
|
@@ -1775,4 +1801,4 @@ declare function buildModelRequest(input: ModelRequestInput, options?: {
|
|
|
1775
1801
|
model?: LlmModelRef;
|
|
1776
1802
|
}): LlmRequest;
|
|
1777
1803
|
|
|
1778
|
-
export { type AbortEvent, type AfterPhaseEvent, type AfterPhaseHookResult, type AfterPhaseResult, type AfterToolCallEvent, type AfterToolCallResult, Agent, type AgentConfigFile, type AgentContext, type AgentEndEvent, AgentEventStream, type AgentOptions, type AgentStartEvent, type AgentStatus, type BeforePhaseEvent, type BeforePhaseHookResult, type BeforePhaseResult, type BeforePromptEvent, type BeforePromptResult, type BeforeToolCallEvent, type BeforeToolCallResult, EmptyResponseError, type EventBus, EventStream, type ExecOptions, type ExecResult, type ExecutionTurn, type Extension, type ExtensionAPI, type ExtensionContext, type ExtensionError, type ExtensionErrorListener, type ExtensionFactory, type ExtensionManifest, type ExtensionPackageManifest, ExtensionRunner, type ExtensionRunnerOptions, type ExtensionRuntime, type ExtensionUtils, HookError, type HookEvent, type HookEventType, type HookHandler, type HookResultMap, HooksManager, type LoadExtensionsResult, type LoadedExtension, LocalJsonlSessionManager, type LoopMetrics, type MessageEndEvent, type MessageStartEvent, type MessageUpdateEvent,
|
|
1804
|
+
export { type AbortEvent, type AfterPhaseEvent, type AfterPhaseHookResult, type AfterPhaseResult, type AfterToolCallEvent, type AfterToolCallResult, Agent, type AgentConfigFile, type AgentContext, type AgentEndEvent, AgentEventStream, type AgentOptions, type AgentStartEvent, type AgentStatus, type BeforePhaseEvent, type BeforePhaseHookResult, type BeforePhaseResult, type BeforePromptEvent, type BeforePromptResult, type BeforeToolCallEvent, type BeforeToolCallResult, EmptyResponseError, type EventBus, EventStream, type ExecOptions, type ExecResult, type ExecutionTurn, type Extension, type ExtensionAPI, type ExtensionContext, type ExtensionError, type ExtensionErrorListener, type ExtensionFactory, type ExtensionManifest, type ExtensionPackageManifest, ExtensionRunner, type ExtensionRunnerOptions, type ExtensionRuntime, type ExtensionUtils, HookError, type HookEvent, type HookEventType, type HookHandler, type HookResultMap, HooksManager, type LoadExtensionsResult, type LoadedExtension, LocalJsonlSessionManager, type LoopMetrics, type MessageEndEvent, type MessageStartEvent, type MessageUpdateEvent, type ModelConfigFromFile, type ModelTranscript, type Phase, type PhaseContext, type PhaseDefinition, type PhaseExecution, type PhaseOutput, type PhaseRegistration, type PhaseRegistry, type PhaseRun, type PhaseState, type ProviderConfigFromFile, type QueueUpdateEvent, type RegisteredPhase, type RegisteredTool, type RunOptions, type RunResult, type SavePointEvent, type Session, type SessionListItem, type SettledEvent, type SourceInfo, type Tool, type ToolDefinition, type ToolExecutionEndEvent, type ToolExecutionResult, type ToolExecutionStartEvent, type ToolExecutionUpdateEvent, type TurnEndEvent, type TurnStartEvent, type WorkspacePaths, appendUserTurn, buildModelRequest, buildSystemPrompt, conversationMessages, createCoreTools, createEventBus, createExtension, createExtensionAPI, createExtensionRunner, createExtensionRuntime, createId, createMessage, createSession, createSourceInfo, createTimestamp, getGlobalHooks, interpolateEnvVars, latestUserInput, loadConfigFile, loadPhase, loadPhases, messageContentText, parseModelRef, registerConfigModels, reloadPhases, resetGlobalHooks, resolveDefaultModel, resolveInWorkspace, resolveWorkspacePaths, serializeSkills };
|
package/dist/index.js
CHANGED
|
@@ -21,19 +21,6 @@ var EmptyResponseError = class extends Error {
|
|
|
21
21
|
this.name = "EmptyResponseError";
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
|
-
var MissingRouteToolCallError = class extends Error {
|
|
25
|
-
code = "missing_route_tool_call";
|
|
26
|
-
phase;
|
|
27
|
-
toolCallNames;
|
|
28
|
-
constructor(phase, toolCallNames) {
|
|
29
|
-
super(
|
|
30
|
-
`Phase "${phase}" requires a route tool call, but the model did not emit one.`
|
|
31
|
-
);
|
|
32
|
-
this.name = "MissingRouteToolCallError";
|
|
33
|
-
this.phase = phase;
|
|
34
|
-
this.toolCallNames = toolCallNames;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
24
|
var LoopGuard = {
|
|
38
25
|
/** Returns abort result if signal is aborted */
|
|
39
26
|
checkAbort(signal) {
|
|
@@ -85,7 +72,11 @@ function extractToolResults(transcript) {
|
|
|
85
72
|
}
|
|
86
73
|
var createOutcome = {
|
|
87
74
|
phaseNotFound(output) {
|
|
88
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
id: createId("out"),
|
|
77
|
+
message: `Phase "${output.phase ?? "Unknown"}" not found.`,
|
|
78
|
+
display: true
|
|
79
|
+
};
|
|
89
80
|
},
|
|
90
81
|
default(output, transcript) {
|
|
91
82
|
const toolResults = transcript ? extractToolResults(transcript) : [];
|
|
@@ -102,11 +93,17 @@ var createOutcome = {
|
|
|
102
93
|
}
|
|
103
94
|
return outcome;
|
|
104
95
|
},
|
|
96
|
+
phase(output, transcript) {
|
|
97
|
+
return {
|
|
98
|
+
...this.default(output, transcript),
|
|
99
|
+
display: true
|
|
100
|
+
};
|
|
101
|
+
},
|
|
105
102
|
aborted() {
|
|
106
103
|
return { id: createId("out"), message: "Agent run aborted." };
|
|
107
104
|
},
|
|
108
105
|
error(message) {
|
|
109
|
-
return { id: createId("out"), message };
|
|
106
|
+
return { id: createId("out"), message, display: true };
|
|
110
107
|
}
|
|
111
108
|
};
|
|
112
109
|
|
|
@@ -196,9 +193,13 @@ async function collectStreamResult(input) {
|
|
|
196
193
|
let stopReason;
|
|
197
194
|
let doneResponse;
|
|
198
195
|
for await (const event of input.events) {
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
|
|
196
|
+
const abortResult2 = LoopGuard.checkAbort(input.config.signal);
|
|
197
|
+
if (abortResult2.stopReason !== "none") {
|
|
198
|
+
if (activeMessageId) {
|
|
199
|
+
await input.message.end(activeMessageId);
|
|
200
|
+
activeMessageId = void 0;
|
|
201
|
+
}
|
|
202
|
+
return { text: abortResult2.message, contentBlocks: [], toolCalls: [], stopReason: "aborted" };
|
|
202
203
|
}
|
|
203
204
|
if (event.type === "model_requested") {
|
|
204
205
|
input.config.emit?.({
|
|
@@ -249,6 +250,14 @@ async function collectStreamResult(input) {
|
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
}
|
|
253
|
+
const abortResult = LoopGuard.checkAbort(input.config.signal);
|
|
254
|
+
if (abortResult.stopReason !== "none") {
|
|
255
|
+
if (activeMessageId) {
|
|
256
|
+
await input.message.end(activeMessageId);
|
|
257
|
+
activeMessageId = void 0;
|
|
258
|
+
}
|
|
259
|
+
return { text: abortResult.message, contentBlocks: [], toolCalls: [], stopReason: "aborted" };
|
|
260
|
+
}
|
|
252
261
|
if (activeMessageId) {
|
|
253
262
|
await input.message.end(activeMessageId);
|
|
254
263
|
}
|
|
@@ -1753,11 +1762,19 @@ function createRunResult(config, state, outcome) {
|
|
|
1753
1762
|
};
|
|
1754
1763
|
}
|
|
1755
1764
|
async function completeRun(config, state, outcome) {
|
|
1765
|
+
if (outcome.display) {
|
|
1766
|
+
const messages = createMessageManager(config.context, config.emit, config.onMessage);
|
|
1767
|
+
const messageId = messages.start("assistant", outcome.message, { outcomeId: outcome.id });
|
|
1768
|
+
await messages.end(messageId);
|
|
1769
|
+
}
|
|
1756
1770
|
state.metrics.endedAt = createTimestamp();
|
|
1757
1771
|
state.metrics.durationMs = Date.now() - state.metrics.startedAtMs;
|
|
1758
1772
|
await config.onOutcome?.(outcome);
|
|
1759
1773
|
return createRunResult(config, state, outcome);
|
|
1760
1774
|
}
|
|
1775
|
+
function createTerminalOutcome(phase, output, transcript) {
|
|
1776
|
+
return phase.run || phase.factory ? createOutcome.phase(output, transcript) : createOutcome.default(output, transcript);
|
|
1777
|
+
}
|
|
1761
1778
|
function buildToolsWithRouting(config, availablePhases) {
|
|
1762
1779
|
const tools = [...config.context.tools];
|
|
1763
1780
|
if (availablePhases.length > 0) {
|
|
@@ -1991,10 +2008,34 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1991
2008
|
const routeToolAvailable = phaseContext.tools.some((tool) => tool.name === PhaseRouteTool);
|
|
1992
2009
|
const routeRequired = !phase.target && !phase.run && !phase.factory && routeToolAvailable && registry.phases.size > 1;
|
|
1993
2010
|
if (routeRequired && !routeDecision) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2011
|
+
if (config.waitForInput) {
|
|
2012
|
+
const preAbort = LoopGuard.checkAbort(config.signal);
|
|
2013
|
+
if (preAbort.stopReason !== "none") {
|
|
2014
|
+
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
2015
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
2016
|
+
previousPhaseMsgId = void 0;
|
|
2017
|
+
return completeRun(config, state, createOutcome.aborted());
|
|
2018
|
+
}
|
|
2019
|
+
config.emit?.({ type: "user_prompt_requested", phase: currentPhaseId, ts: createTimestamp() });
|
|
2020
|
+
const userMessages = await config.waitForInput();
|
|
2021
|
+
const abortResult2 = LoopGuard.checkAbort(config.signal);
|
|
2022
|
+
if (abortResult2.stopReason !== "none") {
|
|
2023
|
+
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
2024
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
2025
|
+
previousPhaseMsgId = void 0;
|
|
2026
|
+
return completeRun(config, state, createOutcome.aborted());
|
|
2027
|
+
}
|
|
2028
|
+
for (const message of userMessages) {
|
|
2029
|
+
config.context.messages.push(message);
|
|
2030
|
+
}
|
|
2031
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
2032
|
+
previousPhaseMsgId = void 0;
|
|
2033
|
+
isContinuing = true;
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
2037
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
2038
|
+
return completeRun(config, state, createOutcome.default(output, config.context.messages));
|
|
1998
2039
|
}
|
|
1999
2040
|
if (output.route === "continue") {
|
|
2000
2041
|
isContinuing = true;
|
|
@@ -2020,7 +2061,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
2020
2061
|
pendingInstruction = routeDecision.instruction;
|
|
2021
2062
|
const entryPhaseId = phase.target ?? registry.entryPhaseId;
|
|
2022
2063
|
if (entryPhaseId === "stop") {
|
|
2023
|
-
return completeRun(config, state,
|
|
2064
|
+
return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
|
|
2024
2065
|
}
|
|
2025
2066
|
currentPhaseId = entryPhaseId;
|
|
2026
2067
|
previousPayload = void 0;
|
|
@@ -2036,7 +2077,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
2036
2077
|
}
|
|
2037
2078
|
if (nextRoute === "stop") {
|
|
2038
2079
|
if (routeToolCalled) removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
2039
|
-
return completeRun(config, state,
|
|
2080
|
+
return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
|
|
2040
2081
|
}
|
|
2041
2082
|
const targetPhaseId = nextRoute;
|
|
2042
2083
|
if (!registry.phases.has(targetPhaseId)) {
|
|
@@ -3439,7 +3480,8 @@ var Agent = class {
|
|
|
3439
3480
|
context: prepareAgentContext(context),
|
|
3440
3481
|
model: this.options.model,
|
|
3441
3482
|
tools: this.options.context.tools ?? [],
|
|
3442
|
-
|
|
3483
|
+
initialized: false,
|
|
3484
|
+
running: false
|
|
3443
3485
|
};
|
|
3444
3486
|
}
|
|
3445
3487
|
subscribe(listener) {
|
|
@@ -3448,6 +3490,161 @@ var Agent = class {
|
|
|
3448
3490
|
this.listeners.delete(listener);
|
|
3449
3491
|
};
|
|
3450
3492
|
}
|
|
3493
|
+
appendUserMessage(input) {
|
|
3494
|
+
this.appendMessage(createMessage("user", input));
|
|
3495
|
+
}
|
|
3496
|
+
appendMessage(message) {
|
|
3497
|
+
this.appendMessages([message]);
|
|
3498
|
+
}
|
|
3499
|
+
appendMessages(messages) {
|
|
3500
|
+
this.setMessages([...this.options.context.messages, ...snapshotMessages(messages)]);
|
|
3501
|
+
}
|
|
3502
|
+
runWithUserInput(input, options) {
|
|
3503
|
+
const activeRun = this.activeRun;
|
|
3504
|
+
if (activeRun?.resume) {
|
|
3505
|
+
activeRun.resume([createMessage("user", input)]);
|
|
3506
|
+
return activeRun.promise;
|
|
3507
|
+
}
|
|
3508
|
+
if (activeRun) {
|
|
3509
|
+
return Promise.reject(new Error("Agent is already running."));
|
|
3510
|
+
}
|
|
3511
|
+
this.appendUserMessage(input);
|
|
3512
|
+
return this.run(options);
|
|
3513
|
+
}
|
|
3514
|
+
runWithMessage(message, options) {
|
|
3515
|
+
const activeRun = this.activeRun;
|
|
3516
|
+
if (activeRun?.resume) {
|
|
3517
|
+
activeRun.resume([message]);
|
|
3518
|
+
return activeRun.promise;
|
|
3519
|
+
}
|
|
3520
|
+
if (activeRun) {
|
|
3521
|
+
return Promise.reject(new Error("Agent is already running."));
|
|
3522
|
+
}
|
|
3523
|
+
this.appendMessage(message);
|
|
3524
|
+
return this.run(options);
|
|
3525
|
+
}
|
|
3526
|
+
resetInitialization() {
|
|
3527
|
+
this.state.initialized = false;
|
|
3528
|
+
}
|
|
3529
|
+
getContext() {
|
|
3530
|
+
return prepareAgentContext(this.options.context);
|
|
3531
|
+
}
|
|
3532
|
+
setContext(context) {
|
|
3533
|
+
const nextContext = prepareAgentContext(context);
|
|
3534
|
+
this.options = {
|
|
3535
|
+
...this.options,
|
|
3536
|
+
context: nextContext
|
|
3537
|
+
};
|
|
3538
|
+
this.state.context = prepareAgentContext(nextContext, this.extensionRunner);
|
|
3539
|
+
this.state.tools = this.state.context.tools ?? [];
|
|
3540
|
+
}
|
|
3541
|
+
updateContext(updater) {
|
|
3542
|
+
this.setContext(updater(this.getContext()));
|
|
3543
|
+
}
|
|
3544
|
+
forkContext(overrides = {}) {
|
|
3545
|
+
return prepareAgentContext({
|
|
3546
|
+
...this.getContext(),
|
|
3547
|
+
...overrides
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
getMessages() {
|
|
3551
|
+
return snapshotMessages(this.options.context.messages);
|
|
3552
|
+
}
|
|
3553
|
+
setMessages(messages) {
|
|
3554
|
+
this.setContext({
|
|
3555
|
+
...this.options.context,
|
|
3556
|
+
messages: snapshotMessages(messages)
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
clearMessages() {
|
|
3560
|
+
this.setMessages([]);
|
|
3561
|
+
}
|
|
3562
|
+
getTranscript() {
|
|
3563
|
+
return this.getMessages();
|
|
3564
|
+
}
|
|
3565
|
+
replaceTranscript(messages) {
|
|
3566
|
+
this.setMessages(messages);
|
|
3567
|
+
}
|
|
3568
|
+
getConfig() {
|
|
3569
|
+
return this.cloneOptions(this.options);
|
|
3570
|
+
}
|
|
3571
|
+
setConfig(config) {
|
|
3572
|
+
const context = prepareAgentContext(config.context);
|
|
3573
|
+
this.options = {
|
|
3574
|
+
...config,
|
|
3575
|
+
context,
|
|
3576
|
+
...config.extensions ? { extensions: config.extensions.slice() } : {}
|
|
3577
|
+
};
|
|
3578
|
+
this.state.sessionId = config.sessionId;
|
|
3579
|
+
this.state.context = prepareAgentContext(context, this.extensionRunner);
|
|
3580
|
+
this.state.model = config.model;
|
|
3581
|
+
this.state.tools = this.state.context.tools ?? [];
|
|
3582
|
+
}
|
|
3583
|
+
updateConfig(updater) {
|
|
3584
|
+
this.setConfig(updater(this.getConfig()));
|
|
3585
|
+
}
|
|
3586
|
+
setSessionId(sessionId) {
|
|
3587
|
+
this.options = {
|
|
3588
|
+
...this.options,
|
|
3589
|
+
sessionId
|
|
3590
|
+
};
|
|
3591
|
+
this.state.sessionId = sessionId;
|
|
3592
|
+
}
|
|
3593
|
+
getSessionId() {
|
|
3594
|
+
return this.state.sessionId;
|
|
3595
|
+
}
|
|
3596
|
+
setModel(model) {
|
|
3597
|
+
this.options = {
|
|
3598
|
+
...this.options,
|
|
3599
|
+
model
|
|
3600
|
+
};
|
|
3601
|
+
this.state.model = model;
|
|
3602
|
+
}
|
|
3603
|
+
setTools(tools) {
|
|
3604
|
+
this.setContext({
|
|
3605
|
+
...this.options.context,
|
|
3606
|
+
tools: tools.slice()
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
setSkills(skills) {
|
|
3610
|
+
this.setContext({
|
|
3611
|
+
...this.options.context,
|
|
3612
|
+
skills: skills.slice()
|
|
3613
|
+
});
|
|
3614
|
+
}
|
|
3615
|
+
setPhases(phases) {
|
|
3616
|
+
this.setContext({
|
|
3617
|
+
...this.options.context,
|
|
3618
|
+
phases: clonePhaseRegistry(phases)
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
setCwd(cwd) {
|
|
3622
|
+
this.options = {
|
|
3623
|
+
...this.options,
|
|
3624
|
+
cwd
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
setStream(stream) {
|
|
3628
|
+
this.options = {
|
|
3629
|
+
...this.options,
|
|
3630
|
+
stream
|
|
3631
|
+
};
|
|
3632
|
+
}
|
|
3633
|
+
getModel() {
|
|
3634
|
+
return this.state.model;
|
|
3635
|
+
}
|
|
3636
|
+
getTools() {
|
|
3637
|
+
return this.state.context.tools.slice();
|
|
3638
|
+
}
|
|
3639
|
+
getSkills() {
|
|
3640
|
+
return this.state.context.skills.slice();
|
|
3641
|
+
}
|
|
3642
|
+
getPhases() {
|
|
3643
|
+
return this.state.context.phases ? clonePhaseRegistry(this.state.context.phases) : void 0;
|
|
3644
|
+
}
|
|
3645
|
+
getCwd() {
|
|
3646
|
+
return this.options.cwd;
|
|
3647
|
+
}
|
|
3451
3648
|
emitToListeners(event) {
|
|
3452
3649
|
for (const listener of this.listeners) {
|
|
3453
3650
|
try {
|
|
@@ -3543,31 +3740,30 @@ var Agent = class {
|
|
|
3543
3740
|
const message = error instanceof Error ? error.message : "Agent run failed.";
|
|
3544
3741
|
this.state.error = message;
|
|
3545
3742
|
}
|
|
3546
|
-
|
|
3743
|
+
runWithLifecycle(executor) {
|
|
3547
3744
|
if (this.activeRun) {
|
|
3548
|
-
|
|
3745
|
+
return Promise.reject(new Error("Agent is already running."));
|
|
3549
3746
|
}
|
|
3550
|
-
let resolvePromise;
|
|
3551
3747
|
const abortController = new AbortController();
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3748
|
+
const waitForInput = () => new Promise((resolve7) => {
|
|
3749
|
+
const activeRun = this.activeRun;
|
|
3750
|
+
activeRun.resume = (messages) => {
|
|
3751
|
+
activeRun.resume = void 0;
|
|
3752
|
+
resolve7(messages);
|
|
3753
|
+
};
|
|
3554
3754
|
});
|
|
3555
|
-
|
|
3556
|
-
this.state.isRunning = true;
|
|
3557
|
-
try {
|
|
3558
|
-
const result = await executor(abortController.signal);
|
|
3559
|
-
resolvePromise(result);
|
|
3560
|
-
return result;
|
|
3561
|
-
} catch (error) {
|
|
3755
|
+
const promise = Promise.resolve().then(() => executor({ signal: abortController.signal, waitForInput })).catch((error) => {
|
|
3562
3756
|
this.handleRunFailure(error, abortController.signal.aborted);
|
|
3563
|
-
resolvePromise(void 0);
|
|
3564
3757
|
throw error;
|
|
3565
|
-
}
|
|
3758
|
+
}).finally(() => {
|
|
3566
3759
|
this.finishRun();
|
|
3567
|
-
}
|
|
3760
|
+
});
|
|
3761
|
+
this.activeRun = { promise, abortController };
|
|
3762
|
+
this.state.running = true;
|
|
3763
|
+
return promise;
|
|
3568
3764
|
}
|
|
3569
3765
|
finishRun() {
|
|
3570
|
-
this.state.
|
|
3766
|
+
this.state.running = false;
|
|
3571
3767
|
this.activeRun = void 0;
|
|
3572
3768
|
}
|
|
3573
3769
|
async loadExtensions(extensions, cwd) {
|
|
@@ -3589,22 +3785,30 @@ var Agent = class {
|
|
|
3589
3785
|
this.loadedExtensions = extensions;
|
|
3590
3786
|
this.loadedExtensionsCwd = cwd;
|
|
3591
3787
|
}
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3788
|
+
run(config) {
|
|
3789
|
+
return this.runWithLifecycle(async ({ signal, waitForInput }) => {
|
|
3790
|
+
const resolved = this.resolveRunConfig(config);
|
|
3791
|
+
const extensions = resolved.extensions ?? [];
|
|
3792
|
+
await this.loadExtensions(extensions, resolved.cwd);
|
|
3793
|
+
const runContext = prepareAgentContext(resolved.context, this.extensionRunner);
|
|
3794
|
+
const entryPhaseId = this.state.initialized ? DEFAULT_PHASE_ID : runContext.phases?.entryPhaseId ?? DEFAULT_PHASE_ID;
|
|
3795
|
+
const loopContext = {
|
|
3796
|
+
...runContext,
|
|
3797
|
+
phases: {
|
|
3798
|
+
...runContext.phases,
|
|
3799
|
+
entryPhaseId
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3802
|
+
const sessionId = resolved.sessionId ?? this.state.sessionId;
|
|
3803
|
+
this.options = resolved;
|
|
3804
|
+
if (sessionId) {
|
|
3805
|
+
this.state.sessionId = sessionId;
|
|
3806
|
+
}
|
|
3807
|
+
this.state.context = runContext;
|
|
3808
|
+
this.state.model = resolved.model;
|
|
3809
|
+
this.state.tools = runContext.tools ?? [];
|
|
3810
|
+
this.state.currentResult = void 0;
|
|
3811
|
+
this.state.error = void 0;
|
|
3608
3812
|
const emit = (event) => {
|
|
3609
3813
|
this.processEvents(event);
|
|
3610
3814
|
};
|
|
@@ -3627,7 +3831,7 @@ var Agent = class {
|
|
|
3627
3831
|
return this.handleAfterToolCall(input.tool, result2);
|
|
3628
3832
|
};
|
|
3629
3833
|
const result = await runAgentLoop({
|
|
3630
|
-
context:
|
|
3834
|
+
context: loopContext,
|
|
3631
3835
|
sessionId: sessionId ?? createId("ses"),
|
|
3632
3836
|
model: resolved.model,
|
|
3633
3837
|
stream: resolved.stream,
|
|
@@ -3641,7 +3845,8 @@ var Agent = class {
|
|
|
3641
3845
|
emit,
|
|
3642
3846
|
onMessage: resolved.onMessage,
|
|
3643
3847
|
onOutcome: resolved.onOutcome,
|
|
3644
|
-
onModelTranscript: resolved.onModelTranscript
|
|
3848
|
+
onModelTranscript: resolved.onModelTranscript,
|
|
3849
|
+
waitForInput
|
|
3645
3850
|
});
|
|
3646
3851
|
const nextContext = {
|
|
3647
3852
|
...prepareAgentContext(resolved.context),
|
|
@@ -3650,6 +3855,7 @@ var Agent = class {
|
|
|
3650
3855
|
this.state.sessionId = result.sessionId;
|
|
3651
3856
|
this.state.context = prepareAgentContext(nextContext, this.extensionRunner);
|
|
3652
3857
|
this.state.currentResult = result;
|
|
3858
|
+
this.state.initialized = true;
|
|
3653
3859
|
this.options = {
|
|
3654
3860
|
...resolved,
|
|
3655
3861
|
sessionId: result.sessionId,
|
|
@@ -3659,7 +3865,9 @@ var Agent = class {
|
|
|
3659
3865
|
});
|
|
3660
3866
|
}
|
|
3661
3867
|
abort(reason = "Aborted by caller.") {
|
|
3662
|
-
this.activeRun
|
|
3868
|
+
const activeRun = this.activeRun;
|
|
3869
|
+
activeRun?.resume?.([]);
|
|
3870
|
+
activeRun?.abortController.abort(reason);
|
|
3663
3871
|
}
|
|
3664
3872
|
/**
|
|
3665
3873
|
* Get formatted skill content for LLM consumption.
|
|
@@ -3718,7 +3926,20 @@ ${additionalInstructions}` : content;
|
|
|
3718
3926
|
createContextSnapshot() {
|
|
3719
3927
|
return prepareAgentContext(this.options.context);
|
|
3720
3928
|
}
|
|
3929
|
+
cloneOptions(options) {
|
|
3930
|
+
return {
|
|
3931
|
+
...options,
|
|
3932
|
+
context: prepareAgentContext(options.context),
|
|
3933
|
+
...options.extensions ? { extensions: options.extensions.slice() } : {}
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3721
3936
|
};
|
|
3937
|
+
function clonePhaseRegistry(registry) {
|
|
3938
|
+
return {
|
|
3939
|
+
phases: new Map(registry.phases),
|
|
3940
|
+
entryPhaseId: registry.entryPhaseId
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3722
3943
|
function prepareAgentContext(context, extensionRunner) {
|
|
3723
3944
|
const phases = /* @__PURE__ */ new Map();
|
|
3724
3945
|
phases.set(DEFAULT_PHASE_ID, createDefaultPhase());
|
|
@@ -4273,7 +4494,6 @@ export {
|
|
|
4273
4494
|
ExtensionRunner,
|
|
4274
4495
|
HooksManager,
|
|
4275
4496
|
LocalJsonlSessionManager,
|
|
4276
|
-
MissingRouteToolCallError,
|
|
4277
4497
|
appendUserTurn,
|
|
4278
4498
|
buildModelRequest,
|
|
4279
4499
|
buildSystemPrompt,
|