oasis_test_v2 2.2.44 → 2.2.46

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 (2) hide show
  1. package/dist/index.js +455 -148
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -109,22 +109,27 @@ var init_manifest = __esm({
109
109
  });
110
110
 
111
111
  // ../contract/src/file-limits.ts
112
+ function fileUploadRequestMaxBytes(limits) {
113
+ return Math.min(Number.MAX_SAFE_INTEGER, Math.max(
114
+ 4 * Math.ceil(Math.max(limits.attachmentBytes, limits.knowledgeBytes, limits.cliBytes) / 3),
115
+ 6 * Math.max(limits.skillFileBytes, limits.textEditBytes)
116
+ ) + MIB);
117
+ }
112
118
  function validateFileLimits(input) {
113
119
  if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("\u6587\u4EF6\u9650\u5236\u5FC5\u987B\u662F\u5BF9\u8C61");
114
120
  const raw = input;
115
- if (Object.keys(raw).some((key2) => !Object.hasOwn(DEFAULT_FILE_LIMITS, key2))) throw new Error("\u5B58\u5728\u672A\u77E5\u7684\u6587\u4EF6\u9650\u5236\u5B57\u6BB5");
121
+ if (Object.keys(raw).some((key2) => !Object.hasOwn(DEFAULT_FILE_LIMITS, key2) && key2 !== "skillBundleBytes" && key2 !== "skillFileCount")) throw new Error("\u5B58\u5728\u672A\u77E5\u7684\u6587\u4EF6\u9650\u5236\u5B57\u6BB5");
116
122
  const result = { ...DEFAULT_FILE_LIMITS };
117
123
  for (const key2 of Object.keys(result)) {
118
124
  const value2 = raw[key2];
119
- if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 <= 0 || value2 > FILE_LIMIT_CAPACITY[key2]) {
120
- throw new Error(`${key2} \u5FC5\u987B\u662F 1 \u5230 ${FILE_LIMIT_CAPACITY[key2]} \u7684\u6574\u6570`);
125
+ if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 <= 0) {
126
+ throw new Error(`${key2} \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08\u5B57\u8282\uFF09`);
121
127
  }
122
128
  result[key2] = value2;
123
129
  }
124
- if (result.skillFileBytes > result.skillBundleBytes) throw new Error("\u6280\u80FD\u5355\u6587\u4EF6\u4E0A\u9650\u4E0D\u80FD\u8D85\u8FC7\u6574\u5305\u4E0A\u9650");
125
130
  return result;
126
131
  }
127
- var MIB, DEFAULT_FILE_LIMITS, FILE_LIMIT_CAPACITY, FILE_UPLOAD_REQUEST_MAX_BYTES, FILE_TRANSFER_INPUT_MAX_BYTES, FILE_TRANSFER_MAX_BYTES;
132
+ var MIB, DEFAULT_FILE_LIMITS, FILE_TRANSFER_MAX_BYTES;
128
133
  var init_file_limits = __esm({
129
134
  "../contract/src/file-limits.ts"() {
130
135
  "use strict";
@@ -133,16 +138,11 @@ var init_file_limits = __esm({
133
138
  attachmentBytes: 200 * MIB,
134
139
  knowledgeBytes: 200 * MIB,
135
140
  cliBytes: 200 * MIB,
136
- skillFileBytes: 20 * MIB,
137
- skillBundleBytes: 200 * MIB,
138
- skillFileCount: 128,
141
+ skillFileBytes: 200 * MIB,
139
142
  textEditBytes: 20 * MIB,
140
143
  officePreviewBytes: 200 * MIB
141
144
  });
142
- FILE_LIMIT_CAPACITY = DEFAULT_FILE_LIMITS;
143
- FILE_UPLOAD_REQUEST_MAX_BYTES = 4 * Math.ceil(200 * MIB / 3) + MIB;
144
- FILE_TRANSFER_INPUT_MAX_BYTES = DEFAULT_FILE_LIMITS.attachmentBytes;
145
- FILE_TRANSFER_MAX_BYTES = 4 * Math.ceil((FILE_TRANSFER_INPUT_MAX_BYTES + 32 * MIB) / 3) + MIB;
145
+ FILE_TRANSFER_MAX_BYTES = 4 * Math.ceil((200 * MIB + 32 * MIB) / 3) + MIB;
146
146
  }
147
147
  });
148
148
 
@@ -1749,6 +1749,130 @@ var init_file_preview = __esm({
1749
1749
  }
1750
1750
  });
1751
1751
 
1752
+ // ../contract/src/chunked-message.ts
1753
+ function* tokens(value2, key2 = null, strings2 = /* @__PURE__ */ new Map()) {
1754
+ if (value2 && typeof value2 === "object" && "toJSON" in value2 && typeof value2.toJSON === "function") value2 = value2.toJSON();
1755
+ if (typeof value2 === "string") {
1756
+ if (strings2.has(value2)) {
1757
+ yield ["ref", key2, strings2.get(value2)];
1758
+ return;
1759
+ }
1760
+ strings2.set(value2, strings2.size);
1761
+ yield ["string", key2];
1762
+ for (let i = 0; i < value2.length; i += STRING_CHARS) yield ["part", value2.slice(i, i + STRING_CHARS)];
1763
+ yield ["end"];
1764
+ } else if (value2 && typeof value2 === "object") {
1765
+ yield [Array.isArray(value2) ? "array" : "object", key2];
1766
+ if (Array.isArray(value2)) {
1767
+ for (const child of value2) yield* tokens(child, null, strings2);
1768
+ } else {
1769
+ for (const [name, child] of Object.entries(value2)) {
1770
+ if (child !== void 0 && typeof child !== "function" && typeof child !== "symbol") yield* tokens(child, name, strings2);
1771
+ }
1772
+ }
1773
+ yield ["end"];
1774
+ } else {
1775
+ if (typeof value2 === "bigint") throw new Error("BigInt is not JSON serializable");
1776
+ yield ["value", key2, value2 ?? null];
1777
+ }
1778
+ }
1779
+ function* encodeChunkedMessage(value2) {
1780
+ let parts = [], length = 0, start = true;
1781
+ const frame = (done) => `{"type":"chunked_message","start":${start},"done":${done},"tokens":[${parts.join(",")}]}`;
1782
+ for (const token of tokens(value2)) {
1783
+ const part = JSON.stringify(token);
1784
+ if (length + part.length > FRAME_CHARS && parts.length) {
1785
+ yield frame(false);
1786
+ start = false;
1787
+ parts = [];
1788
+ length = 0;
1789
+ }
1790
+ parts.push(part);
1791
+ length += part.length + 1;
1792
+ }
1793
+ yield frame(true);
1794
+ }
1795
+ var FRAME_CHARS, STRING_CHARS, ChunkedMessageDecoder;
1796
+ var init_chunked_message = __esm({
1797
+ "../contract/src/chunked-message.ts"() {
1798
+ "use strict";
1799
+ FRAME_CHARS = 512 * 1024;
1800
+ STRING_CHARS = 64 * 1024;
1801
+ ChunkedMessageDecoder = class {
1802
+ stack = [];
1803
+ strings = [];
1804
+ active = false;
1805
+ hasRoot = false;
1806
+ root;
1807
+ attach(key2, value2) {
1808
+ const parent = this.stack.at(-1);
1809
+ if (!parent) {
1810
+ if (this.hasRoot || key2 !== null) throw new Error("Invalid chunked message root");
1811
+ this.root = value2;
1812
+ this.hasRoot = true;
1813
+ } else if (parent.kind === "array") {
1814
+ if (key2 !== null) throw new Error("Invalid array key");
1815
+ parent.value.push(value2);
1816
+ } else if (parent.kind === "object") {
1817
+ if (typeof key2 !== "string" || Object.hasOwn(parent.value, key2)) throw new Error("Invalid object key");
1818
+ Object.defineProperty(parent.value, key2, { value: value2, enumerable: true, writable: true, configurable: true });
1819
+ } else throw new Error("Unclosed string");
1820
+ }
1821
+ /** undefined means that more frames are needed; plain legacy JSON passes through. */
1822
+ accept(input) {
1823
+ if (!input || typeof input !== "object" || !("type" in input) || input.type !== "chunked_message") return input;
1824
+ const frame = input;
1825
+ if (typeof frame.start !== "boolean" || typeof frame.done !== "boolean" || !Array.isArray(frame.tokens)) throw new Error("Invalid chunked frame");
1826
+ if (frame.start) {
1827
+ if (this.active) throw new Error("Overlapping chunked messages");
1828
+ this.active = true;
1829
+ this.hasRoot = false;
1830
+ this.root = void 0;
1831
+ this.stack = [];
1832
+ this.strings = [];
1833
+ }
1834
+ if (!this.active) throw new Error("Missing chunked message start");
1835
+ for (const token of frame.tokens) {
1836
+ if (!Array.isArray(token)) throw new Error("Invalid chunked token");
1837
+ const [kind, key2, value2] = token;
1838
+ if (kind === "object" || kind === "array") {
1839
+ const child = kind === "array" ? [] : {};
1840
+ this.attach(key2, child);
1841
+ this.stack.push({ kind, value: child });
1842
+ } else if (kind === "string") {
1843
+ if (key2 !== null && typeof key2 !== "string") throw new Error("Invalid string key");
1844
+ this.stack.push({ kind, key: key2, parts: [] });
1845
+ } else if (kind === "part") {
1846
+ const parent = this.stack.at(-1);
1847
+ if (parent?.kind !== "string" || typeof key2 !== "string") throw new Error("Invalid string part");
1848
+ parent.parts.push(key2);
1849
+ } else if (kind === "ref") {
1850
+ if (!Number.isInteger(value2) || value2 < 0 || value2 >= this.strings.length) throw new Error("Invalid string reference");
1851
+ this.attach(key2, this.strings[value2]);
1852
+ } else if (kind === "value") this.attach(key2, value2);
1853
+ else if (kind === "end") {
1854
+ const parent = this.stack.pop();
1855
+ if (!parent) throw new Error("Unexpected end");
1856
+ if (parent.kind === "string") {
1857
+ const text6 = parent.parts.join("");
1858
+ this.strings.push(text6);
1859
+ this.attach(parent.key, text6);
1860
+ }
1861
+ } else throw new Error("Unknown chunked token");
1862
+ }
1863
+ if (!frame.done) return void 0;
1864
+ if (!this.hasRoot || this.stack.length) throw new Error("Incomplete chunked message");
1865
+ const result = this.root;
1866
+ this.root = void 0;
1867
+ this.active = false;
1868
+ this.hasRoot = false;
1869
+ this.strings = [];
1870
+ return result;
1871
+ }
1872
+ };
1873
+ }
1874
+ });
1875
+
1752
1876
  // ../contract/src/index.ts
