oh-my-opencode 3.8.4 → 3.9.0

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 (62) hide show
  1. package/README.ja.md +3 -3
  2. package/README.ko.md +3 -3
  3. package/README.md +3 -3
  4. package/README.zh-cn.md +3 -3
  5. package/bin/oh-my-opencode.js +96 -34
  6. package/bin/platform.d.ts +14 -0
  7. package/bin/platform.js +44 -0
  8. package/bin/platform.test.ts +56 -1
  9. package/dist/agents/atlas/agent.d.ts +1 -1
  10. package/dist/agents/env-context.d.ts +1 -1
  11. package/dist/agents/hephaestus.d.ts +1 -1
  12. package/dist/agents/sisyphus.d.ts +1 -1
  13. package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +3 -3
  14. package/dist/cli/index.js +199 -59
  15. package/dist/cli/run/event-state.d.ts +2 -0
  16. package/dist/cli/run/poll-for-completion.d.ts +2 -0
  17. package/dist/config/schema/agent-overrides.d.ts +1 -0
  18. package/dist/config/schema/categories.d.ts +2 -0
  19. package/dist/config/schema/oh-my-opencode-config.d.ts +3 -12
  20. package/dist/features/background-agent/manager.d.ts +9 -0
  21. package/dist/features/boulder-state/storage.d.ts +1 -1
  22. package/dist/features/boulder-state/types.d.ts +2 -0
  23. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  24. package/dist/hooks/anthropic-context-window-limit-recovery/types.d.ts +1 -0
  25. package/dist/hooks/atlas/boulder-continuation-injector.d.ts +1 -0
  26. package/dist/hooks/atlas/types.d.ts +1 -0
  27. package/dist/hooks/background-notification/hook.d.ts +11 -0
  28. package/dist/hooks/no-hephaestus-non-gpt/hook.d.ts +5 -1
  29. package/dist/hooks/ralph-loop/completion-promise-detector.d.ts +1 -0
  30. package/dist/hooks/ralph-loop/loop-state-controller.d.ts +2 -0
  31. package/dist/hooks/ralph-loop/ralph-loop-hook.d.ts +1 -0
  32. package/dist/hooks/ralph-loop/types.d.ts +1 -0
  33. package/dist/hooks/session-notification.d.ts +1 -0
  34. package/dist/hooks/start-work/index.d.ts +3 -0
  35. package/dist/hooks/start-work/parse-user-request.d.ts +5 -0
  36. package/dist/hooks/start-work/worktree-detector.d.ts +1 -0
  37. package/dist/hooks/stop-continuation-guard/hook.d.ts +6 -1
  38. package/dist/hooks/think-mode/hook.d.ts +14 -2
  39. package/dist/hooks/think-mode/switcher.d.ts +0 -56
  40. package/dist/hooks/think-mode/types.d.ts +1 -15
  41. package/dist/hooks/todo-continuation-enforcer/constants.d.ts +1 -1
  42. package/dist/hooks/todo-continuation-enforcer/pending-question-detection.d.ts +14 -0
  43. package/dist/index.js +1626 -898
  44. package/dist/oh-my-opencode.schema.json +9 -13
  45. package/dist/shared/model-suggestion-retry.d.ts +4 -2
  46. package/dist/shared/prompt-timeout-context.d.ts +12 -0
  47. package/dist/shared/spawn-with-windows-hide.d.ts +15 -0
  48. package/dist/tools/delegate-task/category-resolver.d.ts +1 -0
  49. package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
  50. package/dist/tools/delegate-task/token-limiter.d.ts +4 -0
  51. package/dist/tools/delegate-task/types.d.ts +9 -0
  52. package/dist/tools/hashline-edit/constants.d.ts +0 -1
  53. package/dist/tools/hashline-edit/edit-operation-primitives.d.ts +0 -2
  54. package/dist/tools/hashline-edit/edit-operations.d.ts +0 -1
  55. package/dist/tools/hashline-edit/edit-ordering.d.ts +1 -0
  56. package/dist/tools/hashline-edit/index.d.ts +2 -2
  57. package/dist/tools/hashline-edit/normalize-edits.d.ts +6 -9
  58. package/dist/tools/hashline-edit/tool-description.d.ts +1 -1
  59. package/dist/tools/hashline-edit/types.d.ts +17 -41
  60. package/dist/tools/hashline-edit/validation.d.ts +1 -0
  61. package/package.json +13 -8
  62. package/postinstall.mjs +23 -7
