@oh-my-pi/pi-agent-core 4.2.3 → 4.3.0

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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/agent.ts +64 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-agent-core",
3
- "version": "4.2.3",
3
+ "version": "4.3.0",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -13,8 +13,8 @@
13
13
  "test": "vitest --run"
14
14
  },
15
15
  "dependencies": {
16
- "@oh-my-pi/pi-ai": "4.2.3",
17
- "@oh-my-pi/pi-tui": "4.2.3"
16
+ "@oh-my-pi/pi-ai": "4.3.0",
17
+ "@oh-my-pi/pi-tui": "4.3.0"
18
18
  },
19
19
  "keywords": [
20
20
  "ai",
package/src/agent.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import {
7
+ type CursorExecHandlers,
8
+ type CursorToolResultHandler,
7
9
  getModel,
8
10
  type ImageContent,
9
11
  type Message,
@@ -11,6 +13,7 @@ import {
11
13
  streamSimple,
12
14
  type TextContent,
13
15
  type ThinkingBudgets,
16
+ type ToolResultMessage,
14
17
  } from "@oh-my-pi/pi-ai";
15
18
  import { agentLoop, agentLoopContinue } from "./agent-loop";
16
19
  import type {
@@ -91,6 +94,16 @@ export interface AgentOptions {
91
94
  * Use for late-bound UI or session state access.
92
95
  */
93
96
  getToolContext?: () => AgentToolContext | undefined;
97
+
98
+ /**
99
+ * Cursor exec handlers for local tool execution.
100
+ */
101
+ cursorExecHandlers?: CursorExecHandlers;
102
+
103
+ /**
104
+ * Cursor tool result callback for exec tool responses.
105
+ */
106
+ cursorOnToolResult?: CursorToolResultHandler;
94
107
  }
95
108
 
96
109
  export class Agent {
@@ -120,6 +133,8 @@ export class Agent {
120
133
  private _thinkingBudgets?: ThinkingBudgets;
121
134
  public getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
122
135
  private getToolContext?: () => AgentToolContext | undefined;
136
+ private cursorExecHandlers?: CursorExecHandlers;
137
+ private cursorOnToolResult?: CursorToolResultHandler;
123
138
  private runningPrompt?: Promise<void>;
124
139
  private resolveRunningPrompt?: () => void;
125
140
 
@@ -135,6 +150,8 @@ export class Agent {
135
150
  this._thinkingBudgets = opts.thinkingBudgets;
136
151
  this.getApiKey = opts.getApiKey;
137
152
  this.getToolContext = opts.getToolContext;
153
+ this.cursorExecHandlers = opts.cursorExecHandlers;
154
+ this.cursorOnToolResult = opts.cursorOnToolResult;
138
155
  }
139
156
 
140
157
  /**
@@ -175,6 +192,33 @@ export class Agent {
175
192
  return () => this.listeners.delete(fn);
176
193
  }
177
194
 
195
+ emitExternalEvent(event: AgentEvent) {
196
+ switch (event.type) {
197
+ case "message_start":
198
+ case "message_update":
199
+ this._state.streamMessage = event.message;
200
+ break;
201
+ case "message_end":
202
+ this._state.streamMessage = null;
203
+ this.appendMessage(event.message);
204
+ break;
205
+ case "tool_execution_start": {
206
+ const pending = new Set(this._state.pendingToolCalls);
207
+ pending.add(event.toolCallId);
208
+ this._state.pendingToolCalls = pending;
209
+ break;
210
+ }
211
+ case "tool_execution_end": {
212
+ const pending = new Set(this._state.pendingToolCalls);
213
+ pending.delete(event.toolCallId);
214
+ this._state.pendingToolCalls = pending;
215
+ break;
216
+ }
217
+ }
218
+
219
+ this.emit(event);
220
+ }
221
+
178
222
  // State mutators
179
223
  setSystemPrompt(v: string) {
180
224
  this._state.systemPrompt = v;
@@ -382,6 +426,24 @@ export class Agent {
382
426
  tools: this._state.tools,
383
427
  };
384
428
 
429
+ const cursorOnToolResult =
430
+ this.cursorExecHandlers || this.cursorOnToolResult
431
+ ? async (message: ToolResultMessage) => {
432
+ let finalMessage = message;
433
+ if (this.cursorOnToolResult) {
434
+ try {
435
+ const updated = await this.cursorOnToolResult(message);
436
+ if (updated) {
437
+ finalMessage = updated;
438
+ }
439
+ } catch {}
440
+ }
441
+ this.emitExternalEvent({ type: "message_start", message: finalMessage });
442
+ this.emitExternalEvent({ type: "message_end", message: finalMessage });
443
+ return finalMessage;
444
+ }
445
+ : undefined;
446
+
385
447
  const config: AgentLoopConfig = {
386
448
  model,
387
449
  reasoning,
@@ -392,6 +454,8 @@ export class Agent {
392
454
  transformContext: this.transformContext,
393
455
  getApiKey: this.getApiKey,
394
456
  getToolContext: this.getToolContext,
457
+ cursorExecHandlers: this.cursorExecHandlers,
458
+ cursorOnToolResult,
395
459
  getSteeringMessages: async () => {
396
460
  if (this.steeringMode === "one-at-a-time") {
397
461
  if (this.steeringQueue.length > 0) {