oh-my-opencode 5.1.61 → 5.1.62

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 (120) hide show
  1. package/.agents/command/remove-deadcode.md +1 -1
  2. package/.agents/skills/remove-deadcode/SKILL.md +1 -1
  3. package/.opencode/command/remove-deadcode.md +1 -1
  4. package/dist/cli/index.js +154 -27
  5. package/dist/cli/service-auth/index.d.ts +2 -1
  6. package/dist/cli/service-auth/session.d.ts +27 -0
  7. package/dist/cli/service-auth/sync-pin-retire.d.ts +7 -0
  8. package/dist/cli-node/index.js +154 -27
  9. package/dist/config/schema/agent-names.d.ts +1 -0
  10. package/dist/index.js +645 -189
  11. package/dist/skills/browser/references/owned-engine/README.md +1 -1
  12. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  13. package/dist/skills/frontend/SKILL.md +2 -2
  14. package/dist/skills/frontend/references/design/README.md +2 -2
  15. package/dist/skills/frontend/references/design/component-catalogs.md +1 -1
  16. package/dist/skills/frontend/references/design/design-system-architecture.md +1 -1
  17. package/dist/skills/frontend/references/design/react-dev-tooling-skill.md +1 -1
  18. package/dist/skills/frontend/references/designpowers/lane-a-direction.md +5 -5
  19. package/dist/skills/frontend/references/designpowers/routing.md +2 -2
  20. package/dist/skills/frontend/references/perfection/README.md +1 -1
  21. package/dist/skills/programming/references/python/libraries.md +3 -3
  22. package/dist/skills/programming/references/python/one-liners.md +1 -1
  23. package/dist/skills/programming/references/python/pyproject-strict.md +1 -1
  24. package/dist/skills/programming/references/rust/libraries.md +2 -2
  25. package/dist/skills/refactor/SKILL.md +1 -1
  26. package/dist/skills/ultimate-browsing/SKILL.md +1 -1
  27. package/dist/skills/ultimate-browsing/references/insane-search/README.md +1 -1
  28. package/dist/skills/ulw-execute/SKILL.md +1 -1
  29. package/dist/skills/ulw-research/SKILL.md +2 -2
  30. package/dist/tui.js +15 -14
  31. package/package.json +14 -14
  32. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  33. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  38. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  40. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/lcx/skills/lcx-doctor/SKILL.md +1 -1
  42. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  43. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  44. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  52. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  56. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  78. package/packages/omo-codex/plugin/package-lock.json +12 -12
  79. package/packages/omo-codex/plugin/package.json +1 -1
  80. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +1 -1
  81. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  82. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +2 -2
  83. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  84. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +1 -1
  85. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +1 -1
  86. package/packages/omo-codex/plugin/skills/frontend/references/design/react-dev-tooling-skill.md +1 -1
  87. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-a-direction.md +5 -5
  88. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/routing.md +2 -2
  89. package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +1 -1
  90. package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +1 -1
  91. package/packages/omo-codex/plugin/skills/programming/references/python/libraries.md +3 -3
  92. package/packages/omo-codex/plugin/skills/programming/references/python/one-liners.md +1 -1
  93. package/packages/omo-codex/plugin/skills/programming/references/python/pyproject-strict.md +1 -1
  94. package/packages/omo-codex/plugin/skills/programming/references/rust/libraries.md +2 -2
  95. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +1 -1
  96. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +1 -1
  97. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +1 -1
  98. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  99. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +2 -2
  100. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  101. package/packages/shared-skills/skills/browser/references/owned-engine/README.md +1 -1
  102. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  103. package/packages/shared-skills/skills/frontend/SKILL.md +2 -2
  104. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  105. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +1 -1
  106. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +1 -1
  107. package/packages/shared-skills/skills/frontend/references/design/react-dev-tooling-skill.md +1 -1
  108. package/packages/shared-skills/skills/frontend/references/designpowers/lane-a-direction.md +5 -5
  109. package/packages/shared-skills/skills/frontend/references/designpowers/routing.md +2 -2
  110. package/packages/shared-skills/skills/frontend/references/perfection/README.md +1 -1
  111. package/packages/shared-skills/skills/programming/references/python/libraries.md +3 -3
  112. package/packages/shared-skills/skills/programming/references/python/one-liners.md +1 -1
  113. package/packages/shared-skills/skills/programming/references/python/pyproject-strict.md +1 -1
  114. package/packages/shared-skills/skills/programming/references/rust/libraries.md +2 -2
  115. package/packages/shared-skills/skills/refactor/SKILL.md +1 -1
  116. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +1 -1
  117. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +1 -1
  118. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  119. package/packages/shared-skills/skills/ulw-research/SKILL.md +2 -2
  120. package/.opencode/skills/hyperplan/SKILL.md +0 -450
