oh-my-opencode 5.1.32 → 5.1.34

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 (109) hide show
  1. package/README.ja.md +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/README.ru.md +1 -1
  5. package/README.zh-cn.md +1 -1
  6. package/dist/cli/account-keys/commands.d.ts +9 -0
  7. package/dist/cli/account-keys/keyring.d.ts +16 -0
  8. package/dist/cli/account-keys/keystore.d.ts +34 -0
  9. package/dist/cli/index.js +91401 -90585
  10. package/dist/cli/service-auth/accounts-closed.fixture.d.ts +1 -0
  11. package/dist/cli/service-auth/commands.d.ts +4 -1
  12. package/dist/cli/worktree-sweep/types.d.ts +3 -1
  13. package/dist/cli-node/index.js +91521 -90705
  14. package/dist/features/background-agent/concurrency.d.ts +6 -0
  15. package/dist/features/background-agent/manager.d.ts +2 -0
  16. package/dist/features/background-agent/task-history.d.ts +1 -0
  17. package/dist/features/background-agent/types.d.ts +2 -0
  18. package/dist/index.js +362 -126
  19. package/dist/skills/browser/runtime/omowright/index.js +6 -5
  20. package/dist/skills/browser/runtime/omowright/manifest.json +3 -3
  21. package/dist/skills/ulw-execute/SKILL.md +9 -6
  22. package/dist/skills/ulw-research/AGENTS.md +3 -2
  23. package/dist/skills/ulw-research/SKILL.md +7 -5
  24. package/dist/skills/ulw-research/references/deliverable-phase.md +1 -1
  25. package/dist/skills/ulw-research/references/report-gates.md +2 -0
  26. package/dist/skills/ulw-research/references/report-visualization.md +126 -0
  27. package/dist/skills/ulw-research/scripts/contracts.mjs +3 -1
  28. package/dist/skills/ulw-research/scripts/contracts.test.ts +2 -0
  29. package/dist/skills/ulw-research/scripts/gates-figures.mjs +33 -1
  30. package/dist/skills/ulw-research/scripts/gates-static.mjs +2 -2
  31. package/dist/skills/ulw-research/scripts/gates-static.test.ts +56 -3
  32. package/dist/skills/ulw-research/scripts/tests/fixtures/bad-report.html +4 -0
  33. package/dist/startup-migration.d.ts +3 -0
  34. package/dist/tui.js +55 -24
  35. package/package.json +15 -15
  36. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  37. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +1 -0
  38. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  43. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  45. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  46. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  47. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  48. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  50. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  52. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  58. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  60. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  82. package/packages/omo-codex/plugin/package-lock.json +12 -12
  83. package/packages/omo-codex/plugin/package.json +1 -1
  84. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +6 -5
  85. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +3 -3
  86. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +9 -6
  87. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +3 -2
  88. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -5
  89. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +1 -1
  90. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +2 -0
  91. package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +126 -0
  92. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +3 -1
  93. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-figures.mjs +33 -1
  94. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +2 -2
  95. package/packages/omo-codex/scripts/install-dist/install-local.mjs +3 -2
  96. package/packages/shared-skills/skills/browser/runtime/omowright/index.js +6 -5
  97. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +3 -3
  98. package/packages/shared-skills/skills/ulw-execute/SKILL.md +9 -6
  99. package/packages/shared-skills/skills/ulw-research/AGENTS.md +3 -2
  100. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -5
  101. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +1 -1
  102. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +2 -0
  103. package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +126 -0
  104. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +3 -1
  105. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +2 -0
  106. package/packages/shared-skills/skills/ulw-research/scripts/gates-figures.mjs +33 -1
  107. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +2 -2
  108. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.test.ts +56 -3
  109. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/bad-report.html +4 -0
package/dist/index.js CHANGED
@@ -4713,11 +4713,6 @@ function formatDurationHuman(milliseconds) {
4713
4713
  return `${seconds}s`;
4714
4714
  }
4715
4715
 
