@automatalabs/pi-acp 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -69,19 +69,17 @@ Load replay is the active linear branch and excludes branch topology and compact
69
69
  ### Native session steering
70
70
 
71
71
  Pi advertises the `_session/steering` extension at top-level initialize metadata as
72
- `_meta: { steering: { supported: true } }`, with codex-shaped semantics. While a Pi prompt turn is
72
+ `_meta: { steering: { supported: true } }`, with strict active-turn-only semantics. While a Pi prompt turn is
73
73
  live, the server converts the supplied ACP content with the same text/image conversion as
74
74
  `session/prompt` and calls native `AgentSession.steer(text, images)`, returning
75
75
  `{ outcome: "injected" }`; the original `session/prompt` still owns all output, usage, and
76
76
  settlement. When the session is idle — or a steer arrives just as the turn settles, before pi could
77
- consume it — the content runs as a fire-and-forget turn instead and the call returns
78
- `{ outcome: "startedNewTurn" }`: never an error, never dropped, never silently prepended to the next
79
- prompt. That turn occupies the single turn slot (a concurrent `session/prompt` is `session_busy`)
80
- and streams through normal `session/update`s with no owned `PromptResponse`. A steer racing an
81
- in-progress cancel returns `{ outcome: "failed" }` cancellation wins, and pi's queued steering is
82
- cleared synchronously before aborting so queued content never restarts generation after
83
- cancellation. Unexpected internal failures also resolve as `{ outcome: "failed" }` rather than a
84
- JSON-RPC error.
77
+ consume it — the server removes any unconsumed native queue entry and returns
78
+ `{ outcome: "promptRequired", reason: "noRunningTurn" }`. It never calls `prompt()`, `followUp()`, or
79
+ another turn-start helper from steering, so steering cannot create hidden work or prepend input to a
80
+ later prompt. Cancellation races use the same `promptRequired/noRunningTurn` response when no active
81
+ turn accepted the instruction. Unexpected internal failures reject the JSON-RPC request through the
82
+ normal structured error path.
85
83
 
86
84
  ### Loaded-session turn-terminal state (`_session/loaded_turn`)
87
85
 
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAElB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA0H3C,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAM;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,gBAAgB,CAAS;gBAErB,IAAI,EAAE,SAAS;IAI3B,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmB3D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IAI9D,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,IAAI;YAOE,UAAU;YAkCV,SAAS;IAqLvB,OAAO,CAAC,YAAY;IAKpB,UAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;IA6B1D,OAAO,CAAC,QAAQ;IAgDhB,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;IAI5D,aAAa,CAAC,OAAO,EAAE,mBAAmB,CAAC,oBAAoB,CAAC;;;;IAIhE,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;;IA8EtD,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;;;;;;;;IAqC9D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IA0BpE,OAAO,CAAC,WAAW;IAOnB,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,6BAA6B,CAAC;;;IAU3E,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,aAAa,CAAC;IAIlD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAW/E;yEACqE;IACrE,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,GAAG;QACrE,MAAM,EAAE,gBAAgB,CAAC;KAC1B;IAID,MAAM,CAAC,OAAO,EAAE,wBAAwB,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;YAIxD,eAAe;IAwB7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAWV,iBAAiB;CA4BhC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAElB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA0H3C,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAM;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,gBAAgB,CAAS;gBAErB,IAAI,EAAE,SAAS;IAI3B,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmB3D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IAI9D,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,IAAI;YAOE,UAAU;YAkCV,SAAS;IAqLvB,OAAO,CAAC,YAAY;IAKpB,UAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;IA6B1D,OAAO,CAAC,QAAQ;IAgDhB,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;IAI5D,aAAa,CAAC,OAAO,EAAE,mBAAmB,CAAC,oBAAoB,CAAC;;;;IAIhE,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;;IA8EtD,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;;;;;;;;IAqC9D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IA0BpE,OAAO,CAAC,WAAW;IAOnB,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,6BAA6B,CAAC;;;IAU3E,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,aAAa,CAAC;IAIlD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQ/E;yEACqE;IACrE,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,GAAG;QACrE,MAAM,EAAE,gBAAgB,CAAC;KAC1B;IAID,MAAM,CAAC,OAAO,EAAE,wBAAwB,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;YAIxD,eAAe;IAwB7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAWV,iBAAiB;CA4BhC"}
package/dist/agent.js CHANGED
@@ -646,10 +646,7 @@ export class PiAcpAgent {
646
646
  .catch((error) => {
647
647
  if (isRequestError(error))
648
648
  throw error;
649
- // Codex-shaped catch-all: an unexpected internal failure resolves as a failed
650
- // steering outcome; only typed adapter errors surface as JSON-RPC errors.
651
- console.error("pi-acp steering failed:", error);
652
- return { outcome: "failed" };
649
+ throw unexpectedError(error);
653
650
  });
654
651
  }
