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 +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh-cn.md +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/hooks/ralph-loop/types.d.ts +1 -0
- package/dist/hooks/session-recovery/index.d.ts +2 -0
- package/dist/hooks/session-recovery/index.test.d.ts +1 -0
- package/dist/index.js +33 -21
- package/package.json +1 -1
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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.
|
|
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",
|
|
@@ -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
|
|
19620
|
-
|
|
19621
|
-
|
|
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
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
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}]
|
|
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
|
-
|
|
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:
|
|
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;
|