feat(aircoding): AirCoding V2 baseline — deterministic multi-agent architecture

Forked from OpenCode v1.17.4 with multi-agent system:
- 5 agents: aircoding, scheduler, worker, architect, reviewer
- Deterministic DAG scheduling engine (coordinator_tick)
- Tool whitelists as hard enforcement
- AirCoding validation plugin
- V1 requirements: C4 docs, ADR, AGENTS.md, debug-log.md
- Design documents in docs/
This commit is contained in:
airlongdian
2026-06-13 21:53:43 +08:00
commit deaf7eb806
5757 changed files with 1169969 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
diff --git a/dist/index.js b/dist/index.js
index 546bf5509004510b023212d70b4c0875a24eb302..f211dcb23e01a393f7d5c6221bfb0ef52276520a 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -636,6 +636,9 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
}
}).filter((part) => part !== void 0)
});
+ if (contents[contents.length - 1].parts.length === 0) {
+ contents.pop();
+ }
break;
}
case "tool": {
diff --git a/dist/index.mjs b/dist/index.mjs
index 5c8c20cbbcd4d398523602dd80ae8c3fa10a84ea..f89dc53b3b9cd83653a66b0900ff0bc34d3d6c9f 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -642,6 +642,9 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
}
}).filter((part) => part !== void 0)
});
+ if (contents[contents.length - 1].parts.length === 0) {
+ contents.pop();
+ }
break;
}
case "tool": {
diff --git a/dist/internal/index.js b/dist/internal/index.js
index 947af6f4282f1bb9b46a76fdf199d7155600b550..22c2faa4122069d868a3232bbb708fdc808bf6e8 100644
--- a/dist/internal/index.js
+++ b/dist/internal/index.js
@@ -419,6 +419,9 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
}
}).filter((part) => part !== void 0)
});
+ if (contents[contents.length - 1].parts.length === 0) {
+ contents.pop();
+ }
break;
}
case "tool": {
diff --git a/dist/internal/index.mjs b/dist/internal/index.mjs
index 28853288c1ff8f589448e7ddcddfd8ed36c4d995..4ef91ed90008d2cf7a5f24bb1927a9884e352393 100644
--- a/dist/internal/index.mjs
+++ b/dist/internal/index.mjs
@@ -402,6 +402,9 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
}
}).filter((part) => part !== void 0)
});
+ if (contents[contents.length - 1].parts.length === 0) {
+ contents.pop();
+ }
break;
}
case "tool": {
diff --git a/src/convert-to-google-generative-ai-messages.ts b/src/convert-to-google-generative-ai-messages.ts
index 4bf83e3768d4ccc8ff96e7d683abb4ccf60387ea..f257d2af8f178b7ac36771296eaa6c60c92e04ab 100644
--- a/src/convert-to-google-generative-ai-messages.ts
+++ b/src/convert-to-google-generative-ai-messages.ts
@@ -350,3 +350,8 @@ export function convertToGoogleGenerativeAIMessages(
})
.filter(part => part !== undefined),
});
+ // Empty text and reasoning parts, including signature-bearing ones, are
+ // filtered above. Do not emit a model entry that Gemini rejects.
+ if (contents[contents.length - 1].parts.length === 0) {
+ contents.pop();
+ }

View File