655
652
  /** `_session/loaded_turn/query` (see `src/loaded-turn.ts`): the loaded
package/dist/session.d.ts CHANGED
@@ -40,7 +40,6 @@ export declare class PiSession {
40
40
  private unsubscribe;
41
41
  private activeTurn;
42
42
  private steerChain;
43
- private readonly pendingSteerImages;
44
43
  private configReserved;
45
44
  private cleanupDirty;
46
45
  private cleanupGeneration;
@@ -94,33 +93,15 @@ export declare class PiSession {
94
93
  prompt(params: PromptRequest, requestSignal: AbortSignal): Promise<PromptResponse>;
95
94
  /** Open the session's single turn slot and run one pi prompt through the full turn
96
95
  * machinery (turn boundary, cancellation, settlement, usage). Admission is the caller's
97
- * job: prompt() rejects while busy, and the steering paths start a turn only when the
98
- * slot is free. Steer-started turns pass no requestSignal (cancel() still aborts them)
99
- * and use preflightResult as their turn-committed acceptance signal. */
96
+ * job: prompt() rejects while busy. Steering never calls this method. */
100
97
  private startTurn;
101
- /** `_session/steering`, codex-shaped: inject into the live turn when there is one,
102
- * otherwise start a fire-and-forget turn with the content and answer `startedNewTurn`.
103
- * Requests are serialized per session (pi rejects concurrent runs), so a second steer
104
- * behind an idle one injects into the turn the first one started. */
98
+ /** `_session/steering`: inject only into the live turn. Requests are serialized per
99
+ * session so concurrent steering calls cannot cross a turn boundary. */
105
100
  steer(params: SteeringRequest): Promise<SteeringResponse>;
106
101
  private performSteer;
107
- /** Pull one undelivered steering message back out of pi's queues. Returns false when pi
108
- * already consumed it (or a settlement drain already redispatched it). clearQueue is
109
- * all-or-nothing, so any other queued texts are re-enqueued untouched (text-only: pi
110
- * surfaces only the text mirror of messages it holds). */
111
- private recoverSteer;
112
- /** Idle steer: start a normal turn with the content so it occupies the single turn slot
113
- * (session/prompt during it is legitimately busy; cancel/close still work) and resolve
114
- * as soon as the turn is committed — pi's preflightResult is the acceptance signal, the
115
- * analogue of codex's onTurnStarted. The turn itself is fire-and-forget: nothing owns
116
- * its PromptResponse and its output streams through the usual session/update path. */
117
- private startTurnFromSteer;
118
- /** Settlement-time orphan recovery. pi's steering/follow-up queues are polled only from
119
- * inside a run, so a message still queued when the run settles would silently prepend
120
- * itself to the NEXT session/prompt. Both reference adapters run late content instead of
121
- * leaking or dropping it (codex falls through to a new turn; claude's streaming input
122
- * absorbs it as background generation), so redispatch it as a fire-and-forget turn. */
123
- private redispatchOrphanedSteering;
102
+ /** Remove native Pi queue residue at a turn boundary. Nothing left by steering may be
103
+ * consumed by a later public prompt. Unexpected cleanup failures stay exceptional. */
104
+ private discardOrphanedSteering;
124
105
  cancel(): void;
125
106
  childCleanupFailure(): void;
