@ordewell/core 0.4.18 → 0.4.20

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.
@@ -247,6 +247,14 @@ interface PluginRunnerDef {
247
247
  env?: Record<string, string>;
248
248
  /** When true, the runner requires a PTY. HeadlessRunner wraps with `script` to allocate one. */
249
249
  requiresTty?: boolean;
250
+ /**
251
+ * True when this runner's interactive prompt flag (e.g. opencode's
252
+ * `--prompt`) only pre-fills its TUI's composer instead of running it, so a
253
+ * surface driving that TUI unattended must send an explicit Enter after
254
+ * launch. Only takes effect while the resolved invocation is interactive —
255
+ * see `RunnerInvocation.submitPromptKey`.
256
+ */
257
+ submitPromptKey?: boolean;
250
258
  }
251
259
  interface PluginFeatures {
252
260
  modelSelection: boolean;
@@ -390,6 +398,8 @@ interface RunnerInvocation {
390
398
  args: string[];
391
399
  env: Record<string, string>;
392
400
  promptInArgs: boolean;
401
+ /** True when a surface running this invocation on a real TTY must send an explicit Enter once the process starts. */
402
+ submitPromptKey: boolean;
393
403
  }
394
404
  /**
395
405
  * Persistent storage seam for plugin manifests. The RunnerRegistry delegates
@@ -458,6 +468,14 @@ interface ITerminalSession {
458
468
  kill(): void;
459
469
  getOutput(): string;
460
470
  write(text: string): void;
471
+ /**
472
+ * True when the session runs the agent as a raw-mode TUI (a real PTY for the
473
+ * VS Code terminal, a tmux window). Such a surface submits an input line on
474
+ * the Enter keystroke (`\r`), so a synchronized resume token terminated with
475
+ * `\n` only types the line and never sends it. A line-oriented piped session
476
+ * (`defaultInteractive = false`) leaves this false and accepts `\n`.
477
+ */
478
+ readonly interactive?: boolean;
461
479
  /**
462
480
  * Optional transport-level control channel: PTY resize requests for a session
463
481
  * whose runner renders a TUI. Absent on transports without a resizable PTY
@@ -247,6 +247,14 @@ interface PluginRunnerDef {
247
247
  env?: Record<string, string>;
248
248
  /** When true, the runner requires a PTY. HeadlessRunner wraps with `script` to allocate one. */
249
249
  requiresTty?: boolean;
250
+ /**
251
+ * True when this runner's interactive prompt flag (e.g. opencode's
252
+ * `--prompt`) only pre-fills its TUI's composer instead of running it, so a
253
+ * surface driving that TUI unattended must send an explicit Enter after
254
+ * launch. Only takes effect while the resolved invocation is interactive —
255
+ * see `RunnerInvocation.submitPromptKey`.
256
+ */
257
+ submitPromptKey?: boolean;
250
258
  }