4716
- // packages/utils/src/shell-command-escape.ts
4717
- function shellEscapeForDoubleQuotedCommand(value) {
4718
- return value.replace(/\\/g, "\\\\").replace(/\$/g, "\\$").replace(/`/g, "\\`").replace(/"/g, "\\\"").replace(/;/g, "\\;").replace(/\|/g, "\\|").replace(/&/g, "\\&").replace(/#/g, "\\#").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
4719
- }
4720
-
4721
4716
  // packages/utils/src/jsonc-parser.ts
4722
4717
  function stripBom3(content) {
4723
4718
  return content.charCodeAt(0) === 65279 ? content.slice(1) : content;
@@ -6187,58 +6182,52 @@ function toGitFileStatus(statusToken) {
6187
6182
  return "deleted";
6188
6183
  return "modified";
6189
6184
  }
6190
- function normalizeGitStatusPath(statusToken, filePath) {
6191
- if (statusToken !== "R" && statusToken !== "C")
6192
- return filePath;
6193
- const arrowIndex = filePath.lastIndexOf(" -> ");
6194
- return arrowIndex === -1 ? filePath : filePath.slice(arrowIndex + 4);
6195
- }
6196
- function parseGitStatusPorcelainLine(line) {
6197
- if (!line)
6198
- return null;
6199
- const statusToken = line.substring(0, 2).trim();
6200
- const filePath = normalizeGitStatusPath(statusToken, line.substring(3));
6201
- if (!filePath)
6202
- return null;
6203
- return {
6204
- filePath,
6205
- status: toGitFileStatus(statusToken)
6206
- };
6207
- }
6208
6185
 
6209
- // packages/utils/src/git-worktree/parse-status-porcelain.ts
6210
- function parseGitStatusPorcelain(output) {
6186
+ // packages/utils/src/git-worktree/parse-status-porcelain-z.ts
6187
+ function parseGitStatusPorcelainZ(output) {
6211
6188
  const map = new Map;
6212
6189
  if (!output)
6213
6190
  return map;
6214
- for (const line of output.split(`
6215
- `)) {
6216
- const parsed = parseGitStatusPorcelainLine(line);
6217
- if (!parsed)
6191
+ const fields = output.split("\x00");
6192
+ for (let index = 0;index < fields.length; index++) {
6193
+ const record = fields[index];
6194
+ if (record.length < 4)
6218
6195
  continue;
6219
- map.set(parsed.filePath, parsed.status);
6196
+ const statusCode = record.substring(0, 2);
6197
+ map.set(record.substring(3), statusCode.includes("C") ? "added" : toGitFileStatus(statusCode.trim()));
6198
+ if (statusCode.includes("R") || statusCode.includes("C"))
6199
+ index++;
6220
6200
  }
6221
6201
  return map;
6222
6202
  }
6223
- var init_parse_status_porcelain = () => {};
6203
+ var init_parse_status_porcelain_z = () => {};
6224
6204
 
6225
- // packages/utils/src/git-worktree/parse-diff-numstat.ts
6226
- function parseGitDiffNumstat(output, statusMap) {
6205
+ // packages/utils/src/git-worktree/parse-diff-numstat-z.ts
6206
+ function toCount(value) {
6207
+ return value === "-" ? 0 : parseInt(value, 10);
6208
+ }
6209
+ function parseGitDiffNumstatZ(output, statusMap) {
6227
6210
  if (!output)
6228
6211
  return [];
6229
6212
  const stats = [];
6230
- for (const line of output.split(`
6231
- `)) {
6232
- const parts = line.split("\t");
6233
- if (parts.length < 3)
6213
+ const fields = output.split("\x00");
6214
+ for (let index = 0;index < fields.length; index++) {
6215
+ const field = fields[index];
6216
+ const firstTab = field.indexOf("\t");
6217
+ const secondTab = field.indexOf("\t", firstTab + 1);
6218
+ if (firstTab === -1 || secondTab === -1)
6219
+ continue;
6220
+ let path = field.slice(secondTab + 1);
6221
+ if (path === "") {
6222
+ index += 2;
6223
+ path = fields[index];
6224
+ }
6225
+ if (!path)
6234
6226
  continue;
6235
- const [addedStr, removedStr, path] = parts;
6236
- const added = addedStr === "-" ? 0 : parseInt(addedStr, 10);
6237
- const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10);
6238
6227
  stats.push({
6239
6228
  path,
6240
- added,
6241
- removed,
6229
+ added: toCount(field.slice(0, firstTab)),
6230
+ removed: toCount(field.slice(firstTab + 1, secondTab)),
6242
6231
  status: statusMap.get(path) ?? "modified"
6243
6232
  });
6244
6233
  }
@@ -6251,46 +6240,43 @@ import * as fs3 from "fs";
6251
6240
  import { join as join12 } from "path";
6252
6241
  function collectGitDiffStats(directory) {
6253
6242
  try {
6254
- const diffOutput = childProcess.execFileSync("git", ["diff", "--numstat", "HEAD"], {
6243
+ const diffOutput = childProcess.execFileSync("git", ["diff", "--numstat", "-z", "-M", "HEAD"], {
6255
6244
  cwd: directory,
6256
6245
  encoding: "utf-8",
6257
6246
  timeout: 5000,
6258
6247
  stdio: ["pipe", "pipe", "pipe"]
6259
- }).trimEnd();
6260
- const statusOutput = childProcess.execFileSync("git", ["status", "--porcelain"], {
6248
+ });
6249
+ const statusOutput = childProcess.execFileSync("git", ["status", "--porcelain", "-z", "--find-renames", "--untracked-files=all"], {
6261
6250
  cwd: directory,
6262
6251
  encoding: "utf-8",
6263
6252
  timeout: 5000,
6264
6253
  stdio: ["pipe", "pipe", "pipe"]
6265
- }).trimEnd();
6266
- const untrackedOutput = childProcess.execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
6254
+ });
6255
+ const untrackedOutput = childProcess.execFileSync("git", ["ls-files", "-z", "--others", "--exclude-standard"], {
6267
6256
  cwd: directory,
6268
6257
  encoding: "utf-8",
6269
6258
  timeout: 5000,
6270
6259
  stdio: ["pipe", "pipe", "pipe"]
6271
- }).trimEnd();
6272
- const untrackedNumstat = untrackedOutput ? untrackedOutput.split(`
6273
- `).filter(Boolean).map((filePath) => {
6260
+ });
6261
+ const untrackedNumstat = untrackedOutput ? untrackedOutput.split("\x00").filter(Boolean).map((filePath) => {
6274
6262
  try {
6275
6263
  const content = fs3.readFileSync(join12(directory, filePath), "utf-8");
6276
6264
  const lineCount = content.split(`
6277
6265
  `).length - (content.endsWith(`
6278
6266
  `) ? 1 : 0);
6279
- return `${lineCount} 0 ${filePath}`;
6267
+ return `${lineCount} 0 ${filePath}\x00`;
6280
6268
  } catch (error) {
6281
6269
  if (!(error instanceof Error)) {
6282
6270
  throw error;
6283
6271
  }
6284
- return `0 0 ${filePath}`;
6272
+ return `0 0 ${filePath}\x00`;
6285
6273
  }
6286
- }).join(`
6287
- `) : "";
6288
- const combinedNumstat = [diffOutput, untrackedNumstat].filter(Boolean).join(`
6289
- `).trim();
6274
+ }).join("") : "";
6275
+ const combinedNumstat = diffOutput + untrackedNumstat;
6290
6276
  if (!combinedNumstat)
6291
6277
  return [];
6292
- const statusMap = parseGitStatusPorcelain(statusOutput);
6293
- return parseGitDiffNumstat(combinedNumstat, statusMap);
6278
+ const statusMap = parseGitStatusPorcelainZ(statusOutput);
6279
+ return parseGitDiffNumstatZ(combinedNumstat, statusMap);
6294
6280
  } catch (error) {
6295
6281
  if (!(error instanceof Error)) {
6296
6282
  throw error;
@@ -6299,13 +6285,18 @@ function collectGitDiffStats(directory) {
6299
6285
  }
6300
6286
  }
6301
6287
  var init_collect_git_diff_stats = __esm(() => {
6302
- init_parse_status_porcelain();
6288
+ init_parse_status_porcelain_z();
6303
6289
  });
6304
6290
 
6305
6291
  // packages/utils/src/git-worktree/format-file-changes.ts
6306
6292
  function normalizePath(path) {
6307
6293
  return path.replaceAll("\\", "/");
6308
6294
  }
6295
+ function displayPath2(path) {
6296
+ if (!UNSAFE_DISPLAY_CHARACTER.test(path))
6297
+ return path;
6298
+ return JSON.stringify(path).replace(UNSAFE_DISPLAY_CHARACTERS, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
6299
+ }
6309
6300
  function formatFileChanges(stats, notepadPath) {
6310
6301
  if (stats.length === 0)
6311
6302
  return `[FILE CHANGES SUMMARY]
@@ -6318,21 +6309,21 @@ No file changes detected.
6318
6309
  if (modified.length > 0) {
6319
6310
  lines.push("Modified files:");
6320
6311
  for (const f of modified) {
6321
- lines.push(` ${f.path} (+${f.added}, -${f.removed})`);
6312
+ lines.push(` ${displayPath2(f.path)} (+${f.added}, -${f.removed})`);
6322
6313
  }
6323
6314
  lines.push("");
6324
6315
  }
6325
6316
  if (added.length > 0) {
6326
6317
  lines.push("Created files:");
6327
6318
  for (const f of added) {
6328
- lines.push(` ${f.path} (+${f.added})`);
6319
+ lines.push(` ${displayPath2(f.path)} (+${f.added})`);
6329
6320
  }
6330
6321
  lines.push("");
6331
6322
  }
6332
6323
  if (deleted.length > 0) {
6333
6324
  lines.push("Deleted files:");
6334
6325
  for (const f of deleted) {
6335
- lines.push(` ${f.path} (-${f.removed})`);
6326
+ lines.push(` ${displayPath2(f.path)} (-${f.removed})`);
6336
6327
  }
6337
6328
  lines.push("");
6338
6329
  }
@@ -6344,17 +6335,23 @@ No file changes detected.
6344
6335
  });
6345
6336
  if (notepadStat) {
6346
6337
  lines.push("[NOTEPAD UPDATED]");
6347
- lines.push(` ${notepadStat.path} (+${notepadStat.added})`);
6338
+ lines.push(` ${displayPath2(notepadStat.path)} (+${notepadStat.added})`);
6348
6339
  lines.push("");
6349
6340
  }
6350
6341
  }
6351
6342
  return lines.join(`
6352
6343
  `);
6353
6344
  }
6345
+ var UNSAFE_DISPLAY_CHARACTER, UNSAFE_DISPLAY_CHARACTERS;
6346
+ var init_format_file_changes = __esm(() => {
6347
+ UNSAFE_DISPLAY_CHARACTER = /[\p{Cc}\u2028\u2029]/u;
6348
+ UNSAFE_DISPLAY_CHARACTERS = /[\p{Cc}\u2028\u2029]/gu;
6349
+ });
6354
6350
 
6355
6351
  // packages/utils/src/git-worktree/index.ts
6356
6352
  var init_git_worktree = __esm(() => {
6357
6353
  init_collect_git_diff_stats();
6354
+ init_format_file_changes();
6358
6355
  });
6359
6356
 
6360
6357
  // packages/utils/src/internal-initiator-marker.ts
@@ -8331,9 +8328,22 @@ function buildTmuxAttachCommand(serverUrl, sessionId, directory = process.cwd())
8331
8328
  const escapedDirectory = shellQuoteForNestedCommand(directory || process.cwd());
8332
8329
  return `${TMUX_COMMAND_SHELL} -c "opencode attach ${escapedUrl} --session ${escapedSessionId} --dir ${escapedDirectory}"`;
8333
8330
  }
8331
+ function withoutNul(value) {
8332
+ return value.replaceAll("\x00", "");
8333
+ }
8334
+ function escapeTmuxFormat(value) {
8335
+ return value.replaceAll("#", "##");
8336
+ }
8337
+ function buildSubagentPaneTitle(description) {
8338
+ const shown = withoutNul(description).slice(0, PANE_TITLE_DESCRIPTION_LIMIT);
8339
+ return `omo-subagent-${escapeTmuxFormat(shown)}`;
8340
+ }
8334
8341
  function buildTmuxPlaceholderCommand(description) {
8335
- const escapedDescription = shellEscapeForDoubleQuotedCommand(description);
8336
- return `${TMUX_COMMAND_SHELL} -c "printf '%s\\n%s\\n' \\"OMO subagent pane ready: ${escapedDescription}\\" \\"Focus this pane to attach.\\"; while :; do sleep 86400; done"`;
8342
+ const clean = withoutNul(description);
8343
+ const shown = clean.length > PLACEHOLDER_DESCRIPTION_LIMIT ? `${clean.slice(0, PLACEHOLDER_DESCRIPTION_LIMIT)}\u2026` : clean;
8344
+ const banner = shellQuoteForNestedCommand(`OMO subagent pane ready: ${shown}`);
8345
+ const hint = shellQuoteForNestedCommand("Focus this pane to attach.");
8346
+ return `${TMUX_COMMAND_SHELL} -c "printf '%s\\n%s\\n' ${banner} ${hint}; while :; do sleep 86400; done"`;
8337
8347
  }
8338
8348
  function buildPaneAuthEnvironmentArgs() {
8339
8349
  const password = process.env.OPENCODE_SERVER_PASSWORD;
@@ -8347,10 +8357,7 @@ function buildPaneAuthEnvironmentArgs() {
8347
8357
  }
8348
8358
  return args;
8349
8359
  }
8350
- var TMUX_COMMAND_SHELL = "/bin/sh";
8351
- var init_pane_command = __esm(() => {
8352
- init_src();
8353
- });
8360
+ var TMUX_COMMAND_SHELL = "/bin/sh", PLACEHOLDER_DESCRIPTION_LIMIT = 2000, PANE_TITLE_DESCRIPTION_LIMIT = 20;
8354
8361
 
8355
8362
  // packages/tmux-core/src/tmux-utils/pane-spawn.ts
8356
8363
  async function resolveSpawnTmuxPaneDeps(deps) {
@@ -8422,7 +8429,7 @@ async function spawnTmuxPane(sessionId, description, config, serverUrl, _directo
8422
8429
  if (result.exitCode !== 0 || !paneId) {
8423
8430
  return { success: false };
8424
8431
  }
8425
- const title = `omo-subagent-${description.slice(0, 20)}`;
8432
+ const title = buildSubagentPaneTitle(description);
8426
8433
  const titleResult = await runTmuxCommand(tmux, ["select-pane", "-t", paneId, "-T", title]);
8427
8434
  if (titleResult.exitCode !== 0) {
8428
8435
  log("[spawnTmuxPane] WARNING: failed to set pane title", {
@@ -8437,7 +8444,6 @@ async function spawnTmuxPane(sessionId, description, config, serverUrl, _directo
8437
8444
  var init_pane_spawn = __esm(() => {
8438
8445
  init_environment();
8439
8446
  init_server_health();
8440
- init_pane_command();
8441
8447
  });
8442
8448
 
8443
8449
  // packages/tmux-core/src/tmux-utils/pane-close.ts
@@ -8510,7 +8516,7 @@ async function replaceTmuxPane(paneId, sessionId, description, config, _serverUr
8510
8516
  log("[replaceTmuxPane] FAILED", { paneId, exitCode: result.exitCode, stderr: result.stderr.trim() });
8511
8517
  return { success: false };
8512
8518
  }
8513
- const title = `omo-subagent-${description.slice(0, 20)}`;
8519
+ const title = buildSubagentPaneTitle(description);
8514
8520
  const titleResult = await runTmuxCommand(tmux, ["select-pane", "-t", paneId, "-T", title]);
8515
8521
  if (titleResult.exitCode !== 0) {
8516
8522
  log("[replaceTmuxPane] WARNING: failed to set pane title", {
@@ -8525,7 +8531,6 @@ async function replaceTmuxPane(paneId, sessionId, description, config, _serverUr
8525
8531
  }
8526
8532
  var init_pane_replace = __esm(() => {
8527
8533
  init_environment();
8528
- init_pane_command();
8529
8534
  });
8530
8535
 
8531
8536
  // packages/tmux-core/src/tmux-utils/pane-activate.ts
@@ -8570,7 +8575,6 @@ async function activateTmuxPane(paneId, sessionId, serverUrl, directory, deps =
8570
8575
  var init_pane_activate = __esm(() => {
8571
8576
  init_runner();
8572
8577
  init_environment();
8573
- init_pane_command();
8574
8578
  });
8575
8579
 
8576
8580
  // packages/tmux-core/src/tmux-utils/window-spawn.ts
@@ -8638,7 +8642,7 @@ async function spawnTmuxWindow(sessionId, description, config, serverUrl, _direc
8638
8642
  log("[spawnTmuxWindow] FAILED", { exitCode: result.exitCode, stderr: result.stderr.trim() });
8639
8643
  return { success: false };
8640
8644
  }
8641
- const title = `omo-subagent-${description.slice(0, 20)}`;
8645
+ const title = buildSubagentPaneTitle(description);
8642
8646
  const titleResult = await runTmuxCommand(tmux, ["select-pane", "-t", paneId, "-T", title]);
8643
8647
  if (titleResult.exitCode !== 0) {
8644
8648
  log("[spawnTmuxWindow] WARNING: failed to set pane title", {
@@ -8655,7 +8659,6 @@ var ISOLATED_WINDOW_NAME = "omo-agents";
8655
8659
  var init_window_spawn = __esm(() => {
8656
8660
  init_environment();
8657
8661
  init_server_health();
8658
- init_pane_command();
8659
8662
  });
8660
8663
 
8661
8664
  // packages/tmux-core/src/tmux-utils/session-spawn.ts
@@ -8762,7 +8765,7 @@ async function spawnTmuxSession(sessionId, description, config, serverUrl, _dire
8762
8765
  log("[spawnTmuxSession] FAILED", { exitCode: result.exitCode, stderr: result.stderr.trim() });
8763
8766
  return { success: false };
8764
8767
  }
8765
- const title = `omo-subagent-${description.slice(0, 20)}`;
8768
+ const title = buildSubagentPaneTitle(description);
8766
8769
  const titleResult = await runTmuxCommand(tmux, ["select-pane", "-t", paneId, "-T", title]);
8767
8770
  if (titleResult.exitCode !== 0) {
8768
8771
  log("[spawnTmuxSession] WARNING: failed to set pane title", {
@@ -8779,7 +8782,6 @@ var ISOLATED_SESSION_NAME_PREFIX = "omo-agents";
8779
8782
  var init_session_spawn = __esm(() => {
8780
8783
  init_environment();
8781
8784
  init_server_health();
8782
- init_pane_command();
8783
8785
  });
8784
8786
 
8785
8787
  // packages/tmux-core/src/tmux-utils/session-kill.ts
@@ -8970,7 +8972,6 @@ var init_tmux_utils = __esm(() => {
8970
8972
  init_session_spawn();
8971
8973
  init_session_kill();
8972
8974
  init_stale_session_sweep();
8973
- init_pane_command();
8974
8975
  init_layout();
8975
8976
  init_spawn_process();
8976
8977
  });
@@ -9469,7 +9470,7 @@ var init_stale_attach_pane_sweep = __esm(() => {
9469
9470
  });
9470
9471
 
9471
9472
  // packages/omo-opencode/src/shared/tmux/tmux-utils/pane-command.ts
9472
- var init_pane_command2 = () => {};
9473
+ var init_pane_command = () => {};
9473
9474
 
9474
9475
  // packages/omo-opencode/src/shared/tmux/tmux-utils/layout.ts
9475
9476
  async function enforceMainPaneWidth2(mainPaneId, windowWidth, mainPaneSizeOrOptions) {
@@ -9502,7 +9503,7 @@ var init_tmux_utils2 = __esm(() => {
9502
9503
  init_session_kill2();
9503
9504
  init_stale_session_sweep2();
9504
9505
  init_stale_attach_pane_sweep();
9505
- init_pane_command2();
9506
+ init_pane_command();
9506
9507
  init_layout2();
9507
9508
  });
9508
9509
 
@@ -19641,7 +19642,7 @@ function selectExistingTeammatePane(teammatePanes, callerPaneId) {
19641
19642
  function buildSplitArgs(callerPaneId, teammatePanes, member) {
19642
19643
  const environmentArgs = buildPaneEnvironmentArgs();
19643
19644
  if (teammatePanes.length === 0) {
19644
- return ["split-window", ...environmentArgs, "-t", callerPaneId, "-h", "-d", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", getPaneWorkingDirectory(member)];
19645
+ return ["split-window", ...environmentArgs, "-t", callerPaneId, "-h", "-d", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", escapeTmuxFormat(getPaneWorkingDirectory(member))];
19645
19646
  }
19646
19647
  return [
19647
19648
  "split-window",
@@ -19654,7 +19655,7 @@ function buildSplitArgs(callerPaneId, teammatePanes, member) {
19654
19655
  "-F",
19655
19656
  "#{pane_id}",
19656
19657
  "-c",
19657
- getPaneWorkingDirectory(member)
19658
+ escapeTmuxFormat(getPaneWorkingDirectory(member))
19658
19659
  ];
19659
19660
  }
19660
19661
  async function createTeamLayoutInCallerWindow(tmuxPath, callerPaneId, windowTarget, members, serverUrl, deps) {
@@ -20317,6 +20318,7 @@ var nonNegativeInteger = z5.number().int().nonnegative();
20317
20318
  var OmoComputerSettingsLayerSchema = z5.object({
20318
20319
  enabled: z5.boolean().describe("Experimental: register the computer tool in OmO Native sessions (default: on where the host is supported; false leaves it unregistered)"),
20319
20320
  display: z5.string().min(1),
20321
+ showCursor: z5.boolean().describe("Show the display-only macOS cursor overlay (default: true; false hides it)"),
20320
20322
  max_width: positiveInteger,
20321
20323
  max_height: positiveInteger,
20322
20324
  screenshot_max_bytes: positiveInteger,
@@ -22117,8 +22119,17 @@ function isFileExistsError(error) {
22117
22119
  function backupCandidate(basePath, attempt) {
22118
22120
  return attempt === 0 ? basePath : `${basePath}.${attempt}`;
22119
22121
  }
22122
+ function writePlannedBackup(plannedPath, content, fileSystem) {
22123
+ try {
22124
+ if (fileSystem.existsSync(plannedPath) && fileSystem.readFileSync(plannedPath, "utf-8") === content)
22125
+ return plannedPath;
22126
+ } catch {}
22127
+ return writeBackupFrom(plannedPath, content, fileSystem);
22128
+ }
22120
22129
  function writeBackup(path, content, fileSystem) {
22121
- const basePath = `${path}.bak.${backupSuffix()}`;
22130
+ return writeBackupFrom(`${path}.bak.${backupSuffix()}`, content, fileSystem);
22131
+ }
22132
+ function writeBackupFrom(basePath, content, fileSystem) {
22122
22133
  let attempt = 0;
22123
22134
  while (true) {
22124
22135
  const candidate = backupCandidate(basePath, attempt);
@@ -22223,7 +22234,7 @@ function updateOmoConfig(options) {
22223
22234
  if (existed) {
22224
22235
  try {
22225
22236
  assertConfigPathIsSafe(path, fileSystem);
22226
- backupPath = writeBackup(path, content, fileSystem);
22237
+ backupPath = options.backupPath === undefined ? writeBackup(path, content, fileSystem) : writePlannedBackup(options.backupPath, content, fileSystem);
22227
22238
  } catch (error) {
22228
22239
  if (error instanceof OmoConfigWriteError)
22229
22240
  throw error;
@@ -22540,6 +22551,7 @@ var writeOmoMigrationTarget = (input) => {
22540
22551
  const options = writerInput(input.targetPath, input.env);
22541
22552
  const result = updateOmoConfig({
22542
22553
  ...options,
22554
+ ...input.backupPath === undefined ? {} : { backupPath: input.backupPath },
22543
22555
  edits: input.edits,
22544
22556
  env: input.env,
22545
22557
  fileSystem: input.fileSystem,
@@ -22548,6 +22560,7 @@ var writeOmoMigrationTarget = (input) => {
22548
22560
  if (!sameResolvedPath(result.path, input.targetPath)) {
22549
22561
  throw new MigrationTransactionError(`Migration writer resolved ${result.path} instead of ${input.targetPath}`);
22550
22562
  }
22563
+ return result.backupPath === undefined ? {} : { backupPath: result.backupPath };
22551
22564
  };
22552
22565
  function prepareTargetWrite(input) {
22553
22566
  const targetCleanup = stripRetiredCodegraph(input.target);
@@ -22585,12 +22598,14 @@ function prepareTargetReplacement(input) {
22585
22598
  };
22586
22599
  }
22587
22600
  function writePreparedTarget(input) {
22588
- input.writeTarget({
22601
+ const written = input.writeTarget({
22602
+ ...input.backupPath === undefined ? {} : { backupPath: input.backupPath },
22589
22603
  edits: input.prepared.edits,
22590
22604
  env: input.env,
22591
22605
  fileSystem: input.fileSystem,
22592
22606
  targetPath: input.targetPath
22593
22607
  });
22608
+ return written?.backupPath;
22594
22609
  }
22595
22610
 
22596
22611
  // packages/omo-config-core/src/migration/journal.ts
@@ -22630,6 +22645,9 @@ function parseJournal(value) {
22630
22645
  if (!value["completedMoves"].every((path) => typeof path === "string")) {
22631
22646
  throw new Error("Migration journal completed moves are invalid");
22632
22647
  }
22648
+ const targetBackupPath = value["targetBackupPath"];
22649
+ if (targetBackupPath !== undefined && typeof targetBackupPath !== "string")
22650
+ throw new Error("Migration journal target backup is invalid");
22633
22651
  if (!Array.isArray(value["backupMoves"]))
22634
22652
  throw new Error("Migration journal backup plan is invalid");
22635
22653
  const backupMoves = [];
@@ -22644,6 +22662,7 @@ function parseJournal(value) {
22644
22662
  completedMoves: [...value["completedMoves"]],
22645
22663
  diagnostics: diagnostics === undefined ? [] : [...diagnostics],
22646
22664
  migrationId: value["migrationId"],
22665
+ ...targetBackupPath === undefined ? {} : { targetBackupPath },
22647
22666
  targetPath: value["targetPath"],
22648
22667
  targetWrite: {
22649
22668
  additions: { ...value["targetWrite"]["additions"] },
@@ -22832,12 +22851,24 @@ function acquireMigrationLock(input) {
22832
22851
  }
22833
22852
 
22834
22853
  // packages/omo-config-core/src/migration/recovery.ts
22854
+ function backupPathForResume(planned, targetPath, fileSystem) {
22855
+ if (planned === undefined || !fileSystem.existsSync(planned) || !fileSystem.existsSync(targetPath))
22856
+ return planned;
22857
+ if (fileSystem.readFileSync(planned, "utf-8") === fileSystem.readFileSync(targetPath, "utf-8"))
22858
+ return planned;
22859
+ let attempt = 1;
22860
+ while (fileSystem.existsSync(`${planned}.${attempt}`))
22861
+ attempt += 1;
22862
+ return `${planned}.${attempt}`;
22863
+ }
22835
22864
  function resumeMigrationJournal(input) {
22836
- const journal = readMigrationJournal(input.fileSystem, input.env);
22837
- if (journal === null)
22838
- return false;
22865
+ const read = readMigrationJournal(input.fileSystem, input.env);
22866
+ if (read === null)
22867
+ return { resumed: false };
22868
+ let journal = read;
22839
22869
  input.renewLock();
22840
22870
  const target = targetDocument(journal.targetPath, input.fileSystem);
22871
+ let writtenBackupPath;
22841
22872
  if (!hasMigrationMarker(target, journal.migrationId)) {
22842
22873
  const prepared = journal.targetWrite.mode === "replace-target" ? prepareTargetReplacement({
22843
22874
  document: journal.targetWrite.additions,
@@ -22850,7 +22881,13 @@ function resumeMigrationJournal(input) {
22850
22881
  target,
22851
22882
  targetPath: journal.targetPath
22852
22883
  });
22853
- writePreparedTarget({
22884
+ const backupPath = backupPathForResume(journal.targetBackupPath, journal.targetPath, input.fileSystem);
22885
+ if (backupPath !== journal.targetBackupPath && backupPath !== undefined) {
22886
+ journal = { ...journal, targetBackupPath: backupPath };
22887
+ writeMigrationJournal(journal, input.fileSystem, input.env, input.process, input.clock);
22888
+ }
22889
+ writtenBackupPath = writePreparedTarget({
22890
+ ...journal.targetBackupPath === undefined ? {} : { backupPath: journal.targetBackupPath },
22854
22891
  env: input.env,
22855
22892
  fileSystem: input.fileSystem,
22856
22893
  prepared,
@@ -22876,7 +22913,8 @@ function resumeMigrationJournal(input) {
22876
22913
  writeMigrationJournal(targetRecorded, input.fileSystem, input.env, input.process, input.clock);
22877
22914
  }
22878
22915
  removeMigrationJournal(input.fileSystem, input.env);
22879
- return true;
22916
+ const targetBackupPath = writtenBackupPath ?? (journal.targetBackupPath !== undefined && input.fileSystem.existsSync(journal.targetBackupPath) ? journal.targetBackupPath : undefined);
22917
+ return targetBackupPath === undefined ? { resumed: true } : { resumed: true, targetBackupPath };
22880
22918
  }
22881
22919
 
22882
22920
  // packages/omo-config-core/src/migration/batch.ts
@@ -22930,6 +22968,15 @@ function assertSafeSourcePaths(sources, protectedPaths) {
22930
22968
  function directoryPath2(path) {
22931
22969
  return path.startsWith("/") ? posix3.dirname(path) : dirname4(path);
22932
22970
  }
22971
+ function plannedTargetBackupPath(targetPath, clock, fileSystem) {
22972
+ if (!fileSystem.existsSync(targetPath))
22973
+ return;
22974
+ const base = `${targetPath}.bak.${new Date(clock.now()).toISOString().replace(/[:.]/g, "-")}`;
22975
+ let candidate = base;
22976
+ for (let attempt = 1;fileSystem.existsSync(candidate); attempt += 1)
22977
+ candidate = `${base}.${attempt}`;
22978
+ return candidate;
22979
+ }
22933
22980
  function ensureBackupDirectories(moves, fileSystem) {
22934
22981
  for (const move of moves)
22935
22982
  fileSystem.mkdirSync(directoryPath2(move.to), { recursive: true });
@@ -22968,11 +23015,13 @@ function executePlan(input) {
22968
23015
  if (input.dryRun)
22969
23016
  return { diagnostics, journalResumed, preview, status: "planned" };
22970
23017
  ensureBackupDirectories(moves, fileSystem);
23018
+ const targetBackupPath = plannedTargetBackupPath(plan.targetPath, input.clock, fileSystem);
22971
23019
  const journal = {
22972
23020
  backupMoves: moves,
22973
23021
  completedMoves: [],
22974
23022
  diagnostics,
22975
23023
  migrationId: plan.id,
23024
+ ...targetBackupPath === undefined ? {} : { targetBackupPath },
22976
23025
  targetPath: plan.targetPath,
22977
23026
  targetWrite: {
22978
23027
  additions: transformed.document,
@@ -22984,7 +23033,7 @@ function executePlan(input) {
22984
23033
  writeMigrationJournal(journal, fileSystem, env, input.process, input.clock);
22985
23034
  input.onBoundary?.("journal-written");
22986
23035
  input.renewLock();
22987
- writePreparedTarget({ env, fileSystem, prepared, targetPath: plan.targetPath, writeTarget: input.writeTarget });
23036
+ writePreparedTarget({ ...targetBackupPath === undefined ? {} : { backupPath: targetBackupPath }, env, fileSystem, prepared, targetPath: plan.targetPath, writeTarget: input.writeTarget });
22988
23037
  input.onBoundary?.("target-written");
22989
23038
  const targetRecorded = { ...journal, targetWritten: true };
22990
23039
  writeMigrationJournal(targetRecorded, fileSystem, env, input.process, input.clock);
@@ -23000,7 +23049,7 @@ function executePlan(input) {
23000
23049
  input.onBoundary?.("source-recorded");
23001
23050
  }
23002
23051
  removeMigrationJournal(fileSystem, env);
23003
- return { diagnostics, journalResumed, preview, status: "migrated" };
23052
+ return { diagnostics, journalResumed, preview, status: "migrated", ...targetBackupPath === undefined ? {} : { targetBackupPath } };
23004
23053
  }
23005
23054
  function runMigrations(options) {
23006
23055
  const clock = options.clock ?? DEFAULT_MIGRATION_CLOCK;
@@ -23014,7 +23063,8 @@ function runMigrations(options) {
23014
23063
  if (lock === null)
23015
23064
  return { journalResumed: false, results: [], status: "locked" };
23016
23065
  try {
23017
- const journalResumed = resumeMigrationJournal({ clock, env, fileSystem, process: process2, renewLock: lock.renew, writeTarget });
23066
+ const resumed = resumeMigrationJournal({ clock, env, fileSystem, process: process2, renewLock: lock.renew, writeTarget });
23067
+ const journalResumed = resumed.resumed;
23018
23068
  lock.renew();
23019
23069
  const results = options.discover().map((plan) => executePlan({
23020
23070
  clock,
@@ -23030,7 +23080,7 @@ function runMigrations(options) {
23030
23080
  }));
23031
23081
  if (options.dryRun !== true)
23032
23082
  options.afterMigrations?.(results);
23033
- return { journalResumed, results, status: "completed" };
23083
+ return { journalResumed, ...resumed.targetBackupPath === undefined ? {} : { resumedTargetBackupPath: resumed.targetBackupPath }, results, status: "completed" };
23034
23084
  } finally {
23035
23085
  lock.release();
23036
23086
  }
@@ -23094,6 +23144,11 @@ var AGENT_MODEL_REQUIREMENTS = {
23094
23144
  fallbackChain: [
23095
23145
  { providers: ["openai", "chatgpt-subscription", "opencode"], model: "gpt-5.6-sol", variant: "xhigh" },
23096
23146
  { providers: ["github-copilot"], model: "gpt-5.6-sol", variant: "high" },
23147
+ {
23148
+ providers: ["google", "google-vertex", "opencode"],
23149
+ model: "gemini-4-argon",
23150
+ variant: "high"
23151
+ },
23097
23152
  {
23098
23153
  providers: ["google", "github-copilot", "opencode"],
23099
23154
  model: "gemini-3.1-pro",
@@ -23183,6 +23238,11 @@ var AGENT_MODEL_REQUIREMENTS = {
23183
23238
  model: "claude-opus-5-5",
23184
23239
  variant: "max"
23185
23240
  },
23241
+ {
23242
+ providers: ["google", "google-vertex", "opencode"],
23243
+ model: "gemini-4-argon",
23244
+ variant: "high"
23245
+ },
23186
23246
  {
23187
23247
  providers: ["google", "github-copilot", "opencode"],
23188
23248
  model: "gemini-3.1-pro",
@@ -23225,6 +23285,7 @@ var AGENT_MODEL_REQUIREMENTS = {
23225
23285
  var CATEGORY_MODEL_REQUIREMENTS = {
23226
23286
  "visual-engineering": {
23227
23287
  fallbackChain: [
23288
+ { providers: ["google", "google-vertex", "opencode"], model: "gemini-4-argon", variant: "low" },
23228
23289
  {
23229
23290
  providers: ["anthropic", "anthropic-api", "github-copilot", "opencode"],
23230
23291
  model: "claude-fable-5-1",
@@ -23360,6 +23421,7 @@ var CATEGORY_MODEL_REQUIREMENTS = {
23360
23421
  writing: {
23361
23422
  requiresAnyModel: true,
23362
23423
  fallbackChain: [
23424
+ { providers: ["google", "google-vertex", "opencode"], model: "gemini-4-argon", variant: "low" },
23363
23425
  {
23364
23426
  providers: ["anthropic", "anthropic-api", "github-copilot", "opencode"],
23365
23427
  model: "claude-opus-5-5",
@@ -27880,7 +27942,7 @@ Approach:
27880
27942
  var GOOGLE_CATEGORIES = [
27881
27943
  {
27882
27944
  name: "visual-engineering",
27883
- config: { model: "anthropic/claude-fable-5-1", variant: "max" },
27945
+ config: { model: "google/gemini-4-argon", variant: "low" },
27884
27946
  description: "Visual design, UI/UX, frontend, styling, animation, and design systems",
27885
27947
  promptAppend: VISUAL_CATEGORY_PROMPT_APPEND
27886
27948
  },
@@ -27921,7 +27983,7 @@ ANTI-AI-SLOP RULES (NON-NEGOTIABLE):
27921
27983
  var KIMI_CATEGORIES = [
27922
27984
  {
27923
27985
  name: "writing",
27924
- config: { model: "anthropic/claude-opus-5-5", variant: "low" },
27986
+ config: { model: "google/gemini-4-argon", variant: "low" },
27925
27987
  description: "Documentation, prose, technical writing",
27926
27988
  promptAppend: WRITING_CATEGORY_PROMPT_APPEND
27927
27989
  }
@@ -29434,6 +29496,28 @@ var model_capabilities_generated_default = {
29434
29496
  generatedAt: "2026-07-29T03:28:21.339Z",
29435
29497
  sourceUrl: "https://models.dev/api.json",
29436
29498
  models: {
29499
+ "gemini-4-argon": {
29500
+ id: "gemini-4-argon",
29501
+ family: "gemini",
29502
+ reasoning: true,
29503
+ temperature: false,
29504
+ toolCall: true,
29505
+ modalities: {
29506
+ input: [
29507
+ "text",
29508
+ "image",
29509
+ "audio",
29510
+ "pdf"
29511
+ ],
29512
+ output: [
29513
+ "text"
29514
+ ]
29515
+ },
29516
+ limit: {
29517
+ context: 1048576,
29518
+ output: 65535
29519
+ }
29520
+ },
29437
29521
  "glm-5": {
29438
29522
  id: "glm-5",
29439
29523
  family: "glm",
@@ -88194,7 +88278,7 @@ function parseToolsConfig(toolsValue) {
88194
88278
  var ANTHROPIC_PREFIX = "anthropic/";
88195
88279
  var CLAUDE_CODE_ALIAS_MAP = new Map([
88196
88280
  ["sonnet", `${ANTHROPIC_PREFIX}claude-sonnet-4-6`],
88197
- ["opus", `${ANTHROPIC_PREFIX}claude-opus-4-8`],
88281
+ ["opus", `${ANTHROPIC_PREFIX}claude-opus-5-5`],
88198
88282
  ["haiku", `${ANTHROPIC_PREFIX}claude-haiku-5-5`]
88199
88283
  ]);
88200
88284
  function mapClaudeModelString(model, anthropicProvider) {
@@ -99137,7 +99221,7 @@ function getCachedVersion(options = {}) {
99137
99221
  // package.json
99138
99222
  var package_default = {
99139
99223
  name: "oh-my-opencode",
99140
- version: "5.1.32",
99224
+ version: "5.1.34",
99141
99225
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
99142
99226
  main: "./dist/index.js",
99143
99227
  types: "dist/index.d.ts",
@@ -99314,7 +99398,7 @@ var package_default = {
99314
99398
  },
99315
99399
  devDependencies: {
99316
99400
  "@clack/prompts": "^1.8.1",
99317
- "@code-yeongyu/senpi": "2026.10.12",
99401
+ "@code-yeongyu/senpi": "2026.10.16",
99318
99402
  "@modelcontextprotocol/sdk": "^1.30.0",
99319
99403
  "@oh-my-opencode/agents-md-core": "workspace:*",
99320
99404
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -99365,7 +99449,7 @@ var package_default = {
99365
99449
  "js-yaml": "^5.4.2",
99366
99450
  "jsonc-parser": "^3.3.1",
99367
99451
  "node-pty": "^1.1.0",
99368
- omowright: "github:code-yeongyu/omowright#293ca5002cbd4c8b0c104c5934683385ef7e0d3a",
99452
+ omowright: "github:code-yeongyu/omowright#f87ef4d63fe7b56fe117dbab246c8ad88dd8a780",
99369
99453
  picocolors: "^1.1.1",
99370
99454
  picomatch: "^4.0.7",
99371
99455
  "posthog-node": "^5.52.4",
@@ -99373,18 +99457,18 @@ var package_default = {
99373
99457
  typescript: "^7.0.2"
99374
99458
  },
99375
99459
  optionalDependencies: {
99376
- "oh-my-opencode-darwin-arm64": "5.1.32",
99377
- "oh-my-opencode-darwin-x64": "5.1.32",
99378
- "oh-my-opencode-darwin-x64-baseline": "5.1.32",
99379
- "oh-my-opencode-linux-arm64": "5.1.32",
99380
- "oh-my-opencode-linux-arm64-musl": "5.1.32",
99381
- "oh-my-opencode-linux-x64": "5.1.32",
99382
- "oh-my-opencode-linux-x64-baseline": "5.1.32",
99383
- "oh-my-opencode-linux-x64-musl": "5.1.32",
99384
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.32",
99385
- "oh-my-opencode-windows-arm64": "5.1.32",
99386
- "oh-my-opencode-windows-x64": "5.1.32",
99387
- "oh-my-opencode-windows-x64-baseline": "5.1.32"
99460
+ "oh-my-opencode-darwin-arm64": "5.1.34",
99461
+ "oh-my-opencode-darwin-x64": "5.1.34",
99462
+ "oh-my-opencode-darwin-x64-baseline": "5.1.34",
99463
+ "oh-my-opencode-linux-arm64": "5.1.34",
99464
+ "oh-my-opencode-linux-arm64-musl": "5.1.34",
99465
+ "oh-my-opencode-linux-x64": "5.1.34",
99466
+ "oh-my-opencode-linux-x64-baseline": "5.1.34",
99467
+ "oh-my-opencode-linux-x64-musl": "5.1.34",
99468
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.34",
99469
+ "oh-my-opencode-windows-arm64": "5.1.34",
99470
+ "oh-my-opencode-windows-x64": "5.1.34",
99471
+ "oh-my-opencode-windows-x64-baseline": "5.1.34"
99388
99472
  },
99389
99473
  overrides: {
99390
99474
  hono: "^4.13.8",
@@ -100605,7 +100689,8 @@ function runOpenCodeStartupMigration(options) {
100605
100689
  migratedFrom: [],
100606
100690
  reloadRequired: false,
100607
100691
  results: [],
100608
- skippedConflictCount: 0
100692
+ skippedConflictCount: 0,
100693
+ targetBackupPaths: []
100609
100694
  };
100610
100695
  }
100611
100696
  try {
@@ -100629,12 +100714,18 @@ function runOpenCodeStartupMigration(options) {
100629
100714
  })
100630
100715
  });
100631
100716
  const migratedFrom = uniqueSources(batch.results);
100717
+ const targetBackupPaths = [
100718
+ ...batch.resumedTargetBackupPath === undefined ? [] : [batch.resumedTargetBackupPath],
100719
+ ...batch.results.flatMap((result) => result.targetBackupPath === undefined ? [] : [result.targetBackupPath])
100720
+ ];
100632
100721
  return {
100633
100722
  journalResumed: batch.journalResumed,
100634
100723
  migratedFrom,
100635
100724
  reloadRequired: batch.journalResumed || migratedFrom.length > 0,
100636
100725
  results: batch.results,
100637
100726
  skippedConflictCount: skippedConflictCount(batch.results),
100727
+ targetBackupPaths,
100728
+ ...batch.resumedTargetBackupPath === undefined ? {} : { resumedTargetBackupPath: batch.resumedTargetBackupPath },
100638
100729
  ...batch.status === "locked" ? { error: "Configuration migration is already running" } : {}
100639
100730
  };
100640
100731
  } catch (error) {
@@ -100644,7 +100735,8 @@ function runOpenCodeStartupMigration(options) {
100644
100735
  migratedFrom: [],
100645
100736
  reloadRequired: false,
100646
100737
  results: [],
100647
- skippedConflictCount: 0
100738
+ skippedConflictCount: 0,
100739
+ targetBackupPaths: []
100648
100740
  };
100649
100741
  }
100650
100742
  }
@@ -142452,17 +142544,24 @@ class ConcurrencyManager {
142452
142544
  }
142453
142545
  return model;
142454
142546
  }
142455
- async acquire(model, taskId) {
142547
+ tryAcquire(model) {
142456
142548
  const key = this.getConcurrencyKey(model);
142457
142549
  const limit = this.getConcurrencyLimit(model);
142458
142550
  if (limit === Infinity) {
142459
- return;
142551
+ return true;
142460
142552
  }
142461
142553
  const current = this.counts.get(key) ?? 0;
142462
142554
  if (current < limit) {
142463
142555
  this.counts.set(key, current + 1);
142556
+ return true;
142557
+ }
142558
+ return false;
142559
+ }
142560
+ async acquire(model, taskId) {
142561
+ if (this.tryAcquire(model)) {
142464
142562
  return;
142465
142563
  }
142564
+ const key = this.getConcurrencyKey(model);
142466
142565
  return new Promise((resolve, reject) => {
142467
142566
  const queue = this.queues.get(key) ?? [];
142468
142567
  const entry = {
@@ -142765,9 +142864,17 @@ async function tryFallbackRetry(args) {
142765
142864
  const rawKey = task.model ? `${task.model.providerID}/${task.model.modelID}` : task.agent;
142766
142865
  const key = concurrencyManager.getConcurrencyKey(rawKey);
142767
142866
  const queue = queuesByKey.get(key) ?? [];
142867
+ const hasContinuation = !!task.originalPrompt && task.originalPrompt !== task.prompt;
142868
+ const retryPrompt = hasContinuation ? `${task.originalPrompt}
142869
+
142870
+ [Latest continuation instruction]
142871
+ ${task.prompt}` : task.prompt;
142872
+ const retryNote = hasContinuation ? `
142873
+
142874
+ [Note: this is a retry in a new session after the previous session failed; the earlier session's context is not available. Work from the original task and latest continuation instruction above.]` : "";
142768
142875
  const retryInput = {
142769
142876
  description: task.description,
142770
- prompt: task.prompt,
142877
+ prompt: `${retryPrompt}${retryNote}`,
142771
142878
  agent: task.agent,
142772
142879
  parentSessionId: task.parentSessionId,
142773
142880
  parentMessageId: task.parentMessageId,
@@ -144701,6 +144808,25 @@ class TaskHistory {
144701
144808
  clearSession(parentSessionID) {
144702
144809
  this.entries.delete(parentSessionID);
144703
144810
  }
144811
+ restoreEntry(parentSessionID, taskID, entry) {
144812
+ const list = this.entries.get(parentSessionID);
144813
+ if (!list) {
144814
+ if (entry)
144815
+ this.record(parentSessionID, entry);
144816
+ return;
144817
+ }
144818
+ const index = list.findIndex((current) => current.id === taskID);
144819
+ if (index !== -1) {
144820
+ if (entry)
144821
+ list[index] = { ...entry };
144822
+ else
144823
+ list.splice(index, 1);
144824
+ } else if (entry) {
144825
+ this.record(parentSessionID, entry);
144826
+ }
144827
+ if (list.length === 0)
144828
+ this.entries.delete(parentSessionID);
144829
+ }
144704
144830
  clearAll() {
144705
144831
  this.entries.clear();
144706
144832
  }
@@ -145300,6 +145426,7 @@ class BackgroundManager {
145300
145426
  processingKeys = new Set;
145301
145427
  completionTimers = new Map;
145302
145428
  syncAttachedSessions = new Set;
145429
+ resumesAwaitingAdmission = new Set;
145303
145430
  completedTaskArchive = new Map;
145304
145431
  completedTaskSummaries = new Map;
145305
145432
  idleDeferralTimers = new Map;
@@ -145506,8 +145633,12 @@ class BackgroundManager {
145506
145633
  taskIDs.add(task.id);
145507
145634
  this.tasksByParentSession.set(parentSessionID, taskIDs);
145508
145635
  }
145509
- captureResumeTaskSnapshot(task) {
145636
+ captureResumeTaskSnapshot(task, resumeParentSessionID) {
145510
145637
  return {
145638
+ prompt: task.prompt,
145639
+ originalPrompt: task.originalPrompt,
145640
+ queuedAt: task.queuedAt,
145641
+ historyEntry: this.taskHistory.getByParentSession(resumeParentSessionID).find((entry) => entry.id === task.id),
145511
145642
  status: task.status,
145512
145643
  completedAt: task.completedAt,
145513
145644
  error: task.error,
@@ -145523,16 +145654,23 @@ class BackgroundManager {
145523
145654
  };
145524
145655
  }
145525
145656
  restoreTaskAfterSkippedResume(task, snapshot, skippedStatus) {
145657
+ if (this.shutdownTriggered)
145658
+ return;
145526
145659
  log2("[background-agent] Restoring task after skipped resume prompt:", {
145527
145660
  taskId: task.id,
145528
145661
  sessionID: task.sessionId,
145529
145662
  skippedStatus
145530
145663
  });
145664
+ const resumeParentSessionID = task.parentSessionId;
145531
145665
  this.cleanupPendingByParent(task);
145666
+ this.taskHistory.restoreEntry(resumeParentSessionID, task.id, snapshot.historyEntry);
145532
145667
  if (task.concurrencyKey) {
145533
145668
  this.concurrencyManager.release(task.concurrencyKey);
145534
145669
  }
145535
145670
  task.status = snapshot.status;
145671
+ task.prompt = snapshot.prompt;
145672
+ task.originalPrompt = snapshot.originalPrompt;
145673
+ task.queuedAt = snapshot.queuedAt;
145536
145674
  task.completedAt = snapshot.completedAt;
145537
145675
  task.error = snapshot.error;
145538
145676
  task.startedAt = snapshot.startedAt;
@@ -145549,6 +145687,9 @@ class BackgroundManager {
145549
145687
  this.scheduleTaskRemoval(task.id);
145550
145688
  }
145551
145689
  this.updateBackgroundTaskMarker(task.parentSessionId);
145690
+ if (resumeParentSessionID !== task.parentSessionId) {
145691
+ this.updateBackgroundTaskMarker(resumeParentSessionID);
145692
+ }
145552
145693
  }
145553
145694
  removeTaskFromParentIndex(taskID, parentSessionID) {
145554
145695
  if (!parentSessionID) {
@@ -146145,14 +146286,74 @@ The fallback retry session is now created and can be inspected directly.
146145
146286
  if (existingTask.status === "running") {
146146
146287
  throw new Error(`Task ${existingTask.id} is currently running and cannot accept a continuation prompt. ` + "Wait for it to complete before resuming it with task_id.");
146147
146288
  }
146148
- const resumeSnapshot = this.captureResumeTaskSnapshot(existingTask);
146289
+ if (existingTask.status === "pending" && existingTask.pendingResume) {
146290
+ throw new Error(`Task ${existingTask.id} already has a continuation queued for admission and cannot accept another one. ` + "Wait for it to start and complete before resuming it with task_id.");
146291
+ }
146292
+ if (this.resumesAwaitingAdmission.has(existingTask.id)) {
146293
+ throw new Error(`Task ${existingTask.id} is already being resumed and cannot accept another continuation. ` + "Wait for it to complete before resuming it with task_id.");
146294
+ }
146295
+ const resumeSnapshot = this.captureResumeTaskSnapshot(existingTask, input.parentSessionId);
146149
146296
  const completionTimer = this.completionTimers.get(existingTask.id);
146150
146297
  if (completionTimer) {
146151
146298
  clearTimeout(completionTimer);
146152
146299
  this.completionTimers.delete(existingTask.id);
146153
146300
  }
146154
146301
  const concurrencyKey = this.concurrencyManager.getConcurrencyKey(existingTask.concurrencyGroup ?? existingTask.agent);
146155
- await this.concurrencyManager.acquire(concurrencyKey);
146302
+ if (this.concurrencyManager.tryAcquire(concurrencyKey)) {
146303
+ this.resumesAwaitingAdmission.add(existingTask.id);
146304
+ try {
146305
+ await Promise.resolve();
146306
+ return await this.startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey);
146307
+ } finally {
146308
+ this.resumesAwaitingAdmission.delete(existingTask.id);
146309
+ }
146310
+ }
146311
+ const admission = this.concurrencyManager.acquire(concurrencyKey, existingTask.id);
146312
+ const resumeToken = {};
146313
+ existingTask.pendingResume = resumeToken;
146314
+ existingTask.status = "pending";
146315
+ existingTask.queuedAt = new Date;
146316
+ existingTask.completedAt = undefined;
146317
+ existingTask.error = undefined;
146318
+ this.updateTaskParent(existingTask, input.parentSessionId);
146319
+ existingTask.parentMessageId = input.parentMessageId;
146320
+ existingTask.parentModel = input.parentModel;
146321
+ existingTask.parentAgent = input.parentAgent;
146322
+ if (input.parentTools)
146323
+ existingTask.parentTools = input.parentTools;
146324
+ if (input.parentSessionId) {
146325
+ const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set;
146326
+ pending.add(existingTask.id);
146327
+ this.pendingByParent.set(input.parentSessionId, pending);
146328
+ }
146329
+ this.taskHistory.record(input.parentSessionId, { id: existingTask.id, sessionID: existingTask.sessionId, agent: existingTask.agent, description: existingTask.description, status: "pending", category: existingTask.category });
146330
+ getTaskToastManager()?.addTask({ id: existingTask.id, description: existingTask.description, agent: existingTask.agent, isBackground: true, status: "queued" });
146331
+ this.updateBackgroundTaskMarker(existingTask.parentSessionId);
146332
+ admission.then(async () => {
146333
+ if (this.shutdownTriggered || existingTask.status !== "pending" || existingTask.pendingResume !== resumeToken) {
146334
+ this.concurrencyManager.release(concurrencyKey);
146335
+ return;
146336
+ }
146337
+ await this.startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey);
146338
+ }).catch((error) => {
146339
+ this.logger("[background-agent] Queued resume was not admitted:", { taskId: existingTask.id, error: error instanceof Error ? error.message : String(error) });
146340
+ if (this.shutdownTriggered || existingTask.status !== "pending" || existingTask.pendingResume !== resumeToken)
146341
+ return;
146342
+ existingTask.pendingResume = undefined;
146343
+ this.restoreTaskAfterSkippedResume(existingTask, resumeSnapshot, "unavailable");
146344
+ });
146345
+ return existingTask;
146346
+ }
146347
+ async startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey) {
146348
+ if (this.shutdownTriggered) {
146349
+ this.concurrencyManager.release(concurrencyKey);
146350
+ return existingTask;
146351
+ }
146352
+ if (!existingTask.sessionId)
146353
+ throw new Error(`Task has no sessionID: ${existingTask.id}`);
146354
+ existingTask.pendingResume = undefined;
146355
+ existingTask.originalPrompt ??= existingTask.prompt;
146356
+ existingTask.prompt = input.prompt;
146156
146357
  existingTask.concurrencyKey = concurrencyKey;
146157
146358
  existingTask.concurrencyGroup = concurrencyKey;
146158
146359
  existingTask.status = "running";
@@ -146166,6 +146367,15 @@ The fallback retry session is now created and can be inspected directly.
146166
146367
  existingTask.parentTools = input.parentTools;
146167
146368
  }
146168
146369
  existingTask.startedAt = new Date;
146370
+ this.taskHistory.record(input.parentSessionId, {
146371
+ id: existingTask.id,
146372
+ sessionID: existingTask.sessionId,
146373
+ agent: existingTask.agent,
146374
+ description: existingTask.description,
146375
+ status: "running",
146376
+ category: existingTask.category,
146377
+ startedAt: existingTask.startedAt
146378
+ });
146169
146379
  existingTask.progress = {
146170
146380
  toolCalls: existingTask.progress?.toolCalls ?? 0,
146171
146381
  toolCallWindow: existingTask.progress?.toolCallWindow,
@@ -146276,6 +146486,16 @@ The fallback retry session is now created and can be inspected directly.
146276
146486
  const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error));
146277
146487
  existingTask.error = errorMessage;
146278
146488
  existingTask.completedAt = new Date;
146489
+ this.taskHistory.record(existingTask.parentSessionId, {
146490
+ id: existingTask.id,
146491
+ sessionID: existingTask.sessionId,
146492
+ agent: existingTask.agent,
146493
+ description: existingTask.description,
146494
+ status: "interrupt",
146495
+ category: existingTask.category,
146496
+ startedAt: existingTask.startedAt,
146497
+ completedAt: existingTask.completedAt
146498
+ });
146279
146499
  if (existingTask.rootSessionId) {
146280
146500
  this.unregisterRootDescendant(existingTask.rootSessionId);
146281
146501
  }
@@ -147596,7 +147816,17 @@ The task was re-queued on a fallback model after a retryable failure.
147596
147816
  this.stopPolling();
147597
147817
  const trackedSessionIDs = new Set;
147598
147818
  const abortRequests = [];
147819
+ const queuedResumeParents = new Set;
147599
147820
  for (const task of this.tasks.values()) {
147821
+ if (task.pendingResume) {
147822
+ queuedResumeParents.add(task.parentSessionId);
147823
+ task.pendingResume = undefined;
147824
+ task.status = "cancelled";
147825
+ task.completedAt = new Date;
147826
+ this.concurrencyManager.cancelWaiter(task.concurrencyGroup ?? task.agent, task.id);
147827
+ this.cleanupPendingByParent(task);
147828
+ removeTaskToastTracking(task.id);
147829
+ }
147600
147830
  if (task.sessionId) {
147601
147831
  trackedSessionIDs.add(task.sessionId);
147602
147832
  }
@@ -147662,6 +147892,9 @@ The task was re-queued on a fallback model after a retryable failure.
147662
147892
  this.queuesByKey.clear();
147663
147893
  this.processingKeys.clear();
147664
147894
  this.taskHistory.clearAll();
147895
+ for (const parentSessionID of queuedResumeParents) {
147896
+ this.updateBackgroundTaskMarker(parentSessionID);
147897
+ }
147665
147898
  this.completedTaskSummaries.clear();
147666
147899
  this.unregisterProcessCleanup();
147667
147900
  log2("[background-agent] Shutdown complete");
@@ -187988,11 +188221,12 @@ function startupToastBody(input) {
187988
188221
  const summary = input.migratedFrom.length === 0 ? "" : `Migrated ${input.migratedFrom.length} legacy source${input.migratedFrom.length === 1 ? "" : "s"}.`;
187989
188222
  const conflicts = input.skippedConflictCount === 0 ? "" : ` Kept ${input.skippedConflictCount} existing value${input.skippedConflictCount === 1 ? "" : "s"}.`;
187990
188223
  const diagnostics = input.diagnostics.length === 0 ? "" : ` ${input.diagnostics.join(" ")}`;
188224
+ const backups = input.targetBackupPaths.length === 0 ? "" : ` Previous config saved as ${input.targetBackupPaths.join(", ")}.`;
187991
188225
  if (input.error !== undefined) {
187992
- return { title: "Configuration migration failed", message: `${input.error}${diagnostics}`, variant: "error" };
188226
+ return { title: "Configuration migration failed", message: `${input.error}${backups}${diagnostics}`, variant: "error" };
187993
188227
  }
187994
- if (summary.length > 0)
187995
- return { title: "Configuration migrated", message: `${summary}${conflicts}${diagnostics}`, variant: "success" };
188228
+ if (summary.length > 0 || backups.length > 0)
188229
+ return { title: "Configuration migrated", message: `${summary}${conflicts}${backups}${diagnostics}`.trim(), variant: "success" };
187996
188230
  if (diagnostics.length > 0)
187997
188231
  return { title: "Configuration diagnostics", message: input.diagnostics.join(" "), variant: "warning" };
187998
188232
  return;
@@ -188015,7 +188249,8 @@ function createPluginModule(overrides = {}) {
188015
188249
  error: startupMigration.error,
188016
188250
  journalResumed: startupMigration.journalResumed,
188017
188251
  migratedFrom: startupMigration.migratedFrom,
188018
- skippedConflictCount: startupMigration.skippedConflictCount
188252
+ skippedConflictCount: startupMigration.skippedConflictCount,
188253
+ targetBackupPaths: startupMigration.targetBackupPaths
188019
188254
  });
188020
188255
  if (startupMigration.error !== undefined) {
188021
188256
  console.warn(`[config-migration] legacy configuration changes were not applied: ${startupMigration.error}`);
@@ -188024,7 +188259,8 @@ function createPluginModule(overrides = {}) {
188024
188259
  diagnostics: startupDiagnostics,
188025
188260
  ...startupMigration.error === undefined ? {} : { error: startupMigration.error },
188026
188261
  migratedFrom: startupMigration.migratedFrom,
188027
- skippedConflictCount: startupMigration.skippedConflictCount
188262
+ skippedConflictCount: startupMigration.skippedConflictCount,
188263
+ targetBackupPaths: startupMigration.targetBackupPaths
188028
188264
  });
188029
188265
  if (toast !== undefined)
188030
188266
  showStartupToast(input.client, toast, deps.log);