@oh-my-pi/pi-coding-agent 15.4.1 → 15.4.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.4.2] - 2026-05-26
6
+
7
+ ### Fixed
8
+
9
+ - Fixed plan-mode subagents being unable to terminate because `yield` was registered but missing from the active tool set when `requireYieldTool` was combined with an explicit `toolNames` list ([#1408](https://github.com/can1357/oh-my-pi/issues/1408))
10
+
5
11
  ## [15.4.1] - 2026-05-26
6
12
 
7
13
  ### Breaking Changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "15.4.1",
4
+ "version": "15.4.2",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -47,12 +47,12 @@
47
47
  "@agentclientprotocol/sdk": "0.21.0",
48
48
  "@babel/parser": "^7.29.3",
49
49
  "@mozilla/readability": "^0.6.0",
50
- "@oh-my-pi/omp-stats": "15.4.1",
51
- "@oh-my-pi/pi-agent-core": "15.4.1",
52
- "@oh-my-pi/pi-ai": "15.4.1",
53
- "@oh-my-pi/pi-natives": "15.4.1",
54
- "@oh-my-pi/pi-tui": "15.4.1",
55
- "@oh-my-pi/pi-utils": "15.4.1",
50
+ "@oh-my-pi/omp-stats": "15.4.2",
51
+ "@oh-my-pi/pi-agent-core": "15.4.2",
52
+ "@oh-my-pi/pi-ai": "15.4.2",
53
+ "@oh-my-pi/pi-natives": "15.4.2",
54
+ "@oh-my-pi/pi-tui": "15.4.2",
55
+ "@oh-my-pi/pi-utils": "15.4.2",
56
56
  "@puppeteer/browsers": "^2.13.0",
57
57
  "@types/turndown": "5.0.6",
58
58
  "@xterm/headless": "^6.0.0",
@@ -13,7 +13,7 @@ import {
13
13
  TabBar,
14
14
  Text,
15
15
  } from "@oh-my-pi/pi-tui";
16
- import { type SettingPath, settings } from "../../config/settings";
16
+ import { getDefault, type SettingPath, settings } from "../../config/settings";
17
17
  import type {
18
18
  SettingTab,
19
19
  StatusLinePreset,
@@ -294,6 +294,7 @@ export class SettingsSelectorComponent extends Container {
294
294
  }
295
295
 
296
296
  const currentValue = this.#getCurrentValue(def);
297
+ const changed = this.#isChanged(def, currentValue);
297
298
 
298
299
  switch (def.type) {
299
300
  case "boolean":
@@ -303,6 +304,7 @@ export class SettingsSelectorComponent extends Container {
303
304
  description: def.description,
304
305
  currentValue: currentValue ? "true" : "false",
305
306
  values: ["true", "false"],
307
+ changed,
306
308
  };
307
309
 
308
310
  case "enum":
@@ -312,6 +314,7 @@ export class SettingsSelectorComponent extends Container {
312
314
  description: def.description,
313
315
  currentValue: currentValue as string,
314
316
  values: [...def.values],
317
+ changed,
315
318
  };
316
319
 
317
320
  case "submenu":
@@ -321,6 +324,7 @@ export class SettingsSelectorComponent extends Container {
321
324
  description: def.description,
322
325
  currentValue: this.#getSubmenuCurrentValue(def.path, currentValue),
323
326
  submenu: (cv, done) => this.#createSubmenu(def, cv, done),
327
+ changed,
324
328
  };
325
329
 
326
330
  case "text":
@@ -330,6 +334,7 @@ export class SettingsSelectorComponent extends Container {
330
334
  description: def.description,
331
335
  currentValue: (currentValue as string) ?? "",
332
336
  submenu: (cv, done) => this.#createTextInput(def, cv, done),
337
+ changed,
333
338
  };
334
339
  }
335
340
  }
@@ -341,6 +346,10 @@ export class SettingsSelectorComponent extends Container {
341
346
  return settings.get(def.path);
342
347
  }
343
348
 
349
+ #isChanged(def: SettingDef, currentValue: unknown): boolean {
350
+ return !Object.is(currentValue, getDefault(def.path));
351
+ }
352
+
344
353
  #getSubmenuCurrentValue(path: SettingPath, value: unknown): string {
345
354
  const rawValue = String(value ?? "");
346
355
  if (path === "compaction.thresholdPercent" && (rawValue === "-1" || rawValue === "")) {
@@ -2404,8 +2404,10 @@ export function getEditorTheme(): EditorTheme {
2404
2404
 
2405
2405
  export function getSettingsListTheme(): import("@oh-my-pi/pi-tui").SettingsListTheme {
2406
2406
  return {
2407
- label: (text: string, selected: boolean) => (selected ? theme.fg("accent", text) : text),
2408
- value: (text: string, selected: boolean) => (selected ? theme.fg("accent", text) : theme.fg("muted", text)),
2407
+ label: (text: string, selected: boolean, changed: boolean) =>
2408
+ changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : text,
2409
+ value: (text: string, selected: boolean, changed: boolean) =>
2410
+ selected ? theme.fg("accent", text) : changed ? theme.fg("statusLineGitDirty", text) : theme.fg("muted", text),
2409
2411
  description: (text: string) => theme.fg("dim", text),
2410
2412
  cursor: theme.fg("accent", `${theme.nav.cursor} `),
2411
2413
  hint: (text: string) => theme.fg("dim", text),
package/src/sdk.ts CHANGED
@@ -1658,9 +1658,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1658
1658
  };
1659
1659
 
1660
1660
  const toolNamesFromRegistry = Array.from(toolRegistry.keys());
1661
- const requestedToolNames =
1662
- (options.toolNames ? [...new Set(options.toolNames.map(name => name.toLowerCase()))] : undefined) ??
1663
- toolNamesFromRegistry;
1661
+ const explicitlyRequestedToolNames = options.toolNames
1662
+ ? [...new Set(options.toolNames.map(name => name.toLowerCase()))]
1663
+ : undefined;
1664
+ // When `requireYieldTool` is set, the subagent's prompts and idle-reminders demand a
1665
+ // `yield` call to terminate. The tool registry already includes `yield` (see
1666
+ // `createTools`), but an explicit `toolNames` list would otherwise drop it from the
1667
+ // active set — leaving the model unable to satisfy the contract. Mirror the same
1668
+ // invariant `parseAgentFields` enforces on frontmatter `tools`.
1669
+ if (
1670
+ options.requireYieldTool === true &&
1671
+ explicitlyRequestedToolNames &&
1672
+ !explicitlyRequestedToolNames.includes("yield")
1673
+ ) {
1674
+ explicitlyRequestedToolNames.push("yield");
1675
+ }
1676
+ const requestedToolNames = explicitlyRequestedToolNames ?? toolNamesFromRegistry;
1664
1677
  const normalizedRequested = requestedToolNames.filter(name => toolRegistry.has(name));
1665
1678
  const requestedToolNameSet = new Set(normalizedRequested);
1666
1679
  // Effective discovery mode: tools.discoveryMode takes precedence; mcp.discoveryMode is back-compat alias.