@mariozechner/pi-coding-agent 0.32.1 → 0.32.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +11 -2
  3. package/dist/core/agent-session.d.ts +17 -3
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +68 -14
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/slash-commands.d.ts +4 -1
  8. package/dist/core/slash-commands.d.ts.map +1 -1
  9. package/dist/core/slash-commands.js +12 -4
  10. package/dist/core/slash-commands.js.map +1 -1
  11. package/dist/main.d.ts.map +1 -1
  12. package/dist/main.js +1 -1
  13. package/dist/main.js.map +1 -1
  14. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  15. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  16. package/dist/modes/interactive/components/tool-execution.js +48 -2
  17. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  18. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  19. package/dist/modes/interactive/interactive-mode.js +13 -19
  20. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  21. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  22. package/dist/modes/rpc/rpc-mode.js +3 -1
  23. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  24. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  25. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  26. package/dist/modes/rpc/rpc-types.js.map +1 -1
  27. package/dist/utils/image-convert.d.ts +9 -0
  28. package/dist/utils/image-convert.d.ts.map +1 -0
  29. package/dist/utils/image-convert.js +24 -0
  30. package/dist/utils/image-convert.js.map +1 -0
  31. package/dist/utils/image-resize.d.ts +8 -1
  32. package/dist/utils/image-resize.d.ts.map +1 -1
  33. package/dist/utils/image-resize.js +104 -49
  34. package/dist/utils/image-resize.js.map +1 -1
  35. package/docs/rpc.md +62 -14
  36. package/docs/sdk.md +40 -0
  37. package/package.json +4 -4
@@ -317,19 +317,17 @@ export class AgentSession {
317
317
  // =========================================================================
318
318
  /**
319
319
  * Send a prompt to the agent.
320
- * - Validates model and API key before sending
321
- * - Handles hook commands (registered via pi.registerCommand)
320
+ * - Handles hook commands (registered via pi.registerCommand) immediately, even during streaming
322
321
  * - Expands file-based slash commands by default
323
- * @throws Error if no model selected or no API key available
322
+ * - During streaming, queues via steer() or followUp() based on streamingBehavior option
323
+ * - Validates model and API key before sending (when not streaming)
324
+ * @throws Error if streaming and no streamingBehavior specified
325
+ * @throws Error if no model selected or no API key available (when not streaming)
324
326
  */
325
327
  async prompt(text, options) {
326
- if (this.isStreaming) {
327
- throw new Error("Agent is already processing. Use steer() or followUp() to queue messages during streaming.");
328
- }
329
- // Flush any pending bash messages before the new prompt
330
- this._flushPendingBashMessages();
331
328
  const expandCommands = options?.expandSlashCommands ?? true;
332
- // Handle hook commands first (if enabled and text is a slash command)
329
+ // Handle hook commands first (execute immediately, even during streaming)
330
+ // Hook commands manage their own LLM interaction via pi.sendMessage()
333
331
  if (expandCommands && text.startsWith("/")) {
334
332
  const handled = await this._tryExecuteHookCommand(text);
335
333
  if (handled) {
@@ -337,6 +335,23 @@ export class AgentSession {
337
335
  return;
338
336
  }
339
337
  }
338
+ // Expand file-based slash commands if requested
339
+ const expandedText = expandCommands ? expandSlashCommand(text, [...this._fileCommands]) : text;
340
+ // If streaming, queue via steer() or followUp() based on option
341
+ if (this.isStreaming) {
342
+ if (!options?.streamingBehavior) {
343
+ throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
344
+ }
345
+ if (options.streamingBehavior === "followUp") {
346
+ await this._queueFollowUp(expandedText);
347
+ }
348
+ else {
349
+ await this._queueSteer(expandedText);
350
+ }
351
+ return;
352
+ }
353
+ // Flush any pending bash messages before the new prompt
354
+ this._flushPendingBashMessages();
340
355
  // Validate model
341
356
  if (!this.model) {
342
357
  throw new Error("No model selected.\n\n" +
@@ -354,8 +369,6 @@ export class AgentSession {
354
369
  if (lastAssistant) {
355
370
  await this._checkCompaction(lastAssistant, false);
356
371
  }
357
- // Expand file-based slash commands if requested
358
- const expandedText = expandCommands ? expandSlashCommand(text, [...this._fileCommands]) : text;
359
372
  // Build messages array (hook message if any, then user message)
360
373
  const messages = [];
361
374
  // Add user message
@@ -417,8 +430,37 @@ export class AgentSession {
417
430
  /**
418
431
  * Queue a steering message to interrupt the agent mid-run.
419
432
  * Delivered after current tool execution, skips remaining tools.
433
+ * Expands file-based slash commands. Errors on hook commands.
434
+ * @throws Error if text is a hook command
420
435
  */
421
436
  async steer(text) {
437
+ // Check for hook commands (cannot be queued)
438
+ if (text.startsWith("/")) {
439
+ this._throwIfHookCommand(text);
440
+ }
441
+ // Expand file-based slash commands
442
+ const expandedText = expandSlashCommand(text, [...this._fileCommands]);
443
+ await this._queueSteer(expandedText);
444
+ }
445
+ /**
446
+ * Queue a follow-up message to be processed after the agent finishes.
447
+ * Delivered only when agent has no more tool calls or steering messages.
448
+ * Expands file-based slash commands. Errors on hook commands.
449
+ * @throws Error if text is a hook command
450
+ */
451
+ async followUp(text) {
452
+ // Check for hook commands (cannot be queued)
453
+ if (text.startsWith("/")) {
454
+ this._throwIfHookCommand(text);
455
+ }
456
+ // Expand file-based slash commands
457
+ const expandedText = expandSlashCommand(text, [...this._fileCommands]);
458
+ await this._queueFollowUp(expandedText);
459
+ }
460
+ /**
461
+ * Internal: Queue a steering message (already expanded, no hook command check).
462
+ */
463
+ async _queueSteer(text) {
422
464
  this._steeringMessages.push(text);
423
465
  this.agent.steer({
424
466
  role: "user",
@@ -427,10 +469,9 @@ export class AgentSession {
427
469
  });
428
470
  }
429
471
  /**
430
- * Queue a follow-up message to be processed after the agent finishes.
431
- * Delivered only when agent has no more tool calls or steering messages.
472
+ * Internal: Queue a follow-up message (already expanded, no hook command check).
432
473
  */
433
- async followUp(text) {
474
+ async _queueFollowUp(text) {
434
475
  this._followUpMessages.push(text);
435
476
  this.agent.followUp({
436
477
  role: "user",
@@ -438,6 +479,19 @@ export class AgentSession {
438
479
  timestamp: Date.now(),
439
480
  });
440
481
  }
482
+ /**
483
+ * Throw an error if the text is a hook command.
484
+ */
485
+ _throwIfHookCommand(text) {
486
+ if (!this._hookRunner)
487
+ return;
488
+ const spaceIndex = text.indexOf(" ");
489
+ const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
490
+ const command = this._hookRunner.getCommand(commandName);
491
+ if (command) {
492
+ throw new Error(`Hook command "/${commandName}" cannot be queued. Use prompt() or execute the command when not streaming.`);
493
+ }
494
+ }
441
495
  /**
442
496
  * Send a hook message to the session. Creates a CustomMessageEntry.
443
497
  *