@@ -0,0 +1,99 @@
diff --git a/dist/index.js b/dist/index.js
index 135b95946139bbd1fc4b62239032931586189da0..7913520ad2f1c26b0f9621e7654f5fb570cba926 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1077,6 +1077,20 @@ async function convertToXaiResponsesInput({
const mediaType = block.mediaType === "image/*" ? "image/jpeg" : block.mediaType;
const imageUrl = block.data instanceof URL ? block.data.toString() : `data:${mediaType};base64,${(0, import_provider_utils5.convertToBase64)(block.data)}`;
contentParts.push({ type: "input_image", image_url: imageUrl });
+ } else if (block.mediaType === "application/pdf") {
+ if (block.data instanceof URL) {
+ contentParts.push({ type: "input_file", file_url: block.data.toString() });
+ } else {
+ contentParts.push({
+ type: "input_file",
+ ...(typeof block.data === "string" && block.data.startsWith("file-")
+ ? { file_id: block.data }
+ : {
+ filename: block.filename ?? "file",
+ file_data: `data:application/pdf;base64,${(0, import_provider_utils5.convertToBase64)(block.data)}`,
+ }),
+ });
+ }
} else {
throw new import_provider4.UnsupportedFunctionalityError({
functionality: `file part media type ${block.mediaType}`
diff --git a/dist/index.mjs b/dist/index.mjs
index 61be60d452682b94dcdda39ffc47cb994eb8bfb1..d928f7f46e91057cd97fade9cc238db611345bcf 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -1080,6 +1080,20 @@ async function convertToXaiResponsesInput({
const mediaType = block.mediaType === "image/*" ? "image/jpeg" : block.mediaType;
const imageUrl = block.data instanceof URL ? block.data.toString() : `data:${mediaType};base64,${convertToBase642(block.data)}`;
contentParts.push({ type: "input_image", image_url: imageUrl });
+ } else if (block.mediaType === "application/pdf") {
+ if (block.data instanceof URL) {
+ contentParts.push({ type: "input_file", file_url: block.data.toString() });
+ } else {
+ contentParts.push({
+ type: "input_file",
+ ...(typeof block.data === "string" && block.data.startsWith("file-")
+ ? { file_id: block.data }
+ : {
+ filename: block.filename ?? "file",
+ file_data: `data:application/pdf;base64,${convertToBase642(block.data)}`,
+ }),
+ });
+ }
} else {
throw new UnsupportedFunctionalityError3({
functionality: `file part media type ${block.mediaType}`
diff --git a/src/responses/convert-to-xai-responses-input.ts b/src/responses/convert-to-xai-responses-input.ts
index 19958d9fd90f7ce61bca70ad2d5f7b89a59fa63b..9329a1d56210af8aa33e5dbcb2916e24847070c1 100644
--- a/src/responses/convert-to-xai-responses-input.ts
+++ b/src/responses/convert-to-xai-responses-input.ts
@@ -54,6 +54,24 @@ export async function convertToXaiResponsesInput({
: `data:${mediaType};base64,${convertToBase64(block.data)}`;
contentParts.push({ type: 'input_image', image_url: imageUrl });
+ } else if (block.mediaType === 'application/pdf') {
+ if (block.data instanceof URL) {
+ contentParts.push({
+ type: 'input_file',
+ file_url: block.data.toString(),
+ });
+ } else {
+ contentParts.push({
+ type: 'input_file',
+ ...(typeof block.data === 'string' &&
+ block.data.startsWith('file-')
+ ? { file_id: block.data }
+ : {
+ filename: block.filename ?? 'file',
+ file_data: `data:application/pdf;base64,${convertToBase64(block.data)}`,
+ }),
+ });
+ }
} else {
throw new UnsupportedFunctionalityError({
functionality: `file part media type ${block.mediaType}`,
diff --git a/src/responses/xai-responses-api.ts b/src/responses/xai-responses-api.ts
index df24c42d29fe7fc1dd7649cc2c4712a68c3a536b..00195468a83d43c1bfb50854ea040b55ea352433 100644
--- a/src/responses/xai-responses-api.ts
+++ b/src/responses/xai-responses-api.ts
@@ -26,7 +26,14 @@ export type XaiResponsesSystemMessage = {
export type XaiResponsesUserMessageContentPart =
| { type: 'input_text'; text: string }
- | { type: 'input_image'; image_url: string };
+ | { type: 'input_image'; image_url: string }
+ | {
+ type: 'input_file';
+ file_url?: string;
+ file_id?: string;
+ file_data?: string;
+ filename?: string;
+ };
export type XaiResponsesUserMessage = {
role: 'user';

View File

@@ -0,0 +1,31 @@
diff --git a/src/download.ts b/src/download.ts
index 3454256..6dca25a 100644
--- a/src/download.ts
+++ b/src/download.ts
@@ -7,7 +7,7 @@
*/
+declare const FFF_LIBC: "gnu" | "musl";
import { existsSync } from "node:fs";
-import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { getLibFilename, getNpmPackageName } from "./platform";
@@ -54,14 +54,10 @@ export function binaryExists(): boolean {
* in the same directory.
*/
function resolveFromNpmPackage(): string | null {
- const packageName = getNpmPackageName();
-
try {
- // Use createRequire to resolve the platform package's location
- const require = createRequire(join(getPackageDir(), "package.json"));
- const packageJsonPath = require.resolve(`${packageName}/package.json`);
- const packageDir = dirname(packageJsonPath);
- const binaryPath = join(packageDir, getLibFilename());
+ const binaryPath = require(
+ `@ff-labs/fff-bin-${process.platform === "linux" ? `linux-${process.arch}-${typeof FFF_LIBC === "string" ? FFF_LIBC : getNpmPackageName().endsWith("musl") ? "musl" : "gnu"}` : `${process.platform}-${process.arch}`}/${process.platform === "win32" ? "fff_c.dll" : process.platform === "darwin" ? "libfff_c.dylib" : "libfff_c.so"}`,
+ );
if (existsSync(binaryPath)) {
return binaryPath;

View File

@@ -0,0 +1,13 @@
diff --git a/lib/agents.js b/lib/agents.js
index 45ec59c4c13757379095131c4f0a5ea6f7284f45..0763b031e355a755ec6a26f98461aa3f63b8339b 100644
--- a/lib/agents.js
+++ b/lib/agents.js
@@ -32,7 +32,7 @@ module.exports = class Agent extends AgentBase {
}
get proxy () {
- return this.#proxy ? { url: this.#proxy } : {}
+ return this.#proxy ? { url: this.#proxy.toString() } : {}
}
#getProxy (options) {

View File

@@ -0,0 +1,285 @@
diff --git a/photon_rs.js b/photon_rs.js
index 8f4144d..29a964a 100644
--- a/photon_rs.js
+++ b/photon_rs.js
@@ -1,6 +1,7 @@
let imports = {};
-imports['__wbindgen_placeholder__'] = module.exports;
+const __wbindgen_placeholder__ = {};
+imports['__wbindgen_placeholder__'] = __wbindgen_placeholder__;
let wasm;
const { TextEncoder, TextDecoder } = require(`util`);
@@ -4272,12 +4273,12 @@ class Rgba {
}
module.exports.Rgba = Rgba;
-module.exports.__wbg_new_abda76e883ba8a5f = function() {
+__wbindgen_placeholder__.__wbg_new_abda76e883ba8a5f = function() {
const ret = new Error();
return ret;
};
-module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) {
const ret = arg1.stack;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
@@ -4285,7 +4286,7 @@ module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) {
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
-module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
let deferred0_0;
let deferred0_1;
try {
@@ -4297,7 +4298,7 @@ module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
}
};
-module.exports.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) {
+__wbindgen_placeholder__.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) {
let result;
try {
result = arg0 instanceof Window;
@@ -4308,42 +4309,42 @@ module.exports.__wbg_instanceof_Window_c4b70662a0d2c5ec = function(arg0) {
return ret;
};
-module.exports.__wbg_document_e5c1786dea6542e4 = function(arg0) {
+__wbindgen_placeholder__.__wbg_document_e5c1786dea6542e4 = function(arg0) {
const ret = arg0.document;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
-module.exports.__wbg_body_e70ae6abd01ae584 = function(arg0) {
+__wbindgen_placeholder__.__wbg_body_e70ae6abd01ae584 = function(arg0) {
const ret = arg0.body;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
-module.exports.__wbg_createElement_5d4c76f218b78145 = function() { return handleError(function (arg0, arg1, arg2) {
+__wbindgen_placeholder__.__wbg_createElement_5d4c76f218b78145 = function() { return handleError(function (arg0, arg1, arg2) {
const ret = arg0.createElement(getStringFromWasm0(arg1, arg2));
return ret;
}, arguments) };
-module.exports.__wbg_width_4c6f0048d64cf86b = function(arg0) {
+__wbindgen_placeholder__.__wbg_width_4c6f0048d64cf86b = function(arg0) {
const ret = arg0.width;
return ret;
};
-module.exports.__wbg_height_21f0d3fd8f753394 = function(arg0) {
+__wbindgen_placeholder__.__wbg_height_21f0d3fd8f753394 = function(arg0) {
const ret = arg0.height;
return ret;
};
-module.exports.__wbg_width_79e0847ed5883b03 = function(arg0) {
+__wbindgen_placeholder__.__wbg_width_79e0847ed5883b03 = function(arg0) {
const ret = arg0.width;
return ret;
};
-module.exports.__wbg_height_e4e4e4779f8feac0 = function(arg0) {
+__wbindgen_placeholder__.__wbg_height_e4e4e4779f8feac0 = function(arg0) {
const ret = arg0.height;
return ret;
};
-module.exports.__wbg_data_fda507064d127f5b = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_data_fda507064d127f5b = function(arg0, arg1) {
const ret = arg1.data;
const ptr1 = passArray8ToWasm0(ret, wasm.__wbindgen_malloc);
const len1 = WASM_VECTOR_LEN;
@@ -4351,12 +4352,12 @@ module.exports.__wbg_data_fda507064d127f5b = function(arg0, arg1) {
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
-module.exports.__wbg_newwithu8clampedarrayandsh_1fddccb3a94a5e05 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+__wbindgen_placeholder__.__wbg_newwithu8clampedarrayandsh_1fddccb3a94a5e05 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
const ret = new ImageData(getClampedArrayU8FromWasm0(arg0, arg1), arg2 >>> 0, arg3 >>> 0);
return ret;
}, arguments) };
-module.exports.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = function(arg0) {
+__wbindgen_placeholder__.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = function(arg0) {
let result;
try {
result = arg0 instanceof CanvasRenderingContext2D;
@@ -4367,24 +4368,24 @@ module.exports.__wbg_instanceof_CanvasRenderingContext2d_3abbe7ec7af32cae = fun
return ret;
};
-module.exports.__wbg_drawImage_fede06db74e39a60 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+__wbindgen_placeholder__.__wbg_drawImage_fede06db74e39a60 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
arg0.drawImage(arg1, arg2, arg3);
}, arguments) };
-module.exports.__wbg_drawImage_f395c8e43c79a909 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
+__wbindgen_placeholder__.__wbg_drawImage_f395c8e43c79a909 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
arg0.drawImage(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}, arguments) };
-module.exports.__wbg_getImageData_5e1c242046e6b59e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
+__wbindgen_placeholder__.__wbg_getImageData_5e1c242046e6b59e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
const ret = arg0.getImageData(arg1, arg2, arg3, arg4);
return ret;
}, arguments) };
-module.exports.__wbg_putImageData_a8b3e177ee06d521 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+__wbindgen_placeholder__.__wbg_putImageData_a8b3e177ee06d521 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
arg0.putImageData(arg1, arg2, arg3);
}, arguments) };
-module.exports.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(arg0) {
+__wbindgen_placeholder__.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(arg0) {
let result;
try {
result = arg0 instanceof HTMLCanvasElement;
@@ -4395,93 +4396,93 @@ module.exports.__wbg_instanceof_HtmlCanvasElement_25d964a0dde6717e = function(a
return ret;
};
-module.exports.__wbg_width_dc225e55343b745e = function(arg0) {
+__wbindgen_placeholder__.__wbg_width_dc225e55343b745e = function(arg0) {
const ret = arg0.width;
return ret;
};
-module.exports.__wbg_setwidth_488780db69b08846 = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_setwidth_488780db69b08846 = function(arg0, arg1) {
arg0.width = arg1 >>> 0;
};
-module.exports.__wbg_height_3a8bec2f3fe71b26 = function(arg0) {
+__wbindgen_placeholder__.__wbg_height_3a8bec2f3fe71b26 = function(arg0) {
const ret = arg0.height;
return ret;
};
-module.exports.__wbg_setheight_1761808c18403921 = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_setheight_1761808c18403921 = function(arg0, arg1) {
arg0.height = arg1 >>> 0;
};
-module.exports.__wbg_getContext_fc99dbd3a9a7e318 = function() { return handleError(function (arg0, arg1, arg2) {
+__wbindgen_placeholder__.__wbg_getContext_fc99dbd3a9a7e318 = function() { return handleError(function (arg0, arg1, arg2) {
const ret = arg0.getContext(getStringFromWasm0(arg1, arg2));
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
}, arguments) };
-module.exports.__wbg_settextContent_f82a86a8df347e1c = function(arg0, arg1, arg2) {
+__wbindgen_placeholder__.__wbg_settextContent_f82a86a8df347e1c = function(arg0, arg1, arg2) {
arg0.textContent = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2);
};
-module.exports.__wbg_appendChild_fa3b00dade9fc4cf = function() { return handleError(function (arg0, arg1) {
+__wbindgen_placeholder__.__wbg_appendChild_fa3b00dade9fc4cf = function() { return handleError(function (arg0, arg1) {
const ret = arg0.appendChild(arg1);
return ret;
}, arguments) };
-module.exports.__wbg_newnoargs_e643855c6572a4a8 = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbg_newnoargs_e643855c6572a4a8 = function(arg0, arg1) {
const ret = new Function(getStringFromWasm0(arg0, arg1));
return ret;
};
-module.exports.__wbg_call_f96b398515635514 = function() { return handleError(function (arg0, arg1) {
+__wbindgen_placeholder__.__wbg_call_f96b398515635514 = function() { return handleError(function (arg0, arg1) {
const ret = arg0.call(arg1);
return ret;
}, arguments) };
-module.exports.__wbg_self_b9aad7f1c618bfaf = function() { return handleError(function () {
+__wbindgen_placeholder__.__wbg_self_b9aad7f1c618bfaf = function() { return handleError(function () {
const ret = self.self;
return ret;
}, arguments) };
-module.exports.__wbg_window_55e469842c98b086 = function() { return handleError(function () {
+__wbindgen_placeholder__.__wbg_window_55e469842c98b086 = function() { return handleError(function () {
const ret = window.window;
return ret;
}, arguments) };
-module.exports.__wbg_globalThis_d0957e302752547e = function() { return handleError(function () {
+__wbindgen_placeholder__.__wbg_globalThis_d0957e302752547e = function() { return handleError(function () {
const ret = globalThis.globalThis;
return ret;
}, arguments) };
-module.exports.__wbg_global_ae2f87312b8987fb = function() { return handleError(function () {
+__wbindgen_placeholder__.__wbg_global_ae2f87312b8987fb = function() { return handleError(function () {
const ret = global.global;
return ret;
}, arguments) };
-module.exports.__wbindgen_is_undefined = function(arg0) {
+__wbindgen_placeholder__.__wbindgen_is_undefined = function(arg0) {
const ret = arg0 === undefined;
return ret;
};
-module.exports.__wbg_buffer_fcbfb6d88b2732e9 = function(arg0) {
+__wbindgen_placeholder__.__wbg_buffer_fcbfb6d88b2732e9 = function(arg0) {
const ret = arg0.buffer;
return ret;
};
-module.exports.__wbg_new_bc5d9aad3f9ac80e = function(arg0) {
+__wbindgen_placeholder__.__wbg_new_bc5d9aad3f9ac80e = function(arg0) {
const ret = new Uint8Array(arg0);
return ret;
};
-module.exports.__wbg_set_4b3aa8445ac1e91c = function(arg0, arg1, arg2) {
+__wbindgen_placeholder__.__wbg_set_4b3aa8445ac1e91c = function(arg0, arg1, arg2) {
arg0.set(arg1, arg2 >>> 0);
};
-module.exports.__wbg_length_d9c4ded7e708c6a1 = function(arg0) {
+__wbindgen_placeholder__.__wbg_length_d9c4ded7e708c6a1 = function(arg0) {
const ret = arg0.length;
return ret;
};
-module.exports.__wbindgen_debug_string = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(arg1);
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
@@ -4489,16 +4490,16 @@ module.exports.__wbindgen_debug_string = function(arg0, arg1) {
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
-module.exports.__wbindgen_throw = function(arg0, arg1) {
+__wbindgen_placeholder__.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
-module.exports.__wbindgen_memory = function() {
+__wbindgen_placeholder__.__wbindgen_memory = function() {
const ret = wasm.memory;
return ret;
};
-module.exports.__wbindgen_init_externref_table = function() {
+__wbindgen_placeholder__.__wbindgen_init_externref_table = function() {
const table = wasm.__wbindgen_export_2;
const offset = table.grow(4);
table.set(0, undefined);
@@ -4509,7 +4510,8 @@ module.exports.__wbindgen_init_externref_table = function() {
;
};
-const path = require('path').join(__dirname, 'photon_rs_bg.wasm');
+// Allow opencode's Bun compiled binary to point photon-node at its embedded wasm asset.
+const path = globalThis.__OPENCODE_PHOTON_WASM_PATH || require('path').join(__dirname, 'photon_rs_bg.wasm');
const bytes = require('fs').readFileSync(path);
const wasmModule = new WebAssembly.Module(bytes);

View File

@@ -0,0 +1,16 @@
diff --git a/dist/vendors/convert.js b/dist/vendors/convert.js
index 0d615eebfd7cd646937ec1b29f8332db73586ec1..7b146f903c07a9377d676753691cba67781879be 100644
--- a/dist/vendors/convert.js
+++ b/dist/vendors/convert.js
@@ -74,7 +74,10 @@ function convertToOpenAPISchema(jsonSchema, context) {
$ref: `#/components/schemas/${id}`
};
} else if (_jsonSchema.$ref) {
- const { $ref, $defs } = _jsonSchema;
+ const { $ref, $defs, ...rest } = _jsonSchema;
+ if ($ref.includes("://")) {
+ return Object.keys(rest).length > 0 ? rest : { type: "string" };
+ }
const ref = $ref.split("/").pop();
context.components.schemas = {
...context.components.schemas,

View File

@@ -0,0 +1,14 @@
diff --git a/build/src/index.js b/build/src/index.js
--- a/build/src/index.js
+++ b/build/src/index.js
@@ -323,6 +323,10 @@ async function isAvailable() {
if (process.env.DEBUG_AUTH) {
console.info(err);
}
+ // Promise.any() rejects with AggregateError when neither metadata host
+ // is available. This is expected outside GCP, not a warning condition.
+ if (err instanceof AggregateError)
+ return false;
if (err.type === 'request-timeout') {
// If running in a GCP environment, metadata endpoint should return
// within ms.

120
patches/install-korean-ime-fix.sh Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env bash
set -euo pipefail
# opencode Korean IME Fix Installer
# https://github.com/anomalyco/opencode/issues/14371
#
# Patches opencode to prevent Korean (and other CJK) IME last character
# truncation when pressing Enter in Kitty and other terminals.
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/claudianus/opencode/fix-zhipuai-coding-plan-thinking/patches/install-korean-ime-fix.sh | bash
# # or from a cloned repo:
# ./patches/install-korean-ime-fix.sh
RED='\033[0;31m'
GREEN='\033[0;32m'
ORANGE='\033[38;5;214m'
MUTED='\033[0;2m'
NC='\033[0m'
OPENCODE_DIR="${OPENCODE_DIR:-$HOME/.opencode}"
OPENCODE_SRC="${OPENCODE_SRC:-$HOME/.opencode-src}"
FORK_REPO="${FORK_REPO:-https://github.com/claudianus/opencode.git}"
FORK_BRANCH="${FORK_BRANCH:-fix-zhipuai-coding-plan-thinking}"
info() { echo -e "${MUTED}$*${NC}"; }
warn() { echo -e "${ORANGE}$*${NC}"; }
err() { echo -e "${RED}$*${NC}" >&2; }
ok() { echo -e "${GREEN}$*${NC}"; }
need() {
if ! command -v "$1" >/dev/null 2>&1; then
err "Error: $1 is required but not installed."
exit 1
fi
}
need git
need bun
# ── 1. Clone or update fork ────────────────────────────────────────────
if [ -d "$OPENCODE_SRC/.git" ]; then
info "Updating existing source at $OPENCODE_SRC ..."
git -C "$OPENCODE_SRC" fetch origin "$FORK_BRANCH"
git -C "$OPENCODE_SRC" checkout "$FORK_BRANCH"
git -C "$OPENCODE_SRC" reset --hard "origin/$FORK_BRANCH"
else
info "Cloning fork (shallow) to $OPENCODE_SRC ..."
git clone --depth 1 --branch "$FORK_BRANCH" "$FORK_REPO" "$OPENCODE_SRC"
fi
# ── 2. Verify the IME fix is present in source ────────────────────────
PROMPT_FILE="$OPENCODE_SRC/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx"
if [ ! -f "$PROMPT_FILE" ]; then
err "Prompt file not found: $PROMPT_FILE"
exit 1
fi
if grep -q "setTimeout(() => setTimeout" "$PROMPT_FILE"; then
ok "IME fix already present in source."
else
warn "IME fix not found. Applying patch ..."
# Apply the fix: replace onSubmit={submit} with double-deferred version
sed -i 's|onSubmit={submit}|onSubmit={() => {\n // IME: double-defer so the last composed character (e.g. Korean\n // hangul) is flushed to plainText before we read it for submission.\n setTimeout(() => setTimeout(() => submit(), 0), 0)\n }}|' "$PROMPT_FILE"
if grep -q "setTimeout(() => setTimeout" "$PROMPT_FILE"; then
ok "Patch applied."
else
err "Failed to apply patch. The source may have changed."
exit 1
fi
fi
# ── 3. Install dependencies ────────────────────────────────────────────
info "Installing dependencies (this may take a minute) ..."
cd "$OPENCODE_SRC"
bun install --frozen-lockfile 2>/dev/null || bun install
# ── 4. Build (current platform only) ──────────────────────────────────
info "Building opencode for current platform ..."
cd "$OPENCODE_SRC/packages/opencode"
bun run build --single
# ── 5. Install binary ──────────────────────────────────────────────────
mkdir -p "$OPENCODE_DIR/bin"
PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "aarch64" ] && ARCH="arm64"
[ "$ARCH" = "x86_64" ] && ARCH="x64"
[ "$PLATFORM" = "darwin" ] && true
[ "$PLATFORM" = "linux" ] && true
BUILT_BINARY="$OPENCODE_SRC/packages/opencode/dist/opencode-${PLATFORM}-${ARCH}/bin/opencode"
if [ ! -f "$BUILT_BINARY" ]; then
BUILT_BINARY=$(find "$OPENCODE_SRC/packages/opencode/dist" -name "opencode" -type f -executable 2>/dev/null | head -1)
fi
if [ -f "$BUILT_BINARY" ]; then
if [ -f "$OPENCODE_DIR/bin/opencode" ]; then
cp "$OPENCODE_DIR/bin/opencode" "$OPENCODE_DIR/bin/opencode.bak.$(date +%Y%m%d%H%M%S)"
fi
cp "$BUILT_BINARY" "$OPENCODE_DIR/bin/opencode"
chmod +x "$OPENCODE_DIR/bin/opencode"
ok "Installed to $OPENCODE_DIR/bin/opencode"
else
err "Build failed - binary not found in dist/"
info "Try running manually:"
echo " cd $OPENCODE_SRC/packages/opencode && bun run build --single"
exit 1
fi
echo ""
ok "Done! Korean IME fix is now active."
echo ""
info "To uninstall and revert to the official release:"
echo " curl -fsSL https://opencode.ai/install | bash"
echo ""
info "To update (re-pull and rebuild):"
echo " $0"

View File

@@ -0,0 +1,18 @@
diff --git a/lib/git.js b/lib/git.js
index 000ee9fc..a2a6cbb7 100644
--- a/lib/git.js
+++ b/lib/git.js
@@ -254,8 +254,11 @@ class GitFetcher extends Fetcher {
resolved: this.resolved,
integrity: null, // it'll always be different, if we have one
}).extract(tmp).then(() => handler(`${tmp}${this.spec.gitSubdir || ''}`), er => {
- // fall back to ssh download if tarball fails
- if (er.constructor.name.match(/^Http/)) {
+ // fall back to clone if the tarball download fails due to an
+ // HTTP error or if the response is not a valid tarball (e.g.
+ // a hosted provider returning an HTML sign-in page with 200)
+ if ((typeof er.statusCode === 'number' && er.statusCode >= 400) ||
+ /^TAR_/.test(er.code)) {
return this.#clone(handler, false)
} else {
throw er

View File

@@ -0,0 +1,58 @@
diff --git a/Users/brendonovich/github.com/anomalyco/opencode/node_modules/solid-js/.bun-tag-6fcb6b48d6947d2c b/.bun-tag-6fcb6b48d6947d2c
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Users/brendonovich/github.com/anomalyco/opencode/node_modules/solid-js/.bun-tag-b272f631c12927b0 b/.bun-tag-b272f631c12927b0
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/dist/dev.cjs b/dist/dev.cjs
index 7104749486e4361e8c4ee7836a8046582cec7aa1..0501eb1ec5d13b81ecb13a5ac1a82db42502b976 100644
--- a/dist/dev.cjs
+++ b/dist/dev.cjs
@@ -764,6 +764,8 @@ function runComputation(node, value, time) {
if (node.updatedAt != null && "observers" in node) {
writeSignal(node, nextValue, true);
} else if (Transition && Transition.running && node.pure) {
+ // On first computation during transition, also set committed value #2046
+ if (!Transition.sources.has(node)) node.value = nextValue;
Transition.sources.add(node);
node.tValue = nextValue;
} else node.value = nextValue;
diff --git a/dist/dev.js b/dist/dev.js
index ea5e4bc2fd4f0b3922a73d9134439529dc81339f..4b3ec07e624d20fdd23d6941a4fdde6d3a78cca3 100644
--- a/dist/dev.js
+++ b/dist/dev.js
@@ -762,6 +762,8 @@ function runComputation(node, value, time) {
if (node.updatedAt != null && "observers" in node) {
writeSignal(node, nextValue, true);
} else if (Transition && Transition.running && node.pure) {
+ // On first computation during transition, also set committed value #2046
+ if (!Transition.sources.has(node)) node.value = nextValue;
Transition.sources.add(node);
node.tValue = nextValue;
} else node.value = nextValue;
diff --git a/dist/solid.cjs b/dist/solid.cjs
index 7c133a2b254678a84fd61d719fbeffad766e1331..2f68c99f2698210cc0bac62f074cc8cd3beb2881 100644
--- a/dist/solid.cjs
+++ b/dist/solid.cjs
@@ -717,6 +717,8 @@ function runComputation(node, value, time) {
if (node.updatedAt != null && "observers" in node) {
writeSignal(node, nextValue, true);
} else if (Transition && Transition.running && node.pure) {
+ // On first computation during transition, also set committed value #2046
+ if (!Transition.sources.has(node)) node.value = nextValue;
Transition.sources.add(node);
node.tValue = nextValue;
} else node.value = nextValue;
diff --git a/dist/solid.js b/dist/solid.js
index 656fd26e7e5c794aa22df19c2377ff5c0591fc29..f08e9f5a7157c3506e5b6922fe2ef991335a80be 100644
--- a/dist/solid.js
+++ b/dist/solid.js
@@ -715,6 +715,8 @@ function runComputation(node, value, time) {
if (node.updatedAt != null && "observers" in node) {
writeSignal(node, nextValue, true);
} else if (Transition && Transition.running && node.pure) {
+ // On first computation during transition, also set committed value #2046
+ if (!Transition.sources.has(node)) node.value = nextValue;
Transition.sources.add(node);
node.tValue = nextValue;
} else node.value = nextValue;

View File

@@ -0,0 +1,93 @@
diff --git a/lib/solid/Virtualizer.d.ts b/lib/solid/Virtualizer.d.ts
index 144dd7f..819aab9 100644
--- a/lib/solid/Virtualizer.d.ts
+++ b/lib/solid/Virtualizer.d.ts
@@ -38,6 +38,10 @@ export interface VirtualizerHandle {
* @param index index of item
*/
getItemSize(index: number): number;
+ /**
+ * Synchronously measure currently mounted items and update cached item sizes.
+ */
+ measure(): void;
/**
* Scroll to the item specified by index.
* @param index index of item
diff --git a/lib/solid/index.jsx b/lib/solid/index.jsx
index 029201a..3949cd4 100644
--- a/lib/solid/index.jsx
+++ b/lib/solid/index.jsx
@@ -1085,6 +1085,7 @@ const createResizer = (store, isHorizontal) => {
let viewportElement;
const sizeKey = isHorizontal ? "width" : "height";
const mountedIndexes = new WeakMap();
+ const mountedItems = new Map();
const resizeObserver = createResizeObserver((entries) => {
const resizes = [];
for (const { target, contentRect } of entries) {
@@ -1111,12 +1112,27 @@ const createResizer = (store, isHorizontal) => {
},
$observeItem: (el, i) => {
mountedIndexes.set(el, i);
+ mountedItems.set(i, el);
resizeObserver._observe(el);
return () => {
mountedIndexes.delete(el);
+ if (mountedItems.get(i) === el) {
+ mountedItems.delete(i);
+ }
resizeObserver._unobserve(el);
};
},
+ $measureItems: () => {
+ const resizes = [];
+ mountedItems.forEach((el, index) => {
+ if (!el.offsetParent)
+ return;
+ resizes.push([index, el.getBoundingClientRect()[sizeKey]]);
+ });
+ if (resizes.length) {
+ store.$update(ACTION_ITEM_RESIZE, resizes);
+ }
+ },
$dispose: resizeObserver._dispose,
};
};
@@ -1354,6 +1370,8 @@ const Virtualizer = (props) => {
const range = createMemo((prev) => {
stateVersion();
const next = store.$getRange(props.bufferSize);
+ next[0] = Math.max(0, next[0]);
+ next[1] = Math.min(props.data.length - 1, next[1]);
if (prev && isSameRange(prev, next)) {
return prev;
}
@@ -1380,6 +1398,7 @@ const Virtualizer = (props) => {
findItemIndex: store.$findItemIndex,
getItemOffset: store.$getItemOffset,
getItemSize: store.$getItemSize,
+ measure: resizer.$measureItems,
scrollToIndex: scroller.$scrollToIndex,
scrollTo: scroller.$scrollTo,
scrollBy: scroller.$scrollBy,
@@ -1417,6 +1436,11 @@ const Virtualizer = (props) => {
const indexes = [];
if (props.keepMounted) {
const mounted = new Set(props.keepMounted);
+ mounted.forEach((index) => {
+ if (index < 0 || index >= count) {
+ mounted.delete(index);
+ }
+ });
for (let [i, j] = range(); i <= j; i++) {
mounted.add(i);
}
@@ -1528,6 +1552,8 @@ const WindowVirtualizer = (props) => {
const range = createMemo((prev) => {
stateVersion();
const next = store.$getRange(props.bufferSize);
+ next[0] = Math.max(0, next[0]);
+ next[1] = Math.min(props.data.length - 1, next[1]);
if (prev && isSameRange(prev, next)) {
return prev;
}