neko-harness 0.4.8 → 0.4.9

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.
@@ -4,13 +4,47 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="color-scheme" content="light" />
7
+ <script>
8
+ // 首屏前应用外观偏好(与 src/client/lib/appearance.ts 的键名/属性名对拍,test/appearance.test.ts)
9
+ (() => {
10
+ try {
11
+ const data = JSON.parse(localStorage.getItem("neko.appearance") || "null");
12
+ if (!data || typeof data !== "object") return;
13
+ const attrs = {
14
+ theme: "data-theme",
15
+ font: "data-chat-font",
16
+ size: "data-chat-size",
17
+ weight: "data-chat-weight",
18
+ leading: "data-chat-leading",
19
+ };
20
+ for (const key of Object.keys(attrs)) {
21
+ const value = data[key];
22
+ if (typeof value === "string" && /^[a-z0-9-]{1,16}$/.test(value))
23
+ document.documentElement.setAttribute(attrs[key], value);
24
+ }
25
+ const meta = document.querySelector('meta[name="color-scheme"]');
26
+ if (data.theme === "dark" && meta) meta.setAttribute("content", "dark");
27
+ } catch {}
28
+ })();
29
+ </script>
30
+ <script>
31
+ // 首屏前同步界面语言到 <html lang>(与 src/client/lib/language.ts 的键名对拍,test/language.test.ts)
32
+ (() => {
33
+ try {
34
+ document.documentElement.setAttribute(
35
+ "lang",
36
+ localStorage.getItem("neko.language") === "en" ? "en" : "zh-CN",
37
+ );
38
+ } catch {}
39
+ })();
40
+ </script>
7
41
  <link
8
42
  rel="icon"
9
43
  href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='26' fill='%23ffb7c5'/%3E%3Ctext x='50' y='70' font-size='58' text-anchor='middle' fill='%23ffffff' font-family='sans-serif' font-weight='bold'%3En%3C/text%3E%3C/svg%3E"
10
44
  />
11
45
  <title>neko harness</title>
12
- <script type="module" crossorigin src="/assets/index-vrkP1dHF.js"></script>
13
- <link rel="stylesheet" crossorigin href="/assets/index-wmHY1s4T.css">
46
+ <script type="module" crossorigin src="/assets/index-LHUxaMG9.js"></script>
47
+ <link rel="stylesheet" crossorigin href="/assets/index-CP-ffuep.css">
14
48
  </head>
15
49
  <body>
16
50
  <div id="root"></div>
