oasis_test_v2 2.2.45 → 2.2.47

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 +288 -128
  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
  }
@@ -5115,18 +5240,19 @@ async function probeCodexAppServer(bin, timeoutMs = 15e3) {
5115
5240
  if (config2 === void 0 || listState === "pending") return;
5116
5241
  finish({ config: config2, models: listState === "done" ? listed : null });
5117
5242
  };
5118
- const timer = setTimeout(() => finish(null), timeoutMs);
5243
+ const abort = () => finish(config2 === void 0 ? null : { config: config2, models: listState === "done" ? listed : null });
5244
+ const timer = setTimeout(abort, timeoutMs);
5119
5245
  const send = (id, method, params) => {
5120
5246
  try {
5121
5247
  child.stdin?.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
5122
5248
  `);
5123
5249
  } catch {
5124
- finish(null);
5250
+ abort();
5125
5251
  }
5126
5252
  };
5127
- child.on("error", () => finish(null));
5128
- child.stdin?.on("error", () => finish(null));
5129
- child.on("close", () => finish(null));
5253
+ child.on("error", abort);
5254
+ child.stdin?.on("error", abort);
5255
+ child.on("close", abort);
5130
5256
  let buf = "";
5131
5257
  child.stdout?.on("data", (chunk) => {
5132
5258
  if (settled) return;
@@ -10140,14 +10266,14 @@ function accountUsage(model, usage) {
10140
10266
  const input = nonNegative(usage.inputTokens);
10141
10267
  const output = nonNegative(usage.outputTokens);
10142
10268
  const cache = nonNegative(usage.cacheReadTokens) + nonNegative(usage.cacheCreationTokens);
10143
- const tokens = { input, cache, output, total: input + cache + output };
10269
+ const tokens2 = { input, cache, output, total: input + cache + output };
10144
10270
  const estimated = estimateUsageCostBreakdown(resolved, usage);
10145
10271
  const weights = estimated ? { input: estimated.inputMicros, cache: estimated.cacheMicros, output: estimated.outputMicros } : null;
10146
10272
  const reported = Math.max(usage.costUsdMicros ?? 0, 0);
10147
10273
  if (reported > 0) {
10148
10274
  const parts = weights && estimated?.currency === "USD" ? splitByWeight(reported, weights) : { input: 0, cache: 0, output: reported };
10149
10275
  return {
10150
- tokens,
10276
+ tokens: tokens2,
10151
10277
  currency: "USD",
10152
10278
  amountMicros: reported,
10153
10279
  parts,
@@ -10160,7 +10286,7 @@ function accountUsage(model, usage) {
10160
10286
  const amount = weights.input + weights.cache + weights.output;
10161
10287
  const displayUsdMicros = toDisplayUsd(estimated.currency, amount);
10162
10288
  return {
10163
- tokens,
10289
+ tokens: tokens2,
10164
10290
  currency: estimated.currency,
10165
10291
  amountMicros: amount,
10166
10292
  parts: weights,
@@ -10170,7 +10296,7 @@ function accountUsage(model, usage) {
10170
10296
  };
10171
10297
  }
10172
10298
  return {
10173
- tokens,
10299
+ tokens: tokens2,
10174
10300
  currency: "USD",
10175
10301
  amountMicros: 0,
10176
10302
  parts: { input: 0, cache: 0, output: 0 },
@@ -10195,8 +10321,8 @@ function estimateSubagentCostUsdMicros(usage) {
10195
10321
  const models = Object.entries(usage.byModel);
10196
10322
  if (models.length === 0) return void 0;
10197
10323
  let total = 0;
10198
- for (const [model, tokens] of models) {
10199
- const account = accountUsage(model, { ...tokens, costUsdMicros: null });
10324
+ for (const [model, tokens2] of models) {
10325
+ const account = accountUsage(model, { ...tokens2, costUsdMicros: null });
10200
10326
  if (!account || account.source !== "estimated") return void 0;
10201
10327
  total += account.displayUsdMicros;
10202
10328
  }
@@ -24975,13 +25101,13 @@ var require_axios = __commonJS({
24975
25101
  return utils$1.isArray(value2) ? value2.map(normalizeValue) : String(value2);
24976
25102
  }
24977
25103
  function parseTokens(str5) {
24978
- const tokens = /* @__PURE__ */ Object.create(null);
25104
+ const tokens2 = /* @__PURE__ */ Object.create(null);
24979
25105
  const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;
24980
25106
  let match;
24981
25107
  while (match = tokensRE.exec(str5)) {
24982
- tokens[match[1]] = match[2];
25108
+ tokens2[match[1]] = match[2];
24983
25109
  }
24984
- return tokens;
25110
+ return tokens2;
24985
25111
  }
24986
25112
  var isValidHeaderName = (str5) => /^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(str5.trim());
24987
25113
  function matchHeaderValue(context2, value2, header, filter, isHeaderNameFilter) {
@@ -166604,8 +166730,6 @@ async function prepareSessionFileRestore(input) {
166604
166730
  }
166605
166731
  if (input.fresh) issues.push(...saveIssues.map((i) => ({ ...i, phase: "restore", message: `\u539F\u6587\u4EF6\u5C1A\u672A\u4FDD\u5B58\uFF1A${i.message}` })));
166606
166732
  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", ""];
166607
- let total = 0;
166608
- const inlineMax = input.inlineMaxBytes ?? RESTORE_INLINE_MAX_BYTES;
166609
166733
  const seen = /* @__PURE__ */ new Set();
166610
166734
  const add = async (name, ref2, path40, replace2) => {
166611
166735
  if (seen.has(path40)) return;
@@ -166615,12 +166739,9 @@ async function prepareSessionFileRestore(input) {
166615
166739
  return;
166616
166740
  }
166617
166741
  try {
166618
- 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");
166619
166742
  const bytes2 = await input.readBlob(ref2);
166620
- 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`);
166621
166743
  const hash3 = (0, import_node_crypto24.createHash)("sha256").update(bytes2).digest("hex");
166622
166744
  if (hash3 !== ref2.replace(/^blob:/, "")) throw new Error("\u539F\u4EF6\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
166623
- total += bytes2.byteLength;
166624
166745
  restoreFiles[path40] = { contentBase64: Buffer.from(bytes2).toString("base64"), sha256: hash3, overwrite: replace2 };
166625
166746
  lines.push(`- ${JSON.stringify(name)} \u2192 ${JSON.stringify(path40)}`);
166626
166747
  } catch (error2) {
@@ -166652,17 +166773,14 @@ async function prepareSessionFileRestore(input) {
166652
166773
  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")}`);
166653
166774
  return { restoreFiles, index: lines.join("\n") };
166654
166775
  }
166655
- var import_node_crypto24, RESTORE_INLINE_MAX_BYTES, RESTORE_MAX_FILES;
166776
+ var import_node_crypto24;
166656
166777
  var init_file_restore = __esm({
166657
166778
  "../server/src/domains/chat-sessions/file-restore.ts"() {
166658
166779
  "use strict";
166659
166780
  import_node_crypto24 = require("node:crypto");
166660
- init_src();
166661
166781
  init_chat_attachment_files();
166662
166782
  init_router();
166663
166783
  init_persistent_files();
166664
- RESTORE_INLINE_MAX_BYTES = FILE_TRANSFER_INPUT_MAX_BYTES;
166665
- RESTORE_MAX_FILES = 500;
166666
166784
  }
166667
166785
  });
166668
166786
 
@@ -179356,20 +179474,20 @@ function knowledgeSearchTokenStream(value2) {
179356
179474
  raw.push(token);
179357
179475
  }
179358
179476
  }
179359
- const tokens = [];
179477
+ const tokens2 = [];
179360
179478
  for (let index2 = 0; index2 < raw.length; index2++) {
179361
179479
  const token = raw[index2];
179362
179480
  const next = raw[index2 + 1];
179363
179481
  const merged = new RegExp("^\\p{Script=Han}$", "u").test(token) && next && new RegExp("^\\p{Script=Han}$", "u").test(next) ? `${token}${next}` : token;
179364
179482
  if (merged !== token) index2++;
179365
179483
  if (LEXICAL_STOPWORDS.has(merged)) continue;
179366
- tokens.push(merged);
179484
+ tokens2.push(merged);
179367
179485
  const chars = [...merged];
179368
179486
  if (new RegExp("^\\p{Script=Han}+$", "u").test(merged) && chars.length > 2) {
179369
- for (let index3 = 0; index3 < chars.length - 1; index3++) tokens.push(`${chars[index3]}${chars[index3 + 1]}`);
179487
+ for (let index3 = 0; index3 < chars.length - 1; index3++) tokens2.push(`${chars[index3]}${chars[index3 + 1]}`);
179370
179488
  }
179371
179489
  }
179372
- return tokens;
179490
+ return tokens2;
179373
179491
  }