251
259
  interface PluginFeatures {
252
260
  modelSelection: boolean;
@@ -390,6 +398,8 @@ interface RunnerInvocation {
390
398
  args: string[];
391
399
  env: Record<string, string>;
392
400
  promptInArgs: boolean;
401
+ /** True when a surface running this invocation on a real TTY must send an explicit Enter once the process starts. */
402
+ submitPromptKey: boolean;
393
403
  }
394
404
  /**
395
405
  * Persistent storage seam for plugin manifests. The RunnerRegistry delegates
@@ -458,6 +468,14 @@ interface ITerminalSession {
458
468
  kill(): void;
459
469
  getOutput(): string;
460
470
  write(text: string): void;
471
+ /**
472
+ * True when the session runs the agent as a raw-mode TUI (a real PTY for the
473
+ * VS Code terminal, a tmux window). Such a surface submits an input line on
474
+ * the Enter keystroke (`\r`), so a synchronized resume token terminated with
475
+ * `\n` only types the line and never sends it. A line-oriented piped session
476
+ * (`defaultInteractive = false`) leaves this false and accepts `\n`.
477
+ */
478
+ readonly interactive?: boolean;
461
479
  /**
462
480
  * Optional transport-level control channel: PTY resize requests for a session
463
481
  * whose runner renders a TUI. Absent on transports without a resizable PTY
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, a as DiscoveredModel, T as Task, d as ResearchProgress, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, b as PlanState, v as Verdict } from './Task-4rDGr8Su.mjs';
2
2
  export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, e as ResearchStep, f as ResearchStepOutcome, S as StreamEvent, i as StreamStepEvent, j as StreamThinkingEvent, k as TaskMode, l as TaskModelAssignment, m as TaskOutputSummary, o as TaskStatus, p as TaskType, q as TaskWithParent, r as ThinkingBlock, U as UserPromptEntry, s as UserStep, V as ValidationCheck, t as ValidationContext, u as ValidationResult, w as VerificationCheck, x as addTaskToPlan, y as createEmptyPlan, z as createTask, B as emptyWarnings, E as flattenTasks, F as flattenTasksWithParents, G as migrateLegacyPlan, H as migratePlanState, I as migrateTask, J as removeTaskFromPlan, K as renumberTasks, N as updateTaskInPlan, O as validateModifiedPlan, W as warningsText } from './Task-4rDGr8Su.mjs';
3
- import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-E3-6QNUS.mjs';
4
- export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-E3-6QNUS.mjs';
3
+ import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-CuNYBnqK.mjs';
4
+ export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-CuNYBnqK.mjs';
5
5
  import { I as IApproval, d as ApprovalRequest, a as ApprovalMode } from './ApprovalPolicy-BVhGdECT.mjs';
6
6
  export { A as ApprovalKind, b as ApprovalPolicy, c as ApprovalPolicyOptions, e as ApprovalSource, D as DENY_ALL } from './ApprovalPolicy-BVhGdECT.mjs';
7
7
  import { R as RunnerModeInfo } from './ModeResolver-mtIWrLXn.mjs';
@@ -936,10 +936,11 @@ interface HeadlessRunnerDeps {
936
936
  }
937
937
  declare class HeadlessSession extends AbstractTerminalSession {
938
938
  private spawnImpl;
939
+ readonly interactive: boolean;
939
940
  private process;
940
941
  private outputBuffer;
941
942
  private controlStream;
942
- constructor(id: string, taskId: string, spawnImpl: SpawnFn);
943
+ constructor(id: string, taskId: string, spawnImpl: SpawnFn, interactive?: boolean);
943
944
  get isStarted(): boolean;
944
945
  start(launch: LaunchPlan, cwd: string, resolvedPath: string, env?: Record<string, string>, options?: {
945
946
  controlChannel?: boolean;
@@ -958,6 +959,8 @@ interface PreparedLaunch {
958
959
  env: Record<string, string>;
959
960
  /** True when the invocation was wrapped in `script` to allocate a PTY. */
960
961
  pty: boolean;
962
+ /** True when the started process needs an explicit Enter sent to submit its pre-filled prompt. */
963
+ submitPromptKey: boolean;
961
964
  }
962
965
  declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
963
966
  private spawnImpl;
@@ -2868,6 +2871,14 @@ declare class VerdictEngine {
2868
2871
  private touchIdle;
2869
2872
  /** Tear down idle tracking for a task; broadcasts the null transition if it was idle. */
2870
2873
  private clearIdle;
2874
+ /**
2875
+ * Submit a synchronized resume token to the paused session. An interactive
2876
+ * TUI only accepts the Enter keystroke (`\r`) — a `\n` types the token into
2877
+ * its composer without sending it, leaving the agent paused until a human
2878
+ * presses Enter. A line-oriented piped session has no composer; it reads a
2879
+ * `\n`-terminated line, and the leading newline flushes a partial line.
2880
+ */
2881
+ private resumeToken;
2871
2882
  approveCheckpoint(taskId: string): void;
2872
2883
  rejectCheckpoint(taskId: string, reason: string): void;
2873
2884
  /**
@@ -3166,6 +3177,8 @@ declare class TmuxSession extends AbstractTerminalSession {
3166
3177
  private pollIntervalMs;
3167
3178
  private logPath;
3168
3179
  private socket;
3180
+ /** A tmux window is a real TTY running the agent's TUI, so it submits on Enter. */
3181
+ readonly interactive = true;
3169
3182
  private outputBuffer;
3170
3183
  private offset;
3171
3184
  private timer;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, a as DiscoveredModel, T as Task, d as ResearchProgress, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, b as PlanState, v as Verdict } from './Task-4rDGr8Su.js';
2
2
  export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, e as ResearchStep, f as ResearchStepOutcome, S as StreamEvent, i as StreamStepEvent, j as StreamThinkingEvent, k as TaskMode, l as TaskModelAssignment, m as TaskOutputSummary, o as TaskStatus, p as TaskType, q as TaskWithParent, r as ThinkingBlock, U as UserPromptEntry, s as UserStep, V as ValidationCheck, t as ValidationContext, u as ValidationResult, w as VerificationCheck, x as addTaskToPlan, y as createEmptyPlan, z as createTask, B as emptyWarnings, E as flattenTasks, F as flattenTasksWithParents, G as migrateLegacyPlan, H as migratePlanState, I as migrateTask, J as removeTaskFromPlan, K as renumberTasks, N as updateTaskInPlan, O as validateModifiedPlan, W as warningsText } from './Task-4rDGr8Su.js';