package/dist/gateway.js CHANGED
@@ -19605,7 +19605,9 @@ class JobManager {
19605
19605
  #reporter = createReporter();
19606
19606
  #runner = this.#createRunner();
19607
19607
  extension;
19608
- constructor() {
19608
+ #startWindowMs;
19609
+ constructor(options = {}) {
19610
+ this.#startWindowMs = options.startWindowMs ?? JOB_LIMITS.startWindowMs;
19609
19611
  this.extension = this.#createExtension();
19610
19612
  }
19611
19613
  #closing = false;
@@ -19795,7 +19797,7 @@ class JobManager {
19795
19797
  throw error;
19796
19798
  }
19797
19799
  const live = entry;
19798
- await Promise.race([live.exited, delay(JOB_LIMITS.startWindowMs, context.abortSignal)]);
19800
+ await Promise.race([live.exited, delay(this.#startWindowMs, context.abortSignal)]);
19799
19801
  live.windowOpen = false;
19800
19802
  if (live.suppressReport)
19801
19803
  await live.exited;
@@ -20996,6 +20998,8 @@ var SessionMetadataDoc = defineDoc5({
20996
20998
  namingUsage: { models: {}, tools: {} }
20997
20999
  })
20998
21000
  });
21001
+ var NAMING_REQUEST_TIMEOUT_MS = 20000;
21002
+ var NAMING_RETRY_DELAY_MS = 2000;
20999
21003
  function shortText(value, limit = 60) {
21000
21004
  return [
21001
21005
  ...stripVTControlCharacters(value).replace(/\p{Cc}/gu, " ").replace(/\s+/g, " ").trim()
@@ -21033,7 +21037,7 @@ function mergeUsage(base, extra) {
21033
21037
  }
21034
21038
  return result;
21035
21039
  }
21036
- function namingTask(canUse) {
21040
+ function namingTask(canUse, { requestTimeoutMs, retryDelayMs }) {
21037
21041
  let active = 0;
21038
21042
  const waits = new Set;
21039
21043
  return defineTask3({
@@ -21084,7 +21088,7 @@ function namingTask(canUse) {
21084
21088
  return { status: "running", checkpoint: { phase: "generate", attempt: d.attempts } };
21085
21089
  }, context);
21086
21090
  let title = "", usage;
21087
- const signal = AbortSignal.any([runtime.signal, AbortSignal.timeout(20000)]);
21091
+ const signal = AbortSignal.any([runtime.signal, AbortSignal.timeout(requestTimeoutMs)]);
21088
21092
  try {
21089
21093
  const budget = Math.max(32, Math.min(12000, Math.floor((model.contextWindow - 512) / 4)));
21090
21094
  const response = await Promise.race([
@@ -21123,7 +21127,7 @@ function namingTask(canUse) {
21123
21127
  d.answer = "";
21124
21128
  } else
21125
21129
  d.naming = d.attempts >= 2 ? "fallback" : "pending";
21126
- return title || d.attempts >= 2 ? { status: "terminal", outcome: { status: "completed", result: null } } : { status: "running", checkpoint: { phase: "wait", until: runtime.now() + 2000 } };
21130
+ return title || d.attempts >= 2 ? { status: "terminal", outcome: { status: "completed", result: null } } : { status: "running", checkpoint: { phase: "wait", until: runtime.now() + retryDelayMs } };
21127
21131
  }, context);
21128
21132
  } finally {
21129
21133
  active--;
@@ -21155,8 +21159,11 @@ class SessionMetadataManager {
21155
21159
  storage;
21156
21160
  context;
21157
21161
  report;
21158
- constructor(harness, storage, context, registry, report = console.error, canUseModel = async () => true) {
21159
- this.task = namingTask(canUseModel);
21162
+ constructor(harness, storage, context, registry, report = console.error, canUseModel = async () => true, timings = {}) {
21163
+ this.task = namingTask(canUseModel, {
21164
+ requestTimeoutMs: timings.requestTimeoutMs ?? NAMING_REQUEST_TIMEOUT_MS,
21165
+ retryDelayMs: timings.retryDelayMs ?? NAMING_RETRY_DELAY_MS
21166
+ });
21160
21167
  this.harness = harness;
21161
21168
  this.storage = storage;
21162
21169
  this.context = context;
@@ -22736,7 +22743,7 @@ async function createNekoRuntime(options = {}) {
22736
22743
  registry.install(createSubagentExtension(async (id, requestId, cwd) => {
22737
22744
  resourceSnapshots.stage(id, requestId, cwd, await loadResources(cwd));
22738
22745
  }, resourceSnapshots.discard, (ref) => guardProvider(ref)));
22739
- const jobs = new JobManager;
22746
+ const jobs = new JobManager(options.timings?.jobStartWindowMs === undefined ? {} : { startWindowMs: options.timings.jobStartWindowMs });
22740
22747
  registry.install(jobs.extension);
22741
22748
  registry.install(createTodoExtension());
22742
22749
  let questions;
@@ -22754,6 +22761,7 @@ async function createNekoRuntime(options = {}) {
22754
22761
  models,
22755
22762
  registry,
22756
22763
  settings: {
22764
+ ...options.timings?.retryBaseDelayMs === undefined ? {} : { retry: { baseDelayMs: options.timings.retryBaseDelayMs } },
22757
22765
  get compaction() {
22758
22766
  return compactionPolicy(contextState, allModels().filter((model) => !isModelProvider(model.provider) || enabled.has(model.provider)).map((model) => model.contextWindow));
22759
22767
  }
@@ -22852,7 +22860,10 @@ async function createNekoRuntime(options = {}) {
22852
22860
  };
22853
22861
  if (existing.length === 0)
22854
22862
  await touchConversation(conversation.id);
22855
- const metadata = new SessionMetadataManager(harness, storage, context, registry, options.onReport, (provider, modelId) => guardProvider({ provider, modelId }).then(() => true, () => false));
22863
+ const metadata = new SessionMetadataManager(harness, storage, context, registry, options.onReport, (provider, modelId) => guardProvider({ provider, modelId }).then(() => true, () => false), {
22864
+ ...options.timings?.namingRequestTimeoutMs === undefined ? {} : { requestTimeoutMs: options.timings.namingRequestTimeoutMs },
22865
+ ...options.timings?.namingRetryDelayMs === undefined ? {} : { retryDelayMs: options.timings.namingRetryDelayMs }
22866
+ });
22856
22867
  if (existing.length === 0)
22857
22868
  await metadata.initialize(conversation.id);
22858
22869
  for (const record of existing)
@@ -24969,7 +24980,7 @@ function subagentClient(connect) {
24969
24980
  import { createHash as createHash2 } from "crypto";
24970
24981
 
24971
24982
  // packages/core/src/application-version.ts
24972
- var RUNNING_VERSION = "0.4.8";
24983
+ var RUNNING_VERSION = "0.4.9";
24973
24984
  var APPLICATION_PROTOCOL = 8;
24974
24985
  function stableVersion(value) {
24975
24986
  return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
@@ -30490,7 +30501,8 @@ async function startGateway(options = {}) {
30490
30501
  ...options.extensions !== undefined ? { extensions: options.extensions } : {},
30491
30502
  ...options.promptHome !== undefined ? { promptHome: options.promptHome } : {},
30492
30503
  ...fileConfig.shell?.path !== undefined ? { shellPath: fileConfig.shell.path } : {},
30493
- ...options.shellResolver !== undefined ? { shellResolver: options.shellResolver } : {}
30504
+ ...options.shellResolver !== undefined ? { shellResolver: options.shellResolver } : {},
30505
+ ...options.timings !== undefined ? { timings: options.timings } : {}
30494
30506
  });
30495
30507
  try {
30496
30508
  return await bindGateway(runtime, {