@mjasnikovs/pi-task 0.17.26 → 0.17.27

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.
@@ -27,6 +27,14 @@ export interface AskSpec {
27
27
  question: string;
28
28
  /** Primary recommended option, prefilled in the local TUI. */
29
29
  recommended?: string;
30
+ /**
31
+ * Placeholder for the local TUI text input when there is NO recommended
32
+ * option. Local-only: the remote card must not see it, or it would render as
33
+ * an acceptable recommendation ("✓ Accept" answering with placeholder text).
34
+ * Used by the steer prompt, whose hint ("type guidance, or leave empty…") is
35
+ * instructional copy, not an answer.
36
+ */
37
+ localPlaceholder?: string;
30
38
  /** Secondary recommended option shown as a second button on the remote. */
31
39
  recommended2?: string;
32
40
  /** Whether the browser card shows a Skip button (answers with empty string). */
@@ -102,7 +102,9 @@ export class SessionUI {
102
102
  signal
103
103
  });
104
104
  }
105
- return this.ctx.ui.input(spec.localTitle, spec.recommended, { signal });
105
+ return this.ctx.ui.input(spec.localTitle, spec.recommended ?? spec.localPlaceholder, {
106
+ signal
107
+ });
106
108
  }
107
109
  }
108
110
  export function publishNotify(message, level) {
@@ -92,8 +92,9 @@ export interface RunSingleTaskOptions {
92
92
  * Ask the user for a steering message after they interrupt (ESC) the
93
93
  * implementation turn. Return text to continue the same task as another turn,
94
94
  * or undefined/empty to pause the run. Only consulted with
95
- * waitForImplementation. Defaults to a ctx.ui.input prompt; injectable so the
96
- * steer loop is testable without a real dialog.
95
+ * waitForImplementation. Defaults to a bridged SessionUI.ask (local TUI input
96
+ * raced against a remote browser card); injectable so the steer loop is
97
+ * testable without a real dialog.
97
98
  */
98
99
  promptSteer?: (ctx: ExtensionCommandContext) => Promise<string | undefined>;
99
100
  /**
@@ -25,7 +25,7 @@ import { readTextFile } from '../shared/fs-text.js';
25
25
  import { allocateTaskId, ensureTasksDir, readSection, readTaskFile, setTaskSection, taskFilePath, tasksDir, updateTaskFrontMatter, writeTaskFile } from './task-io.js';
26
26
  import { startWidget } from './widget.js';
27
27
  import { armImplWidget, disarmImplWidget, setupImplWidget } from './impl-widget.js';
28
- import { publishViewer, publishNotify, publishLifecycleNotice, registerBridgeCommand, getBridge } from '../remote/bridge.js';
28
+ import { publishViewer, publishNotify, publishLifecycleNotice, registerBridgeCommand, getBridge, SessionUI } from '../remote/bridge.js';
29
29
  import { pushNotify } from '../remote/push.js';
30
30
  import { getConfig } from '../config/config.js';
31
31
  import { buildGateDeps } from './gate-deps.js';
@@ -342,6 +342,11 @@ export class TaskRunner {
342
342
  /** Dialog copy for the post-interrupt steering prompt. */
343
343
  const STEER_TITLE = 'Paused — steer the model';
344
344
  const STEER_PLACEHOLDER = 'Type guidance to continue this task, or leave empty to pause';
345
+ /** Remote-card copy for the same prompt. The browser has no placeholder ghost
346
+ * text, so the pause affordance must be spelled out in the question itself
347
+ * (Skip = empty answer = pause, same as an empty local submit). */
348
+ const STEER_QUESTION = 'Paused — the implementation was interrupted.\n'
349
+ + 'Type guidance to continue this task, or Skip to pause the run.';
345
350
  /**
346
351
  * True when the most recent assistant turn ended because the user interrupted it
347
352
  * (pressed ESC). pi records a user abort as stopReason "aborted" on the assistant
@@ -472,7 +477,19 @@ export async function resumeAcrossCompactions(ctx) {
472
477
  * should pause; false when the implementation completed (steered or not).
473
478
  */
474
479
  async function steerUntilDone(ctx, promptSteer) {
475
- const ask = promptSteer ?? (c => c.ui.input(STEER_TITLE, STEER_PLACEHOLDER));
480
+ // Fan the prompt out through the bridge (local TUI input + remote browser
481
+ // card, first answer wins) instead of a raw ctx.ui.input: an interrupt can
482
+ // come from the remote Stop button just as well as a terminal ESC, and a
483
+ // terminal-only dialog leaves the remote viewer staring at a silently
484
+ // paused run. Remote Skip returns '' → same pause path as an empty local
485
+ // submit.
486
+ const ask = promptSteer
487
+ ?? (c => new SessionUI(c).ask({
488
+ localTitle: STEER_TITLE,
489
+ localPlaceholder: STEER_PLACEHOLDER,
490
+ question: STEER_QUESTION,
491
+ allowSkip: true
492
+ }));
476
493
  while (wasInterrupted(ctx)) {
477
494
  const steer = await ask(ctx);
478
495
  if (steer === undefined || steer.trim().length === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.17.26",
3
+ "version": "0.17.27",
4
4
  "description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",