179374
179492
  function knowledgeSearchTerms(value2) {
179375
179493
  return [...new Set(knowledgeSearchTokenStream(value2))];
@@ -179381,20 +179499,20 @@ function documentSearchTokens(chunk) {
179381
179499
  [chunk.logicalPath, 2]
179382
179500
  ];
179383
179501
  return fields.flatMap(([value2, repeats]) => {
179384
- const tokens = knowledgeSearchTokenStream(value2);
179385
- return Array.from({ length: repeats }, () => tokens).flat();
179502
+ const tokens2 = knowledgeSearchTokenStream(value2);
179503
+ return Array.from({ length: repeats }, () => tokens2).flat();
179386
179504
  });
179387
179505
  }
179388
179506
  function layeredSearchTokens(chunk, lexicalLayer) {
179389
179507
  return lexicalLayer === "document" ? documentSearchTokens(chunk) : knowledgeSearchTokenStream(chunk.text);
179390
179508
  }
179391
179509
  function knowledgeSearchIndexFields(chunk, lexicalLayer) {
179392
- const tokens = layeredSearchTokens(chunk, lexicalLayer);
179510
+ const tokens2 = layeredSearchTokens(chunk, lexicalLayer);
179393
179511
  return {
179394
- terms: [...new Set(tokens)],
179512
+ terms: [...new Set(tokens2)],
179395
179513
  lexicalIndexVersion: KNOWLEDGE_LEXICAL_INDEX_VERSION,
179396
179514
  lexicalLayer,
179397
- tokenCount: tokens.length
179515
+ tokenCount: tokens2.length
179398
179516
  };
179399
179517
  }
