@witqq/agent-sdk 0.4.0 → 0.5.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/README.md CHANGED
@@ -225,6 +225,7 @@ for await (const event of agent.streamWithContext(messages)) {
225
225
  | `permission_response` | `toolName`, `decision` | Permission decision made |
226
226
  | `ask_user` | `request` | User input requested |
227
227
  | `ask_user_response` | `answer` | User response received |
228
+ | `session_info` | `sessionId`, `transcriptPath?`, `backend` | CLI session metadata (streaming only) |
228
229
  | `usage_update` | `promptTokens`, `completionTokens`, `model?`, `backend?` | Token usage with metadata |
229
230
  | `heartbeat` | — | Keepalive signal during long operations |
230
231
  | `error` | `error`, `recoverable` | Error during execution |
@@ -283,6 +284,15 @@ agent.dispose(); // destroys the persistent session
283
284
 
284
285
  In persistent mode, if a session encounters an error, it is automatically cleared and recreated on the next call. The `sessionId` property exposes the CLI session ID for logging or external storage.
285
286
 
287
+ ### Interrupting Running Operations
288
+
289
+ Call `interrupt()` to gracefully stop a running operation. For CLI backends, this calls the SDK's interrupt/abort method on the active session:
290
+
291
+ ```typescript
292
+ // In another context (e.g., timeout handler)
293
+ await agent.interrupt();
294
+ ```
295
+
286
296
  Default (`"per-call"`): each call creates and destroys a fresh session. Multi-message context is passed via prompt augmentation through `runWithContext()`/`streamWithContext()`.
287
297
 
288
298
  API-based backends (Vercel AI) ignore `sessionMode` — they are stateless by design.
@@ -300,6 +310,7 @@ const service = createCopilotService({
300
310
  workingDirectory: process.cwd(),
301
311
  githubToken: "ghp_...", // optional, alternative to useLoggedInUser
302
312
  cliArgs: ["--allow-all"], // extra CLI flags for the subprocess
313
+ env: { PATH: "/custom/bin" }, // custom env vars for subprocess
303
314
  });
304
315
  ```
305
316
 
@@ -328,11 +339,14 @@ const service = createClaudeService({
328
339
  cliPath: "/path/to/claude", // optional custom CLI path
329
340
  workingDirectory: process.cwd(),
330
341
  maxTurns: 10,
342
+ env: { CLAUDE_CONFIG_DIR: "/custom/config" }, // custom env vars for subprocess
331
343
  });
332
344
  ```
333
345
 
334
346
  `supervisor.onAskUser` is not supported by the Claude backend; a warning is emitted if set.
335
347
 
348
+ When `supervisor.onPermission` is set, the Claude backend automatically sets `permissionMode: "default"` so the CLI invokes the callback instead of using built-in rules.
349
+
336
350
  ### Vercel AI (OpenRouter / OpenAI-compatible)
337
351
 
338
352
  ```typescript
@@ -132,6 +132,10 @@ var BaseAgent = class {
132
132
  this.abortController.abort();
133
133
  }
134
134
  }
135
+ /** Default interrupt — falls back to abort(). Backends may override with graceful shutdown. */
136
+ async interrupt() {
137
+ this.abort();
138
+ }
135
139
  getState() {
136
140
  return this.state;
137
141
  }
@@ -539,6 +543,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
539
543
  case "stream_event": {
540
544
  const event = msg.event;
541
545
  if (!event) return null;
546
+ if (event.type === "content_block_delta" && event.index !== void 0 && thinkingBlockIndices?.has(event.index)) {
547
+ const thinkingText = String(event.delta?.thinking ?? event.delta?.text ?? "");
548
+ if (thinkingText) {
549
+ return { type: "thinking_delta", text: thinkingText };
550
+ }
551
+ return null;
552
+ }
542
553
  if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
543
554
  return { type: "text_delta", text: event.delta.text };
544
555
  }
@@ -589,6 +600,7 @@ var ClaudeAgent = class extends BaseAgent {
589
600
  canUseTool;
590
601
  isPersistent;
591
602
  _sessionId;
603
+ activeQuery = null;
592
604
  constructor(config, options) {
593
605
  super(config);
594
606
  this.options = options;
@@ -604,10 +616,25 @@ var ClaudeAgent = class extends BaseAgent {
604
616
  get sessionId() {
605
617
  return this._sessionId;
606
618
  }
619
+ async interrupt() {
620
+ try {
621
+ if (this.activeQuery) {
622
+ await this.activeQuery.interrupt();
623
+ }
624
+ } catch {
625
+ } finally {
626
+ this.abort();
627
+ }
628
+ }
607
629
  /** Clear persistent session state after an error so next call starts fresh */
608
630
  clearPersistentSession() {
609
631
  this._sessionId = void 0;
610
632
  }
633
+ emitSessionInfo(sessionId) {
634
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
635
+ const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
636
+ return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
637
+ }
611
638
  buildQueryOptions(signal) {
612
639
  const ac = new AbortController();
613
640
  signal.addEventListener("abort", () => ac.abort(), { once: true });
@@ -627,8 +654,15 @@ var ClaudeAgent = class extends BaseAgent {
627
654
  if (this.config.systemPrompt) {
628
655
  opts.systemPrompt = this.config.systemPrompt;
629
656
  }
630
- if (this.options.oauthToken) {
631
- opts.env = { ...process.env, CLAUDE_CODE_OAUTH_TOKEN: this.options.oauthToken };
657
+ if (this.options.oauthToken || this.options.env) {
658
+ opts.env = {
659
+ ...process.env,
660
+ ...this.options.env ?? {},
661
+ ...this.options.oauthToken ? { CLAUDE_CODE_OAUTH_TOKEN: this.options.oauthToken } : {}
662
+ };
663
+ }
664
+ if (opts.canUseTool && !opts.permissionMode) {
665
+ opts.permissionMode = "default";
632
666
  }
633
667
  return opts;
634
668
  }
@@ -653,6 +687,7 @@ var ClaudeAgent = class extends BaseAgent {
653
687
  const toolResultCapture = /* @__PURE__ */ new Map();
654
688
  opts = await this.buildMcpConfig(opts, toolResultCapture);
655
689
  const q = sdk.query({ prompt, options: opts });
690
+ this.activeQuery = q;
656
691
  const toolCalls = [];
657
692
  let output = null;
658
693
  let usage;
@@ -702,6 +737,8 @@ var ClaudeAgent = class extends BaseAgent {
702
737
  if (this.isPersistent) this.clearPersistentSession();
703
738
  if (signal.aborted) throw new AbortError();
704
739
  throw e;
740
+ } finally {
741
+ this.activeQuery = null;
705
742
  }
706
743
  return {
707
744
  output,
@@ -728,6 +765,7 @@ var ClaudeAgent = class extends BaseAgent {
728
765
  schema: jsonSchema
729
766
  };
730
767
  const q = sdk.query({ prompt, options: opts });
768
+ this.activeQuery = q;
731
769
  const toolCalls = [];
732
770
  let output = null;
733
771
  let structuredOutput;
@@ -769,6 +807,8 @@ var ClaudeAgent = class extends BaseAgent {
769
807
  if (this.isPersistent) this.clearPersistentSession();
770
808
  if (signal.aborted) throw new AbortError();
771
809
  throw e;
810
+ } finally {
811
+ this.activeQuery = null;
772
812
  }
773
813
  return {
774
814
  output,
@@ -790,6 +830,7 @@ var ClaudeAgent = class extends BaseAgent {
790
830
  let opts = this.buildQueryOptions(signal);
791
831
  opts = await this.buildMcpConfig(opts);
792
832
  const q = sdk.query({ prompt, options: opts });
833
+ this.activeQuery = q;
793
834
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
794
835
  const toolCallTracker = new ClaudeToolCallTracker();
795
836
  try {
@@ -805,8 +846,11 @@ var ClaudeAgent = class extends BaseAgent {
805
846
  }
806
847
  if (msg.type === "result" && msg.subtype === "success") {
807
848
  const r = msg;
808
- if (this.isPersistent && r.session_id) {
809
- this._sessionId = r.session_id;
849
+ if (r.session_id) {
850
+ if (this.isPersistent) {
851
+ this._sessionId = r.session_id;
852
+ }
853
+ yield this.emitSessionInfo(r.session_id);
810
854
  }
811
855
  yield { type: "done", finalOutput: r.result };
812
856
  }
@@ -815,6 +859,8 @@ var ClaudeAgent = class extends BaseAgent {
815
859
  if (this.isPersistent) this.clearPersistentSession();
816
860
  if (signal.aborted) throw new AbortError();
817
861
  throw e;
862
+ } finally {
863
+ this.activeQuery = null;
818
864
  }
819
865
  }
820
866
  dispose() {