@@ -114,7 +114,7 @@ For EACH batch, fire a deep agent:
114
114
  ```
115
115
  task(
116
116
  category="deep-low",
117
- load_skills=["typescript-programmer", "git-master"],
117
+ load_skills=["programming", "git-master"],
118
118
  run_in_background=true,
119
119
  description="Remove dead code batch N: [brief description]",
120
120
  prompt="[see template below]"
@@ -114,7 +114,7 @@ For EACH batch, fire a deep agent:
114
114
  ```
115
115
  task(
116
116
  category="deep-low",
117
- load_skills=["typescript-programmer", "git-master"],
117
+ load_skills=["programming", "git-master"],
118
118
  run_in_background=true,
119
119
  description="Remove dead code batch N: [brief description]",
120
120
  prompt="[see template below]"
@@ -114,7 +114,7 @@ For EACH batch, fire a deep agent:
114
114
  ```
115
115
  task(
116
116
  category="deep",
117
- load_skills=["typescript-programmer", "git-master"],
117
+ load_skills=["programming", "git-master"],
118
118
  run_in_background=true,
119
119
  description="Remove dead code batch N: [brief description]",
120
120
  prompt="[see template below]"
package/dist/cli/index.js CHANGED
@@ -2132,7 +2132,7 @@ var package_default;
2132
2132
  var init_package = __esm(() => {
2133
2133
  package_default = {
2134
2134
  name: "oh-my-opencode",
2135
- version: "5.1.61",
2135
+ version: "5.1.62",
2136
2136
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
2137
2137
  main: "./dist/index.js",
2138
2138
  types: "dist/index.d.ts",
@@ -2319,7 +2319,7 @@ var init_package = __esm(() => {
2319
2319
  },
2320
2320
  devDependencies: {
2321
2321
  "@clack/prompts": "^1.8.1",
2322
- "@code-yeongyu/senpi": "2026.11.3-3",
2322
+ "@code-yeongyu/senpi": "2026.11.5-1",
2323
2323
  "@modelcontextprotocol/sdk": "^1.30.0",
2324
2324
  "@oh-my-opencode/agents-md-core": "workspace:*",
2325
2325
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -2384,18 +2384,18 @@ var init_package = __esm(() => {
2384
2384
  typescript: "^7.0.2"
2385
2385
  },
2386
2386
  optionalDependencies: {
2387
- "oh-my-opencode-darwin-arm64": "5.1.61",
2388
- "oh-my-opencode-darwin-x64": "5.1.61",
2389
- "oh-my-opencode-darwin-x64-baseline": "5.1.61",
2390
- "oh-my-opencode-linux-arm64": "5.1.61",
2391
- "oh-my-opencode-linux-arm64-musl": "5.1.61",
2392
- "oh-my-opencode-linux-x64": "5.1.61",
2393
- "oh-my-opencode-linux-x64-baseline": "5.1.61",
2394
- "oh-my-opencode-linux-x64-musl": "5.1.61",
2395
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.61",
2396
- "oh-my-opencode-windows-arm64": "5.1.61",
2397
- "oh-my-opencode-windows-x64": "5.1.61",
2398
- "oh-my-opencode-windows-x64-baseline": "5.1.61"
2387
+ "oh-my-opencode-darwin-arm64": "5.1.62",
2388
+ "oh-my-opencode-darwin-x64": "5.1.62",
2389
+ "oh-my-opencode-darwin-x64-baseline": "5.1.62",
2390
+ "oh-my-opencode-linux-arm64": "5.1.62",
2391
+ "oh-my-opencode-linux-arm64-musl": "5.1.62",
2392
+ "oh-my-opencode-linux-x64": "5.1.62",
2393
+ "oh-my-opencode-linux-x64-baseline": "5.1.62",
2394
+ "oh-my-opencode-linux-x64-musl": "5.1.62",
2395
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.62",
2396
+ "oh-my-opencode-windows-arm64": "5.1.62",
2397
+ "oh-my-opencode-windows-x64": "5.1.62",
2398
+ "oh-my-opencode-windows-x64-baseline": "5.1.62"
2399
2399
  },
2400
2400
  overrides: {
2401
2401
  hono: "^4.13.8",
@@ -92710,7 +92710,7 @@ var package_default2;
92710
92710
  var init_package2 = __esm(() => {
92711
92711
  package_default2 = {
92712
92712
  name: "@oh-my-opencode/omo-codex",
92713
- version: "5.1.61",
92713
+ version: "5.1.62",
92714
92714
  type: "module",
92715
92715
  private: true,
92716
92716
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -104373,6 +104373,7 @@ var BuiltinSkillNameSchema = _enum([
104373
104373
  "debugging",
104374
104374
  "security-research",
104375
104375
  "security-review",
104376
+ "hyperplan",
104376
104377
  "visual-qa",
104377
104378
  "team-mode"
104378
104379
  ]);
@@ -109471,8 +109472,8 @@ var package_default3 = {
109471
109472
  },
109472
109473
  dependencies: {
109473
109474
  "@clack/prompts": "^1.8.1",
109474
- "@code-yeongyu/senpi": "2026.11.3-3",
109475
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.3-3",
109475
+ "@code-yeongyu/senpi": "2026.11.5-1",
109476
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.5-1",
109476
109477
  "@modelcontextprotocol/sdk": "^1.30.0",
109477
109478
  "@oh-my-opencode/agents-md-core": "workspace:*",
109478
109479
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -112744,6 +112745,24 @@ function retryOnEintrSync(operation) {
112744
112745
 
112745
112746
  // packages/memory-core/src/fs/rename-contention.ts
112746
112747
  var CONTENTION_CODES = new Set(["EPERM", "EBUSY", "EACCES"]);
112748
+ var CONTENTION_DELAYS_MS = [10, 25, 50, 100, 200, 400];
112749
+ var sleepFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
112750
+ async function renameWithContentionRetry(from, to, deps) {
112751
+ const delays = deps.platform === "win32" ? deps.delaysMs ?? CONTENTION_DELAYS_MS : [];
112752
+ const sleep = deps.sleep ?? sleepFor;
112753
+ for (let attempt = 0;; attempt += 1) {
112754
+ try {
112755
+ await retryOnEintr(() => deps.admit === undefined ? deps.rename(from, to) : deps.admit(() => deps.rename(from, to)));
112756
+ return;
112757
+ } catch (error) {
112758
+ const delay = delays[attempt];
112759
+ const code = fsErrorCode(error);
112760
+ if (delay === undefined || code === undefined || !CONTENTION_CODES.has(code))
112761
+ throw error;
112762
+ await sleep(delay);
112763
+ }
112764
+ }
112765
+ }
112747
112766
 
112748
112767
  // packages/memory-core/src/fs/resilient.ts
112749
112768
  function isFunction2(value) {
@@ -112776,6 +112795,13 @@ function resilientNamespace(namespace, kind) {
112776
112795
  }
112777
112796
  var IDEMPOTENT_HANDLE_METHODS = new Set(["stat", "sync", "datasync", "chmod", "chown", "utimes", "truncate"]);
112778
112797
  var promises = resilientNamespace(fsp, "async");
112798
+ function rename8(from, to, admit) {
112799
+ return renameWithContentionRetry(from, to, {
112800
+ rename: (source, target) => fsp.rename(source, target),
112801
+ platform: process.platform,
112802
+ ...admit === undefined ? {} : { admit }
112803
+ });
112804
+ }
112779
112805
  var sync = resilientNamespace(fsSync, "sync");
112780
112806
  var {
112781
112807
  access: access3,
@@ -113671,15 +113697,60 @@ async function loginLoopback(session, options) {
113671
113697
  }
113672
113698
  }
113673
113699
 
113674
- // packages/omo-opencode/src/cli/service-auth/session.ts
113700
+ // packages/memory-core/src/sync/device-retire.ts
113701
+ import { createHash as createHash14, randomBytes as randomBytes6 } from "crypto";
113702
+ import { join as join109 } from "path";
113703
+
113704
+ // packages/memory-core/src/identity/layout.ts
113675
113705
  import { homedir as homedir32 } from "os";
113706
+ import { join as join107, resolve as resolve31 } from "path";
113707
+ var MEMORY_ROOT_ENV_VAR = "OMO_MEMORY_HOME";
113708
+ function defaultMemoryRoot() {
113709
+ return join107(homedir32(), ".omo", "memory");
113710
+ }
113711
+ function resolveMemoryRoot(env, cwd) {
113712
+ const override = env[MEMORY_ROOT_ENV_VAR];
113713
+ if (override === undefined || override.trim() === "") {
113714
+ return defaultMemoryRoot();
113715
+ }
113716
+ return resolve31(cwd, override);
113717
+ }
113718
+ // packages/memory-core/src/sync/pin-dir.ts
113719
+ import { join as join108 } from "path";
113720
+ var syncPinDir = (memoryRoot) => join108(memoryRoot, "sync-device");
113721
+
113722
+ // packages/memory-core/src/sync/device-retire.ts
113723
+ var trustAnchorRoot = (pinDir) => join109(pinDir, "trust-anchor");
113724
+ var trustAnchorKey = (account) => createHash14("sha256").update(account, "utf8").digest("base64url");
113725
+ var trustAnchorDir = (pinDir, account) => join109(trustAnchorRoot(pinDir), trustAnchorKey(account));
113726
+ var hasLiveTrustAnchor = async (pinDir) => {
113727
+ try {
113728
+ return (await readdir17(trustAnchorRoot(pinDir))).some((name) => !name.includes(".retired-"));
113729
+ } catch (error) {
113730
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
113731
+ return false;
113732
+ throw error;
113733
+ }
113734
+ };
113735
+ var retireSyncDevice = async (input) => {
113736
+ const dir = trustAnchorDir(input.pinDir, input.account);
113737
+ try {
113738
+ await rename8(dir, `${dir}.retired-${Date.now()}-${randomBytes6(4).toString("hex")}`);
113739
+ } catch (error) {
113740
+ if (!(error instanceof Error && ("code" in error) && error.code === "ENOENT"))
113741
+ throw error;
113742
+ }
113743
+ };
113744
+
113745
+ // packages/omo-opencode/src/cli/service-auth/session.ts
113746
+ import { homedir as homedir33 } from "os";
113676
113747
 
113677
113748
  // packages/omo-opencode/src/cli/service-auth/signed-out-marker.ts
113678
113749
  init_zod();
113679
- import { mkdir as mkdir12, readFile as readFile35, rename as rename8, rm as rm17, writeFile as writeFile17 } from "fs/promises";
113680
- import { dirname as dirname43, join as join107 } from "path";
113750
+ import { mkdir as mkdir12, readFile as readFile35, rename as rename9, rm as rm17, writeFile as writeFile17 } from "fs/promises";
113751
+ import { dirname as dirname43, join as join110 } from "path";
113681
113752
  var markerSchema = object({ device: string2().nullable(), at: string2() });
113682
- var signedOutMarkerPath = (home, credential) => join107(home, ".omo", "account-state", `${credential}.signed-out.json`);
113753
+ var signedOutMarkerPath = (home, credential) => join110(home, ".omo", "account-state", `${credential}.signed-out.json`);
113683
113754
  async function readSignedOutMarker(path) {
113684
113755
  const text = await readFile35(path, "utf8").catch(() => null);
113685
113756
  if (text === null)
@@ -113697,7 +113768,7 @@ async function writeSignedOutMarker(path, device) {
113697
113768
  await mkdir12(dirname43(path), { recursive: true, mode: 448 });
113698
113769
  const staging = `${path}.${process.pid}.tmp`;
113699
113770
  await writeFile17(staging, JSON.stringify({ device, at: new Date().toISOString() }), { mode: 384 });
113700
- await rename8(staging, path);
113771
+ await rename9(staging, path);
113701
113772
  }
113702
113773
  var clearSignedOutMarker = (path) => rm17(path, { force: true });
113703
113774
 
@@ -113712,11 +113783,18 @@ class SessionEnded extends SignInError {
113712
113783
  }
113713
113784
  }
113714
113785
  var DEVICE_REVOKING_REFUSALS = new Set(["account_deleted", "reauth_required", "device_revoked"]);
113786
+ var discardedOf = (credentials, cause) => ({
113787
+ kind: "credential",
113788
+ deviceId: credentials.device.id,
113789
+ accountId: credentials.accountId,
113790
+ owner: credentials.owner,
113791
+ cause
113792
+ });
113715
113793
  function createSession(options) {
113716
113794
  const api = serviceOrigin(options.api);
113717
113795
  const now = options.now ?? Date.now;
113718
- const { store, signal } = options;
113719
- const markerPath = signedOutMarkerPath(options.home ?? homedir32(), credentialId(api));
113796
+ const { store, signal, beforeKeysDiscarded } = options;
113797
+ const markerPath = signedOutMarkerPath(options.home ?? homedir33(), credentialId(api));
113720
113798
  async function locked(action) {
113721
113799
  signal?.throwIfAborted();
113722
113800
  return withCredentialLock(api, async () => {
@@ -113725,6 +113803,11 @@ function createSession(options) {
113725
113803
  }, options.home);
113726
113804
  }
113727
113805
  const request = (path, init) => requestApi(api, path, init, signal);
113806
+ const accountOf = async (access) => {
113807
+ const body = await request("/v1/me", { headers: { authorization: `Bearer ${access}` } }).catch(() => null);
113808
+ const id = typeof body === "object" && body !== null && "account" in body && typeof body.account === "object" && body.account !== null && "id" in body.account ? body.account.id : undefined;
113809
+ return typeof id === "string" && id !== "" ? id : undefined;
113810
+ };
113728
113811
  async function refreshed(current) {
113729
113812
  if (current.refreshState === "uncertain" || current.refreshAttempt !== undefined) {
113730
113813
  throw new SessionEnded(`Refresh recovery can no longer be proved safe. ${signInHint("again")}`, false, "refresh_recovery_expired");
@@ -113741,6 +113824,7 @@ function createSession(options) {
113741
113824
  if (error instanceof CredentialStoreError)
113742
113825
  throw error;
113743
113826
  if (error instanceof ApiRefusal && error.status < 500 && ["unauthorized", "account_deleted", "reauth_required", "invalid_grant", "device_revoked"].includes(error.code)) {
113827
+ await beforeKeysDiscarded(discardedOf(current, "refused"));
113744
113828
  await store.clear();
113745
113829
  throw new SessionEnded(error.code === "reauth_required" ? `This session was ended because its refresh token was reused. ${signInHint("again")}` : refusalMessage(refusalGuidance(error.code, error.status)), DEVICE_REVOKING_REFUSALS.has(error.code), error.code);
113746
113830
  }
@@ -113766,6 +113850,17 @@ function createSession(options) {
113766
113850
  },
113767
113851
  async save(value) {
113768
113852
  await locked(async () => {
113853
+ let stored;
113854
+ try {
113855
+ stored = await store.readAuthoritative();
113856
+ } catch (error) {
113857
+ if (!(error instanceof InvalidCredentialState))
113858
+ throw error;
113859
+ await beforeKeysDiscarded({ kind: "unreadable" });
113860
+ stored = null;
113861
+ }
113862
+ if (stored !== null && stored.device.id !== value.device.id)
113863
+ await beforeKeysDiscarded(discardedOf(stored, "replaced"));
113769
113864
  await store.write(value);
113770
113865
  await clearSignedOutMarker(markerPath);
113771
113866
  });
@@ -113792,12 +113887,16 @@ function createSession(options) {
113792
113887
  async logout() {
113793
113888
  return locked(async () => {
113794
113889
  let current = null;
113890
+ let account;
113795
113891
  try {
113796
113892
  current = await store.readAuthoritative();
113797
113893
  if (current !== null) {
113798
113894
  if (current.refreshState === "uncertain")
113799
113895
  return false;
113800
113896
  const access = Date.parse(current.accessTokenExpiresAt) <= now() ? (await refreshed(current)).accessToken : current.accessToken;
113897
+ if (current.accountId === undefined && current.owner === undefined && await beforeKeysDiscarded.wantsAccount?.() === true) {
113898
+ account = await accountOf(access);
113899
+ }
113801
113900
  await request(`/v1/devices/${encodeURIComponent(current.device.id)}`, {
113802
113901
  method: "DELETE",
113803
113902
  headers: { authorization: `Bearer ${access}` }
@@ -113812,6 +113911,9 @@ function createSession(options) {
113812
113911
  throw error;
113813
113912
  } finally {
113814
113913
  await writeSignedOutMarker(markerPath, current?.device.id ?? null);
113914
+ const stored = current === null ? null : await store.read().catch(() => current);
113915
+ if (stored !== null)
113916
+ await beforeKeysDiscarded(discardedOf(stored.accountId === undefined && account !== undefined ? { ...stored, accountId: account } : stored, "logout"));
113815
113917
  await store.clear();
113816
113918
  }
113817
113919
  });
@@ -113848,6 +113950,26 @@ function createSession(options) {
113848
113950
  };
113849
113951
  }
113850
113952
 
113953
+ // packages/omo-opencode/src/cli/service-auth/sync-pin-retire.ts
113954
+ var reason = (error) => error instanceof Error && ("code" in error) && typeof error.code === "string" ? error.code : "unknown";
113955
+ function retireSyncPinOnDiscard(options) {
113956
+ const hook = async (discarded) => {
113957
+ if (discarded.kind === "unreadable") {
113958
+ options.log("memory sync: a stored sign-in could not be read while it was replaced; its sync pin was left in place");
113959
+ return;
113960
+ }
113961
+ const account = discarded.owner?.accountId ?? discarded.accountId;
113962
+ if (account === undefined)
113963
+ return;
113964
+ try {
113965
+ await retireSyncDevice({ pinDir: options.pinDir(), account });
113966
+ } catch (error) {
113967
+ options.log(`memory sync: the sync pin could not be retired (${reason(error)}); it was left in place`);
113968
+ }
113969
+ };
113970
+ return Object.assign(hook, { wantsAccount: () => hasLiveTrustAnchor(options.pinDir()).catch(() => false) });
113971
+ }
113972
+
113851
113973
  // packages/omo-opencode/src/cli/service-auth/commands.ts
113852
113974
  async function ensureServiceAvailable(session, api) {
113853
113975
  try {
@@ -113890,7 +114012,12 @@ async function run2(action, options) {
113890
114012
  try {
113891
114013
  const api = serviceOrigin(options.api ?? process.env.OMO_SERVICE_API_URL ?? "https://api.omo.dev");
113892
114014
  const store = createCredentialStore(api, { wait: INTERACTIVE_READ_WAIT });
113893
- const session = createSession({ api, store, signal: controller.signal });
114015
+ const session = createSession({
114016
+ api,
114017
+ store,
114018
+ signal: controller.signal,
114019
+ beforeKeysDiscarded: retireSyncPinOnDiscard({ pinDir: () => syncPinDir(resolveMemoryRoot(process.env, process.cwd())), log: (line) => console.error(line) })
114020
+ });
113894
114021
  if (action === "logout") {
113895
114022
  const revoked = await session.logout();
113896
114023
  console.log("Signed out of OmO. Stored credentials and device keys were removed.");
@@ -114158,11 +114285,11 @@ async function boulder(options) {
114158
114285
  // packages/omo-opencode/src/cli/codex-ulw-loop.ts
114159
114286
  import { spawn as spawn6 } from "child_process";
114160
114287
  import { existsSync as existsSync67, readFileSync as readFileSync45, realpathSync as realpathSync14 } from "fs";
114161
- import { homedir as homedir33 } from "os";
114288
+ import { homedir as homedir34 } from "os";
114162
114289
  var ULW_LOOP_DELEGATION_SENTINEL = "OMO_ULW_LOOP_DELEGATED";
114163
114290
  function resolveCodexUlwLoopCommand(input = {}) {
114164
114291
  const env = input.env ?? process.env;
114165
- const homeDir = input.homeDir ?? homedir33();
114292
+ const homeDir = input.homeDir ?? homedir34();
114166
114293
  const localComponentBin = resolveLocalUlwLoopBin(env, homeDir);
114167
114294
  if (localComponentBin !== null)
114168
114295
  return { executable: localComponentBin, argsPrefix: [] };
@@ -2,4 +2,5 @@ export { credentialLockPaths, withCredentialLock } from "./credential-lock";
2
2
  export { currentServiceDevice } from "./protocol";
3
3
  export { CredentialIdentityChanged, type CredentialStore, createCredentialStore, KeychainApprovalPendingError, quietNotice } from "./keystore";
4
4
  export { type Credentials, SignInError, serviceOrigin } from "./protocol";
5
- export { createSession, type Session } from "./session";
5
+ export { type BeforeKeysDiscarded, createSession, type DiscardedCredential, type Session } from "./session";
6
+ export { retireSyncPinOnDiscard } from "./sync-pin-retire";
@@ -1,3 +1,4 @@
1
+ import type { CredentialOwner } from "./credential-owner";
1
2
  import { type CredentialStore } from "./keystore";
2
3
  import { type ChooseDevice, type Credentials, SignInError } from "./protocol";
3
4
  export declare class SessionEnded extends SignInError {
@@ -5,12 +6,33 @@ export declare class SessionEnded extends SignInError {
5
6
  readonly code: string;
6
7
  constructor(message: string, deviceRevoked: boolean, code: string);
7
8
  }
9
+ /**
10
+ * A stored principal whose private keys are about to be discarded (#12280), or a stored item that
11
+ * could not be read when a save replaced it. The hook retires that principal's memory-sync pin; it
12
+ * never throws, so a failure to retire never blocks the discard.
13
+ */
14
+ export type DiscardedCredential = {
15
+ readonly kind: "credential";
16
+ readonly deviceId: string;
17
+ readonly accountId: string | undefined;
18
+ readonly owner: CredentialOwner | undefined;
19
+ readonly cause: "refused" | "forget" | "logout" | "replaced";
20
+ } | {
21
+ readonly kind: "unreadable";
22
+ };
23
+ export type BeforeKeysDiscarded = ((discarded: DiscardedCredential) => Promise<void>) & {
24
+ /** Whether a sign-in that names no account should have it read before logout revokes the device. */
25
+ readonly wantsAccount?: () => Promise<boolean>;
26
+ };
27
+ export declare const discardedOf: (credentials: Credentials, cause: "refused" | "forget" | "logout" | "replaced") => DiscardedCredential;
8
28
  export declare function createSession(options: {
9
29
  readonly api: string;
10
30
  readonly store: CredentialStore;
11
31
  readonly home?: string;
12
32
  readonly signal?: AbortSignal;
13
33
  readonly now?: () => number;
34
+ /** Runs inside the credential lock immediately before a stored principal's keys are discarded. */
35
+ readonly beforeKeysDiscarded: BeforeKeysDiscarded;
14
36
  }): {
15
37
  request: (path: string, init: RequestInit) => Promise<unknown>;
16
38
  signal: AbortSignal | undefined;
@@ -26,6 +48,11 @@ export declare function createSession(options: {
26
48
  update<T>(action: (current: Credentials | null) => Promise<T>, options?: {
27
49
  readonly replaceInvalid?: boolean;
28
50
  }): Promise<T>;
51
+ /**
52
+ * Deletes the item without reading it (a corrupt or locked item must still be removable, with one
53
+ * native call), so it cannot name the account whose pin to retire: `omo account forget --device`
54
+ * and `logout` read first and retire; this one leaves the pin (a stale pin is harmless for keys).
55
+ */
29
56
  forget(): Promise<void>;
30
57
  accessSnapshot: () => Promise<Credentials | null>;
31
58
  logout(): Promise<boolean>;
@@ -0,0 +1,7 @@
1
+ import type { BeforeKeysDiscarded } from "./session";
2
+ export declare function retireSyncPinOnDiscard(options: {
3
+ /** The trust-pin directory of the memory root the caller already resolves. */
4
+ readonly pinDir: () => string;
5
+ /** The caller's own logger; one line per pin it could not retire. */
6
+ readonly log: (line: string) => void;
7
+ }): BeforeKeysDiscarded;