179400
179518
  function knowledgeSearchIndexEntries(input) {
@@ -179478,10 +179596,10 @@ function splitKnowledgeChunks(content3, maxChars = 1600) {
179478
179596
  function scoreKnowledgeChunk(query, chunk, corpus) {
179479
179597
  const queryTerms = knowledgeSearchTerms(query);
179480
179598
  if (queryTerms.length === 0) return 0;
179481
- const tokens = layeredSearchTokens(chunk, chunk.lexicalLayer ?? "chunk");
179599
+ const tokens2 = layeredSearchTokens(chunk, chunk.lexicalLayer ?? "chunk");
179482
179600
  const frequencies2 = /* @__PURE__ */ new Map();
179483
- for (const token of tokens) frequencies2.set(token, (frequencies2.get(token) ?? 0) + 1);
179484
- const documentLength = chunk.tokenCount ?? tokens.length;
179601
+ for (const token of tokens2) frequencies2.set(token, (frequencies2.get(token) ?? 0) + 1);
179602
+ const documentLength = chunk.tokenCount ?? tokens2.length;
179485
179603
  const averageLength = Math.max(1, corpus.averageTokenCount);
179486
179604
  const documentCount = Math.max(1, corpus.size);
179487
179605
  const k1 = 1.2;
@@ -183308,7 +183426,7 @@ ${pointer}
183308
183426
  var require_resolve_props = __commonJS({
183309
183427
  "../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports2) {
183310
183428
  "use strict";
183311
- function resolveProps(tokens, { flow: flow3, indicator, next, offset, onError, parentIndent, startOnNewline }) {
183429
+ function resolveProps(tokens2, { flow: flow3, indicator, next, offset, onError, parentIndent, startOnNewline }) {
183312
183430
  let spaceBefore = false;
183313
183431
  let atNewline = startOnNewline;
183314
183432
  let hasSpace = startOnNewline;
@@ -183323,7 +183441,7 @@ var require_resolve_props = __commonJS({
183323
183441
  let comma = null;
183324
183442
  let found2 = null;
183325
183443
  let start = null;
183326
- for (const token of tokens) {
183444
+ for (const token of tokens2) {
183327
183445
  if (reqSpace) {
183328
183446
  if (token.type !== "space" && token.type !== "newline" && token.type !== "comma")
183329
183447
  onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
@@ -183414,7 +183532,7 @@ var require_resolve_props = __commonJS({
183414
183532
  hasSpace = false;
183415
183533
  }
183416
183534
  }
183417
- const last = tokens[tokens.length - 1];
183535
+ const last = tokens2[tokens2.length - 1];
183418
183536
  const end = last ? last.offset + last.source.length : offset;
183419
183537
  if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) {
183420
183538
  onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
@@ -184753,8 +184871,8 @@ ${cb}` : comment;
184753
184871
  * @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.
184754
184872
  * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.
184755
184873
  */
184756
- *compose(tokens, forceDoc = false, endOffset = -1) {
184757
- for (const token of tokens)
184874
+ *compose(tokens2, forceDoc = false, endOffset = -1) {
184875
+ for (const token of tokens2)
184758
184876
  yield* this.next(token);
184759
184877
  yield* this.end(forceDoc, endOffset);
184760
184878
  }
@@ -192924,8 +193042,8 @@ var init_governance_service = __esm({
192924
193042
  return false;
192925
193043
  };
192926
193044
  const addFragment = (source, content3) => {
192927
- const tokens = estimateKnowledgeTokens(content3);
192928
- if (usedTokens + tokens > selectedPolicy.tokenBudget) return false;
193045
+ const tokens2 = estimateKnowledgeTokens(content3);
193046
+ if (usedTokens + tokens2 > selectedPolicy.tokenBudget) return false;
192929
193047
  const fragment = this.snapshot(ctx, "context_pack", content3, trustedProjectId, void 0);
192930
193048
  snapshots2.push(fragment);
192931
193049
  entries.push({
@@ -192941,12 +193059,12 @@ var init_governance_service = __esm({
192941
193059
  ...source.score !== void 0 ? { relevanceScore: source.score } : {},
192942
193060
  ...source.evidenceRole ? { evidenceRole: source.evidenceRole } : {},
192943
193061
  contentHash: fragment.contentHash,
192944
- tokenCount: tokens,
193062
+ tokenCount: tokens2,
192945
193063
  ordinal: entries.length,
192946
193064
  renderedFragmentRef: fragment.ref
192947
193065
  });
192948
193066
  fragments.push(content3);
192949
- usedTokens += tokens;
193067
+ usedTokens += tokens2;
192950
193068
  return true;
192951
193069
  };
192952
193070
  const taskFacts = [...request2.facts];
@@ -203190,9 +203308,9 @@ function lexicalMatchRatio(query, value2) {
203190
203308
  const actual = new Set(lexicalTerms(value2));
203191
203309
  return expected.filter((term) => actual.has(term)).length / expected.length;
203192
203310
  }
203193
- function frequencies(tokens) {
203311
+ function frequencies(tokens2) {
203194
203312
  const values = /* @__PURE__ */ new Map();
203195
- for (const token of tokens) values.set(token, (values.get(token) ?? 0) + 1);
203313
+ for (const token of tokens2) values.set(token, (values.get(token) ?? 0) + 1);
203196
203314
  return values;
203197
203315
  }
203198
203316
  function bm25Scores(query, documents, options = {}) {
@@ -203200,21 +203318,21 @@ function bm25Scores(query, documents, options = {}) {
203200
203318
  const queryTokens = [...new Set(typeof query === "string" ? lexicalTerms(query) : query)];
203201
203319
  if (queryTokens.length === 0) return documents.map(() => 0);
203202
203320
  const tokenized = documents.map((document4) => lexicalTerms(document4));
203203
- const averageLength = tokenized.reduce((total, tokens) => total + tokens.length, 0) / Math.max(1, tokenized.length);
203321
+ const averageLength = tokenized.reduce((total, tokens2) => total + tokens2.length, 0) / Math.max(1, tokenized.length);
203204
203322
  const documentFrequencies = /* @__PURE__ */ new Map();
203205
203323
  for (const term of queryTokens) {
203206
- documentFrequencies.set(term, tokenized.filter((tokens) => tokens.includes(term)).length);
203324
+ documentFrequencies.set(term, tokenized.filter((tokens2) => tokens2.includes(term)).length);
203207
203325
  }
203208
203326
  const k1 = options.k1 ?? 1.2;
203209
203327
  const b2 = options.b ?? 0.75;
203210
- const raw = tokenized.map((tokens) => {
203211
- const counts2 = frequencies(tokens);
203328
+ const raw = tokenized.map((tokens2) => {
203329
+ const counts2 = frequencies(tokens2);
203212
203330
  return queryTokens.reduce((score, term) => {
203213
203331
  const tf = counts2.get(term) ?? 0;
203214
203332
  if (tf === 0) return score;
203215
203333
  const df = documentFrequencies.get(term) ?? 0;
203216
203334
  const idf = Math.log(1 + (documents.length - df + 0.5) / (df + 0.5));
203217
- const lengthNormalization = 1 - b2 + b2 * tokens.length / Math.max(1, averageLength);
203335
+ const lengthNormalization = 1 - b2 + b2 * tokens2.length / Math.max(1, averageLength);
203218
203336
  return score + idf * tf * (k1 + 1) / (tf + k1 * lengthNormalization);
203219
203337
  }, 0);
203220
203338
  });
@@ -207589,12 +207707,9 @@ async function startOasisServer(opts) {
207589
207707
  }));
207590
207708
  return;
207591
207709
  }
207592
- await serveFileRequest(
207593
- req,
207594
- res,
207595
- url,
207596
- await resolveEngine(company?.kind === "ok" ? company.companyId : void 0)
207597
- );
207710
+ const companyId = company?.kind === "ok" ? company.companyId : void 0;
207711
+ const limits = await opts.fileLimits?.get(companyId) ?? DEFAULT_FILE_LIMITS;
207712
+ await withFileLimits(limits, async () => serveFileRequest(req, res, url, await resolveEngine(companyId)));
207598
207713
  return;
207599
207714
  }
207600
207715
  if (url.pathname === "/api/whoami" && req.method === "GET") {
@@ -207621,17 +207736,18 @@ async function startOasisServer(opts) {
207621
207736
  const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
207622
207737
  const currentCompanyId = companyCtx?.kind === "ok" ? companyCtx.companyId : void 0;
207623
207738
  const fileLimits = await opts.fileLimits?.get(currentCompanyId) ?? { ...DEFAULT_FILE_LIMITS };
207739
+ const requestMaxBytes = fileUploadRequestMaxBytes(fileLimits);
207624
207740
  const chunks = [];
207625
207741
  let requestBytes = 0;
207626
207742
  const declared = Number(req.headers["content-length"] ?? 0);
207627
- if (declared > FILE_UPLOAD_REQUEST_MAX_BYTES) {
207743
+ if (declared > requestMaxBytes) {
207628
207744
  req.resume();
207629
207745
  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" } }));
207630
207746
  return;
207631
207747
  }
207632
207748
  for await (const chunk of req) {
207633
207749
  requestBytes += chunk.length;
207634
- if (requestBytes > FILE_UPLOAD_REQUEST_MAX_BYTES) {
207750
+ if (requestBytes > requestMaxBytes) {
207635
207751
  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" } }));
207636
207752
  return;
207637
207753
  }
@@ -207658,9 +207774,9 @@ async function startOasisServer(opts) {
207658
207774
  return;
207659
207775
  }
207660
207776
  await opts.fileLimits.put(currentCompanyId, limits);
207661
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits, capacity: FILE_LIMIT_CAPACITY, canManage }));
207777
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits, canManage }));
207662
207778
  } else {
207663
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits: fileLimits, capacity: FILE_LIMIT_CAPACITY, canManage }));
207779
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ limits: fileLimits, canManage }));
207664
207780
  }
207665
207781
  return;
207666
207782
  }
@@ -221679,6 +221795,15 @@ var init_handshake_throttle = __esm({
221679
221795
  });
221680
221796
 
221681
221797
  // ../server/src/daemon-hub.ts
221798
+ function legacyMessage(msg, maxBytes = FILE_TRANSFER_MAX_BYTES) {
221799
+ try {
221800
+ const json = JSON.stringify(msg);
221801
+ if (Buffer.byteLength(json) <= maxBytes) return json;
221802
+ } catch (error2) {
221803
+ if (!(error2 instanceof RangeError)) throw error2;
221804
+ }
221805
+ 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");
221806
+ }
221682
221807
  function clientIpOf(req) {
221683
221808
  const fwd = req.headers["x-forwarded-for"];
221684
221809
  const raw = Array.isArray(fwd) ? fwd[0] : fwd;
@@ -221941,11 +222066,15 @@ var init_daemon_hub = __esm({
221941
222066
  handleSessionConnection(ws, auth) {
221942
222067
  const { nodeId, dispatchId } = auth;
221943
222068
  let registered = false;
222069
+ const decoder = new ChunkedMessageDecoder();
221944
222070
  ws.on("message", (raw) => {
221945
222071
  let msg;
221946
222072
  try {
221947
- msg = JSON.parse(raw.toString());
222073
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
222074
+ if (decoded === void 0) return;
222075
+ msg = decoded;
221948
222076
  } catch {
222077
+ ws.close(1007, "Invalid message framing");
221949
222078
  return;
221950
222079
  }
221951
222080
  if (msg.type === "session_hello") {
@@ -221962,6 +222091,7 @@ var init_daemon_hub = __esm({
221962
222091
  sessionId: msg.sessionId,
221963
222092
  connectedAt: Date.now(),
221964
222093
  supportsInputConsumptionReceipts: msg.supportsInputConsumptionReceipts === true,
222094
+ supportsChunkedMessages: msg.supportsChunkedMessages === true,
221965
222095
  ...msg.jobArtifactId ? { jobArtifactId: msg.jobArtifactId } : {}
221966
222096
  });
221967
222097
  for (const l of this.sessionConnectListeners) l({ dispatchId, sessionId: msg.sessionId, nodeId });
@@ -222158,7 +222288,7 @@ var init_daemon_hub = __esm({
222158
222288
  sendToSession(dispatchId, msg) {
222159
222289
  const ws = this.sessionSockets.get(dispatchId);
222160
222290
  if (!ws || ws.readyState !== import_websocket.default.OPEN) return false;
222161
- ws.send(JSON.stringify(msg));
222291
+ for (const frame of this.sessionMeta.get(dispatchId)?.supportsChunkedMessages ? encodeChunkedMessage(msg) : [legacyMessage(msg, 100 * MIB)]) ws.send(frame);
222162
222292
  return true;
222163
222293
  }
222164
222294
  /**
@@ -222219,16 +222349,20 @@ var init_daemon_hub = __esm({
222219
222349
  sourceIp: clientIpOf(req),
222220
222350
  send: (msg) => {
222221
222351
  if (ws.readyState !== import_websocket.default.OPEN) return false;
222222
- ws.send(JSON.stringify(msg));
222352
+ for (const frame of daemon.meta.supportsChunkedMessages ? encodeChunkedMessage(msg) : [legacyMessage(msg)]) ws.send(frame);
222223
222353
  return true;
222224
222354
  },
222225
222355
  close: () => ws.close()
222226
222356
  };
222357
+ const decoder = new ChunkedMessageDecoder();
222227
222358
  ws.on("message", (raw) => {
222228
222359
  let msg;
222229
222360
  try {
222230
- msg = JSON.parse(raw.toString());
222361
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
222362
+ if (decoded === void 0) return;
222363
+ msg = decoded;
222231
222364
  } catch {
222365
+ ws.close(1007, "Invalid message framing");
222232
222366
  return;
222233
222367
  }
222234
222368
  this.lastSeen.set(daemonId, Date.now());
@@ -222266,7 +222400,7 @@ var init_daemon_hub = __esm({
222266
222400
  ws.on("error", (err) => {
222267
222401
  console.error(`[hub] daemon ${daemonId} error:`, err.message);
222268
222402
  });
222269
- daemon.send({ type: "ping" });
222403
+ daemon.send({ type: "ping", supportsChunkedMessages: true });
222270
222404
  }
222271
222405
  /**
222272
222406
  * 周期性活性 sweep:
@@ -222289,7 +222423,7 @@ var init_daemon_hub = __esm({
222289
222423
  this.sockets.get(id)?.terminate();
222290
222424
  continue;
222291
222425
  }
222292
- daemon.send({ type: "ping" });
222426
+ daemon.send({ type: "ping", supportsChunkedMessages: true });
222293
222427
  }
222294
222428
  }
222295
222429
  /** 关闭 hub:停 liveness 定时器并断开所有 daemon。serve 关停时调用。 */
@@ -222547,17 +222681,11 @@ var init_skill_fetcher = __esm({
222547
222681
  SKILL_DESCRIPTION_MAX = 600;
222548
222682
  Bundle = class {
222549
222683
  files = {};
222550
- totalBytes = 0;
222551
- fileCount = 0;
222552
222684
  add(path40, content3) {
222553
222685
  const bytes2 = new TextEncoder().encode(content3).length;
222554
- if (this.fileCount >= currentFileLimits().skillFileCount)
222555
- throw new Error(`Import bundle exceeds ${currentFileLimits().skillFileCount} file limit`);
222556
- if (this.totalBytes + bytes2 > currentFileLimits().skillBundleBytes)
222557
- throw new Error(`Import bundle exceeds ${currentFileLimits().skillBundleBytes} byte limit`);
222686
+ if (bytes2 > currentFileLimits().skillFileBytes)
222687
+ throw new Error(`Skill file exceeds ${currentFileLimits().skillFileBytes} byte limit: ${path40}`);
222558
222688
  this.files[path40] = content3;
222559
- this.totalBytes += bytes2;
222560
- this.fileCount++;
222561
222689
  }
222562
222690
  };
222563
222691
  }
@@ -223330,7 +223458,7 @@ ${input.description}
223330
223458
  const file = this.buildSkillFile(skillId, path40, content3, now);
223331
223459
  const existing = await this.opts.store.listSkillFiles(skillId);
223332
223460
  const next = [...existing.filter((f2) => f2.path !== path40), file];
223333
- assertSkillFileLimits(next);
223461
+ assertSkillFileLimits([file]);
223334
223462
  await this.opts.store.putSkillFiles(skillId, next);
223335
223463
  if (path40 === "SKILL.md") {
223336
223464
  const { name, description } = parseSkillFrontmatter(content3);
@@ -224379,17 +224507,14 @@ function unpackSkillBundle(bytes2) {
224379
224507
  throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u7F3A\u5C11 files \u5BF9\u8C61");
224380
224508
  }
224381
224509
  const out = [];
224382
- let total = 0;
224383
224510
  for (const [path40, content3] of Object.entries(files)) {
224384
224511
  if (typeof content3 !== "string") throw new SkillPackageInvalidError(`\u6587\u4EF6 ${path40} \u4E0D\u662F\u6587\u672C`);
224385
224512
  if (!isCanonicalRelativePath(path40)) throw new SkillPackageInvalidError(`\u975E\u6CD5\u6280\u80FD\u6587\u4EF6\u8DEF\u5F84\uFF1A${path40}`);
224386
224513
  const size = Buffer.byteLength(content3, "utf8");
224387
224514
  if (size > currentFileLimits().skillFileBytes) throw new SkillPackageInvalidError(`\u6587\u4EF6\u8FC7\u5927\uFF1A${path40}`);
224388
- total += size;
224389
- if (total > currentFileLimits().skillBundleBytes) throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u603B\u5927\u5C0F\u8D85\u9650");
224390
224515
  out.push({ path: path40, content: content3 });
224391
224516
  }
224392
- if (out.length === 0 || out.length > currentFileLimits().skillFileCount) {
224517
+ if (out.length === 0) {
224393
224518
  throw new SkillPackageInvalidError("\u6280\u80FD\u5305\u6587\u4EF6\u6570\u91CF\u975E\u6CD5");
224394
224519
  }
224395
224520
  if (!out.some((f2) => f2.path === "SKILL.md")) {
@@ -240049,7 +240174,7 @@ function createChatSessionsDomain(opts) {
240049
240174
  const outcome = await bridge.read(
240050
240175
  loc.nodeId,
240051
240176
  { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: req.query?.get("path") ?? "" },
240052
- FILE_CONTENT_MAX_BYTES
240177
+ currentFileReadMaxBytes()
240053
240178
  );
240054
240179
  if (outcome.ok) {
240055
240180
  const buf = Buffer.from(outcome.contentBase64, "base64");
@@ -244477,7 +244602,13 @@ var init_daemon_adapter = __esm({
244477
244602
  };
244478
244603
  this.pending.set(dispatchId, entry);
244479
244604
  const sessionAuth = this.sessionAuth ? { ...this.sessionAuth.issue({ nodeId, dispatchId }), gatewayUrl: this.sessionAuth.gatewayUrl } : void 0;
244480
- const sent = this.hub.dispatch(nodeId, { type: "dispatch", dispatchId, job, ...sessionAuth ? { sessionAuth } : {} });
244605
+ let sent;
244606
+ try {
244607
+ sent = this.hub.dispatch(nodeId, { type: "dispatch", dispatchId, job, ...sessionAuth ? { sessionAuth } : {} });
244608
+ } catch (error2) {
244609
+ this.pending.delete(dispatchId);
244610
+ throw error2;
244611
+ }
244481
244612
  if (!sent) {
244482
244613
  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` }));
244483
244614
  } else if (this.ackTimeoutMs > 0) {
@@ -263233,6 +263364,7 @@ var init_trajectory = __esm({
263233
263364
 
263234
263365
  // src/index.ts
263235
263366
  var fs44 = __toESM(require("node:fs"));
263367
+ init_src();
263236
263368
  var os14 = __toESM(require("node:os"));
263237
263369
  var path39 = __toESM(require("node:path"));
263238
263370
  var import_node_child_process22 = require("node:child_process");
@@ -271572,6 +271704,7 @@ var SessionManager = class {
271572
271704
  // ../cli/src/daemon/session-process.ts
271573
271705
  var fs35 = __toESM(require("node:fs"), 1);
271574
271706
  var path30 = __toESM(require("node:path"), 1);
271707
+ var import_node_readline = require("node:readline");
271575
271708
  init_wrapper();
271576
271709
  init_src();
271577
271710
 
@@ -271754,7 +271887,7 @@ var OUTBOX_MAX_FRAMES = 8e3;
271754
271887
  var isKeyFrame = (m2) => m2.type === "session_started" || m2.type === "session_exited";
271755
271888
  async function runSessionProcess(specFile, adapter) {
271756
271889
  process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] = "1";
271757
- const spec = readSpec(specFile);
271890
+ const spec = await readSpec(specFile);
271758
271891
  const { dispatchId, job, auth, sessionDir } = spec;
271759
271892
  const lock = acquireSessionLock(path30.join(sessionDir, "lock"));
271760
271893
  if (!lock && hasFlock()) {
@@ -271767,13 +271900,24 @@ async function runSessionProcess(specFile, adapter) {
271767
271900
  const state = new SessionProcessState(spec, adapter, lock);
271768
271901
  await state.run();
271769
271902
  }
271770
- function readSpec(specFile) {
271771
- const spec = JSON.parse(fs35.readFileSync(specFile, "utf8"));
271903
+ async function readSpec(specFile) {
271904
+ const decoder = new ChunkedMessageDecoder();
271905
+ const source = fs35.createReadStream(specFile, { encoding: "utf8" });
271906
+ const lines = (0, import_node_readline.createInterface)({ input: source, crlfDelay: Infinity });
271772
271907
  try {
271773
- fs35.unlinkSync(specFile);
271774
- } catch {
271908
+ for await (const line of lines) {
271909
+ const spec = decoder.accept(JSON.parse(line));
271910
+ if (spec !== void 0) return spec;
271911
+ }
271912
+ throw new Error("Incomplete session specification");
271913
+ } finally {
271914
+ lines.close();
271915
+ source.destroy();
271916
+ try {
271917
+ fs35.unlinkSync(specFile);
271918
+ } catch {
271919
+ }
271775
271920
  }
271776
- return spec;
271777
271921
  }
271778
271922
  var SessionProcessState = class {
271779
271923
  constructor(spec, adapter, lock) {
@@ -271880,16 +272024,21 @@ var SessionProcessState = class {
271880
272024
  ...this.spec.job.artifactId ? { jobArtifactId: this.spec.job.artifactId } : {},
271881
272025
  // ADR 0300 D2:自报支持帧号;新服务端据此回带 ackSeq 的 hello-ack,届时才把 serverSupportsDedup 置真。
271882
272026
  supportsFrameSeq: true,
272027
+ supportsChunkedMessages: true,
271883
272028
  supportsInputConsumptionReceipts: true
271884
272029
  });
271885
272030
  const toResend = this.serverSupportsDedup ? this.outbox : this.outbox.filter(isKeyFrame);
271886
272031
  for (const m2 of toResend) this.rawSend(m2);
271887
272032
  });
272033
+ const decoder = new ChunkedMessageDecoder();
271888
272034
  ws.on("message", (raw) => {
271889
272035
  let msg;
271890
272036
  try {
271891
- msg = JSON.parse(raw.toString());
272037
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
272038
+ if (decoded === void 0) return;
272039
+ msg = decoded;
271892
272040
  } catch {
272041
+ ws.close(1007, "Invalid message framing");
271893
272042
  return;
271894
272043
  }
271895
272044
  void this.onServerMessage(msg);
@@ -272576,6 +272725,7 @@ var DaemonWsClient = class {
272576
272725
  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");
272577
272726
  this.stop();
272578
272727
  }
272728
+ serverSupportsChunkedMessages = false;
272579
272729
  connect() {
272580
272730
  if (this.stopped) return;
272581
272731
  const ws = new import_websocket.default(this.serverUrl, {
@@ -272589,6 +272739,7 @@ var DaemonWsClient = class {
272589
272739
  handshakeTimeout: this.handshakeTimeoutMs
272590
272740
  });
272591
272741
  this.ws = ws;
272742
+ this.serverSupportsChunkedMessages = false;
272592
272743
  ws.on("open", () => {
272593
272744
  log2("[node-cli]", `connected id=${this.daemonId}`);
272594
272745
  this.authRejectStreak = 0;
@@ -272606,12 +272757,16 @@ var DaemonWsClient = class {
272606
272757
  }
272607
272758
  }, this.pingIntervalMs);
272608
272759
  });
272760
+ const decoder = new ChunkedMessageDecoder();
272609
272761
  ws.on("message", (raw) => {
272610
272762
  this.lastReceivedAt = Date.now();
272611
272763
  let msg;
272612
272764
  try {
272613
- msg = JSON.parse(raw.toString());
272765
+ const decoded = decoder.accept(JSON.parse(raw.toString()));
272766
+ if (decoded === void 0) return;
272767
+ msg = decoded;
272614
272768
  } catch {
272769
+ ws.close(1007, "Invalid message framing");
272615
272770
  return;
272616
272771
  }
272617
272772
  void this.handleMessage(msg);
@@ -272728,6 +272883,7 @@ var DaemonWsClient = class {
272728
272883
  break;
272729
272884
  }
272730
272885
  case "ping":
272886
+ this.serverSupportsChunkedMessages = msg.supportsChunkedMessages === true;
272731
272887
  this.send({ type: "pong" });
272732
272888
  break;
272733
272889
  case "ack":
@@ -272833,7 +272989,9 @@ var DaemonWsClient = class {
272833
272989
  });
272834
272990
  }
272835
272991
  send(msg) {
272836
- if (this.ws?.readyState === import_websocket.default.OPEN) this.ws.send(JSON.stringify(msg));
272992
+ if (this.ws?.readyState === import_websocket.default.OPEN) {
272993
+ for (const frame of this.serverSupportsChunkedMessages && msg.type === "workdir_read_result" ? encodeChunkedMessage(msg) : [JSON.stringify(msg)]) this.ws.send(frame);
272994
+ }
272837
272995
  }
272838
272996
  /**
272839
272997
  * 待补发流帧数(观测/测试用)。
@@ -272958,6 +273116,7 @@ var DaemonWsClient = class {
272958
273116
  // ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
272959
273117
  supportsWorkdirWalk: true,
272960
273118
  supportsFileRestore: true,
273119
+ supportsChunkedMessages: true,
272961
273120
  ...activeSessions.length > 0 ? { activeSessions } : {},
272962
273121
  // 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
272963
273122
  pendingDispatchIds: [...this.pendingDispatchIds]
@@ -277580,8 +277739,6 @@ ${res.warning}`);
277580
277739
  const SKIP2 = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store"]);
277581
277740
  const limits = await loadFileLimits();
277582
277741
  const MAX_FILE_BYTES3 = limits.skillFileBytes;
277583
- const MAX_FILE_COUNT = limits.skillFileCount;
277584
- let totalBytes = 0;
277585
277742
  const local = {};
277586
277743
  const binary = [];
277587
277744
  const walk = (abs, rel) => {
@@ -277595,8 +277752,6 @@ ${res.warning}`);
277595
277752
  }
277596
277753
  const size = fs39.statSync(childAbs).size;
277597
277754
  if (size > MAX_FILE_BYTES3) throw new Error(`\u6280\u80FD\u6587\u4EF6\u8D85\u8FC7 ${MAX_FILE_BYTES3 / 1024 / 1024} MiB\uFF1A${childRel}`);
277598
- totalBytes += size;
277599
- if (totalBytes > limits.skillBundleBytes) throw new Error(`\u6280\u80FD\u6574\u5305\u8D85\u8FC7 ${limits.skillBundleBytes / 1024 / 1024} MiB`);
277600
277755
  const buf = fs39.readFileSync(childAbs);
277601
277756
  if (buf.includes(0)) {
277602
277757
  binary.push(childRel);
@@ -277612,7 +277767,6 @@ ${res.warning}`);
277612
277767
  }
277613
277768
  const paths = Object.keys(local);
277614
277769
  if (paths.length === 0) throw new Error(`\u76EE\u5F55\u91CC\u6CA1\u6709\u53EF\u4E0A\u4F20\u7684\u6587\u4EF6\uFF1A${dir2}`);
277615
- if (paths.length > MAX_FILE_COUNT) throw new Error(`\u6280\u80FD\u6587\u4EF6\u6570\u8D85\u8FC7 ${MAX_FILE_COUNT} \u4E2A\uFF1A${paths.length}`);
277616
277770
  const skillMd = local["SKILL.md"];
277617
277771
  if (skillMd === void 0) {
277618
277772
  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`);
@@ -278883,7 +279037,7 @@ function shimScript() {
278883
279037
  }
278884
279038
 
278885
279039
  // src/index.ts
278886
- var PKG_VERSION = true ? "2.2.45" : "dev";
279040
+ var PKG_VERSION = true ? "2.2.47" : "dev";
278887
279041
  var LOCAL_BIN = localBin();
278888
279042
  var NPM_PREFIX = npmPrefix();
278889
279043
  var INSTANCE = DEFAULT_INSTANCE;
@@ -279294,13 +279448,19 @@ function spawnSessionProcess(spec) {
279294
279448
  fs44.mkdirSync(dir, { recursive: true, mode: 448 });
279295
279449
  const specFile = path39.join(dir, "spec.json");
279296
279450
  const cfg = readConfig();
279297
- fs44.writeFileSync(specFile, JSON.stringify({
279298
- ...spec,
279299
- sessionDir: dir,
279300
- nodeId: cfg?.nodeId ?? INSTANCE,
279301
- daemonVersion: PKG_VERSION,
279302
- daemonPkg: PKG_NAME
279303
- }), { mode: 384 });
279451
+ const fd = fs44.openSync(specFile, "w", 384);
279452
+ try {
279453
+ for (const frame of encodeChunkedMessage({
279454
+ ...spec,
279455
+ sessionDir: dir,
279456
+ nodeId: cfg?.nodeId ?? INSTANCE,
279457
+ daemonVersion: PKG_VERSION,
279458
+ daemonPkg: PKG_NAME
279459
+ })) fs44.writeFileSync(fd, `${frame}
279460
+ `);
279461
+ } finally {
279462
+ fs44.closeSync(fd);
279463
+ }
279304
279464
  let pinnedDir = INST.currentLink;
279305
279465
  try {
279306
279466
  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.45",
3
+ "version": "2.2.47",
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": "c27918782543065f65990ee2acc32b3da89bba06"
29
+ "sourceHead": "98d67615dd3606fcebb19d6561d2a5e7abe5ae31"
30
30
  }
31
31
  }