oh-my-opencode 2.12.1 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -4,7 +4,7 @@
4
4
  >
5
5
  > 一緒に歩みましょう!
6
6
  >
7
- > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/PWpXmbhF) | [Discordコミュニティ](https://discord.gg/PWpXmbhF)に参加して、コントリビューターや`oh-my-opencode`仲間とつながりましょう。 |
7
+ > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/aSfGzWtYxM) | [Discordコミュニティ](https://discord.gg/aSfGzWtYxM)に参加して、コントリビューターや`oh-my-opencode`仲間とつながりましょう。 |
8
8
  > | :-----| :----- |
9
9
  > | [<img alt="X link" src="https://img.shields.io/badge/Follow-%40justsisyphus-00CED1?style=flat-square&logo=x&labelColor=black" width="156px" />](https://x.com/justsisyphus) | `oh-my-opencode`に関するニュースは私のXアカウントで投稿していましたが、無実の罪で凍結されたため、<br />[@justsisyphus](https://x.com/justsisyphus)が代わりに更新を投稿しています。 |
10
10
  > | [<img alt="Sponsor" src="https://img.shields.io/badge/Sponsor-❤-ff69b4?style=flat-square&logo=github-sponsors&labelColor=black" width="156px" />](https://github.com/sponsors/code-yeongyu) | [スポンサーになって](https://github.com/sponsors/code-yeongyu) `oh-my-opencode` の開発を応援してください。皆さまのご支援がこのプロジェクトを成長させます。 |
package/README.ko.md CHANGED
@@ -4,7 +4,7 @@
4
4
  >
5
5
  > 함께해주세요!
6
6
  >
7
- > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/PWpXmbhF) | [Discord 커뮤니티](https://discord.gg/PWpXmbhF)에서 기여자들과 `oh-my-opencode` 사용자들을 만나보세요. |
7
+ > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/aSfGzWtYxM) | [Discord 커뮤니티](https://discord.gg/aSfGzWtYxM)에서 기여자들과 `oh-my-opencode` 사용자들을 만나보세요. |
8
8
  > | :-----| :----- |
9
9
  > | [<img alt="X link" src="https://img.shields.io/badge/Follow-%40justsisyphus-00CED1?style=flat-square&logo=x&labelColor=black" width="156px" />](https://x.com/justsisyphus) | `oh-my-opencode` 관련 소식은 제 X 계정에서 올렸었는데, 억울하게 정지당해서 <br />[@justsisyphus](https://x.com/justsisyphus)가 대신 소식을 전하고 있습니다. |
10
10
  > | [<img alt="Sponsor" src="https://img.shields.io/badge/Sponsor-❤-ff69b4?style=flat-square&logo=github-sponsors&labelColor=black" width="156px" />](https://github.com/sponsors/code-yeongyu) | [스폰서가 되어](https://github.com/sponsors/code-yeongyu) `oh-my-opencode` 개발을 응원해주세요. 여러분의 후원이 이 프로젝트를 계속 성장시킵니다. |
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  >
8
8
  > Be with us!
9
9
  >
10
- > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/PWpXmbhF) | Join our [Discord community](https://discord.gg/PWpXmbhF) to connect with contributors and fellow `oh-my-opencode` users. |
10
+ > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/aSfGzWtYxM) | Join our [Discord community](https://discord.gg/aSfGzWtYxM) to connect with contributors and fellow `oh-my-opencode` users. |
11
11
  > | :-----| :----- |
12
12
  > | [<img alt="X link" src="https://img.shields.io/badge/Follow-%40justsisyphus-00CED1?style=flat-square&logo=x&labelColor=black" width="156px" />](https://x.com/justsisyphus) | News and updates for `oh-my-opencode` used to be posted on my X account. <br /> Since it was suspended mistakenly, [@justsisyphus](https://x.com/justsisyphus) now posts updates on my behalf. |
13
13
  > | [<img alt="Sponsor" src="https://img.shields.io/badge/Sponsor-❤-ff69b4?style=flat-square&logo=github-sponsors&labelColor=black" width="156px" />](https://github.com/sponsors/code-yeongyu) | Support the development of `oh-my-opencode` by [becoming a sponsor](https://github.com/sponsors/code-yeongyu). Your contribution helps keep this project alive and growing. |
package/README.zh-cn.md CHANGED
@@ -4,7 +4,7 @@
4
4
  >
5
5
  > 与我们同行!
6
6
  >
7
- > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/PWpXmbhF) | 加入我们的 [Discord 社区](https://discord.gg/PWpXmbhF),和贡献者们、`oh-my-opencode` 用户们一起交流。 |
7
+ > | [<img alt="Discord link" src="https://img.shields.io/discord/1452487457085063218?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square" width="156px" />](https://discord.gg/aSfGzWtYxM) | 加入我们的 [Discord 社区](https://discord.gg/aSfGzWtYxM),和贡献者们、`oh-my-opencode` 用户们一起交流。 |
8
8
  > | :-----| :----- |
9
9
  > | [<img alt="X link" src="https://img.shields.io/badge/Follow-%40justsisyphus-00CED1?style=flat-square&logo=x&labelColor=black" width="156px" />](https://x.com/justsisyphus) | `oh-my-opencode` 的消息之前在我的 X 账号发,但账号被无辜封了,<br />现在 [@justsisyphus](https://x.com/justsisyphus) 替我发更新。 |
10
10
  > | [<img alt="Sponsor" src="https://img.shields.io/badge/Sponsor-❤-ff69b4?style=flat-square&logo=github-sponsors&labelColor=black" width="156px" />](https://github.com/sponsors/code-yeongyu) | [成为赞助者](https://github.com/sponsors/code-yeongyu),支持 `oh-my-opencode` 的开发。您的支持让这个项目持续成长。 |
package/dist/cli/index.js CHANGED
@@ -2657,7 +2657,7 @@ var require_napi = __commonJS((exports, module) => {
2657
2657
  var require_package = __commonJS((exports, module) => {
2658
2658
  module.exports = {
2659
2659
  name: "oh-my-opencode",
2660
- version: "2.12.0",
2660
+ version: "2.12.1",
2661
2661
  description: "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
2662
2662
  main: "dist/index.js",
2663
2663
  types: "dist/index.d.ts",
@@ -11,4 +11,5 @@ export interface RalphLoopState {
11
11
  export interface RalphLoopOptions {
12
12
  config?: RalphLoopConfig;
13
13
  getTranscriptPath?: (sessionId: string) => string;
14
+ apiTimeout?: number;
14
15
  }
@@ -3,6 +3,7 @@ import type { ExperimentalConfig } from "../../config";
3
3
  export interface SessionRecoveryOptions {
4
4
  experimental?: ExperimentalConfig;
5
5
  }
6
+ type RecoveryErrorType = "tool_result_missing" | "thinking_block_order" | "thinking_disabled_violation" | null;
6
7
  interface MessageInfo {
7
8
  id?: string;
8
9
  role?: string;
@@ -10,6 +11,7 @@ interface MessageInfo {
10
11
  parentID?: string;
11
12
  error?: unknown;
12
13
  }
14
+ export declare function detectErrorType(error: unknown): RecoveryErrorType;
13
15
  export interface SessionRecoveryHook {
14
16
  handleSessionRecovery: (info: MessageInfo) => Promise<boolean>;
15
17
  isRecoverableError: (error: unknown) => boolean;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js CHANGED
@@ -9650,7 +9650,7 @@ function detectErrorType(error) {
9650
9650
  if (message.includes("tool_use") && message.includes("tool_result")) {
9651
9651
  return "tool_result_missing";
9652
9652
  }
9653
- if (message.includes("thinking") && (message.includes("first block") || message.includes("must start with") || message.includes("preceeding") || message.includes("expected") && message.includes("found"))) {
9653
+ if (message.includes("thinking") && (message.includes("first block") || message.includes("must start with") || message.includes("preceeding") || message.includes("final block") || message.includes("cannot be thinking") || message.includes("expected") && message.includes("found"))) {
9654
9654
  return "thinking_block_order";
9655
9655
  }
9656
9656
  if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
@@ -19585,11 +19585,13 @@ IMPORTANT:
19585
19585
 
19586
19586
  Original task:
19587
19587
  {{PROMPT}}`;
19588
+ var DEFAULT_API_TIMEOUT = 3000;
19588
19589
  function createRalphLoopHook(ctx, options) {
19589
19590
  const sessions = new Map;
19590
19591
  const config = options?.config;
19591
19592
  const stateDir = config?.state_dir;
19592
19593
  const getTranscriptPath2 = options?.getTranscriptPath ?? getTranscriptPath;
19594
+ const apiTimeout = options?.apiTimeout ?? DEFAULT_API_TIMEOUT;
19593
19595
  function getSessionState(sessionID) {
19594
19596
  let state2 = sessions.get(sessionID);
19595
19597
  if (!state2) {
@@ -19616,28 +19618,26 @@ function createRalphLoopHook(ctx, options) {
19616
19618
  }
19617
19619
  async function detectCompletionInSessionMessages(sessionID, promise) {
19618
19620
  try {
19619
- const response = await ctx.client.session.messages({
19620
- path: { id: sessionID },
19621
- query: { directory: ctx.directory }
19622
- });
19621
+ const response = await Promise.race([
19622
+ ctx.client.session.messages({
19623
+ path: { id: sessionID },
19624
+ query: { directory: ctx.directory }
19625
+ }),
19626
+ new Promise((_, reject) => setTimeout(() => reject(new Error("API timeout")), apiTimeout))
19627
+ ]);
19623
19628
  const messages = response.data ?? [];
19624
19629
  if (!Array.isArray(messages))
19625
19630
  return false;
19631
+ const assistantMessages = messages.filter((msg) => msg.info?.role === "assistant");
19632
+ const lastAssistant = assistantMessages[assistantMessages.length - 1];
19633
+ if (!lastAssistant?.parts)
19634
+ return false;
19626
19635
  const pattern = new RegExp(`<promise>\\s*${escapeRegex(promise)}\\s*</promise>`, "is");
19627
- for (const msg of messages) {
19628
- if (msg.info?.role !== "assistant")
19629
- continue;
19630
- for (const part of msg.parts || []) {
19631
- if (part.type === "text" && part.text) {
19632
- if (pattern.test(part.text)) {
19633
- return true;
19634
- }
19635
- }
19636
- }
19637
- }
19638
- return false;
19636
+ const responseText = lastAssistant.parts.filter((p) => p.type === "text").map((p) => p.text ?? "").join(`
19637
+ `);
19638
+ return pattern.test(responseText);
19639
19639
  } catch (err) {
19640
- log(`[${HOOK_NAME3}] Failed to fetch session messages`, { sessionID, error: String(err) });
19640
+ log(`[${HOOK_NAME3}] Session messages check failed`, { sessionID, error: String(err) });
19641
19641
  return false;
19642
19642
  }
19643
19643
  }
@@ -19695,15 +19695,15 @@ function createRalphLoopHook(ctx, options) {
19695
19695
  if (state2.session_id && state2.session_id !== sessionID) {
19696
19696
  return;
19697
19697
  }
19698
- const completionDetectedViaApi = await detectCompletionInSessionMessages(sessionID, state2.completion_promise);
19699
19698
  const transcriptPath = getTranscriptPath2(sessionID);
19700
19699
  const completionDetectedViaTranscript = detectCompletionPromise(transcriptPath, state2.completion_promise);
19701
- if (completionDetectedViaApi || completionDetectedViaTranscript) {
19700
+ const completionDetectedViaApi = completionDetectedViaTranscript ? false : await detectCompletionInSessionMessages(sessionID, state2.completion_promise);
19701
+ if (completionDetectedViaTranscript || completionDetectedViaApi) {
19702
19702
  log(`[${HOOK_NAME3}] Completion detected!`, {
19703
19703
  sessionID,
19704
19704
  iteration: state2.iteration,
19705
19705
  promise: state2.completion_promise,
19706
- detectedVia: completionDetectedViaApi ? "session_messages_api" : "transcript_file"
19706
+ detectedVia: completionDetectedViaTranscript ? "transcript_file" : "session_messages_api"
19707
19707
  });
19708
19708
  clearState(ctx.directory, stateDir);
19709
19709
  await ctx.client.tui.showToast({
@@ -19780,6 +19780,18 @@ function createRalphLoopHook(ctx, options) {
19780
19780
  }
19781
19781
  if (event2.type === "session.error") {
19782
19782
  const sessionID = props?.sessionID;
19783
+ const error = props?.error;
19784
+ if (error?.name === "MessageAbortedError") {
19785
+ if (sessionID) {
19786
+ const state2 = readState(ctx.directory, stateDir);
19787
+ if (state2?.session_id === sessionID) {
19788
+ clearState(ctx.directory, stateDir);
19789
+ log(`[${HOOK_NAME3}] User aborted, loop cleared`, { sessionID });
19790
+ }
19791
+ sessions.delete(sessionID);
19792
+ }
19793
+ return;
19794
+ }
19783
19795
  if (sessionID) {
19784
19796
  const sessionState = getSessionState(sessionID);
19785
19797
  sessionState.isRecovering = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "2.12.1",
3
+ "version": "2.12.2",
4
4
  "description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",