@rowan-agent/agent 0.5.1 → 0.5.3

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 CHANGED
@@ -42,14 +42,51 @@ 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<AgentRunResult>;
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
+
54
+ // Context and transcript
55
+ getContext(): AgentContext;
56
+ setContext(context: AgentContext): void;
57
+ updateContext(updater: (context: AgentContext) => AgentContext): void;
58
+ forkContext(overrides?: Partial<AgentContext>): AgentContext;
59
+ getMessages(): AgentMessage[];
60
+ setMessages(messages: AgentMessage[]): void;
61
+ clearMessages(): void;
62
+ getTranscript(): AgentMessage[];
63
+ replaceTranscript(messages: AgentMessage[]): void;
64
+
65
+ // Config access and shortcuts
66
+ getConfig(): AgentOptions;
67
+ setConfig(config: AgentOptions): void;
68
+ updateConfig(updater: (config: AgentOptions) => AgentOptions): void;
69
+ setSessionId(sessionId: string): void;
70
+ getSessionId(): string | undefined;
71
+ setModel(model: LlmModelRef): void;
72
+ setTools(tools: Tool[]): void;
73
+ setSkills(skills: Skill[]): void;
74
+ setPhases(phases: PhaseRegistry): void;
75
+ setCwd(cwd: string): void;
76
+ setStream(stream: StreamFn): void;
77
+ getModel(): LlmModelRef;
78
+ getTools(): Tool[];
79
+ getSkills(): Skill[];
80
+ getPhases(): PhaseRegistry | undefined;
81
+ getCwd(): string | undefined;
82
+
46
83
  abort(): void;
47
84
  subscribe(listener: AgentEventListener): () => void;
48
- skill(name: string): Promise<void>;
85
+ skill(name: string, additionalInstructions?: string): string;
49
86
  phase(name: string): Promise<string>;
50
87
  waitForIdle(): Promise<void>;
51
88
  flushEvents(): Promise<void>;
52
- readonly status: AgentStatus;
89
+ readonly state: AgentStatus;
53
90
 
54
91
  // Resource loading — replaces standalone loadSkills/loadPhases/loadExtensions
55
92
  static loadSkills(targetPath: string): Promise<Skill[]>;