126
107
  dispose(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
package/dist/session.js CHANGED
@@ -31,7 +31,6 @@ export class PiSession {
31
31
  unsubscribe;
32
32
  activeTurn;
33
33
  steerChain = Promise.resolve();
34
- pendingSteerImages = new Map();
35
34
  configReserved = false;
36
35
  cleanupDirty = false;
37
36
  cleanupGeneration;
@@ -320,7 +319,6 @@ export class PiSession {
320
319
  if (turn && !turn.controller.signal.aborted)
321
320
  turn.controller.abort();
322
321
  let clearQueuePi = Promise.resolve();
323
- this.pendingSteerImages.clear();
324
322
  try {
325
323
  this.pi.clearQueue();
326
324
  }
@@ -437,9 +435,8 @@ export class PiSession {
437
435
  return;
438
436
  const terminal = terminalAssistant(messages);
439
437
  const stopReason = stopReasonFor(terminal, turn.controller.signal.aborted);
438
+ this.discardOrphanedSteering();
440
439
  this.finish(turn, { response: { stopReason, usage: promptUsage(messages) } });
441
- if (!turn.controller.signal.aborted)
442
- this.redispatchOrphanedSteering();
443
440
  }
444
441
  catch (error) {
445
442
  if (this.pumpFailure !== undefined)
@@ -455,8 +452,8 @@ export class PiSession {
455
452
  turn.diagnosticOpen = false;
456
453
  try {
457
454
  await this.drain();
455
+ this.discardOrphanedSteering();
458
456
  this.finish(turn, { error: classifyPreflight(error) });
459
- this.redispatchOrphanedSteering();
460
457
  }
461
458
  catch {
462
459
  this.notificationFailure();
@@ -486,9 +483,7 @@ export class PiSession {
486
483
  }
487
484
  /** Open the session's single turn slot and run one pi prompt through the full turn
488
485
  * machinery (turn boundary, cancellation, settlement, usage). Admission is the caller's
489
- * job: prompt() rejects while busy, and the steering paths start a turn only when the
490
- * slot is free. Steer-started turns pass no requestSignal (cancel() still aborts them)
491
- * and use preflightResult as their turn-committed acceptance signal. */
486
+ * job: prompt() rejects while busy. Steering never calls this method. */
492
487
  startTurn(converted, opts) {
493
488
  const text = converted.text;
494
489
  let startMessageIndex;
@@ -554,10 +549,7 @@ export class PiSession {
554
549
  }
555
550
  let piPromise;
556
551
  try {
557
- piPromise = this.pi.prompt(text, {
558
- images: converted.images,
559
- ...(opts.preflightResult ? { preflightResult: opts.preflightResult } : {}),
560
- });
552
+ piPromise = this.pi.prompt(text, { images: converted.images });
561
553
  }
562
554
  catch (error) {
563
555
  await this.handlePiRejected(turn, error);
@@ -567,149 +559,46 @@ export class PiSession {
567
559
  })());
568
560
  return result;
569
561
  }
570
- /** `_session/steering`, codex-shaped: inject into the live turn when there is one,
571
- * otherwise start a fire-and-forget turn with the content and answer `startedNewTurn`.
572
- * Requests are serialized per session (pi rejects concurrent runs), so a second steer
573
- * behind an idle one injects into the turn the first one started. */
562
+ /** `_session/steering`: inject only into the live turn. Requests are serialized per
563
+ * session so concurrent steering calls cannot cross a turn boundary. */
574
564
  async steer(params) {
575
- const converted = convertPromptContent(params.prompt);
576
- const run = this.steerChain.then(() => this.performSteer(converted));
565
+ const run = this.steerChain.then(() => this.performSteer(params));
577
566
  this.steerChain = run.then(() => undefined, () => undefined);
578
567
  return run;
579
568
  }
580
- async performSteer(converted) {
581
- for (;;) {
582
- if (this.closing || this.disposed)
583
- throw adapterError("session_terminated");
584
- const turn = this.activeTurn;
585
- if (!turn) {
586
- if (this.busy)
587
- throw adapterError("session_busy");
588
- return this.startTurnFromSteer(converted);
589
- }
590
- if (turn.completed) {
591
- await turn.settlement;
592
- continue;
593
- }
594
- if (turn.controller.signal.aborted) {
595
- // Cancellation wins: cleanupTurn clears pi's queues so a steer racing a cancel
596
- // must not restart the generation the user just stopped. The steer did not take.
597
- return { outcome: "failed" };
598
- }
599
- // A committed-but-not-yet-streaming turn is steerable too: pi's run loop polls the
600
- // steering queue before its first LLM call, so the message joins the imminent turn.
601
- this.pendingSteerImages.set(converted.text, converted.images);
602
- await this.pi.steer(converted.text, converted.images);
603
- if (this.activeTurn === turn && !turn.completed && !turn.controller.signal.aborted) {
604
- return { outcome: "injected" };
605
- }
606
- if (turn.controller.signal.aborted) {
607
- this.recoverSteer(converted.text);
608
- return { outcome: "failed" };
609
- }
610
- // The run settled underneath the enqueue. pi's queues are polled only from inside a
611
- // run, so an undelivered message would silently prepend itself to the next
612
- // session/prompt; recover it and place it into a live turn or a new one instead.
613
- if (!this.recoverSteer(converted.text))
614
- return { outcome: "injected" };
615
- }
616
- }
617
- /** Pull one undelivered steering message back out of pi's queues. Returns false when pi
618
- * already consumed it (or a settlement drain already redispatched it). clearQueue is
619
- * all-or-nothing, so any other queued texts are re-enqueued untouched (text-only: pi
620
- * surfaces only the text mirror of messages it holds). */
621
- recoverSteer(text) {
622
- try {
623
- if (!this.pi.getSteeringMessages().includes(text)) {
624
- this.pendingSteerImages.delete(text);
625
- return false;
626
- }
627
- const cleared = this.pi.clearQueue();
628
- const steering = [...cleared.steering];
629
- const index = steering.indexOf(text);
630
- if (index !== -1)
631
- steering.splice(index, 1);
632
- for (const other of steering)
633
- void this.pi.steer(other).catch(() => undefined);
634
- for (const other of cleared.followUp)
635
- void this.pi.followUp(other).catch(() => undefined);
636
- this.pendingSteerImages.delete(text);
637
- return index !== -1;
638
- }
639
- catch (error) {
640
- console.error("pi-acp steering recovery error:", error);
641
- return false;
642
- }
643
- }
644
- /** Idle steer: start a normal turn with the content so it occupies the single turn slot
645
- * (session/prompt during it is legitimately busy; cancel/close still work) and resolve
646
- * as soon as the turn is committed — pi's preflightResult is the acceptance signal, the
647
- * analogue of codex's onTurnStarted. The turn itself is fire-and-forget: nothing owns
648
- * its PromptResponse and its output streams through the usual session/update path. */
649
- async startTurnFromSteer(converted) {
650
- let commit;
651
- const committed = new Promise((resolve) => {
652
- commit = (result) => resolve({ kind: result });
653
- });
654
- const done = this.startTurn(converted, {
655
- preflightResult: (success) => commit(success ? "committed" : "preflight-failed"),
656
- });
657
- const doneTagged = done.then((response) => ({ kind: "settled", response }), (error) => ({ kind: "rejected", error }));
658
- const raced = await Promise.race([committed, doneTagged]);
659
- switch (raced.kind) {
660
- case "committed":
661
- done.catch((error) => console.error("pi-acp steer-started turn failed:", error));
662
- return { outcome: "startedNewTurn" };
663
- case "settled":
664
- // The prompt finished without a generation (e.g. extension-handled input). A
665
- // cancel that beat the turn start means the steer never took.
666
- return raced.response.stopReason === "cancelled"
667
- ? { outcome: "failed" }
668
- : { outcome: "startedNewTurn" };
669
- case "preflight-failed": {
670
- // Surface the same mapped preflight rejection a prompt() caller would see.
671
- const settled = await doneTagged;
672
- if (settled.kind === "rejected")
673
- throw settled.error;
674
- return { outcome: "startedNewTurn" };
675
- }
676
- case "rejected":
677
- throw raced.error;
569
+ async performSteer(params) {
570
+ if (this.closing || this.disposed)
571
+ throw adapterError("session_terminated");
572
+ const turn = this.activeTurn;
573
+ if (!turn || turn.completed || turn.controller.signal.aborted) {
574
+ return { outcome: "promptRequired", reason: "noRunningTurn" };
678
575
  }
679
- }
680
- /** Settlement-time orphan recovery. pi's steering/follow-up queues are polled only from
681
- * inside a run, so a message still queued when the run settles would silently prepend
682
- * itself to the NEXT session/prompt. Both reference adapters run late content instead of
683
- * leaking or dropping it (codex falls through to a new turn; claude's streaming input
684
- * absorbs it as background generation), so redispatch it as a fire-and-forget turn. */
685
- redispatchOrphanedSteering() {
686
- if (this.closing || this.disposed || this.busy)
687
- return;
688
- let texts;
689
- let images;
576
+ const converted = convertPromptContent(params.prompt);
577
+ // A committed-but-not-yet-streaming turn is steerable too: pi's run loop polls the
578
+ // steering queue before its first LLM call, so the message joins the imminent turn.
690
579
  try {
691
- if (this.pi.pendingMessageCount === 0) {
692
- this.pendingSteerImages.clear();
693
- return;
694
- }
695
- const cleared = this.pi.clearQueue();
696
- texts = [...cleared.steering, ...cleared.followUp].filter((text) => text.length > 0);
697
- images = cleared.steering.flatMap((text) => this.pendingSteerImages.get(text) ?? []);
580
+ await this.pi.steer(converted.text, converted.images);
698
581
  }
699
582
  catch (error) {
700
- console.error("pi-acp orphaned steering recovery error:", error);
701
- return;
702
- }
703
- this.pendingSteerImages.clear();
704
- if (texts.length === 0)
705
- return;
706
- try {
707
- const done = this.startTurn({ text: texts.join("\n\n"), images }, {});
708
- done.catch((error) => console.error("pi-acp orphaned steering turn failed:", error));
583
+ if (this.activeTurn !== turn || turn.completed || turn.controller.signal.aborted) {
584
+ this.discardOrphanedSteering();
585
+ }
586
+ throw error;
709
587
  }
710
- catch (error) {
711
- console.error("pi-acp orphaned steering turn failed:", error);
588
+ if (this.activeTurn === turn && !turn.completed && !turn.controller.signal.aborted) {
589
+ return { outcome: "injected" };
712
590
  }
591
+ // The run settled (or cancellation won) underneath the native enqueue. Pi only polls
592
+ // this queue from an active run, so leaving any residue would prepend hidden input to a
593
+ // later session/prompt. Remove it and require the caller to issue an explicit prompt.
594
+ this.discardOrphanedSteering();
595
+ return { outcome: "promptRequired", reason: "noRunningTurn" };
596
+ }
597
+ /** Remove native Pi queue residue at a turn boundary. Nothing left by steering may be
598
+ * consumed by a later public prompt. Unexpected cleanup failures stay exceptional. */
599
+ discardOrphanedSteering() {
600
+ if (this.pi.pendingMessageCount > 0)
601
+ this.pi.clearQueue();
713
602
  }
714
603
  cancel() {
715
604
  if (this.activeTurn)
@@ -1,14 +1,16 @@
1
1
  import { type ContentBlock, type PromptRequest } from "@agentclientprotocol/sdk";
2
2
  export declare const SESSION_STEERING_METHOD: "_session/steering";
3
- export type SteeringOutcome = "injected" | "startedNewTurn" | "failed";
4
3
  export interface SteeringRequest {
5
4
  sessionId: string;
6
5
  prompt: ContentBlock[];
7
6
  _meta?: PromptRequest["_meta"];
8
7
  }
9
- export interface SteeringResponse {
10
- outcome: SteeringOutcome;
11
- }
8
+ export type SteeringResponse = {
9
+ outcome: "injected";
10
+ } | {
11
+ outcome: "promptRequired";
12
+ reason: "noRunningTurn";
13
+ };
12
14
  /** Runtime parser used by the ACP SDK's custom-request overload. */
13
15
  export declare const steeringRequestParser: {
14
16
  parse(value: unknown): SteeringRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../src/steering.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,gBAAgB,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,CAAC;CAC1B;AA2BD,oEAAoE;AACpE,eAAO,MAAM,qBAAqB;iBACnB,OAAO,GAAG,eAAe;CAgBvC,CAAC"}
1
+ {"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../src/steering.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GACvB;IAAE,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC;AA2B3D,oEAAoE;AACpE,eAAO,MAAM,qBAAqB;iBACnB,OAAO,GAAG,eAAe;CAgBvC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automatalabs/pi-acp",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "engines": {
6
6
  "node": ">=22.19.0"
@@ -31,13 +31,13 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@agentclientprotocol/sdk": "1.4.0",
34
- "@earendil-works/pi-ai": "0.84.2",
35
- "@earendil-works/pi-coding-agent": "0.84.2",
34
+ "@earendil-works/pi-ai": "0.84.3",
35
+ "@earendil-works/pi-coding-agent": "0.84.3",
36
36
  "@modelcontextprotocol/sdk": "1.29.0",
37
37
  "typebox": "1.3.2"
38
38
  },
39
39
  "devDependencies": {
40
- "@earendil-works/pi-agent-core": "0.84.2"
40
+ "@earendil-works/pi-agent-core": "0.84.3"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "tsc -b",