@pi-oxide/pi-host-web 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -5,11 +5,11 @@
5
5
  "Irving Ou <irving@pi-oxide.dev>"
6
6
  ],
7
7
  "description": "WASM host for pi-core. Browser FileSystem Access API, fetch(), JS event loop.",
8
- "version": "0.3.0",
8
+ "version": "0.3.1",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "git+https://github.com/pi-oxide/pi-oxide.git"
12
+ "url": "https://github.com/pi-oxide/pi-oxide"
13
13
  },
14
14
  "files": [
15
15
  "pi_host_web_bg.wasm",
package/pi_host_web.d.ts CHANGED
@@ -389,8 +389,12 @@ export type ToolOutputStream = "stdout" | "stderr" | "status";
389
389
  export type WaitMode = "steering" | "follow_up" | "any";
390
390
 
391
391
 
392
+ export function abort(handle: number): StepResult;
393
+
392
394
  export function appendSessionEntry(handle: number, entry: SessionEntry): EmptyResult;
393
395
 
396
+ export function continueTurn(handle: number): StepResult;
397
+
394
398
  export function createAgent(options: AgentOptions): CreateAgentResult;
395
399
 
396
400
  export function destroyAgent(handle: number): EmptyResult;
@@ -439,7 +443,9 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
439
443
 
440
444
  export interface InitOutput {
441
445
  readonly memory: WebAssembly.Memory;
446
+ readonly abort: (a: number) => any;
442
447
  readonly appendSessionEntry: (a: number, b: any) => any;
448
+ readonly continueTurn: (a: number) => any;
443
449
  readonly createAgent: (a: any) => any;
444
450
  readonly destroyAgent: (a: number) => any;
445
451
  readonly estimateTokens: (a: any) => any;
package/pi_host_web.js CHANGED
@@ -1,5 +1,14 @@
1
1
  /* @ts-self-types="./pi_host_web.d.ts" */
2
2
 
3
+ /**
4
+ * @param {number} handle
5
+ * @returns {StepResult}
6
+ */
7
+ export function abort(handle) {
8
+ const ret = wasm.abort(handle);
9
+ return ret;
10
+ }
11
+
3
12
  /**
4
13
  * @param {number} handle
5
14
  * @param {SessionEntry} entry
@@ -10,6 +19,15 @@ export function appendSessionEntry(handle, entry) {
10
19
  return ret;
11
20
  }
12
21
 
22
+ /**
23
+ * @param {number} handle
24
+ * @returns {StepResult}
25
+ */
26
+ export function continueTurn(handle) {
27
+ const ret = wasm.continueTurn(handle);
28
+ return ret;
29
+ }
30
+
13
31
  /**
14
32
  * @param {AgentOptions} options
15
33
  * @returns {CreateAgentResult}
Binary file
package/sdk/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export * from "../pi_host_web.js";
8
8
 
9
9
  export declare function ensureInit(): Promise<void>;
10
10
 
11
+ export declare function continueTurn(handle: number): StepResult;
12
+
11
13
  export declare function toolResult(
12
14
  text: string,
13
15
  opts?: { terminate?: boolean }
@@ -24,7 +26,7 @@ export interface LlmStream {
24
26
  }
25
27
 
26
28
  export interface LlmProvider {
27
- call(context: LlmContext): Promise<LlmStream> | LlmStream;
29
+ call(context: LlmContext, signal?: AbortSignal): Promise<LlmStream> | LlmStream;
28
30
  }
29
31
 
30
32
  export type ToolMap = Record<
@@ -36,11 +38,13 @@ export interface AgentRunConfig {
36
38
  llm: LlmProvider;
37
39
  tools: ToolMap;
38
40
  onEvent?: (event: AgentEvent) => void;
41
+ signal?: AbortSignal;
39
42
  }
40
43
 
41
44
  export declare class Agent {
42
45
  static create(options: AgentOptions): Promise<Agent>;
43
46
  run(promptText: string, config: AgentRunConfig): Promise<AgentAction>;
47
+ stop(): void;
44
48
  reset(): void;
45
49
  state(): AgentState;
46
50
  getSessionState(): SessionState;
package/sdk/index.js CHANGED
@@ -9,7 +9,9 @@
9
9
  */
10
10
 
11
11
  import {
12
+ abort,
12
13
  createAgent,
14
+ continueTurn,
13
15
  destroyAgent,
14
16
  feedLlmChunk,
15
17
  followUp,
@@ -109,28 +111,41 @@ export class Agent {
109
111
  * @param {LlmProvider} config.llm
110
112
  * @param {Record<string, (call: ToolCall) => Promise<ToolResult> | ToolResult>} config.tools
111
113
  * @param {(event: AgentEvent) => void} [config.onEvent]
114
+ * @param {AbortSignal} [config.signal] — abort to stop mid-stream or mid-tool
112
115
  * @returns {Promise<AgentAction>} terminal action (finished or wait_for_input)
113
116
  */
114
117
  async run(promptText, config) {
118
+ const signal = config.signal;
119
+ const checkAbort = () => {
120
+ if (signal?.aborted) {
121
+ this.stop();
122
+ throw new HostError("user_aborted", "Turn stopped by user");
123
+ }
124
+ };
125
+
115
126
  let step = unwrap(prompt(this.#handle, { text: promptText }));
116
127
  for (const event of step.events) {
117
128
  config.onEvent?.(event);
118
129
  }
119
130
 
120
131
  while (true) {
132
+ checkAbort();
121
133
  let actions = step.actions ?? [];
122
134
  if (actions.length === 0) {
123
135
  return { type: "finished", messages: [] };
124
136
  }
125
137
 
126
138
  for (const action of actions) {
139
+ checkAbort();
127
140
  switch (action.type) {
128
141
  case "stream_llm": {
129
- const stream = await config.llm.call(action.context);
142
+ const stream = await config.llm.call(action.context, signal);
130
143
  for await (const chunk of stream.chunks) {
144
+ checkAbort();
131
145
  const ev = unwrap(feedLlmChunk(this.#handle, chunk));
132
146
  for (const e of ev.events) config.onEvent?.(e);
133
147
  }
148
+ checkAbort();
134
149
  const result = await stream.result;
135
150
  step = unwrap(onLlmDone(this.#handle, result));
136
151
  for (const e of step.events) config.onEvent?.(e);
@@ -139,6 +154,7 @@ export class Agent {
139
154
 
140
155
  case "execute_tools": {
141
156
  for (const call of action.calls) {
157
+ checkAbort();
142
158
  const started = unwrap(onToolStarted(this.#handle, call.id));
143
159
  for (const e of started.events) config.onEvent?.(e);
144
160
 
@@ -152,6 +168,10 @@ export class Agent {
152
168
  step = unwrap(onToolDone(this.#handle, call.id, result));
153
169
  for (const e of step.events) config.onEvent?.(e);
154
170
  }
171
+ if ((step.actions ?? []).length === 0) {
172
+ step = unwrap(continueTurn(this.#handle));
173
+ for (const e of step.events) config.onEvent?.(e);
174
+ }
155
175
  break;
156
176
  }
157
177
 
@@ -178,6 +198,15 @@ export class Agent {
178
198
  }
179
199
  }
180
200
 
201
+ /** Abort a running turn mid-stream or mid-tool. */
202
+ stop() {
203
+ try {
204
+ unwrap(abort(this.#handle));
205
+ } catch (e) {
206
+ if (e.code !== "wrong_phase") throw e;
207
+ }
208
+ }
209
+
181
210
  /** Reset agent state (clear messages, return to idle). */
182
211
  reset() {
183
212
  unwrap(reset(this.#handle));