1753
1877
  var init_src = __esm({
1754
1878
  "../contract/src/index.ts"() {
@@ -1814,6 +1938,7 @@ var init_src = __esm({
1814
1938
  init_http_api();
1815
1939
  init_file_preview();
1816
1940
  init_file_limits();
1941
+ init_chunked_message();
1817
1942
  }
1818
1943
  });
1819
1944
 
@@ -1884,17 +2009,16 @@ function withFileLimits(limits, fn) {
1884
2009
  }
1885
2010
  function assertSkillFileLimits(files) {
1886
2011
  const limits = currentFileLimits();
1887
- if (files.length > limits.skillFileCount) throw new ApiError(413, "SKILL_TOO_LARGE", `\u6280\u80FD\u6587\u4EF6\u6570\u8D85\u8FC7 ${limits.skillFileCount} \u4E2A`);
1888
- let total = 0;
1889
2012
  for (const file of files) {
1890
2013
  if (file.size > limits.skillFileBytes) throw new ApiError(413, "SKILL_TOO_LARGE", `\u6280\u80FD\u6587\u4EF6\u8D85\u8FC7 ${limits.skillFileBytes / 1024 / 1024} MiB\uFF1A${file.path}`);
1891
- total += file.size;
1892
2014
  }
1893
- if (total > limits.skillBundleBytes) throw new ApiError(413, "SKILL_TOO_LARGE", `\u6280\u80FD\u6574\u5305\u8D85\u8FC7 ${limits.skillBundleBytes / 1024 / 1024} MiB`);
1894
2015
  }
1895
2016
  function assertFileBytes(bytes2, maxBytes = currentFileLimits().attachmentBytes) {
1896
2017
  if (bytes2.byteLength > maxBytes) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${maxBytes / 1024 / 1024} MiB \u4E0A\u9650`);
1897
2018
  }
2019
+ function currentFileReadMaxBytes() {
2020
+ return Math.max(DEFAULT_FILE_LIMITS.attachmentBytes, ...Object.values(currentFileLimits()));
2021
+ }
1898
2022
  function assertEditableBytes(bytes2) {
1899
2023
  assertFileBytes(bytes2, currentFileLimits().textEditBytes);
1900
2024
  if (bytes2.includes(0)) throw new ApiError(400, "INVALID_TEXT", "\u4EC5\u652F\u6301 UTF-8 \u6587\u672C");
@@ -2061,12 +2185,12 @@ async function readArtifactFile(kernel, blobs, artifactId, path40, expectedRef,
2061
2185
  if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
2062
2186
  hash3 = entry.hash;
2063
2187
  name = entry.path;
2064
- if (entry.size !== void 0 && entry.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2188
+ if (entry.size !== void 0 && entry.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2065
2189
  } else if (output.contentKind !== "inline-blob") {
2066
2190
  throw new ApiError(404, "NOT_FOUND", "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
2067
2191
  }
2068
2192
  const meta = await blobs.meta?.(hash3);
2069
- if (meta && meta.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2193
+ if (meta && meta.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2070
2194
  try {
2071
2195
  return { bytes: await blobs.get(hash3), name, contentType: meta?.contentType };
2072
2196
  } catch {
@@ -2094,7 +2218,7 @@ async function serveFileRequest(req, res, url, engine) {
2094
2218
  url.searchParams.get("ref"),
2095
2219
  url.searchParams.get("revisionId")
2096
2220
  );
2097
- if (bytes2.length > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2221
+ if (bytes2.length > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2098
2222
  res.writeHead(200, {
2099
2223
  "content-type": contentType ?? sniffContentType(bytes2) ?? "application/octet-stream",
2100
2224
  "content-length": String(bytes2.length),
@@ -2123,6 +2247,7 @@ var init_file_http = __esm({
2123
2247
  init_src();
2124
2248
  init_artifact_content2();
2125
2249
  init_router();
2250
+ init_file_limits2();
2126
2251
  }
2127
2252
  });
2128
2253
 
@@ -3256,12 +3381,12 @@ function recordSubagentCall(calls, id, call) {
3256
3381
  }
3257
3382
  function byModelOf(calls) {
3258
3383
  const out = {};
3259
- for (const { model, tokens } of calls.values()) {
3384
+ for (const { model, tokens: tokens2 } of calls.values()) {
3260
3385
  const acc = out[model] ?? (out[model] = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 });
3261
- acc.inputTokens += tokens.inputTokens;
3262
- acc.outputTokens += tokens.outputTokens;
3263
- acc.cacheReadTokens += tokens.cacheReadTokens;
3264
- acc.cacheCreationTokens += tokens.cacheCreationTokens;
3386
+ acc.inputTokens += tokens2.inputTokens;
3387
+ acc.outputTokens += tokens2.outputTokens;
3388
+ acc.cacheReadTokens += tokens2.cacheReadTokens;
3389
+ acc.cacheCreationTokens += tokens2.cacheCreationTokens;
3265
3390
  }
3266
3391
  return out;
3267
3392
  }
@@ -4880,7 +5005,7 @@ async function listModels(runtime, executablePath) {
4880
5005
  case "claude-code":
4881
5006
  return claudeStaticModels();
4882
5007
  case "codex":
4883
- return withFallback(runtime, executablePath, () => discoverCodexModels(executablePath), codexStaticModels);
5008
+ return discoverCodexCatalog(executablePath);
4884
5009
  case "gemini":
4885
5010
  return withFallback(runtime, executablePath, async () => await discoverACPModels(executablePath ?? "gemini", "gemini", ["--acp"]) ?? [], geminiStaticModels);
4886
5011
  case "copilot":
@@ -5024,22 +5149,9 @@ function codebuddyStaticModels() {
5024
5149
  function cursorStaticModels() {
5025
5150
  return [{ id: "default[]", label: "Auto", provider: "cursor", default: true }];
5026
5151
  }
5027
- async function isModelValid(runtime, modelId, executablePath) {
5028
- try {
5029
- const models = await listModels(runtime, executablePath);
5030
- if (models.length === 0) return true;
5031
- return models.some((m2) => m2.id === modelId);
5032
- } catch {
5033
- return true;
5034
- }
5035
- }
5036
- async function defaultModelFor(runtime, executablePath) {
5037
- try {
5038
- const models = await listModels(runtime, executablePath);
5039
- return models.find((m2) => m2.default)?.id;
5040
- } catch {
5041
- return void 0;
5042
- }
5152
+ function resolveDispatchModel(candidate, catalog) {
5153
+ if (catalog.length === 0 || catalog.some((m2) => m2.id === candidate)) return { model: candidate, replaced: false };
5154
+ return { model: catalog.find((m2) => m2.default)?.id, replaced: true };
5043
5155
  }
5044
5156
  async function discoverOpenCodeModels(bin) {
5045
5157
  const out = await runCapture(bin ?? "opencode", ["models"]);
@@ -5055,7 +5167,163 @@ async function discoverOpenCodeModels(bin) {
5055
5167
  }
5056
5168
  return models;
5057
5169
  }
5058
- async function discoverCodexModels(bin) {
5170
+ async function discoverCodexCatalog(bin) {
5171
+ const probed = { customProvider: false };
5172
+ const models = await cachedDiscovery(discoveryCacheKey("codex", bin), async () => {
5173
+ const catalog = codexCatalogFromAppServer(await probeCodexAppServer(bin ?? "codex"));
5174
+ if (!catalog) return discoverCodexBundledModels(bin);
5175
+ probed.customProvider = catalog.customProvider;
5176
+ return catalog.models;
5177
+ });
5178
+ return models.length > 0 || probed.customProvider ? models : codexStaticModels();
5179
+ }
5180
+ function codexCatalogFromAppServer(probe) {
5181
+ const config2 = probe?.config;
5182
+ if (!probe || !config2) return null;
5183
+ const providerId = config2.modelProvider?.trim() || "openai";
5184
+ const customProvider = providerId.toLowerCase() !== "openai";
5185
+ if (!customProvider && !probe.models) return null;
5186
+ const provider = customProvider ? config2.providerName?.trim() || providerId : "OpenAI";
5187
+ const listApplies = !customProvider || Boolean(config2.modelCatalogJson);
5188
+ const listed = listApplies ? probe.models ?? [] : [];
5189
+ const configured = config2.model?.trim();
5190
+ const defaultId = configured || listed.find((m2) => m2.isDefault)?.id;
5191
+ const models = listed.map((m2) => ({ id: m2.id, label: m2.label, provider }));
5192
+ if (configured && !models.some((m2) => m2.id === configured)) models.push({ id: configured, label: configured, provider });
5193
+ return { models: models.map((m2) => m2.id === defaultId ? { ...m2, default: true } : m2), customProvider };
5194
+ }
5195
+ async function probeCodexAppServer(bin, timeoutMs = 15e3) {
5196
+ let dir;
5197
+ try {
5198
+ dir = (0, import_node_fs2.mkdtempSync)((0, import_node_path3.join)((0, import_node_os2.tmpdir)(), "oasis-codex-models-"));
5199
+ } catch {
5200
+ return null;
5201
+ }
5202
+ let child;
5203
+ try {
5204
+ child = (0, import_node_child_process5.spawn)(bin, ["app-server", "--stdio", "-c", "allow_login_shell=false"], {
5205
+ cwd: dir,
5206
+ stdio: ["pipe", "pipe", "ignore"],
5207
+ env: { ...process.env }
5208
+ });
5209
+ } catch {
5210
+ (0, import_node_fs2.rmSync)(dir, { recursive: true, force: true });
5211
+ return null;
5212
+ }
5213
+ return await new Promise((resolve10) => {
5214
+ let settled = false;
5215
+ let config2;
5216
+ const listed = [];
5217
+ let listState = "pending";
5218
+ let nextId = 3;
5219
+ const MAX_PAGES = 20;
5220
+ let pages = 0;
5221
+ const finish = (value2) => {
5222
+ if (settled) return;
5223
+ settled = true;
5224
+ clearTimeout(timer);
5225
+ try {
5226
+ child.stdin?.end();
5227
+ } catch {
5228
+ }
5229
+ try {
5230
+ child.kill("SIGKILL");
5231
+ } catch {
5232
+ }
5233
+ try {
5234
+ (0, import_node_fs2.rmSync)(dir, { recursive: true, force: true });
5235
+ } catch {
5236
+ }
5237
+ resolve10(value2);
5238
+ };
5239
+ const maybeFinish = () => {
5240
+ if (config2 === void 0 || listState === "pending") return;
5241
+ finish({ config: config2, models: listState === "done" ? listed : null });
5242
+ };
5243
+ const timer = setTimeout(() => finish(null), timeoutMs);
5244
+ const send = (id, method, params) => {
5245
+ try {
5246
+ child.stdin?.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
5247
+ `);
5248
+ } catch {
5249
+ finish(null);
5250
+ }
5251
+ };
5252
+ child.on("error", () => finish(null));
5253
+ child.stdin?.on("error", () => finish(null));
5254
+ child.on("close", () => finish(null));
5255
+ let buf = "";
5256
+ child.stdout?.on("data", (chunk) => {
5257
+ if (settled) return;
5258
+ buf += chunk.toString("utf8");
5259
+ let nl;
5260
+ while ((nl = buf.indexOf("\n")) >= 0) {
5261
+ const line = buf.slice(0, nl).trim();
5262
+ buf = buf.slice(nl + 1);
5263
+ if (!line) continue;
5264
+ let msg;
5265
+ try {
5266
+ msg = JSON.parse(line);
5267
+ } catch {
5268
+ continue;
5269
+ }
5270
+ if (typeof msg.id !== "number") continue;
5271
+ if (msg.id === 1) {
5272
+ if (msg.error !== void 0) {
5273
+ finish(null);
5274
+ return;
5275
+ }
5276
+ send(2, "config/read", { cwd: dir });
5277
+ send(nextId, "model/list", {});
5278
+ continue;
5279
+ }
5280
+ if (msg.id === 2) {
5281
+ config2 = msg.error !== void 0 ? null : parseCodexConfigRead(msg.result);
5282
+ maybeFinish();
5283
+ continue;
5284
+ }
5285
+ if (msg.id === nextId && listState === "pending") {
5286
+ if (msg.error !== void 0) {
5287
+ listState = "failed";
5288
+ } else {
5289
+ const page = msg.result ?? {};
5290
+ for (const m2 of page.data ?? []) {
5291
+ const id = typeof m2.model === "string" && m2.model ? m2.model : typeof m2.id === "string" ? m2.id : "";
5292
+ if (!id || m2.hidden === true || listed.some((x2) => x2.id === id)) continue;
5293
+ listed.push({ id, label: typeof m2.displayName === "string" && m2.displayName ? m2.displayName : id, isDefault: m2.isDefault === true });
5294
+ }
5295
+ pages += 1;
5296
+ if (typeof page.nextCursor === "string" && page.nextCursor && pages < MAX_PAGES) {
5297
+ nextId += 1;
5298
+ send(nextId, "model/list", { cursor: page.nextCursor });
5299
+ continue;
5300
+ }
5301
+ listState = "done";
5302
+ }
5303
+ maybeFinish();
5304
+ }
5305
+ }
5306
+ });
5307
+ send(1, "initialize", { clientInfo: { name: "oasis-model-discovery", version: "0.1.0" }, capabilities: {} });
5308
+ });
5309
+ }
5310
+ function parseCodexConfigRead(result) {
5311
+ const config2 = (result ?? {}).config ?? {};
5312
+ const str5 = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : void 0;
5313
+ const model = str5(config2["model"]);
5314
+ const modelProvider = str5(config2["model_provider"]);
5315
+ const modelCatalogJson = str5(config2["model_catalog_json"]);
5316
+ const providers = config2["model_providers"];
5317
+ const entry = modelProvider && providers && typeof providers === "object" ? providers[modelProvider] : void 0;
5318
+ const providerName = entry && typeof entry === "object" ? str5(entry["name"]) : void 0;
5319
+ return {
5320
+ ...model ? { model } : {},
5321
+ ...modelProvider ? { modelProvider } : {},
5322
+ ...providerName ? { providerName } : {},
5323
+ ...modelCatalogJson ? { modelCatalogJson } : {}
5324
+ };
5325
+ }
5326
+ async function discoverCodexBundledModels(bin) {
5059
5327
  const out = await runCapture(bin ?? "codex", ["debug", "models", "--bundled"]);
5060
5328
  if (out === void 0) return [];
5061
5329
  try {
@@ -5256,6 +5524,7 @@ async function discoverACPModels(bin, provider, args = ["acp"], timeoutMs = 12e3
5256
5524
  };
5257
5525
  const watchdog = setTimeout(fail, timeoutMs);
5258
5526
  child.on("error", fail);
5527
+ child.stdin?.on("error", fail);
5259
5528
  child.on("exit", () => {
5260
5529
  if (!settled) fail();
5261
5530
  });
@@ -9996,14 +10265,14 @@ function accountUsage(model, usage) {
9996
10265
  const input = nonNegative(usage.inputTokens);
9997
10266
  const output = nonNegative(usage.outputTokens);
9998
10267
  const cache = nonNegative(usage.cacheReadTokens) + nonNegative(usage.cacheCreationTokens);
9999
- const tokens = { input, cache, output, total: input + cache + output };
10268
+ const tokens2 = { input, cache, output, total: input + cache + output };
10000
10269
  const estimated = estimateUsageCostBreakdown(resolved, usage);
10001
10270
  const weights = estimated ? { input: estimated.inputMicros, cache: estimated.cacheMicros, output: estimated.outputMicros } : null;
10002
10271
  const reported = Math.max(usage.costUsdMicros ?? 0, 0);
10003
10272
  if (reported > 0) {
10004
10273
  const parts = weights && estimated?.currency === "USD" ? splitByWeight(reported, weights) : { input: 0, cache: 0, output: reported };
10005
10274
  return {
10006
- tokens,
10275
+ tokens: tokens2,
10007
10276
  currency: "USD",
10008
10277
  amountMicros: reported,
10009
10278
  parts,
@@ -10016,7 +10285,7 @@ function accountUsage(model, usage) {
10016
10285
  const amount = weights.input + weights.cache + weights.output;
10017
10286
  const displayUsdMicros = toDisplayUsd(estimated.currency, amount);
10018
10287
  return {
10019
- tokens,
10288
+ tokens: tokens2,
10020
10289
  currency: estimated.currency,
10021
10290
  amountMicros: amount,
10022
10291
  parts: weights,
@@ -10026,7 +10295,7 @@ function accountUsage(model, usage) {
10026
10295
  };
10027
10296
  }
10028
10297
  return {
10029
- tokens,
10298
+ tokens: tokens2,
10030
10299
  currency: "USD",
10031
10300
  amountMicros: 0,
10032
10301
  parts: { input: 0, cache: 0, output: 0 },
@@ -10051,8 +10320,8 @@ function estimateSubagentCostUsdMicros(usage) {
10051
10320
  const models = Object.entries(usage.byModel);
10052
10321
  if (models.length === 0) return void 0;
10053
10322
  let total = 0;
10054
- for (const [model, tokens] of models) {
10055
- const account = accountUsage(model, { ...tokens, costUsdMicros: null });
10323
+ for (const [model, tokens2] of models) {
10324
+ const account = accountUsage(model, { ...tokens2, costUsdMicros: null });
10056
10325
  if (!account || account.source !== "estimated") return void 0;
10057
10326
  total += account.displayUsdMicros;
10058
10327
  }
@@ -24831,13 +25100,13 @@ var require_axios = __commonJS({
24831
25100
  return utils$1.isArray(value2) ? value2.map(normalizeValue) : String(value2);
24832
25101
  }
24833
25102
  function parseTokens(str5) {
24834
- const tokens = /* @__PURE__ */ Object.create(null);
25103
+ const tokens2 = /* @__PURE__ */ Object.create(null);
24835
25104
  const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;
24836
25105
  let match;
24837
25106
  while (match = tokensRE.exec(str5)) {
24838
- tokens[match[1]] = match[2];
25107
+ tokens2[match[1]] = match[2];
24839
25108
  }
24840
- return tokens;
25109
+ return tokens2;
24841
25110
  }
24842
25111
  var isValidHeaderName = (str5) => /^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(str5.trim());
24843
25112
  function matchHeaderValue(context2, value2, header, filter, isHeaderNameFilter) {
@@ -166460,8 +166729,6 @@ async function prepareSessionFileRestore(input) {
166460
166729
  }
166461
166730
  if (input.fresh) issues.push(...saveIssues.map((i) => ({ ...i, phase: "restore", message: `\u539F\u6587\u4EF6\u5C1A\u672A\u4FDD\u5B58\uFF1A${i.message}` })));
166462
166731
  const lines = ["# \u4F1A\u8BDD\u6587\u4EF6", "", "\u4EE5\u4E0B\u6587\u4EF6\u6765\u81EA\u672C\u4F1A\u8BDD\u7684\u6301\u4E45\u8BB0\u5F55\u3002\u5386\u53F2\u8F93\u5165\u6309\u7248\u672C\u4FDD\u7559\uFF1Boutputs \u662F\u6700\u8FD1\u4FDD\u5B58\u7684\u4EA7\u51FA\u3002", ""];
166463
- let total = 0;
166464
- const inlineMax = input.inlineMaxBytes ?? RESTORE_INLINE_MAX_BYTES;
166465
166732
  const seen = /* @__PURE__ */ new Set();
166466
166733
  const add = async (name, ref2, path40, replace2) => {
166467
166734
  if (seen.has(path40)) return;
@@ -166471,12 +166738,9 @@ async function prepareSessionFileRestore(input) {
166471
166738
  return;
166472
166739
  }
166473
166740
  try {
166474
- if (Object.keys(restoreFiles).length >= RESTORE_MAX_FILES) throw new Error("\u8FD9\u4E00\u8F6E\u8981\u968F\u4EFB\u52A1\u5E26\u8FC7\u53BB\u7684\u6587\u4EF6\u592A\u591A\uFF0C\u5B83\u6CA1\u6392\u4E0A\uFF1B\u539F\u4EF6\u5728\u670D\u52A1\u7AEF\u5B8C\u597D\uFF0C\u53EF\u5728\u9879\u76EE\u300C\u76F8\u5173\u6587\u4EF6\u300D\u91CC\u6253\u5F00");
166475
166741
  const bytes2 = await input.readBlob(ref2);
166476
- if (total + bytes2.byteLength > inlineMax) throw new Error(`\u8FD9\u4E00\u8F6E\u968F\u4EFB\u52A1\u9001\u5230\u8FD0\u884C\u673A\u5668\u7684\u6587\u4EF6\u603B\u91CF\u5DF2\u6EE1 ${Math.round(inlineMax / 1024 / 1024)} MiB\uFF0C\u5B83\u8FD9\u6B21\u6CA1\u9001\u8FC7\u53BB\uFF1B\u539F\u4EF6\u5728\u670D\u52A1\u7AEF\u5B8C\u597D\uFF0C\u53EF\u5728\u9879\u76EE\u300C\u76F8\u5173\u6587\u4EF6\u300D\u91CC\u6253\u5F00`);
166477
166742
  const hash3 = (0, import_node_crypto24.createHash)("sha256").update(bytes2).digest("hex");
166478
166743
  if (hash3 !== ref2.replace(/^blob:/, "")) throw new Error("\u539F\u4EF6\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
166479
- total += bytes2.byteLength;
166480
166744
  restoreFiles[path40] = { contentBase64: Buffer.from(bytes2).toString("base64"), sha256: hash3, overwrite: replace2 };
166481
166745
  lines.push(`- ${JSON.stringify(name)} \u2192 ${JSON.stringify(path40)}`);
166482
166746
  } catch (error2) {
@@ -166508,17 +166772,14 @@ async function prepareSessionFileRestore(input) {
166508
166772
  if (blocking.length) throw new ApiError(409, "CHAT_FILE_RESTORE_FAILED", `\u6587\u4EF6\u6062\u590D\u672A\u5B8C\u6210\uFF1A${blocking.map((i) => i.message).join("\uFF1B")}`);
166509
166773
  return { restoreFiles, index: lines.join("\n") };
166510
166774
  }
166511
- var import_node_crypto24, RESTORE_INLINE_MAX_BYTES, RESTORE_MAX_FILES;
166775
+ var import_node_crypto24;
166512
166776
  var init_file_restore = __esm({
166513
166777
  "../server/src/domains/chat-sessions/file-restore.ts"() {
166514
166778
  "use strict";
166515
166779
  import_node_crypto24 = require("node:crypto");
166516
- init_src();
166517
166780
  init_chat_attachment_files();
166518
166781
  init_router();
166519
166782
  init_persistent_files();
166520
- RESTORE_INLINE_MAX_BYTES = FILE_TRANSFER_INPUT_MAX_BYTES;
166521
- RESTORE_MAX_FILES = 500;
166522
166783
  }
166523
166784
  });
166524
166785
 
@@ -179212,20 +179473,20 @@ function knowledgeSearchTokenStream(value2) {
179212
179473
  raw.push(token);
179213
179474
  }
179214
179475
  }
179215
- const tokens = [];
179476
+ const tokens2 = [];
179216
179477
  for (let index2 = 0; index2 < raw.length; index2++) {
179217
179478
  const token = raw[index2];
179218
179479
  const next = raw[index2 + 1];
179219
179480
  const merged = new RegExp("^\\p{Script=Han}$", "u").test(token) && next && new RegExp("^\\p{Script=Han}$", "u").test(next) ? `${token}${next}` : token;
179220
179481
  if (merged !== token) index2++;
179221
179482
  if (LEXICAL_STOPWORDS.has(merged)) continue;
179222
- tokens.push(merged);
179483
+ tokens2.push(merged);
179223
179484
  const chars = [...merged];
179224
179485
  if (new RegExp("^\\p{Script=Han}+$", "u").test(merged) && chars.length > 2) {
179225
- for (let index3 = 0; index3 < chars.length - 1; index3++) tokens.push(`${chars[index3]}${chars[index3 + 1]}`);
179486
+ for (let index3 = 0; index3 < chars.length - 1; index3++) tokens2.push(`${chars[index3]}${chars[index3 + 1]}`);
179226
179487
  }
179227
179488
  }
179228
- return tokens;
179489
+ return tokens2;
179229
179490
  }
179230
179491
  function knowledgeSearchTerms(value2) {
179231
179492
  return [...new Set(knowledgeSearchTokenStream(value2))];
@@ -179237,20 +179498,20 @@ function documentSearchTokens(chunk) {
179237
179498
  [chunk.logicalPath, 2]
179238
179499
  ];
179239
179500
  return fields.flatMap(([value2, repeats]) => {
179240
- const tokens = knowledgeSearchTokenStream(value2);
179241
- return Array.from({ length: repeats }, () => tokens).flat();
179501
+ const tokens2 = knowledgeSearchTokenStream(value2);
179502
+ return Array.from({ length: repeats }, () => tokens2).flat();
179242
179503
  });
179243
179504
  }
179244
179505
  function layeredSearchTokens(chunk, lexicalLayer) {
179245
179506
  return lexicalLayer === "document" ? documentSearchTokens(chunk) : knowledgeSearchTokenStream(chunk.text);
179246
179507
  }
179247
179508
  function knowledgeSearchIndexFields(chunk, lexicalLayer) {
179248
- const tokens = layeredSearchTokens(chunk, lexicalLayer);
179509
+ const tokens2 = layeredSearchTokens(chunk, lexicalLayer);
179249
179510
  return {
179250
- terms: [...new Set(tokens)],
179511
+ terms: [...new Set(tokens2)],
179251
179512
  lexicalIndexVersion: KNOWLEDGE_LEXICAL_INDEX_VERSION,
179252
179513
  lexicalLayer,
179253
- tokenCount: tokens.length
179514
+ tokenCount: tokens2.length
179254
179515
  };
179255
179516
  }
179256
179517
  function knowledgeSearchIndexEntries(input) {
@@ -179334,10 +179595,10 @@ function splitKnowledgeChunks(content3, maxChars = 1600) {
179334
179595
  function scoreKnowledgeChunk(query, chunk, corpus) {
179335
179596
  const queryTerms = knowledgeSearchTerms(query);
179336
179597
  if (queryTerms.length === 0) return 0;
179337
- const tokens = layeredSearchTokens(chunk, chunk.lexicalLayer ?? "chunk");
179598
+ const tokens2 = layeredSearchTokens(chunk, chunk.lexicalLayer ?? "chunk");
179338
179599
  const frequencies2 = /* @__PURE__ */ new Map();
179339
- for (const token of tokens) frequencies2.set(token, (frequencies2.get(token) ?? 0) + 1);
179340
- const documentLength = chunk.tokenCount ?? tokens.length;
179600
+ for (const token of tokens2) frequencies2.set(token, (frequencies2.get(token) ?? 0) + 1);
179601
+ const documentLength = chunk.tokenCount ?? tokens2.length;
179341
179602
  const averageLength = Math.max(1, corpus.averageTokenCount);
179342
179603
  const documentCount = Math.max(1, corpus.size);
179343
179604
  const k1 = 1.2;
@@ -183164,7 +183425,7 @@ ${pointer}
183164
183425
  var require_resolve_props = __commonJS({
183165
183426
  "../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports2) {
183166
183427
  "use strict";
183167
- function resolveProps(tokens, { flow: flow3, indicator, next, offset, onError, parentIndent, startOnNewline }) {
183428
+ function resolveProps(tokens2, { flow: flow3, indicator, next, offset, onError, parentIndent, startOnNewline }) {
183168
183429
  let spaceBefore = false;
183169
183430
  let atNewline = startOnNewline;
183170
183431
  let hasSpace = startOnNewline;
@@ -183179,7 +183440,7 @@ var require_resolve_props = __commonJS({
183179
183440
  let comma = null;
183180
183441
  let found2 = null;
183181
183442
  let start = null;
183182
- for (const token of tokens) {
183443
+ for (const token of tokens2) {
183183
183444
  if (reqSpace) {
183184
183445
  if (token.type !== "space" && token.type !== "newline" && token.type !== "comma")
183185
183446
  onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
@@ -183270,7 +183531,7 @@ var require_resolve_props = __commonJS({
183270
183531
  hasSpace = false;
183271
183532
  }
183272
183533
  }
183273
- const last = tokens[tokens.length - 1];
183534
+ const last = tokens2[tokens2.length - 1];
183274
183535
  const end = last ? last.offset + last.source.length : offset;
183275
183536
  if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) {
183276
183537
  onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
@@ -184609,8 +184870,8 @@ ${cb}` : comment;
184609
184870
  * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document.
184610
184871
  * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.
184611
184872
  */
184612
- *compose(tokens, forceDoc = false, endOffset = -1) {
184613
- for (const token of tokens)
184873
+ *compose(tokens2, forceDoc = false, endOffset = -1) {
184874
+ for (const token of tokens2)
184614
184875
  yield* this.next(token);
184615
184876
  yield* this.end(forceDoc, endOffset);
184616
184877
  }
@@ -192780,8 +193041,8 @@ var init_governance_service = __esm({
192780
193041
  return false;
192781
193042
  };
192782
193043
  const addFragment = (source, content3) => {
192783
- const tokens = estimateKnowledgeTokens(content3);
192784
- if (usedTokens + tokens > selectedPolicy.tokenBudget) return false;
193044
+ const tokens2 = estimateKnowledgeTokens(content3);
193045
+ if (usedTokens + tokens2 > selectedPolicy.tokenBudget) return false;
192785
193046
  const fragment = this.snapshot(ctx, "context_pack", content3, trustedProjectId, void 0);
192786
193047
  snapshots2.push(fragment);
192787
193048
  entries.push({
@@ -192797,12 +193058,12 @@ var init_governance_service = __esm({
192797
193058
  ...source.score !== void 0 ? { relevanceScore: source.score } : {},
192798
193059
  ...source.evidenceRole ? { evidenceRole: source.evidenceRole } : {},
192799
193060
  contentHash: fragment.contentHash,
192800
- tokenCount: tokens,
193061
+ tokenCount: tokens2,
192801
193062
  ordinal: entries.length,
192802
193063
  renderedFragmentRef: fragment.ref
192803
193064
  });
192804
193065
  fragments.push(content3);
192805
- usedTokens += tokens;
193066
+ usedTokens += tokens2;
192806
193067
  return true;
192807
193068
  };
192808
193069
  const taskFacts = [...request2.facts];
@@ -203046,9 +203307,9 @@ function lexicalMatchRatio(query, value2) {
203046
203307
  const actual = new Set(lexicalTerms(value2));
203047
203308
  return expected.filter((term) => actual.has(term)).length / expected.length;
203048
203309
  }
203049
- function frequencies(tokens) {
203310
+ function frequencies(tokens2) {
203050
203311
  const values = /* @__PURE__ */ new Map();
203051
- for (const token of tokens) values.set(token, (values.get(token) ?? 0) + 1);
203312
+ for (const token of tokens2) values.set(token, (values.get(token) ?? 0) + 1);
203052
203313
  return values;
203053
203314
  }
203054
203315
  function bm25Scores(query, documents, options = {}) {
@@ -203056,21 +203317,21 @@ function bm25Scores(query, documents, options = {}) {
203056
203317
  const queryTokens = [...new Set(typeof query === "string" ? lexicalTerms(query) : query)];
203057
203318
  if (queryTokens.length === 0) return documents.map(() => 0);
203058
203319
  const tokenized = documents.map((document4) => lexicalTerms(document4));
203059
- const averageLength = tokenized.reduce((total, tokens) => total + tokens.length, 0) / Math.max(1, tokenized.length);
203320
+ const averageLength = tokenized.reduce((total, tokens2) => total + tokens2.length, 0) / Math.max(1, tokenized.length);
203060
203321
  const documentFrequencies = /* @__PURE__ */ new Map();
203061
203322
  for (const term of queryTokens) {
203062
- documentFrequencies.set(term, tokenized.filter((tokens) => tokens.includes(term)).length);
203323
+ documentFrequencies.set(term, tokenized.filter((tokens2) => tokens2.includes(term)).length);
203063
203324
  }
203064
203325
  const k1 = options.k1 ?? 1.2;
203065
203326
  const b2 = options.b ?? 0.75;
203066
- const raw = tokenized.map((tokens) => {
203067
- const counts2 = frequencies(tokens);
203327
+ const raw = tokenized.map((tokens2) => {
203328
+ const counts2 = frequencies(tokens2);
203068
203329
  return queryTokens.reduce((score, term) => {
203069
203330
  const tf = counts2.get(term) ?? 0;
203070
203331
  if (tf === 0) return score;
203071
203332
  const df = documentFrequencies.get(term) ?? 0;
203072
203333
  const idf = Math.log(1 + (documents.length - df + 0.5) / (df + 0.5));
203073
- const lengthNormalization = 1 - b2 + b2 * tokens.length / Math.max(1, averageLength);
203334
+ const lengthNormalization = 1 - b2 + b2 * tokens2.length / Math.max(1, averageLength);
203074
203335
  return score + idf * tf * (k1 + 1) / (tf + k1 * lengthNormalization);
203075
203336
  }, 0);
203076
203337
  });
@@ -207445,12 +207706,9 @@ async function startOasisServer(opts) {
207445
207706
  }));
207446
207707
  return;
207447
207708
  }
207448
- await serveFileRequest(
207449
- req,
207450
- res,
207451
- url,
207452
- await resolveEngine(company?.kind === "ok" ? company.companyId : void 0)
207453
- );
207709
+ const companyId = company?.kind === "ok" ? company.companyId : void 0;
207710
+ const limits = await opts.fileLimits?.get(companyId) ?? DEFAULT_FILE_LIMITS;
207711
+ await withFileLimits(limits, async () => serveFileRequest(req, res, url, await resolveEngine(companyId)));
207454
207712
  return;
207455
207713
  }
207456
207714
  if (url.pathname === "/api/whoami" && req.method === "GET") {
@@ -207477,17 +207735,18 @@ async function startOasisServer(opts) {
207477
207735
  const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
207478
207736
  const currentCompanyId = companyCtx?.kind === "ok" ? companyCtx.companyId : void 0;
207479
207737
  const fileLimits = await opts.fileLimits?.get(currentCompanyId) ?? { ...DEFAULT_FILE_LIMITS };
207738
+ const requestMaxBytes = fileUploadRequestMaxBytes(fileLimits);
207480
207739
  const chunks = [];
207481
207740
  let requestBytes = 0;
207482
207741
  const declared = Number(req.headers["content-length"] ?? 0);
207483
- if (declared > FILE_UPLOAD_REQUEST_MAX_BYTES) {
207742
+ if (declared > requestMaxBytes) {
207484
207743
  req.resume();
207485
207744
  res.writeHead(413, { "content-type": "application/json" }).end(JSON.stringify({ error: { code: "UPLOAD_TOO_LARGE", message: "\u4E0A\u4F20\u8BF7\u6C42\u8D85\u8FC7\u901A\u9053\u5BB9\u91CF" } }));
207486
207745
  return;
207487
207746
  }
207488
207747
  for await (const chunk of req) {
207489
207748
  requestBytes += chunk.length;
207490
- if (requestBytes > FILE_UPLOAD_REQUEST_MAX_BYTES) {
207749
+ if (requestBytes > requestMaxBytes) {
207491
207750
  res.writeHead(413, { "content-type": "application/json" }).end(JSON.stringify({ error: { code: "UPLOAD_TOO_LARGE", message: "\u4E0A\u4F20\u8BF7\u6C42\u8D85\u8FC7\u901A\u9053\u5BB9\u91CF" } }));
207492
207751
  return;
207493
207752
  }
@@ -207514,9 +207773,9 @@ async function startOasisServer(opts) {
207514
207773
  return;
207515
207774
  }
207516
207775
  await opts.fileLimits.put(currentCompanyId, limits);
207517
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits, capacity: FILE_LIMIT_CAPACITY, canManage }));
207776
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits, canManage }));
207518
207777
  } else {
207519
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits: fileLimits, capacity: FILE_LIMIT_CAPACITY, canManage }));
207778
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits: fileLimits, canManage }));
207520
207779
  }
207521
207780
  return;
207522
207781
  }
@@ -221535,6 +221794,15 @@ var init_handshake_throttle = __esm({
221535
221794
  });
221536
221795
 
221537
221796
  // ../server/src/daemon-hub.ts
221797
+ function legacyMessage(msg, maxBytes = FILE_TRANSFER_MAX_BYTES) {
221798
+ try {
221799
+ const json = JSON.stringify(msg);
221800
+ if (Buffer.byteLength(json) <= maxBytes) return json;
221801
+ } catch (error2) {
221802
+ if (!(error2 instanceof RangeError)) throw error2;
221803
+ }
221804
+ throw new Error("\u5F53\u524D\u6267\u884C\u8282\u70B9\u6216\u4F1A\u8BDD\u4E0D\u652F\u6301\u5927\u6587\u4EF6\u5206\u5757\u4F20\u8F93\uFF0C\u8BF7\u5347\u7EA7\u8282\u70B9\u5E76\u91CD\u65B0\u5F00\u59CB\u672C\u8F6E\u5BF9\u8BDD");
221805
+ }
221538
221806
  function clientIpOf(req) {
221539
221807
  const fwd = req.headers["x-forwarded-for"];
221540
221808
  const raw = Array.isArray(fwd) ? fwd[0] : fwd;
@@ -221797,11 +222065,15 @@ var init_daemon_hub = __esm({
221797
222065
  handleSessionConnection(ws, auth) {
221798
222066
  const { nodeId, dispatchId } = auth;
221799
222067
  let registered = false;
222068
+ const decoder = new ChunkedMessageDecoder();
221800
222069
  ws.on("message", (raw) => {
221801
222070
  let msg;
221802
222071
  try {
221803
- msg = JSON.parse(raw.toString());
222072
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
222073
+ if (decoded === void 0) return;
222074
+ msg = decoded;
221804
222075
  } catch {
222076
+ ws.close(1007, "Invalid message framing");
221805
222077
  return;
221806
222078
  }
221807
222079
  if (msg.type === "session_hello") {
@@ -221818,6 +222090,7 @@ var init_daemon_hub = __esm({
221818
222090
  sessionId: msg.sessionId,
221819
222091
  connectedAt: Date.now(),
221820
222092
  supportsInputConsumptionReceipts: msg.supportsInputConsumptionReceipts === true,
222093
+ supportsChunkedMessages: msg.supportsChunkedMessages === true,
221821
222094
  ...msg.jobArtifactId ? { jobArtifactId: msg.jobArtifactId } : {}
221822
222095
  });
221823
222096
  for (const l of this.sessionConnectListeners) l({ dispatchId, sessionId: msg.sessionId, nodeId });
@@ -222014,7 +222287,7 @@ var init_daemon_hub = __esm({
222014
222287
  sendToSession(dispatchId, msg) {
222015
222288
  const ws = this.sessionSockets.get(dispatchId);
222016
222289
  if (!ws || ws.readyState !== import_websocket.default.OPEN) return false;
222017
- ws.send(JSON.stringify(msg));
222290
+ for (const frame of this.sessionMeta.get(dispatchId)?.supportsChunkedMessages ? encodeChunkedMessage(msg) : [legacyMessage(msg, 100 * MIB)]) ws.send(frame);
222018
222291
  return true;
222019
222292
  }
222020
222293
  /**
@@ -222075,16 +222348,20 @@ var init_daemon_hub = __esm({
222075
222348
  sourceIp: clientIpOf(req),
222076
222349
  send: (msg) => {
222077
222350
  if (ws.readyState !== import_websocket.default.OPEN) return false;
222078
- ws.send(JSON.stringify(msg));
222351
+ for (const frame of daemon.meta.supportsChunkedMessages ? encodeChunkedMessage(msg) : [legacyMessage(msg)]) ws.send(frame);
222079
222352
  return true;
222080
222353
  },
222081
222354
  close: () => ws.close()
222082
222355
  };
222356
+ const decoder = new ChunkedMessageDecoder();
222083
222357
  ws.on("message", (raw) => {
222084
222358
  let msg;
222085
222359
  try {
222086
- msg = JSON.parse(raw.toString());
222360
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
222361
+ if (decoded === void 0) return;
222362
+ msg = decoded;
222087
222363
  } catch {
222364
+ ws.close(1007, "Invalid message framing");
222088
222365
  return;
222089
222366
  }
222090
222367
  this.lastSeen.set(daemonId, Date.now());
@@ -222122,7 +222399,7 @@ var init_daemon_hub = __esm({
222122
222399
  ws.on("error", (err) => {
222123
222400
  console.error(`[hub] daemon ${daemonId} error:`, err.message);
222124
222401
  });
222125
- daemon.send({ type: "ping" });
222402
+ daemon.send({ type: "ping", supportsChunkedMessages: true });
222126
222403
  }
222127
222404
  /**
222128
222405
  * 周期性活性 sweep:
@@ -222145,7 +222422,7 @@ var init_daemon_hub = __esm({
222145
222422
  this.sockets.get(id)?.terminate();
222146
222423
  continue;
222147
222424
  }
222148
- daemon.send({ type: "ping" });
222425
+ daemon.send({ type: "ping", supportsChunkedMessages: true });
222149
222426
  }
222150
222427
  }
222151
222428
  /** 关闭 hub:停 liveness 定时器并断开所有 daemon。serve 关停时调用。 */
@@ -222403,17 +222680,11 @@ var init_skill_fetcher = __esm({
222403
222680
  SKILL_DESCRIPTION_MAX = 600;
222404
222681
  Bundle = class {
222405
222682
  files = {};
222406
- totalBytes = 0;
222407
- fileCount = 0;
222408
222683
  add(path40, content3) {
222409
222684
  const bytes2 = new TextEncoder().encode(content3).length;
222410
- if (this.fileCount >= currentFileLimits().skillFileCount)
222411
- throw new Error(`Import bundle exceeds ${currentFileLimits().skillFileCount} file limit`);
222412
- if (this.totalBytes + bytes2 > currentFileLimits().skillBundleBytes)
222413
- throw new Error(`Import bundle exceeds ${currentFileLimits().skillBundleBytes} byte limit`);
222685
+ if (bytes2 > currentFileLimits().skillFileBytes)
222686
+ throw new Error(`Skill file exceeds ${currentFileLimits().skillFileBytes} byte limit: ${path40}`);
222414
222687
  this.files[path40] = content3;
222415
- this.totalBytes += bytes2;
222416
- this.fileCount++;
222417
222688
  }
222418
222689
  };
222419
222690
  }
@@ -223186,7 +223457,7 @@ ${input.description}
223186
223457
  const file = this.buildSkillFile(skillId, path40, content3, now);
223187
223458
  const existing = await this.opts.store.listSkillFiles(skillId);
223188
223459
  const next = [...existing.filter((f2) => f2.path !== path40), file];
223189
- assertSkillFileLimits(next);
223460
+ assertSkillFileLimits([file]);
223190
223461
  await this.opts.store.putSkillFiles(skillId, next);
223191
223462
  if (path40 === "SKILL.md") {
223192
223463
  const { name, description } = parseSkillFrontmatter(content3);
@@ -224235,17 +224506,14 @@ function unpackSkillBundle(bytes2) {
224235
224506
  throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u7F3A\u5C11 files \u5BF9\u8C61");
224236
224507
  }
224237
224508
  const out = [];
224238
- let total = 0;
224239
224509
  for (const [path40, content3] of Object.entries(files)) {
224240
224510
  if (typeof content3 !== "string") throw new SkillPackageInvalidError(`\u6587\u4EF6 ${path40} \u4E0D\u662F\u6587\u672C`);
224241
224511
  if (!isCanonicalRelativePath(path40)) throw new SkillPackageInvalidError(`\u975E\u6CD5\u6280\u80FD\u6587\u4EF6\u8DEF\u5F84\uFF1A${path40}`);
224242
224512
  const size = Buffer.byteLength(content3, "utf8");
224243
224513
  if (size > currentFileLimits().skillFileBytes) throw new SkillPackageInvalidError(`\u6587\u4EF6\u8FC7\u5927\uFF1A${path40}`);
224244
- total += size;
224245
- if (total > currentFileLimits().skillBundleBytes) throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u603B\u5927\u5C0F\u8D85\u9650");
224246
224514
  out.push({ path: path40, content: content3 });
224247
224515
  }
224248
- if (out.length === 0 || out.length > currentFileLimits().skillFileCount) {
224516
+ if (out.length === 0) {
224249
224517
  throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u6587\u4EF6\u6570\u91CF\u975E\u6CD5");
224250
224518
  }
224251
224519
  if (!out.some((f2) => f2.path === "SKILL.md")) {
@@ -239905,7 +240173,7 @@ function createChatSessionsDomain(opts) {
239905
240173
  const outcome = await bridge.read(
239906
240174
  loc.nodeId,
239907
240175
  { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: req.query?.get("path") ?? "" },
239908
- FILE_CONTENT_MAX_BYTES
240176
+ currentFileReadMaxBytes()
239909
240177
  );
239910
240178
  if (outcome.ok) {
239911
240179
  const buf = Buffer.from(outcome.contentBase64, "base64");
@@ -244333,7 +244601,13 @@ var init_daemon_adapter = __esm({
244333
244601
  };
244334
244602
  this.pending.set(dispatchId, entry);
244335
244603
  const sessionAuth = this.sessionAuth ? { ...this.sessionAuth.issue({ nodeId, dispatchId }), gatewayUrl: this.sessionAuth.gatewayUrl } : void 0;
244336
- const sent = this.hub.dispatch(nodeId, { type: "dispatch", dispatchId, job, ...sessionAuth ? { sessionAuth } : {} });
244604
+ let sent;
244605
+ try {
244606
+ sent = this.hub.dispatch(nodeId, { type: "dispatch", dispatchId, job, ...sessionAuth ? { sessionAuth } : {} });
244607
+ } catch (error2) {
244608
+ this.pending.delete(dispatchId);
244609
+ throw error2;
244610
+ }
244337
244611
  if (!sent) {
244338
244612
  queueMicrotask(() => this.settle(dispatchId, { code: null, reason: "server-unreachable", errorMessage: `\u6D3E\u53D1\u65F6\u8282\u70B9 ${nodeId} \u4E0D\u5728\u7EBF\uFF08socket \u975E OPEN\uFF09\uFF0C\u5E27\u672A\u9001\u8FBE` }));
244339
244613
  } else if (this.ackTimeoutMs > 0) {
@@ -263089,6 +263363,7 @@ var init_trajectory = __esm({
263089
263363
 
263090
263364
  // src/index.ts
263091
263365
  var fs44 = __toESM(require("node:fs"));
263366
+ init_src();
263092
263367
  var os14 = __toESM(require("node:os"));
263093
263368
  var path39 = __toESM(require("node:path"));
263094
263369
  var import_node_child_process22 = require("node:child_process");
@@ -270097,6 +270372,9 @@ async function startServe(opts) {
270097
270372
  // actor→runtime→default 模型解析(内部字段;非用户 per-session 选择)。
270098
270373
  // 决策 0032 预派发校验:候选 model 不在该 runtime 的模型目录里 → 不烧会话,
270099
270374
  // 回退目录默认并留 warn(曾因 actor 存了残缺 id "claude-sonnet",CLI 秒拒、节点熔断卡死)。
270375
+ // 目录用**绑定那台节点**的(resolveRuntimeModels,与控制台下拉、会话级校验同一份):控制面本机
270376
+ // 的 isModelValid 看的是控制面自己的 CLI 配置,跨机时回落静态 GPT 目录,会把节点上配好的
270377
+ // deepseek-flash 判无效、悄悄换成 gpt-6-astra 派出去(Bug 0209)。
270100
270378
  resolveModel: async (actorId) => {
270101
270379
  const actors2 = await runActors();
270102
270380
  const binding = await actors2.service.resolveBinding()(actorId);
@@ -270107,13 +270385,14 @@ async function startServe(opts) {
270107
270385
  candidate = runtimeCfg?.model;
270108
270386
  }
270109
270387
  if (!candidate) return void 0;
270110
- const kind = binding ? traceRuntimeKind(binding.runtimeKind) : void 0;
270111
- if (kind && kind !== "custom" && !await isModelValid(kind, candidate)) {
270112
- const fallback = await defaultModelFor(kind);
270113
- console.warn(`[dispatch] ${actorId} \u914D\u7F6E\u7684\u6A21\u578B '${candidate}' \u5BF9 runtime ${kind} \u65E0\u6548\uFF0C\u56DE\u9000 ${fallback ?? "CLI \u9ED8\u8BA4"}\uFF08\u51B3\u7B56 0032\uFF09`);
270114
- return fallback;
270115
- }
270116
- return candidate;
270388
+ if (!binding || traceRuntimeKind(binding.runtimeKind) === "custom") return candidate;
270389
+ const runtimeId = `runtime:${binding.nodeId}:${binding.runtimeKind}`;
270390
+ const catalog = await resolveRuntimeModels({ nodeStore, registry: registryStore, nodeReportedModels }, runtimeId).catch(() => []);
270391
+ const checked = resolveDispatchModel(candidate, catalog);
270392
+ if (checked.replaced) {
270393
+ console.warn(`[dispatch] ${actorId} \u914D\u7F6E\u7684\u6A21\u578B '${candidate}' \u4E0D\u5728 ${runtimeId} \u7684\u5019\u9009\u91CC\uFF0C\u56DE\u9000 ${checked.model ?? "CLI \u9ED8\u8BA4"}\uFF08\u51B3\u7B56 0032\uFF09`);
270394
+ }
270395
+ return checked.model;
270117
270396
  },
270118
270397
  // ADR「agent CLI 启动参数员工级可配置化」:每次派发解析员工 actor_configs.extraArgs 下发(形态同 resolveModel)。
270119
270398
  // 只读员工配置,不叠 runtime 默认、不做「没有员工就找 runtime」回退(ADR §3.3 / §4-A)。
@@ -271424,6 +271703,7 @@ var SessionManager = class {
271424
271703
  // ../cli/src/daemon/session-process.ts
271425
271704
  var fs35 = __toESM(require("node:fs"), 1);
271426
271705
  var path30 = __toESM(require("node:path"), 1);
271706
+ var import_node_readline = require("node:readline");
271427
271707
  init_wrapper();
271428
271708
  init_src();
271429
271709
 
@@ -271606,7 +271886,7 @@ var OUTBOX_MAX_FRAMES = 8e3;
271606
271886
  var isKeyFrame = (m2) => m2.type === "session_started" || m2.type === "session_exited";
271607
271887
  async function runSessionProcess(specFile, adapter) {
271608
271888
  process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] = "1";
271609
- const spec = readSpec(specFile);
271889
+ const spec = await readSpec(specFile);
271610
271890
  const { dispatchId, job, auth, sessionDir } = spec;
271611
271891
  const lock = acquireSessionLock(path30.join(sessionDir, "lock"));
271612
271892
  if (!lock && hasFlock()) {
@@ -271619,13 +271899,24 @@ async function runSessionProcess(specFile, adapter) {
271619
271899
  const state = new SessionProcessState(spec, adapter, lock);
271620
271900
  await state.run();
271621
271901
  }
271622
- function readSpec(specFile) {
271623
- const spec = JSON.parse(fs35.readFileSync(specFile, "utf8"));
271902
+ async function readSpec(specFile) {
271903
+ const decoder = new ChunkedMessageDecoder();
271904
+ const source = fs35.createReadStream(specFile, { encoding: "utf8" });
271905
+ const lines = (0, import_node_readline.createInterface)({ input: source, crlfDelay: Infinity });
271624
271906
  try {
271625
- fs35.unlinkSync(specFile);
271626
- } catch {
271907
+ for await (const line of lines) {
271908
+ const spec = decoder.accept(JSON.parse(line));
271909
+ if (spec !== void 0) return spec;
271910
+ }
271911
+ throw new Error("Incomplete session specification");
271912
+ } finally {
271913
+ lines.close();
271914
+ source.destroy();
271915
+ try {
271916
+ fs35.unlinkSync(specFile);
271917
+ } catch {
271918
+ }
271627
271919
  }
271628
- return spec;
271629
271920
  }
271630
271921
  var SessionProcessState = class {
271631
271922
  constructor(spec, adapter, lock) {
@@ -271732,16 +272023,21 @@ var SessionProcessState = class {
271732
272023
  ...this.spec.job.artifactId ? { jobArtifactId: this.spec.job.artifactId } : {},
271733
272024
  // ADR 0300 D2:自报支持帧号;新服务端据此回带 ackSeq 的 hello-ack,届时才把 serverSupportsDedup 置真。
271734
272025
  supportsFrameSeq: true,
272026
+ supportsChunkedMessages: true,
271735
272027
  supportsInputConsumptionReceipts: true
271736
272028
  });
271737
272029
  const toResend = this.serverSupportsDedup ? this.outbox : this.outbox.filter(isKeyFrame);
271738
272030
  for (const m2 of toResend) this.rawSend(m2);
271739
272031
  });
272032
+ const decoder = new ChunkedMessageDecoder();
271740
272033
  ws.on("message", (raw) => {
271741
272034
  let msg;
271742
272035
  try {
271743
- msg = JSON.parse(raw.toString());
272036
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
272037
+ if (decoded === void 0) return;
272038
+ msg = decoded;
271744
272039
  } catch {
272040
+ ws.close(1007, "Invalid message framing");
271745
272041
  return;
271746
272042
  }
271747
272043
  void this.onServerMessage(msg);
@@ -272428,6 +272724,7 @@ var DaemonWsClient = class {
272428
272724
  else log2("[node-cli]", "\u505C\u673A\u6392\u7A7A\u5B8C\u6210\uFF1A\u5728\u9014\u4F1A\u8BDD\u7684\u9000\u51FA\u5E27\u5747\u5DF2\u786E\u8BA4\u9001\u8FBE");
272429
272725
  this.stop();
272430
272726
  }
272727
+ serverSupportsChunkedMessages = false;
272431
272728
  connect() {
272432
272729
  if (this.stopped) return;
272433
272730
  const ws = new import_websocket.default(this.serverUrl, {
@@ -272441,6 +272738,7 @@ var DaemonWsClient = class {
272441
272738
  handshakeTimeout: this.handshakeTimeoutMs
272442
272739
  });
272443
272740
  this.ws = ws;
272741
+ this.serverSupportsChunkedMessages = false;
272444
272742
  ws.on("open", () => {
272445
272743
  log2("[node-cli]", `connected id=${this.daemonId}`);
272446
272744
  this.authRejectStreak = 0;
@@ -272458,12 +272756,16 @@ var DaemonWsClient = class {
272458
272756
  }
272459
272757
  }, this.pingIntervalMs);
272460
272758
  });
272759
+ const decoder = new ChunkedMessageDecoder();
272461
272760
  ws.on("message", (raw) => {
272462
272761
  this.lastReceivedAt = Date.now();
272463
272762
  let msg;
272464
272763
  try {
272465
- msg = JSON.parse(raw.toString());
272764
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
272765
+ if (decoded === void 0) return;
272766
+ msg = decoded;
272466
272767
  } catch {
272768
+ ws.close(1007, "Invalid message framing");
272467
272769
  return;
272468
272770
  }
272469
272771
  void this.handleMessage(msg);
@@ -272580,6 +272882,7 @@ var DaemonWsClient = class {
272580
272882
  break;
272581
272883
  }
272582
272884
  case "ping":
272885
+ this.serverSupportsChunkedMessages = msg.supportsChunkedMessages === true;
272583
272886
  this.send({ type: "pong" });
272584
272887
  break;
272585
272888
  case "ack":
@@ -272685,7 +272988,9 @@ var DaemonWsClient = class {
272685
272988
  });
272686
272989
  }
272687
272990
  send(msg) {
272688
- if (this.ws?.readyState === import_websocket.default.OPEN) this.ws.send(JSON.stringify(msg));
272991
+ if (this.ws?.readyState === import_websocket.default.OPEN) {
272992
+ for (const frame of this.serverSupportsChunkedMessages && msg.type === "workdir_read_result" ? encodeChunkedMessage(msg) : [JSON.stringify(msg)]) this.ws.send(frame);
272993
+ }
272689
272994
  }
272690
272995
  /**
272691
272996
  * 待补发流帧数(观测/测试用)。
@@ -272810,6 +273115,7 @@ var DaemonWsClient = class {
272810
273115
  // ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
272811
273116
  supportsWorkdirWalk: true,
272812
273117
  supportsFileRestore: true,
273118
+ supportsChunkedMessages: true,
272813
273119
  ...activeSessions.length > 0 ? { activeSessions } : {},
272814
273120
  // 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
272815
273121
  pendingDispatchIds: [...this.pendingDispatchIds]
@@ -277432,8 +277738,6 @@ ${res.warning}`);
277432
277738
  const SKIP2 = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store"]);
277433
277739
  const limits = await loadFileLimits();
277434
277740
  const MAX_FILE_BYTES3 = limits.skillFileBytes;
277435
- const MAX_FILE_COUNT = limits.skillFileCount;
277436
- let totalBytes = 0;
277437
277741
  const local = {};
277438
277742
  const binary = [];
277439
277743
  const walk = (abs, rel) => {
@@ -277447,8 +277751,6 @@ ${res.warning}`);
277447
277751
  }
277448
277752
  const size = fs39.statSync(childAbs).size;
277449
277753
  if (size > MAX_FILE_BYTES3) throw new Error(`\u6280\u80FD\u6587\u4EF6\u8D85\u8FC7 ${MAX_FILE_BYTES3 / 1024 / 1024} MiB\uFF1A${childRel}`);
277450
- totalBytes += size;
277451
- if (totalBytes > limits.skillBundleBytes) throw new Error(`\u6280\u80FD\u6574\u5305\u8D85\u8FC7 ${limits.skillBundleBytes / 1024 / 1024} MiB`);
277452
277754
  const buf = fs39.readFileSync(childAbs);
277453
277755
  if (buf.includes(0)) {
277454
277756
  binary.push(childRel);
@@ -277464,7 +277766,6 @@ ${res.warning}`);
277464
277766
  }
277465
277767
  const paths = Object.keys(local);
277466
277768
  if (paths.length === 0) throw new Error(`\u76EE\u5F55\u91CC\u6CA1\u6709\u53EF\u4E0A\u4F20\u7684\u6587\u4EF6\uFF1A${dir2}`);
277467
- if (paths.length > MAX_FILE_COUNT) throw new Error(`\u6280\u80FD\u6587\u4EF6\u6570\u8D85\u8FC7 ${MAX_FILE_COUNT} \u4E2A\uFF1A${paths.length}`);
277468
277769
  const skillMd = local["SKILL.md"];
277469
277770
  if (skillMd === void 0) {
277470
277771
  throw new Error(`${dir2} \u6839\u76EE\u5F55\u4E0B\u6CA1\u6709 SKILL.md\u2014\u2014\u6280\u80FD\u5FC5\u987B\u6709\u5B83\uFF08frontmatter \u5199 name \u548C description\uFF0C\u6B63\u6587\u5199\u505A\u6CD5\uFF09\u3002`);
@@ -278735,7 +279036,7 @@ function shimScript() {
278735
279036
  }
278736
279037
 
278737
279038
  // src/index.ts
278738
- var PKG_VERSION = true ? "2.2.44" : "dev";
279039
+ var PKG_VERSION = true ? "2.2.46" : "dev";
278739
279040
  var LOCAL_BIN = localBin();
278740
279041
  var NPM_PREFIX = npmPrefix();
278741
279042
  var INSTANCE = DEFAULT_INSTANCE;
@@ -279146,13 +279447,19 @@ function spawnSessionProcess(spec) {
279146
279447
  fs44.mkdirSync(dir, { recursive: true, mode: 448 });
279147
279448
  const specFile = path39.join(dir, "spec.json");
279148
279449
  const cfg = readConfig();
279149
- fs44.writeFileSync(specFile, JSON.stringify({
279150
- ...spec,
279151
- sessionDir: dir,
279152
- nodeId: cfg?.nodeId ?? INSTANCE,
279153
- daemonVersion: PKG_VERSION,
279154
- daemonPkg: PKG_NAME
279155
- }), { mode: 384 });
279450
+ const fd = fs44.openSync(specFile, "w", 384);
279451
+ try {
279452
+ for (const frame of encodeChunkedMessage({
279453
+ ...spec,
279454
+ sessionDir: dir,
279455
+ nodeId: cfg?.nodeId ?? INSTANCE,
279456
+ daemonVersion: PKG_VERSION,
279457
+ daemonPkg: PKG_NAME
279458
+ })) fs44.writeFileSync(fd, `${frame}
279459
+ `);
279460
+ } finally {
279461
+ fs44.closeSync(fd);
279462
+ }
279156
279463
  let pinnedDir = INST.currentLink;
279157
279464
  try {
279158
279465
  pinnedDir = fs44.realpathSync(INST.currentLink);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.44",
3
+ "version": "2.2.46",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "91d9408678684a3e8cb17fdae1fdc0b7772cdcb4"
29
+ "sourceHead": "71593b93fa3ef6950c616a185e2ed7b0ee7b88e7"
30
30
  }
31
31
  }