@@ -65,6 +102,7 @@ type AgentOptions = {
65
102
  context: AgentContext;
66
103
  model: LlmModelRef;
67
104
  stream: StreamFn;
105
+ cwd?: string;
68
106
  extensions?: LoadedExtension[];
69
107
  sessionId?: string;
70
108
  maxAttempts?: number;
@@ -78,18 +116,57 @@ type AgentOptions = {
78
116
  };
79
117
  ```
80
118
 
81
- ### AgentRunResult
119
+ ### RunResult
82
120
 
83
121
  ```ts
84
- type AgentRunResult = {
85
- status: AgentStatus;
86
- outcome?: Outcome;
87
- metrics?: LoopMetrics;
88
- messages: AgentMessage[];
122
+ type RunResult = {
89
123
  sessionId: string;
124
+ messages: AgentMessage[];
125
+ outcome: Outcome;
126
+ metrics: LoopMetrics;
90
127
  };
91
128
  ```
92
129
 
130
+ ### Conversation Continuation
131
+
132
+ 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.
133
+
134
+ ```ts
135
+ const first = await agent.runWithUserInput("summarize this repository");
136
+
137
+ agent.appendUserMessage("now focus on the CLI package");
138
+ const second = await agent.run();
139
+
140
+ await agent.runWithMessage(createMessage("user", "what changed since last turn?"));
141
+ ```
142
+
143
+ Transcript helpers return snapshots, so callers can inspect or edit history without accidentally mutating the agent until they call a setter.
144
+
145
+ ```ts
146
+ const messages = agent.getMessages();
147
+ agent.replaceTranscript(messages.slice(-6));
148
+ agent.clearMessages();
149
+ ```
150
+
151
+ ### Updating Config
152
+
153
+ `AgentOptions` can be replaced wholesale with `setConfig()`, or updated through focused shortcuts for common orchestration flows.
154
+
155
+ ```ts
156
+ agent.setSessionId("ses_known");
157
+ agent.setModel({ provider: "openai", id: "gpt-4.1" });
158
+ agent.setTools(createCoreTools({ root: process.cwd() }));
159
+ agent.setSkills(await Agent.loadSkills("./.rowan/skills"));
160
+ agent.setPhases(await Agent.loadPhases("./.rowan/phases"));
161
+ agent.setCwd(process.cwd());
162
+ agent.setStream(createDispatchStream());
163
+
164
+ agent.updateContext((context) => ({
165
+ ...context,
166
+ systemPrompt: `${context.systemPrompt}\n\nPrefer concise answers.`,
167
+ }));
168
+ ```
169
+
93
170
  ## AgentContext
94
171
 
95
172
  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.
package/dist/index.d.ts CHANGED
@@ -1376,6 +1376,36 @@ declare class Agent {
1376
1376
  static loadExtensions(targetPath: string): Promise<LoadExtensionsResult>;
1377
1377
  constructor(options: AgentOptions);
1378
1378
  subscribe(listener: AgentEventListener): Unsubscribe;
1379
+ appendUserMessage(input: string): void;
1380
+ appendMessage(message: AgentMessage): void;
1381
+ appendMessages(messages: AgentMessage[]): void;
1382
+ runWithUserInput(input: string, options?: RunOptions): Promise<RunResult>;
1383
+ runWithMessage(message: AgentMessage, options?: RunOptions): Promise<RunResult>;
1384
+ getContext(): AgentContext;
1385
+ setContext(context: AgentContext): void;
1386
+ updateContext(updater: (context: AgentContext) => AgentContext): void;
1387
+ forkContext(overrides?: Partial<AgentContext>): AgentContext;
1388
+ getMessages(): AgentMessage[];
1389
+ setMessages(messages: AgentMessage[]): void;
1390
+ clearMessages(): void;
1391
+ getTranscript(): AgentMessage[];
1392
+ replaceTranscript(messages: AgentMessage[]): void;
1393
+ getConfig(): AgentOptions;
1394
+ setConfig(config: AgentOptions): void;
1395
+ updateConfig(updater: (config: AgentOptions) => AgentOptions): void;
1396
+ setSessionId(sessionId: string): void;
1397
+ getSessionId(): string | undefined;
1398
+ setModel(model: LlmModelRef): void;
1399
+ setTools(tools: Tool[]): void;
1400
+ setSkills(skills: Skill$1[]): void;
1401
+ setPhases(phases: PhaseRegistry): void;
1402
+ setCwd(cwd: string): void;
1403
+ setStream(stream: StreamFn): void;
1404
+ getModel(): LlmModelRef;
1405
+ getTools(): Tool[];
1406
+ getSkills(): Skill$1[];
1407
+ getPhases(): PhaseRegistry | undefined;
1408
+ getCwd(): string | undefined;
1379
1409
  private emitToListeners;
1380
1410
  flushEvents(): Promise<void>;
1381
1411
  private processEvents;
@@ -1434,6 +1464,7 @@ declare class Agent {
1434
1464
  waitForIdle(): Promise<void>;
1435
1465
  private resolveRunConfig;
1436
1466
  private createContextSnapshot;
1467
+ private cloneOptions;
1437
1468
  }
1438
1469
 
1439
1470
  declare function createId(prefix: string): string;
package/dist/index.js CHANGED
@@ -94,6 +94,9 @@ var createOutcome = {
94
94
  id: createId("out"),
95
95
  message
96
96
  };
97
+ if (output.payload !== void 0) {
98
+ outcome.payload = output.payload;
99
+ }
97
100
  if (toolResults.length > 0) {
98
101
  outcome.toolResults = toolResults;
99
102
  }
@@ -3426,14 +3429,14 @@ var Agent = class {
3426
3429
  return loadExtensionsFromPath(targetPath);
3427
3430
  }
3428
3431
  constructor(options) {
3429
- const context = cloneAgentContext(options.context);
3432
+ const context = prepareAgentContext(options.context);
3430
3433
  this.options = {
3431
3434
  ...options,
3432
3435
  context
3433
3436
  };
3434
3437
  this.state = {
3435
3438
  ...this.options.sessionId ? { sessionId: this.options.sessionId } : {},
3436
- context: cloneAgentContext(context),
3439
+ context: prepareAgentContext(context),
3437
3440
  model: this.options.model,
3438
3441
  tools: this.options.context.tools ?? [],
3439
3442
  isRunning: false
@@ -3445,6 +3448,142 @@ var Agent = class {
3445
3448
  this.listeners.delete(listener);
3446
3449
  };
3447
3450
  }
3451
+ appendUserMessage(input) {
3452
+ this.appendMessage(createMessage("user", input));
3453
+ }
3454
+ appendMessage(message) {
3455
+ this.appendMessages([message]);
3456
+ }
3457
+ appendMessages(messages) {
3458
+ this.setMessages([...this.options.context.messages, ...snapshotMessages(messages)]);
3459
+ }
3460
+ runWithUserInput(input, options) {
3461
+ this.appendUserMessage(input);
3462
+ return this.run(options);
3463
+ }
3464
+ runWithMessage(message, options) {
3465
+ this.appendMessage(message);
3466
+ return this.run(options);
3467
+ }
3468
+ getContext() {
3469
+ return prepareAgentContext(this.options.context);
3470
+ }
3471
+ setContext(context) {
3472
+ const nextContext = prepareAgentContext(context);
3473
+ this.options = {
3474
+ ...this.options,
3475
+ context: nextContext
3476
+ };
3477
+ this.state.context = prepareAgentContext(nextContext, this.extensionRunner);
3478
+ this.state.tools = this.state.context.tools ?? [];
3479
+ }
3480
+ updateContext(updater) {
3481
+ this.setContext(updater(this.getContext()));
3482
+ }
3483
+ forkContext(overrides = {}) {
3484
+ return prepareAgentContext({
3485
+ ...this.getContext(),
3486
+ ...overrides
3487
+ });
3488
+ }
3489
+ getMessages() {
3490
+ return snapshotMessages(this.options.context.messages);
3491
+ }
3492
+ setMessages(messages) {
3493
+ this.setContext({
3494
+ ...this.options.context,
3495
+ messages: snapshotMessages(messages)
3496
+ });
3497
+ }
3498
+ clearMessages() {
3499
+ this.setMessages([]);
3500
+ }
3501
+ getTranscript() {
3502
+ return this.getMessages();
3503
+ }
3504
+ replaceTranscript(messages) {
3505
+ this.setMessages(messages);
3506
+ }
3507
+ getConfig() {
3508
+ return this.cloneOptions(this.options);
3509
+ }
3510
+ setConfig(config) {
3511
+ const context = prepareAgentContext(config.context);
3512
+ this.options = {
3513
+ ...config,
3514
+ context,
3515
+ ...config.extensions ? { extensions: config.extensions.slice() } : {}
3516
+ };
3517
+ this.state.sessionId = config.sessionId;
3518
+ this.state.context = prepareAgentContext(context, this.extensionRunner);
3519
+ this.state.model = config.model;
3520
+ this.state.tools = this.state.context.tools ?? [];
3521
+ }
3522
+ updateConfig(updater) {
3523
+ this.setConfig(updater(this.getConfig()));
3524
+ }
3525
+ setSessionId(sessionId) {
3526
+ this.options = {
3527
+ ...this.options,
3528
+ sessionId
3529
+ };
3530
+ this.state.sessionId = sessionId;
3531
+ }
3532
+ getSessionId() {
3533
+ return this.state.sessionId;
3534
+ }
3535
+ setModel(model) {
3536
+ this.options = {
3537
+ ...this.options,
3538
+ model
3539
+ };
3540
+ this.state.model = model;
3541
+ }
3542
+ setTools(tools) {
3543
+ this.setContext({
3544
+ ...this.options.context,
3545
+ tools: tools.slice()
3546
+ });
3547
+ }
3548
+ setSkills(skills) {
3549
+ this.setContext({
3550
+ ...this.options.context,
3551
+ skills: skills.slice()
3552
+ });
3553
+ }
3554
+ setPhases(phases) {
3555
+ this.setContext({
3556
+ ...this.options.context,
3557
+ phases: clonePhaseRegistry(phases)
3558
+ });
3559
+ }
3560
+ setCwd(cwd) {
3561
+ this.options = {
3562
+ ...this.options,
3563
+ cwd
3564
+ };
3565
+ }
3566
+ setStream(stream) {
3567
+ this.options = {
3568
+ ...this.options,
3569
+ stream
3570
+ };
3571
+ }
3572
+ getModel() {
3573
+ return this.state.model;
3574
+ }
3575
+ getTools() {
3576
+ return this.state.context.tools.slice();
3577
+ }
3578
+ getSkills() {
3579
+ return this.state.context.skills.slice();
3580
+ }
3581
+ getPhases() {
3582
+ return this.state.context.phases ? clonePhaseRegistry(this.state.context.phases) : void 0;
3583
+ }
3584
+ getCwd() {
3585
+ return this.options.cwd;
3586
+ }
3448
3587
  emitToListeners(event) {
3449
3588
  for (const listener of this.listeners) {
3450
3589
  try {
@@ -3590,7 +3729,7 @@ var Agent = class {
3590
3729
  const resolved = this.resolveRunConfig(config);
3591
3730
  const extensions = resolved.extensions ?? [];
3592
3731
  await this.loadExtensions(extensions, resolved.cwd);
3593
- const runContext = cloneAgentContext(resolved.context, this.extensionRunner);
3732
+ const runContext = prepareAgentContext(resolved.context, this.extensionRunner);
3594
3733
  const sessionId = resolved.sessionId ?? this.state.sessionId;
3595
3734
  this.options = resolved;
3596
3735
  if (sessionId) {
@@ -3641,11 +3780,11 @@ var Agent = class {
3641
3780
  onModelTranscript: resolved.onModelTranscript
3642
3781
  });
3643
3782
  const nextContext = {
3644
- ...cloneAgentContext(resolved.context),
3783
+ ...prepareAgentContext(resolved.context),
3645
3784
  messages: snapshotMessages(result.messages)
3646
3785
  };
3647
3786
  this.state.sessionId = result.sessionId;
3648
- this.state.context = cloneAgentContext(nextContext, this.extensionRunner);
3787
+ this.state.context = prepareAgentContext(nextContext, this.extensionRunner);
3649
3788
  this.state.currentResult = result;
3650
3789
  this.options = {
3651
3790
  ...resolved,
@@ -3689,7 +3828,7 @@ ${additionalInstructions}` : content;
3689
3828
  */
3690
3829
  async phase(name) {
3691
3830
  await this.loadExtensions(this.options.extensions ?? [], this.options.cwd);
3692
- const registry = cloneAgentContext(this.options.context, this.extensionRunner).phases?.phases;
3831
+ const registry = prepareAgentContext(this.options.context, this.extensionRunner).phases?.phases;
3693
3832
  const phase = registry?.get(name) ?? [...registry?.values() ?? []].find((candidate) => candidate.name === name);
3694
3833
  return phase ? readPhaseContent(phase) : "";
3695
3834
  }
@@ -3705,7 +3844,7 @@ ${additionalInstructions}` : content;
3705
3844
  ...this.options,
3706
3845
  ...config
3707
3846
  };
3708
- const context = cloneAgentContext(config?.context ?? this.createContextSnapshot());
3847
+ const context = prepareAgentContext(config?.context ?? this.createContextSnapshot());
3709
3848
  return {
3710
3849
  ...merged,
3711
3850
  context,
@@ -3713,10 +3852,23 @@ ${additionalInstructions}` : content;
3713
3852
  };
3714
3853
  }
3715
3854
  createContextSnapshot() {
3716
- return cloneAgentContext(this.options.context);
3855
+ return prepareAgentContext(this.options.context);
3856
+ }
3857
+ cloneOptions(options) {
3858
+ return {
3859
+ ...options,
3860
+ context: prepareAgentContext(options.context),
3861
+ ...options.extensions ? { extensions: options.extensions.slice() } : {}
3862
+ };
3717
3863
  }
3718
3864
  };
3719
- function cloneAgentContext(context, extensionRunner) {
3865
+ function clonePhaseRegistry(registry) {
3866
+ return {
3867
+ phases: new Map(registry.phases),
3868
+ entryPhaseId: registry.entryPhaseId
3869
+ };
3870
+ }
3871
+ function prepareAgentContext(context, extensionRunner) {
3720
3872
  const phases = /* @__PURE__ */ new Map();
3721
3873
  phases.set(DEFAULT_PHASE_ID, createDefaultPhase());
3722
3874
  for (const [id, phase] of context.phases?.phases ?? []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",