3
- import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-CbLHKXT5.js';
4
- export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-CbLHKXT5.js';
3
+ import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-Da1gIAFX.js';
4
+ export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-Da1gIAFX.js';
5
5
  import { I as IApproval, d as ApprovalRequest, a as ApprovalMode } from './ApprovalPolicy-BVhGdECT.js';
6
6
  export { A as ApprovalKind, b as ApprovalPolicy, c as ApprovalPolicyOptions, e as ApprovalSource, D as DENY_ALL } from './ApprovalPolicy-BVhGdECT.js';
7
7
  import { R as RunnerModeInfo } from './ModeResolver-Ob-ihQbI.js';
@@ -936,10 +936,11 @@ interface HeadlessRunnerDeps {
936
936
  }
937
937
  declare class HeadlessSession extends AbstractTerminalSession {
938
938
  private spawnImpl;
939
+ readonly interactive: boolean;
939
940
  private process;
940
941
  private outputBuffer;
941
942
  private controlStream;
942
- constructor(id: string, taskId: string, spawnImpl: SpawnFn);
943
+ constructor(id: string, taskId: string, spawnImpl: SpawnFn, interactive?: boolean);
943
944
  get isStarted(): boolean;
944
945
  start(launch: LaunchPlan, cwd: string, resolvedPath: string, env?: Record<string, string>, options?: {
945
946
  controlChannel?: boolean;
@@ -958,6 +959,8 @@ interface PreparedLaunch {
958
959
  env: Record<string, string>;
959
960
  /** True when the invocation was wrapped in `script` to allocate a PTY. */
960
961
  pty: boolean;
962
+ /** True when the started process needs an explicit Enter sent to submit its pre-filled prompt. */
963
+ submitPromptKey: boolean;
961
964
  }
962
965
  declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
963
966
  private spawnImpl;
@@ -2868,6 +2871,14 @@ declare class VerdictEngine {
2868
2871
  private touchIdle;
2869
2872
  /** Tear down idle tracking for a task; broadcasts the null transition if it was idle. */
2870
2873
  private clearIdle;
2874
+ /**
2875
+ * Submit a synchronized resume token to the paused session. An interactive
2876
+ * TUI only accepts the Enter keystroke (`\r`) — a `\n` types the token into
2877
+ * its composer without sending it, leaving the agent paused until a human
2878
+ * presses Enter. A line-oriented piped session has no composer; it reads a
2879
+ * `\n`-terminated line, and the leading newline flushes a partial line.
2880
+ */
2881
+ private resumeToken;
2871
2882
  approveCheckpoint(taskId: string): void;
2872
2883
  rejectCheckpoint(taskId: string, reason: string): void;
2873
2884
  /**
@@ -3166,6 +3177,8 @@ declare class TmuxSession extends AbstractTerminalSession {
3166
3177
  private pollIntervalMs;
3167
3178
  private logPath;
3168
3179
  private socket;
3180
+ /** A tmux window is a real TTY running the agent's TUI, so it submits on Enter. */
3181
+ readonly interactive = true;
3169
3182
  private outputBuffer;
3170
3183
  private offset;
3171
3184
  private timer;
package/dist/index.js CHANGED
@@ -8114,19 +8114,29 @@ var VerdictEngine = class {
8114
8114
  for (const l of this.idleListeners) l(taskId, null);
8115
8115
  }
8116
8116
  }
8117
+ /**
8118
+ * Submit a synchronized resume token to the paused session. An interactive
8119
+ * TUI only accepts the Enter keystroke (`\r`) — a `\n` types the token into
8120
+ * its composer without sending it, leaving the agent paused until a human
8121
+ * presses Enter. A line-oriented piped session has no composer; it reads a
8122
+ * `\n`-terminated line, and the leading newline flushes a partial line.
8123
+ */
8124
+ resumeToken(session, line) {
8125
+ return session.interactive ? `${line}\r` : `
8126
+ ${line}
8127
+ `;
8128
+ }
8117
8129
  approveCheckpoint(taskId) {
8118
8130
  const session = this.pausedSessions.get(taskId);
8119
8131
  if (session) {
8120
- session.write("\nORDEWELL_CONTINUE\n");
8132
+ session.write(this.resumeToken(session, "ORDEWELL_CONTINUE"));
8121
8133
  this.pausedSessions.delete(taskId);
8122
8134
  }
8123
8135
  }
8124
8136
  rejectCheckpoint(taskId, reason) {
8125
8137
  const session = this.pausedSessions.get(taskId);
8126
8138
  if (session) {
8127
- session.write(`
8128
- ORDEWELL_REJECT: ${reason}
8129
- `);
8139
+ session.write(this.resumeToken(session, `ORDEWELL_REJECT: ${reason}`));
8130
8140
  this.pausedSessions.delete(taskId);
8131
8141
  }
8132
8142
  }
@@ -14392,7 +14402,8 @@ function resolveArgs(manifest, ctx) {
14392
14402
  command: manifest.runner.command,
14393
14403
  args,
14394
14404
  env,
14395
- promptInArgs: manifest.runner.promptInArgs
14405
+ promptInArgs: manifest.runner.promptInArgs,
14406
+ submitPromptKey: manifest.runner.submitPromptKey === true && isInteractive(ctx)
14396
14407
  };
14397
14408
  }
14398
14409
  function parseBlockOpen(raw) {
@@ -14633,11 +14644,13 @@ function defaultHasScriptCmd() {
14633
14644
  }
14634
14645
  }
14635
14646
  var HeadlessSession = class extends AbstractTerminalSession {
14636
- constructor(id, taskId, spawnImpl) {
14647
+ constructor(id, taskId, spawnImpl, interactive = false) {
14637
14648
  super(id, taskId);
14638
14649
  this.spawnImpl = spawnImpl;
14650
+ this.interactive = interactive;
14639
14651
  }
14640
14652
  spawnImpl;
14653
+ interactive;
14641
14654
  process = null;
14642
14655
  outputBuffer = "";
14643
14656
  controlStream = null;
@@ -14719,7 +14732,7 @@ var HeadlessRunner = class extends AbstractRunner {
14719
14732
  this.launchDeps = { resolvePath: this.resolvePath, ...deps.launchDeps };
14720
14733
  }
14721
14734
  createSession(id, taskId) {
14722
- return new HeadlessSession(id, taskId, this.spawnImpl);
14735
+ return new HeadlessSession(id, taskId, this.spawnImpl, this.defaultInteractive);
14723
14736
  }
14724
14737
  /** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
14725
14738
  async prepareLaunch(opts, ptyOptions) {
@@ -14744,7 +14757,8 @@ var HeadlessRunner = class extends AbstractRunner {
14744
14757
  launch: await planDirectLaunch(command, args, this.launchDeps),
14745
14758
  resolvedPath,
14746
14759
  env: invocation.env,
14747
- pty
14760
+ pty,
14761
+ submitPromptKey: invocation.submitPromptKey
14748
14762
  };
14749
14763
  }
14750
14764
  async spawn(opts) {
@@ -14754,6 +14768,7 @@ var HeadlessRunner = class extends AbstractRunner {
14754
14768
  const modeStr = opts.mode === "plan" ? "plan" : "build";
14755
14769
  console.error(`[headless] Starting ${opts.runner} [${modeStr}] (${opts.modelId || "default"}) for task ${opts.taskId.slice(0, 8)}${prepared.pty ? " (PTY)" : ""}`);
14756
14770
  session.start(prepared.launch, opts.cwd, prepared.resolvedPath, prepared.env);
14771
+ if (prepared.submitPromptKey) session.write("\r");
14757
14772
  this.registerSession(id, session);
14758
14773
  return session;
14759
14774
  }
@@ -14836,6 +14851,8 @@ var TmuxSession = class extends AbstractTerminalSession {
14836
14851
  pollIntervalMs;
14837
14852
  logPath;
14838
14853
  socket;
14854
+ /** A tmux window is a real TTY running the agent's TUI, so it submits on Enter. */
14855
+ interactive = true;
14839
14856
  outputBuffer = "";
14840
14857
  offset = 0;
14841
14858
  timer = null;
@@ -15053,6 +15070,7 @@ var TmuxRunner = class extends AbstractRunner {
15053
15070
  );
15054
15071
  invocation.env = { ...invocation.env, PATH: await this.resolvePath() };
15055
15072
  await session.start(invocation.command, invocation.args, opts.cwd, invocation.env);
15073
+ if (invocation.submitPromptKey) session.write("\r");
15056
15074
  this.registerSession(id, session);
15057
15075
  return session;
15058
15076
  }
@@ -15521,7 +15539,12 @@ var OPENCODE_MANIFEST = {
15521
15539
  OPENCODE_CONFIG_CONTENT: "{{if interactiveVariant}}{{opencodeVariantConfig}}{{/if}}"
15522
15540
  },
15523
15541
  promptInArgs: true,
15524
- requiresTty: true
15542
+ requiresTty: true,
15543
+ // `--prompt` only pre-fills the TUI's composer; it does not submit it.
15544
+ // (`opencode run` — the headless subcommand — has no such gap: its
15545
+ // message is a positional arg that runs immediately.) An unattended
15546
+ // surface driving the TUI must press Enter itself.
15547
+ submitPromptKey: true
15525
15548
  },
15526
15549
  features: {
15527
15550
  modelSelection: true,