package/README.ja.md CHANGED
@@ -217,9 +217,9 @@ MCPサーバーがあなたのコンテキスト予算を食いつぶしてい
217
217
  [oh-my-pi](https://github.com/can1357/oh-my-pi) に触発され、**Hashline**を実装しました。エージェントが読むすべての行にコンテンツハッシュがタグ付けされて返されます:
218
218
 
219
219
  ```
220
- 11#VK: function hello() {
221
- 22#XJ: return "world";
222
- 33#MB: }
220
+ 11#VK| function hello() {
221
+ 22#XJ| return "world";
222
+ 33#MB| }
223
223
  ```
224
224
 
225
225
  エージェントはこのタグを参照して編集します。最後に読んだ後でファイルが変更されていた場合、ハッシュが一致せず、コードが壊れる前に編集が拒否されます。空白を正確に再現する必要もなく、間違った行を編集するエラー (stale-line) もありません。
package/README.ko.md CHANGED
@@ -216,9 +216,9 @@ MCP 서버들이 당신의 컨텍스트 예산을 다 잡아먹죠. 우리가
216
216
  [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감을 받아, **Hashline**을 구현했습니다. 에이전트가 읽는 모든 줄에는 콘텐츠 해시 태그가 붙어 나옵니다:
217
217
 
218
218
  ```
219
- 11#VK: function hello() {
220
- 22#XJ: return "world";
221
- 33#MB: }
219
+ 11#VK| function hello() {
220
+ 22#XJ| return "world";
221
+ 33#MB| }
222
222
  ```
223
223
 
224
224
  에이전트는 이 태그를 참조해서 편집합니다. 마지막으로 읽은 후 파일이 변경되었다면 해시가 일치하지 않아 코드가 망가지기 전에 편집이 거부됩니다. 공백을 똑같이 재현할 필요도 없고, 엉뚱한 줄을 수정하는 에러(stale-line)도 없습니다.
package/README.md CHANGED
@@ -220,9 +220,9 @@ The harness problem is real. Most agent failures aren't the model. It's the edit
220
220
  Inspired by [oh-my-pi](https://github.com/can1357/oh-my-pi), we implemented **Hashline**. Every line the agent reads comes back tagged with a content hash:
221
221
 
222
222
  ```
223
- 11#VK: function hello() {
224
- 22#XJ: return "world";
225
- 33#MB: }
223
+ 11#VK| function hello() {
224
+ 22#XJ| return "world";
225
+ 33#MB| }
226
226
  ```
227
227
 
228
228
  The agent edits by referencing those tags. If the file changed since the last read, the hash won't match and the edit is rejected before corruption. No whitespace reproduction. No stale-line errors.
package/README.zh-cn.md CHANGED
@@ -218,9 +218,9 @@ Harness 问题是真的。绝大多数所谓的 Agent 故障,其实并不是
218
218
  受 [oh-my-pi](https://github.com/can1357/oh-my-pi) 的启发,我们实现了 **Hashline** 技术。Agent 读到的每一行代码,末尾都会打上一个强绑定的内容哈希值:
219
219
 
220
220
  ```
221
- 11#VK: function hello() {
222
- 22#XJ: return "world";
223
- 33#MB: }
221
+ 11#VK| function hello() {
222
+ 22#XJ| return "world";
223
+ 33#MB| }
224
224
  ```
225
225
 
226
226
  Agent 发起修改时,必须通过这些标签引用目标行。如果在此期间文件发生过变化,哈希验证就会失败,从而在代码被污染前直接驳回。不再有缩进空格错乱,彻底告别改错行的惨剧。
@@ -3,8 +3,9 @@
3
3
  // Wrapper script that detects platform and spawns the correct binary
4
4
 
5
5
  import { spawnSync } from "node:child_process";
6
+ import { readFileSync } from "node:fs";
6
7
  import { createRequire } from "node:module";
7
- import { getPlatformPackage, getBinaryPath } from "./platform.js";
8
+ import { getPlatformPackageCandidates, getBinaryPath } from "./platform.js";
8
9
 
9
10
  const require = createRequire(import.meta.url);
10
11
 
@@ -26,55 +27,116 @@ function getLibcFamily() {
26
27
  }
27
28
  }
28
29
 
30
+ function supportsAvx2() {
31
+ if (process.arch !== "x64") {
32
+ return null;
33
+ }
34
+
35
+ if (process.env.OH_MY_OPENCODE_FORCE_BASELINE === "1") {
36
+ return false;
37
+ }
38
+
39
+ if (process.platform === "linux") {
40
+ try {
41
+ const cpuInfo = readFileSync("/proc/cpuinfo", "utf8").toLowerCase();
42
+ return cpuInfo.includes("avx2");
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ if (process.platform === "darwin") {
49
+ const probe = spawnSync("sysctl", ["-n", "machdep.cpu.leaf7_features"], {
50
+ encoding: "utf8",
51
+ });
52
+
53
+ if (probe.error || probe.status !== 0) {
54
+ return null;
55
+ }
56
+
57
+ return probe.stdout.toUpperCase().includes("AVX2");
58
+ }
59
+
60
+ return null;
61
+ }
62
+
63
+ function getSignalExitCode(signal) {
64
+ const signalCodeByName = {
65
+ SIGINT: 2,
66
+ SIGILL: 4,
67
+ SIGKILL: 9,
68
+ SIGTERM: 15,
69
+ };
70
+
71
+ return 128 + (signalCodeByName[signal] ?? 1);
72
+ }
73
+
29
74
  function main() {
30
75
  const { platform, arch } = process;
31
76
  const libcFamily = getLibcFamily();
77
+ const avx2Supported = supportsAvx2();
32
78
 
33
- // Get platform package name
34
- let pkg;
79
+ let packageCandidates;
35
80
  try {
36
- pkg = getPlatformPackage({ platform, arch, libcFamily });
81
+ packageCandidates = getPlatformPackageCandidates({
82
+ platform,
83
+ arch,
84
+ libcFamily,
85
+ preferBaseline: avx2Supported === false,
86
+ });
37
87
  } catch (error) {
38
88
  console.error(`\noh-my-opencode: ${error.message}\n`);
39
89
  process.exit(1);
40
90
  }
41
-
42
- // Resolve binary path
43
- const binRelPath = getBinaryPath(pkg, platform);
44
-
45
- let binPath;
46
- try {
47
- binPath = require.resolve(binRelPath);
48
- } catch {
91
+
92
+ const resolvedBinaries = packageCandidates
93
+ .map((pkg) => {
94
+ try {
95
+ return { pkg, binPath: require.resolve(getBinaryPath(pkg, platform)) };
96
+ } catch {
97
+ return null;
98
+ }
99
+ })
100
+ .filter((entry) => entry !== null);
101
+
102
+ if (resolvedBinaries.length === 0) {
49
103
  console.error(`\noh-my-opencode: Platform binary not installed.`);
50
104
  console.error(`\nYour platform: ${platform}-${arch}${libcFamily === "musl" ? "-musl" : ""}`);
51
- console.error(`Expected package: ${pkg}`);
105
+ console.error(`Expected packages (in order): ${packageCandidates.join(", ")}`);
52
106
  console.error(`\nTo fix, run:`);
53
- console.error(` npm install ${pkg}\n`);
107
+ console.error(` npm install ${packageCandidates[0]}\n`);
54
108
  process.exit(1);
55
109
  }
56
-
57
- // Spawn the binary
58
- const result = spawnSync(binPath, process.argv.slice(2), {
59
- stdio: "inherit",
60
- });
61
-
62
- // Handle spawn errors
63
- if (result.error) {
64
- console.error(`\noh-my-opencode: Failed to execute binary.`);
65
- console.error(`Error: ${result.error.message}\n`);
66
- process.exit(2);
67
- }
68
-
69
- // Handle signals
70
- if (result.signal) {
71
- const signalNum = result.signal === "SIGTERM" ? 15 :
72
- result.signal === "SIGKILL" ? 9 :
73
- result.signal === "SIGINT" ? 2 : 1;
74
- process.exit(128 + signalNum);
110
+
111
+ for (let index = 0; index < resolvedBinaries.length; index += 1) {
112
+ const currentBinary = resolvedBinaries[index];
113
+ const hasFallback = index < resolvedBinaries.length - 1;
114
+ const result = spawnSync(currentBinary.binPath, process.argv.slice(2), {
115
+ stdio: "inherit",
116
+ });
117
+
118
+ if (result.error) {
119
+ if (hasFallback) {
120
+ continue;
121
+ }
122
+
123
+ console.error(`\noh-my-opencode: Failed to execute binary.`);
124
+ console.error(`Error: ${result.error.message}\n`);
125
+ process.exit(2);
126
+ }
127
+
128
+ if (result.signal === "SIGILL" && hasFallback) {
129
+ continue;
130
+ }
131
+
132
+ if (result.signal) {
133
+ process.exit(getSignalExitCode(result.signal));
134
+ }
135
+
136
+ process.exit(result.status ?? 1);
75
137
  }
76
138
 
77
- process.exit(result.status ?? 1);
139
+ process.exit(1);
78
140
  }
79
141
 
80
142
  main();
@@ -0,0 +1,14 @@
1
+ export declare function getPlatformPackage(options: {
2
+ platform: string;
3
+ arch: string;
4
+ libcFamily?: string | null;
5
+ }): string;
6
+
7
+ export declare function getPlatformPackageCandidates(options: {
8
+ platform: string;
9
+ arch: string;
10
+ libcFamily?: string | null;
11
+ preferBaseline?: boolean;
12
+ }): string[];
13
+
14
+ export declare function getBinaryPath(pkg: string, platform: string): string;
package/bin/platform.js CHANGED
@@ -26,6 +26,50 @@ export function getPlatformPackage({ platform, arch, libcFamily }) {
26
26
  return `oh-my-opencode-${os}-${arch}${suffix}`;
27
27
  }
28
28
 
29
+ /** @param {{ platform: string, arch: string, libcFamily?: string | null, preferBaseline?: boolean }} options */
30
+ export function getPlatformPackageCandidates({ platform, arch, libcFamily, preferBaseline = false }) {
31
+ const primaryPackage = getPlatformPackage({ platform, arch, libcFamily });
32
+ const baselinePackage = getBaselinePlatformPackage({ platform, arch, libcFamily });
33
+
34
+ if (!baselinePackage) {
35
+ return [primaryPackage];
36
+ }
37
+
38
+ return preferBaseline ? [baselinePackage, primaryPackage] : [primaryPackage, baselinePackage];
39
+ }
40
+
41
+ /** @param {{ platform: string, arch: string, libcFamily?: string | null }} options */
42
+ function getBaselinePlatformPackage({ platform, arch, libcFamily }) {
43
+ if (arch !== "x64") {
44
+ return null;
45
+ }
46
+
47
+ if (platform === "darwin") {
48
+ return "oh-my-opencode-darwin-x64-baseline";
49
+ }
50
+
51
+ if (platform === "win32") {
52
+ return "oh-my-opencode-windows-x64-baseline";
53
+ }
54
+
55
+ if (platform === "linux") {
56
+ if (libcFamily === null || libcFamily === undefined) {
57
+ throw new Error(
58
+ "Could not detect libc on Linux. " +
59
+ "Please ensure detect-libc is installed or report this issue."
60
+ );
61
+ }
62
+
63
+ if (libcFamily === "musl") {
64
+ return "oh-my-opencode-linux-x64-musl-baseline";
65
+ }
66
+
67
+ return "oh-my-opencode-linux-x64-baseline";
68
+ }
69
+
70
+ return null;
71
+ }
72
+
29
73
  /**
30
74
  * Get the path to the binary within a platform package
31
75
  * @param {string} pkg Package name
@@ -1,6 +1,6 @@
1
1
  // bin/platform.test.ts
2
2
  import { describe, expect, test } from "bun:test";
3
- import { getPlatformPackage, getBinaryPath } from "./platform.js";
3
+ import { getBinaryPath, getPlatformPackage, getPlatformPackageCandidates } from "./platform.js";
4
4
 
5
5
  describe("getPlatformPackage", () => {
6
6
  // #region Darwin platforms
@@ -146,3 +146,58 @@ describe("getBinaryPath", () => {
146
146
  expect(result).toBe("oh-my-opencode-linux-x64/bin/oh-my-opencode");
147
147
  });
148
148
  });
149
+
150
+ describe("getPlatformPackageCandidates", () => {
151
+ test("returns x64 and baseline candidates for Linux glibc", () => {
152
+ // #given Linux x64 with glibc
153
+ const input = { platform: "linux", arch: "x64", libcFamily: "glibc" };
154
+
155
+ // #when getting package candidates
156
+ const result = getPlatformPackageCandidates(input);
157
+
158
+ // #then returns modern first then baseline fallback
159
+ expect(result).toEqual([
160
+ "oh-my-opencode-linux-x64",
161
+ "oh-my-opencode-linux-x64-baseline",
162
+ ]);
163
+ });
164
+
165
+ test("returns x64 musl and baseline candidates for Linux musl", () => {
166
+ // #given Linux x64 with musl
167
+ const input = { platform: "linux", arch: "x64", libcFamily: "musl" };
168
+
169
+ // #when getting package candidates
170
+ const result = getPlatformPackageCandidates(input);
171
+
172
+ // #then returns musl modern first then musl baseline fallback
173
+ expect(result).toEqual([
174
+ "oh-my-opencode-linux-x64-musl",
175
+ "oh-my-opencode-linux-x64-musl-baseline",
176
+ ]);
177
+ });
178
+
179
+ test("returns baseline first when preferBaseline is true", () => {
180
+ // #given Windows x64 and baseline preference
181
+ const input = { platform: "win32", arch: "x64", preferBaseline: true };
182
+
183
+ // #when getting package candidates
184
+ const result = getPlatformPackageCandidates(input);
185
+
186
+ // #then baseline package is preferred first
187
+ expect(result).toEqual([
188
+ "oh-my-opencode-windows-x64-baseline",
189
+ "oh-my-opencode-windows-x64",
190
+ ]);
191
+ });
192
+
193
+ test("returns only one candidate for ARM64", () => {
194
+ // #given non-x64 platform
195
+ const input = { platform: "linux", arch: "arm64", libcFamily: "glibc" };
196
+
197
+ // #when getting package candidates
198
+ const result = getPlatformPackageCandidates(input);
199
+
200
+ // #then baseline fallback is not included
201
+ expect(result).toEqual(["oh-my-opencode-linux-arm64"]);
202
+ });
203
+ });
@@ -30,6 +30,6 @@ export interface OrchestratorContext {
30
30
  export declare function getAtlasPrompt(model?: string): string;
31
31
  export declare function createAtlasAgent(ctx: OrchestratorContext): AgentConfig;
32
32
  export declare namespace createAtlasAgent {
33
- var mode: "primary";
33
+ var mode: "all";
34
34
  }
35
35
  export declare const atlasPromptMetadata: AgentPromptMetadata;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Creates OmO-specific environment context (time, timezone, locale).
2
+ * Creates OmO-specific environment context (timezone, locale).
3
3
  * Note: Working directory, platform, and date are already provided by OpenCode's system.ts,
4
4
  * so we only include fields that OpenCode doesn't provide to avoid duplication.
5
5
  * See: https://github.com/code-yeongyu/oh-my-opencode/issues/379
@@ -2,5 +2,5 @@ import type { AgentConfig } from "@opencode-ai/sdk";
2
2
  import type { AvailableAgent, AvailableSkill, AvailableCategory } from "./dynamic-agent-prompt-builder";
3
3
  export declare function createHephaestusAgent(model: string, availableAgents?: AvailableAgent[], availableToolNames?: string[], availableSkills?: AvailableSkill[], availableCategories?: AvailableCategory[], useTaskSystem?: boolean): AgentConfig;
4
4
  export declare namespace createHephaestusAgent {
5
- var mode: "primary";
5
+ var mode: "all";
6
6
  }
@@ -4,5 +4,5 @@ export declare const SISYPHUS_PROMPT_METADATA: AgentPromptMetadata;
4
4
  import type { AvailableAgent, AvailableSkill, AvailableCategory } from "./dynamic-agent-prompt-builder";
5
5
  export declare function createSisyphusAgent(model: string, availableAgents?: AvailableAgent[], availableToolNames?: string[], availableSkills?: AvailableSkill[], availableCategories?: AvailableCategory[], useTaskSystem?: boolean): AgentConfig;
6
6
  export declare namespace createSisyphusAgent {
7
- var mode: "primary";
7
+ var mode: "all";
8
8
  }
@@ -4,10 +4,10 @@
4
4
  * IMPORTANT: Model names MUST use `antigravity-` prefix for stability.
5
5
  *
6
6
  * Since opencode-antigravity-auth v1.3.0, models use a variant system:
7
- * - `antigravity-gemini-3-pro` with variants: low, high
7
+ * - `antigravity-gemini-3.1-pro` with variants: low, high
8
8
  * - `antigravity-gemini-3-flash` with variants: minimal, low, medium, high
9
9
  *
10
- * Legacy tier-suffixed names (e.g., `antigravity-gemini-3-pro-high`) still work
10
+ * Legacy tier-suffixed names (e.g., `antigravity-gemini-3.1-pro-high`) still work
11
11
  * but variants are the recommended approach.
12
12
  *
13
13
  * @see https://github.com/NoeFabris/opencode-antigravity-auth#models
@@ -16,7 +16,7 @@ export declare const ANTIGRAVITY_PROVIDER_CONFIG: {
16
16
  google: {
17
17
  name: string;
18
18
  models: {
19
- "antigravity-gemini-3-pro": {
19
+ "antigravity-gemini-3.1-pro": {
20
20
  name: string;
21
21
  limit: {
22
22
  context: number;