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