hiwork-core 0.0.37 → 0.0.39

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.
package/lib/index.js CHANGED
@@ -8267,7 +8267,7 @@ defineMethod("transform", [
8267
8267
  ], ({ inner }, isInner) => inner.toString(isInner));
8268
8268
 
8269
8269
  // src/version.ts
8270
- var PKG_VERSION = false ? "dev" : "0.0.37";
8270
+ var PKG_VERSION = false ? "dev" : "0.0.39";
8271
8271
 
8272
8272
  // src/browser-login.ts
8273
8273
  import { createHash, randomBytes, randomUUID } from "node:crypto";
@@ -8278,6 +8278,7 @@ var API_KEY_REF = "AI_ROUTER_API_KEY";
8278
8278
  var SESSION_REF = "AI_ROUTER_SESSION";
8279
8279
  var PENDING_PASSWORD_REF = "AI_ROUTER_PENDING_PASSWORD";
8280
8280
  var REMEMBERED_PASSWORD_REF = "AI_ROUTER_REMEMBERED_PASSWORD";
8281
+ var REFRESH_TOKEN_REF = "AI_ROUTER_REFRESH_TOKEN";
8281
8282
  var PROVIDER_ROUTES = {
8282
8283
  "ai-router-chat": "openai-completions",
8283
8284
  "ai-router-anthropic": "anthropic-messages",
@@ -8435,6 +8436,7 @@ async function provisionSession(opts) {
8435
8436
  const displayName = opts.user.display_name?.trim() || void 0;
8436
8437
  const set2 = {
8437
8438
  ...opts.username === void 0 ? {} : { username: opts.username },
8439
+ ...opts.sessionExpiresAt === void 0 ? {} : { sessionExpiresAt: opts.sessionExpiresAt },
8438
8440
  ...displayName === void 0 ? {} : { displayName },
8439
8441
  lastLogin: {
8440
8442
  at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8600,12 +8602,15 @@ async function completeBrowserLogin(input, opts) {
8600
8602
  }
8601
8603
  const baseURL = opts.value.baseURL;
8602
8604
  if (baseURL === void 0 || baseURL === "") return await failBrowserLogin(opts.state, NO_BASE_URL_ERROR, log);
8603
- const { sessionCookie, user } = await opts.gateway.exchangeCode({
8605
+ const { sessionCookie, user, sessionExpiresAt, refreshToken } = await opts.gateway.exchangeCode({
8604
8606
  code: input.code,
8605
8607
  codeVerifier: pending.codeVerifier,
8606
8608
  deviceId: pending.deviceId
8607
8609
  });
8608
8610
  await opts.credentials.set(SESSION_REF, sessionCookie);
8611
+ if (refreshToken === void 0) await opts.credentials.unset(REFRESH_TOKEN_REF).catch(() => {
8612
+ });
8613
+ else await opts.credentials.set(REFRESH_TOKEN_REF, refreshToken);
8609
8614
  const username = typeof user.username === "string" && user.username.trim() !== "" ? user.username.trim() : void 0;
8610
8615
  await provisionSession({
8611
8616
  gateway: opts.gateway,
@@ -8617,6 +8622,7 @@ async function completeBrowserLogin(input, opts) {
8617
8622
  sessionCookie,
8618
8623
  user,
8619
8624
  username,
8625
+ sessionExpiresAt,
8620
8626
  requestUnset: ["browserLoginRequest", "browserLogin"],
8621
8627
  ...opts.defaultModel === void 0 ? {} : { defaultModel: opts.defaultModel },
8622
8628
  ...opts.ensureWorkspace === void 0 ? {} : { ensureWorkspace: opts.ensureWorkspace }
@@ -8647,6 +8653,13 @@ function gatewayUserAgent(version2 = desktopAppVersion()) {
8647
8653
  }
8648
8654
 
8649
8655
  // src/ai-router-client.ts
8656
+ var sessionActivitySink;
8657
+ function setSessionActivitySink(sink) {
8658
+ sessionActivitySink = sink;
8659
+ }
8660
+ function clearSessionActivitySink(sink) {
8661
+ if (sessionActivitySink === sink) sessionActivitySink = void 0;
8662
+ }
8650
8663
  async function knowledgeErrorCodeFromBody(res) {
8651
8664
  try {
8652
8665
  const payload = await res.clone().json();
@@ -8658,6 +8671,7 @@ async function knowledgeErrorCodeFromBody(res) {
8658
8671
  var AUTH_AUTHORIZE_PATH = "/api/auth/authorize";
8659
8672
  var AUTH_TOKEN_PATH = "/api/auth/token";
8660
8673
  var AUTH_SESSIONS_PATH = "/api/auth/sessions";
8674
+ var AUTH_REFRESH_PATH = "/api/auth/refresh";
8661
8675
  var ME_ANALYTICS_SUMMARY_PATH = "/api/me/analytics/summary";
8662
8676
  var ME_ANALYTICS_TRENDS_PATH = "/api/me/analytics/trends";
8663
8677
  var ME_ANALYTICS_MODEL_TRENDS_PATH = "/api/me/analytics/model-trends";
@@ -8758,8 +8772,16 @@ var AiRouterClient = class {
8758
8772
  if (res.status === 401) throw new GatewayError("AUTH_FAILED", "\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF", 401);
8759
8773
  if (!res.ok) throw new GatewayError("GATEWAY_UNREACHABLE", `\u7F51\u5173\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP ${res.status}\uFF09`, res.status);
8760
8774
  if (sid === void 0) throw new GatewayError("GATEWAY_UNREACHABLE", "\u7F51\u5173\u767B\u5F55\u6210\u529F\u4F46\u672A\u8FD4\u56DE\u4F1A\u8BDD Cookie", res.status);
8761
- const user = await res.json();
8762
- return { sessionCookie: `session_id=${sid}`, user };
8775
+ const { expires_at: rawExpiresAt, refresh_token: rawRefreshToken, ...user } = await res.json();
8776
+ const bodyExpiresAt = typeof rawExpiresAt === "string" && !Number.isNaN(Date.parse(rawExpiresAt)) ? rawExpiresAt : void 0;
8777
+ const sessionExpiresAt = bodyExpiresAt ?? parseSessionCookieExpiry(res.headers);
8778
+ const refreshToken = typeof rawRefreshToken === "string" && rawRefreshToken !== "" ? rawRefreshToken : void 0;
8779
+ return {
8780
+ sessionCookie: `session_id=${sid}`,
8781
+ user,
8782
+ ...sessionExpiresAt === void 0 ? {} : { sessionExpiresAt },
8783
+ ...refreshToken === void 0 ? {} : { refreshToken }
8784
+ };
8763
8785
  }
8764
8786
  /**
8765
8787
  * POST /api/auth/token:用一次性 code + PKCE verifier 换取会话。
@@ -8781,7 +8803,49 @@ var AiRouterClient = class {
8781
8803
  if (data === void 0 || data.session_id === void 0 || data.session_id === "") {
8782
8804
  throw new GatewayError("GATEWAY_UNREACHABLE", "\u7F51\u5173\u5151\u6362\u6210\u529F\u4F46\u672A\u8FD4\u56DE\u4F1A\u8BDD\u6807\u8BC6", res.status);
8783
8805
  }
8784
- return { sessionCookie: `session_id=${data.session_id}`, user: data.user };
8806
+ const rawExpiresAt = typeof data.expires_at === "string" ? data.expires_at : void 0;
8807
+ const sessionExpiresAt = rawExpiresAt !== void 0 && !Number.isNaN(Date.parse(rawExpiresAt)) ? rawExpiresAt : void 0;
8808
+ const refreshToken = typeof data.refresh_token === "string" && data.refresh_token !== "" ? data.refresh_token : void 0;
8809
+ return {
8810
+ sessionCookie: `session_id=${data.session_id}`,
8811
+ user: data.user,
8812
+ ...sessionExpiresAt === void 0 ? {} : { sessionExpiresAt },
8813
+ ...refreshToken === void 0 ? {} : { refreshToken }
8814
+ };
8815
+ }
8816
+ /**
8817
+ * POST /api/auth/refresh:用 refresh grant 换一份新会话(M2 网关的静默重认证)。
8818
+ *
8819
+ * 拒绝语义必须分开,调用方据此决定「弃用 grant」还是「保留待重试」:
8820
+ * - 400 = grant 无效(过期 / 已被轮换 / 复用检测拒绝)→ `REFRESH_GRANT_INVALID`,本地作废;
8821
+ * - 404 = 老网关根本没有这条路由 → `REFRESH_GRANT_ABSENT`,本地作废且**静默降级**;
8822
+ * - 其余非 2xx(5xx / 反代错误页)与网络层异常 → `GATEWAY_UNREACHABLE`,grant 保留,
8823
+ * 下次 SESSION_EXPIRED 再试。
8824
+ *
8825
+ * 任何日志/异常信息都不得包含 refresh_token / session_id 明文。
8826
+ */
8827
+ async refreshSession(refreshToken) {
8828
+ const res = await this.requestOrUnreachable(AUTH_REFRESH_PATH, {
8829
+ method: "POST",
8830
+ body: { refresh_token: refreshToken }
8831
+ });
8832
+ if (res.status === 400) throw new GatewayError("REFRESH_GRANT_INVALID", "\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55", 400);
8833
+ if (res.status === 404) throw new GatewayError("REFRESH_GRANT_ABSENT", "\u7F51\u5173\u4E0D\u652F\u6301\u9759\u9ED8\u7EED\u671F", 404);
8834
+ if (!res.ok) throw new GatewayError("GATEWAY_UNREACHABLE", `\u7F51\u5173\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP ${res.status}\uFF09`, res.status);
8835
+ const data = await readJson(res);
8836
+ const sid = typeof data?.session_id === "string" && data.session_id !== "" ? data.session_id : extractSessionId(res.headers);
8837
+ if (data === void 0 || data.user === void 0 || sid === void 0) {
8838
+ throw new GatewayError("GATEWAY_UNREACHABLE", "\u7F51\u5173\u7EED\u671F\u6210\u529F\u4F46\u672A\u8FD4\u56DE\u4F1A\u8BDD\u6807\u8BC6", res.status);
8839
+ }
8840
+ const rawExpiresAt = typeof data.expires_at === "string" ? data.expires_at : void 0;
8841
+ const sessionExpiresAt = rawExpiresAt !== void 0 && !Number.isNaN(Date.parse(rawExpiresAt)) ? rawExpiresAt : void 0;
8842
+ const rotated = typeof data.refresh_token === "string" && data.refresh_token !== "" ? data.refresh_token : void 0;
8843
+ return {
8844
+ sessionCookie: `session_id=${sid}`,
8845
+ user: data.user,
8846
+ ...sessionExpiresAt === void 0 ? {} : { sessionExpiresAt },
8847
+ ...rotated === void 0 ? {} : { refreshToken: rotated }
8848
+ };
8785
8849
  }
8786
8850
  /** GET /api/auth/sessions:当前账号的登录设备列表。 */
8787
8851
  async listSessions(sessionCookie) {
@@ -9016,6 +9080,9 @@ var AiRouterClient = class {
9016
9080
  if (res.status === 503) {
9017
9081
  throw new GatewayError("MCP_ASSERTION_DISABLED", "\u672C\u90E8\u7F72\u672A\u542F\u7528 MCP \u65AD\u8A00\u7B7E\u53D1\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458", 503);
9018
9082
  }
9083
+ if (res.status === 429) {
9084
+ throw new GatewayError("MCP_ASSERTION_RATE_LIMITED", "\u65AD\u8A00\u7B7E\u53D1\u8FC7\u4E8E\u9891\u7E41\uFF0C\u7A0D\u540E\u81EA\u52A8\u91CD\u8BD5", 429);
9085
+ }
9019
9086
  if (!res.ok) throw new GatewayError("GATEWAY_UNREACHABLE", `\u7F51\u5173\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP ${res.status}\uFF09`, res.status);
9020
9087
  return await parseMeJson(res);
9021
9088
  }
@@ -9389,7 +9456,19 @@ var AiRouterClient = class {
9389
9456
  opts.rawBody.byteOffset + opts.rawBody.byteLength
9390
9457
  );
9391
9458
  } else if (opts.body !== void 0) init.body = JSON.stringify(opts.body);
9392
- return fetch(`${this.baseURL}${path}`, init);
9459
+ const res = await fetch(`${this.baseURL}${path}`, init);
9460
+ if (opts.sessionCookie !== void 0) {
9461
+ try {
9462
+ if (res.status === 401) sessionActivitySink?.({ kind: "unauthorized" });
9463
+ else if (res.ok) {
9464
+ const headerExpiresAt = res.headers.get("x-session-expires-at");
9465
+ const expiresAt = headerExpiresAt !== null && !Number.isNaN(Date.parse(headerExpiresAt)) ? headerExpiresAt : void 0;
9466
+ sessionActivitySink?.(expiresAt === void 0 ? { kind: "success" } : { kind: "success", expiresAt });
9467
+ }
9468
+ } catch {
9469
+ }
9470
+ }
9471
+ return res;
9393
9472
  }
9394
9473
  };
9395
9474
  function headerSafeValue(value) {
@@ -9427,6 +9506,27 @@ function extractSessionId(headers) {
9427
9506
  }
9428
9507
  return void 0;
9429
9508
  }
9509
+ function parseSessionCookieExpiry(headers, now = Date.now()) {
9510
+ const raws = typeof headers.getSetCookie === "function" ? headers.getSetCookie() : [headers.get("set-cookie")].filter((raw) => raw !== null);
9511
+ for (const raw of raws) {
9512
+ for (const section of raw.split(/,\s*(?=[!#$%&'*+\-.^_`|~0-9A-Za-z]+=)/)) {
9513
+ if (!/(?:^|;\s*)session_id=/.test(section)) continue;
9514
+ const expires = /;\s*Expires=([^;]+)/i.exec(section);
9515
+ const expiresRaw = expires?.[1];
9516
+ if (expiresRaw !== void 0) {
9517
+ const ms = Date.parse(expiresRaw);
9518
+ if (!Number.isNaN(ms)) return new Date(ms).toISOString();
9519
+ }
9520
+ const maxAge = /;\s*Max-Age=(-?\d+)/i.exec(section);
9521
+ if (maxAge !== null) {
9522
+ const seconds = Number(maxAge[1]);
9523
+ if (Number.isFinite(seconds)) return new Date(now + seconds * 1e3).toISOString();
9524
+ }
9525
+ return void 0;
9526
+ }
9527
+ }
9528
+ return void 0;
9529
+ }
9430
9530
  async function readJson(res) {
9431
9531
  try {
9432
9532
  return await res.json();
@@ -9577,6 +9677,58 @@ async function fetchClientPolicy(opts) {
9577
9677
  return void 0;
9578
9678
  }
9579
9679
  }
9680
+ var CLIENT_POLICY_POLL_INTERVAL_MS = 6 * 60 * 6e4;
9681
+ var CLIENT_POLICY_RETRY_DELAYS_MS = [6e4, 10 * 6e4, 60 * 6e4];
9682
+ function createClientPolicyPoller(deps) {
9683
+ let timer;
9684
+ let disposed = false;
9685
+ let running = false;
9686
+ let retryIndex = 0;
9687
+ const schedule = (delayMs) => {
9688
+ if (disposed) return;
9689
+ if (timer !== void 0) clearTimeout(timer);
9690
+ timer = setTimeout(() => {
9691
+ timer = void 0;
9692
+ void run();
9693
+ }, delayMs);
9694
+ timer.unref?.();
9695
+ };
9696
+ const run = async () => {
9697
+ if (disposed || running) return;
9698
+ running = true;
9699
+ let ok = false;
9700
+ try {
9701
+ ok = await deps.attempt();
9702
+ } catch {
9703
+ ok = false;
9704
+ } finally {
9705
+ running = false;
9706
+ }
9707
+ if (disposed) return;
9708
+ if (ok) {
9709
+ retryIndex = 0;
9710
+ schedule(CLIENT_POLICY_POLL_INTERVAL_MS);
9711
+ return;
9712
+ }
9713
+ const delay2 = CLIENT_POLICY_RETRY_DELAYS_MS[Math.min(retryIndex, CLIENT_POLICY_RETRY_DELAYS_MS.length - 1)];
9714
+ retryIndex += 1;
9715
+ deps.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u62C9\u53D6\u5931\u8D25\uFF0C${Math.round(delay2 / 1e3)}s \u540E\u91CD\u8BD5`);
9716
+ schedule(delay2);
9717
+ };
9718
+ return {
9719
+ trigger: () => {
9720
+ if (disposed || running || timer !== void 0) return;
9721
+ void run();
9722
+ },
9723
+ dispose: () => {
9724
+ disposed = true;
9725
+ if (timer !== void 0) {
9726
+ clearTimeout(timer);
9727
+ timer = void 0;
9728
+ }
9729
+ }
9730
+ };
9731
+ }
9580
9732
 
9581
9733
  // src/default-model.ts
9582
9734
  var DEFAULT_MODEL_NS = "agent-default-model";
@@ -9783,14 +9935,18 @@ async function runLogin(opts) {
9783
9935
  const password = await opts.credentials.resolve(pending.passwordRef);
9784
9936
  if (password === void 0) throw new GatewayError("AUTH_FAILED", "\u767B\u5F55\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
9785
9937
  const device = await resolveDeviceInfo(opts.credentials);
9786
- const { sessionCookie, user } = await opts.gateway.login(pending.username, password.value, device);
9938
+ const { sessionCookie, user, sessionExpiresAt, refreshToken } = await opts.gateway.login(pending.username, password.value, device);
9787
9939
  await opts.credentials.set(SESSION_REF, sessionCookie);
9940
+ if (refreshToken === void 0) await opts.credentials.unset(REFRESH_TOKEN_REF).catch(() => {
9941
+ });
9942
+ else await opts.credentials.set(REFRESH_TOKEN_REF, refreshToken);
9788
9943
  await provisionSession({
9789
9944
  ...opts,
9790
9945
  baseURL,
9791
9946
  sessionCookie,
9792
9947
  user,
9793
9948
  username: pending.username,
9949
+ sessionExpiresAt,
9794
9950
  requestUnset: ["pendingLogin"],
9795
9951
  onApiKeyWritten: async () => {
9796
9952
  if (opts.value.rememberPassword === true) await opts.credentials.set(REMEMBERED_PASSWORD_REF, password.value);
@@ -9814,6 +9970,9 @@ async function runLogin(opts) {
9814
9970
  }
9815
9971
  }
9816
9972
  async function runRefresh(opts) {
9973
+ return refreshWith(opts, false);
9974
+ }
9975
+ async function refreshWith(opts, grantRetried) {
9817
9976
  const baseURL = opts.value.baseURL;
9818
9977
  if (baseURL === void 0) {
9819
9978
  await opts.state.patch({ unset: ["refreshRequest"] });
@@ -9850,6 +10009,28 @@ async function runRefresh(opts) {
9850
10009
  return null;
9851
10010
  } catch (error) {
9852
10011
  const refreshError = toLoginError(error, "\u65E0\u6CD5\u8FDE\u63A5\u7F51\u5173\uFF0C\u8BF7\u68C0\u67E5 baseURL \u6216\u7F51\u7EDC");
10012
+ if (refreshError.code === "SESSION_EXPIRED" && !grantRetried) {
10013
+ const grant = await opts.credentials.resolve(REFRESH_TOKEN_REF).catch(() => void 0);
10014
+ if (grant !== void 0 && grant.value !== "") {
10015
+ try {
10016
+ const renewed = await opts.gateway.refreshSession(grant.value);
10017
+ await opts.credentials.set(SESSION_REF, renewed.sessionCookie);
10018
+ if (renewed.refreshToken === void 0) await opts.credentials.unset(REFRESH_TOKEN_REF).catch(() => {
10019
+ });
10020
+ else await opts.credentials.set(REFRESH_TOKEN_REF, renewed.refreshToken);
10021
+ if (renewed.sessionExpiresAt !== void 0) {
10022
+ await opts.state.patch({ set: { sessionExpiresAt: renewed.sessionExpiresAt } });
10023
+ }
10024
+ return refreshWith(opts, true);
10025
+ } catch (grantError) {
10026
+ const code = grantError instanceof GatewayError ? grantError.code : void 0;
10027
+ if (code === "REFRESH_GRANT_INVALID" || code === "REFRESH_GRANT_ABSENT") {
10028
+ await opts.credentials.unset(REFRESH_TOKEN_REF).catch(() => {
10029
+ });
10030
+ }
10031
+ }
10032
+ }
10033
+ }
9853
10034
  if (refreshError.code === "SESSION_EXPIRED" && opts.value.rememberPassword === true && opts.value.username !== void 0) {
9854
10035
  const remembered = await opts.credentials.resolve(REMEMBERED_PASSWORD_REF).catch(() => void 0);
9855
10036
  if (remembered !== void 0) {
@@ -9874,6 +10055,8 @@ async function runLogout(opts) {
9874
10055
  });
9875
10056
  await opts.credentials.unset(SESSION_REF).catch(() => {
9876
10057
  });
10058
+ await opts.credentials.unset(REFRESH_TOKEN_REF).catch(() => {
10059
+ });
9877
10060
  await opts.credentials.unset(PENDING_PASSWORD_REF).catch(() => {
9878
10061
  });
9879
10062
  await opts.credentials.unset(REMEMBERED_PASSWORD_REF).catch(() => {
@@ -9892,7 +10075,9 @@ async function runLogout(opts) {
9892
10075
  "lastError",
9893
10076
  "keyId",
9894
10077
  "budgets",
9895
- "keys"
10078
+ "keys",
10079
+ // 会话寿命随会话一起清空:残留的旧 expiresAt 会让保活决策基于已失效的会话。
10080
+ "sessionExpiresAt"
9896
10081
  ]
9897
10082
  });
9898
10083
  }
@@ -12490,7 +12675,7 @@ var Service = class Service2 {
12490
12675
  }
12491
12676
  };
12492
12677
 
12493
- // node_modules/.pnpm/@deepseek-ai+dsh-scope@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-invariants_0271d3a82adfe807020938592f267574/node_modules/@deepseek-ai/dsh-scope/lib/index.js
12678
+ // node_modules/.pnpm/@deepseek-ai+dsh-scope@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-invariants_8ef32aa4891a716d53102df556bb759f/node_modules/@deepseek-ai/dsh-scope/lib/index.js
12494
12679
  var NamedEntries = class {
12495
12680
  duplicateError;
12496
12681
  data = /* @__PURE__ */ new Map();
@@ -12706,10 +12891,10 @@ function scopeTarget(base, key) {
12706
12891
  return carrier;
12707
12892
  }
12708
12893
 
12709
- // node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-llm/lib/index.js
12894
+ // node_modules/.pnpm/@deepseek-ai+dsh-llm@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-llm/lib/index.js
12710
12895
  import { createRequire } from "node:module";
12711
12896
 
12712
- // node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
12897
+ // node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
12713
12898
  var RemoteError = class extends Error {
12714
12899
  code;
12715
12900
  details;
@@ -12834,7 +13019,7 @@ function validateName(subject, value) {
12834
13019
  if (!isTypertRemoteSegment(value)) throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
12835
13020
  }
12836
13021
 
12837
- // node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
13022
+ // node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
12838
13023
  function assertNever(value, context) {
12839
13024
  const rendered = JSON.stringify(value) ?? String(value);
12840
13025
  throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
@@ -12843,7 +13028,7 @@ function hasIntrinsicConstructor(prototype, name2) {
12843
13028
  const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
12844
13029
  if (typeof constructor !== "function") return false;
12845
13030
  try {
12846
- return constructor.name === name2 && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === `function ${name2}() { [native code] }`;
13031
+ return constructor.name === name2 && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === Function.prototype.toString.call(name2 === "Array" ? Array : Object);
12847
13032
  } catch {
12848
13033
  return false;
12849
13034
  }
@@ -13015,7 +13200,7 @@ function deepFreeze(value) {
13015
13200
  return value;
13016
13201
  }
13017
13202
 
13018
- // node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-crypto/lib/index.js
13203
+ // node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-crypto/lib/index.js
13019
13204
  function randomUUID2() {
13020
13205
  const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
13021
13206
  const hex = Array.from(bytes, (byte, index) => {
@@ -13024,15 +13209,15 @@ function randomUUID2() {
13024
13209
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
13025
13210
  }
13026
13211
 
13027
- // node_modules/.pnpm/@deepseek-ai+dsh-brand@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-brand/lib/index.js
13212
+ // node_modules/.pnpm/@deepseek-ai+dsh-brand@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-brand/lib/index.js
13028
13213
  function brandString(value) {
13029
13214
  return value;
13030
13215
  }
13031
13216
 
13032
- // node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
13217
+ // node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
13033
13218
  var MAX_TIMER_DELAY_MS = 2147483647;
13034
13219
 
13035
- // node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-llm/lib/index.js
13220
+ // node_modules/.pnpm/@deepseek-ai+dsh-llm@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-llm/lib/index.js
13036
13221
  function freezeMessage(message) {
13037
13222
  return deepFreeze(structuredClone(message));
13038
13223
  }
@@ -14075,7 +14260,7 @@ function adapterFailureChunk(error, signal) {
14075
14260
  };
14076
14261
  }
14077
14262
 
14078
- // node_modules/.pnpm/@deepseek-ai+dsh-sandbox@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-llm@0.1._a8c9906be8c926809212c5fbb3bc7c61/node_modules/@deepseek-ai/dsh-sandbox/lib/index.js
14263
+ // node_modules/.pnpm/@deepseek-ai+dsh-sandbox@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-llm@0.2._910788faa6d5e8808178f15deb98d164/node_modules/@deepseek-ai/dsh-sandbox/lib/index.js
14079
14264
  var WIDER_MODES = {
14080
14265
  "read-only": ["workspace-write", "danger-full-access"],
14081
14266
  "workspace-write": ["danger-full-access"]
@@ -14117,7 +14302,7 @@ async function approveEscalation(request, approval) {
14117
14302
  }
14118
14303
  }
14119
14304
 
14120
- // node_modules/.pnpm/@deepseek-ai+dsh-tools@0.1.7-rc.2_d2e08270f2205e73d65e98cfcd910cae/node_modules/@deepseek-ai/dsh-tools/lib/index.js
14305
+ // node_modules/.pnpm/@deepseek-ai+dsh-tools@0.2.0-rc.1_44958e8f826516598aa7d335d84aa068/node_modules/@deepseek-ai/dsh-tools/lib/index.js
14121
14306
  var JsonSchemaError = class extends HarnessError {
14122
14307
  /** Individual schema violations in walk order. */
14123
14308
  violations;
@@ -18504,6 +18689,140 @@ async function readJson2(response) {
18504
18689
  }
18505
18690
  }
18506
18691
 
18692
+ // src/session-fanout.ts
18693
+ function buildSessionFanout(handlers) {
18694
+ const entries = [
18695
+ ["client policy", handlers.onPolicyRefresh],
18696
+ ["managed mcp", handlers.onManagedMcpRefresh],
18697
+ ["managed skills", handlers.onManagedSkillsRefresh],
18698
+ ["workbench", handlers.onWorkbenchRefresh]
18699
+ ];
18700
+ return () => {
18701
+ for (const [label, run] of entries) {
18702
+ try {
18703
+ run();
18704
+ } catch (error) {
18705
+ handlers.log?.(`hiwork-core: \u4F1A\u8BDD\u8054\u52A8\u5237\u65B0\u300C${label}\u300D\u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
18706
+ }
18707
+ }
18708
+ };
18709
+ }
18710
+
18711
+ // src/session-supervisor.ts
18712
+ var SESSION_TTL_ASSUMPTION_MS = 24 * 60 * 6e4;
18713
+ var SESSION_CHECK_INTERVAL_MS = 60 * 6e4;
18714
+ var SESSION_RECOVERY_COOLDOWN_MS = 6e4;
18715
+ var SessionSupervisor = class {
18716
+ constructor(deps) {
18717
+ this.deps = deps;
18718
+ }
18719
+ timer;
18720
+ /** 上次「已知会话存活」的时刻;undefined = 本进程内还没确认过(重启后的首次检查照打)。 */
18721
+ lastAuthedSuccessAt;
18722
+ /**
18723
+ * 最近一次带会话成功响应里 `X-Session-Expires-At` 给出的**内存**寿命估计(原样时刻串)。
18724
+ * 只活在内存里——热路径绝不写 settings(Review Focus 5),只有心跳成功才落盘。
18725
+ */
18726
+ lastHeaderExpiresAt;
18727
+ /** 恢复请求是否在途(单飞去抖)。 */
18728
+ recoveryInFlight = false;
18729
+ /** 上次触发恢复的时刻(冷却窗口基准)。 */
18730
+ lastRecoveryAt;
18731
+ /** 开始兜底检查;重复调用不叠加定时器。 */
18732
+ start() {
18733
+ if (this.timer !== void 0) return;
18734
+ this.timer = setInterval(() => {
18735
+ void this.wake();
18736
+ }, SESSION_CHECK_INTERVAL_MS);
18737
+ this.timer.unref?.();
18738
+ }
18739
+ /** 停止兜底检查(调用后零活动)。 */
18740
+ stop() {
18741
+ if (this.timer === void 0) return;
18742
+ clearInterval(this.timer);
18743
+ this.timer = void 0;
18744
+ }
18745
+ /**
18746
+ * 带会话的请求成功 → 记活动时间(由 ai-router-client 的活动汇聚驱动)。
18747
+ * `expiresAt` 为响应头 `X-Session-Expires-At` 给出的会话寿命:**只更新内存估计**,
18748
+ * 绝不在此写 settings——热路径上每个成功请求都写盘会打爆设置面(Review Focus 5)。
18749
+ * 不可解析的值直接丢弃(当作没给),不覆盖既有估计。
18750
+ */
18751
+ noteAuthedSuccess(expiresAt) {
18752
+ this.lastAuthedSuccessAt = this.now();
18753
+ if (expiresAt !== void 0 && !Number.isNaN(Date.parse(expiresAt))) this.lastHeaderExpiresAt = expiresAt;
18754
+ }
18755
+ /**
18756
+ * 带会话的请求收到 401 → 去抖后触发一次会话恢复。
18757
+ * 去抖有两层:`recoveryInFlight`(单飞,覆盖「恢复请求还没落地,同刻又来了 5 条 401」)
18758
+ * 与 `SESSION_RECOVERY_COOLDOWN_MS` 冷却(覆盖恢复完成后短时间内的余波 401)。
18759
+ * 恢复失败只记日志——apply 后写入面需要几秒才就绪,那一侧的失败不该惊动宿主。
18760
+ */
18761
+ reportAuthed401(source) {
18762
+ if (this.recoveryInFlight) return;
18763
+ const now = this.now();
18764
+ if (this.lastRecoveryAt !== void 0 && now - this.lastRecoveryAt < SESSION_RECOVERY_COOLDOWN_MS) return;
18765
+ this.recoveryInFlight = true;
18766
+ this.lastRecoveryAt = now;
18767
+ void this.deps.requestRecovery().catch(() => {
18768
+ this.log(`hiwork-core: \u4F1A\u8BDD\u6062\u590D\u89E6\u53D1\u5931\u8D25\uFF08\u6765\u6E90 ${source}\uFF09\uFF0C\u7B49\u5F85\u4E0B\u4E2A 401 \u6216\u5FC3\u8DF3\u91CD\u8BD5`);
18769
+ }).finally(() => {
18770
+ this.recoveryInFlight = false;
18771
+ });
18772
+ }
18773
+ now() {
18774
+ return this.deps.now?.() ?? Date.now();
18775
+ }
18776
+ log(message) {
18777
+ this.deps.log?.(message);
18778
+ }
18779
+ /**
18780
+ * 一次兜底检查。判定顺序(早退即不发请求):
18781
+ * 无会话 / 无 baseURL → 返回;距上次已认证成功不足 TTL/2 → 返回;
18782
+ * 已知剩余寿命 > TTL/2 → 返回;否则 ping。
18783
+ * 寿命未知也 ping——重启后的首次检查即校验会话是否还活着。
18784
+ * 「已知剩余寿命」取 settings 值与内存估计(响应头)的**较大者**:头值只会更晚更准,
18785
+ * 用较大者才不会因为 settings 里的旧估算偏早而白打心跳。
18786
+ */
18787
+ async wake() {
18788
+ try {
18789
+ const session = await this.deps.getSession();
18790
+ if (session === void 0 || session === "") return;
18791
+ if (this.deps.getBaseURL() === void 0) return;
18792
+ const now = this.now();
18793
+ if (this.lastAuthedSuccessAt !== void 0 && now - this.lastAuthedSuccessAt < SESSION_TTL_ASSUMPTION_MS / 2) return;
18794
+ const knownExpiresAtMs = this.knownExpiresAtMs();
18795
+ if (knownExpiresAtMs !== void 0 && knownExpiresAtMs - now > SESSION_TTL_ASSUMPTION_MS / 2) return;
18796
+ let pinged;
18797
+ try {
18798
+ pinged = await this.deps.ping(session);
18799
+ } catch (error) {
18800
+ const expired = error instanceof GatewayError && error.code === "SESSION_EXPIRED";
18801
+ this.log(expired ? "hiwork-core: \u4F1A\u8BDD\u5FC3\u8DF3\u53D1\u73B0\u4F1A\u8BDD\u5DF2\u5931\u6548\uFF0C\u7B49\u5F85 401 \u6C47\u805A\u89E6\u53D1\u6062\u590D" : "hiwork-core: \u4F1A\u8BDD\u5FC3\u8DF3\u8BF7\u6C42\u5931\u8D25\uFF0C\u5C06\u5728\u4E0B\u4E2A\u5468\u671F\u91CD\u8BD5");
18802
+ return;
18803
+ }
18804
+ const pingedExpiresAt = pinged?.expiresAt;
18805
+ const pingedMs = pingedExpiresAt === void 0 ? Number.NaN : Date.parse(pingedExpiresAt);
18806
+ const nextExpiresAt = pingedExpiresAt === void 0 || Number.isNaN(pingedMs) ? new Date(this.now() + SESSION_TTL_ASSUMPTION_MS).toISOString() : pingedExpiresAt;
18807
+ try {
18808
+ await this.deps.patchSessionExpiresAt(nextExpiresAt);
18809
+ } catch {
18810
+ this.log("hiwork-core: \u4F1A\u8BDD\u5FC3\u8DF3\u6210\u529F\u4F46\u5BFF\u547D\u56DE\u5199\u5931\u8D25\uFF0C\u5C06\u5728\u4E0B\u4E2A\u5468\u671F\u91CD\u8BD5");
18811
+ }
18812
+ } catch {
18813
+ this.log("hiwork-core: \u4F1A\u8BDD\u5FC3\u8DF3\u68C0\u67E5\u5931\u8D25\uFF0C\u5C06\u5728\u4E0B\u4E2A\u5468\u671F\u91CD\u8BD5");
18814
+ }
18815
+ }
18816
+ /**
18817
+ * 已知的会话到期时刻(epoch ms):settings 值与内存头值里**有效且较晚**的那个;
18818
+ * 两者都没有(或都不可解析)返回 undefined = 寿命未知。
18819
+ */
18820
+ knownExpiresAtMs() {
18821
+ const values = [this.deps.getSessionExpiresAt(), this.lastHeaderExpiresAt].map((iso) => iso === void 0 ? Number.NaN : Date.parse(iso)).filter((ms) => !Number.isNaN(ms));
18822
+ return values.length === 0 ? void 0 : Math.max(...values);
18823
+ }
18824
+ };
18825
+
18507
18826
  // src/pms-todo-bridge.ts
18508
18827
  var PMS_TODO_RPC_CHANNEL = "/hiwork-pms-todo";
18509
18828
  var PMS_TODO_RPC_ENDPOINTS = [
@@ -19132,6 +19451,70 @@ function provideIdentity(ctx, deps) {
19132
19451
  };
19133
19452
  }
19134
19453
 
19454
+ // src/identity-context.ts
19455
+ var IDENTITY_CONTEXT_NAME = "hiwork:identity";
19456
+ var IDENTITY_CONTEXT_ORDER = 105;
19457
+ var CAVEAT = "\u8BE5\u8EAB\u4EFD\u7528\u4E8E\u4E2A\u6027\u5316\u4E0E\u6309\u4EBA\u8868\u8FF0\uFF1B\u6D89\u53CA\u6570\u636E\u6743\u9650\u7684\u64CD\u4F5C\u5FC5\u987B\u8D70\u53D7\u7BA1\u5DE5\u5177/\u8FDE\u63A5\u5668\u5B8C\u6210\uFF0C\u4E0D\u5F97\u628A\u6B64\u6587\u672C\u5F53\u4F5C\u51ED\u8BC1\u3002";
19458
+ function textOrUndefined6(value) {
19459
+ if (typeof value !== "string") return void 0;
19460
+ const text = value.trim();
19461
+ return text === "" ? void 0 : text;
19462
+ }
19463
+ function renderIdentityContextText(state) {
19464
+ const username = textOrUndefined6(state?.username);
19465
+ if (username === void 0) return "";
19466
+ const displayName = textOrUndefined6(state?.displayName);
19467
+ const who = displayName === void 0 ? `\u5DE5\u53F7 ${username}` : `${displayName}\uFF08\u5DE5\u53F7 ${username}\uFF09`;
19468
+ return `HiWork \u767B\u5F55\u8EAB\u4EFD\uFF1A${who}\u3002${CAVEAT}`;
19469
+ }
19470
+ function registerIdentityContext(systemPrompt, deps) {
19471
+ const dispose = systemPrompt.context({
19472
+ name: IDENTITY_CONTEXT_NAME,
19473
+ order: IDENTITY_CONTEXT_ORDER,
19474
+ text: () => {
19475
+ try {
19476
+ if (!deps.enabled()) return "";
19477
+ return renderIdentityContextText(deps.current());
19478
+ } catch (error) {
19479
+ deps.log?.(
19480
+ `hiwork-core: \u767B\u5F55\u8EAB\u4EFD\u4E0A\u4E0B\u6587\u8BFB\u53D6\u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`
19481
+ );
19482
+ return "";
19483
+ }
19484
+ }
19485
+ });
19486
+ return typeof dispose === "function" ? dispose : () => {
19487
+ };
19488
+ }
19489
+
19490
+ // src/ttl-cache.ts
19491
+ var TtlCache = class {
19492
+ ttlMs;
19493
+ entry;
19494
+ constructor(opts) {
19495
+ this.ttlMs = opts.ttlMs;
19496
+ }
19497
+ /** 命中返回缓存值;会话键不同或已到期返回 undefined。`now` 可注入(缺省 Date.now)。 */
19498
+ get(sessionKey, now = Date.now()) {
19499
+ const hit = this.entry;
19500
+ if (hit === void 0 || hit.sessionKey !== sessionKey) return void 0;
19501
+ if (now - hit.at >= this.ttlMs) return void 0;
19502
+ return hit.value;
19503
+ }
19504
+ /** 写入/覆盖缓存(新会话键会顶掉旧会话那条)。 */
19505
+ put(sessionKey, value, now = Date.now()) {
19506
+ this.entry = { sessionKey, value, at: now };
19507
+ }
19508
+ /** 清掉指定会话键的条目(不影响别的键,虽然只会存在一条);不传参数则清空。 */
19509
+ invalidate(sessionKey) {
19510
+ if (sessionKey === void 0) {
19511
+ this.entry = void 0;
19512
+ return;
19513
+ }
19514
+ if (this.entry?.sessionKey === sessionKey) this.entry = void 0;
19515
+ }
19516
+ };
19517
+
19135
19518
  // src/knowledge-config.ts
19136
19519
  var HIWORK_KNOWLEDGE_SERVICE = "hiworkKnowledge";
19137
19520
  var ME_KNOWLEDGE_CONFIG_PATH = "/api/me/knowledge-config";
@@ -19158,22 +19541,22 @@ function messageFor(reason) {
19158
19541
  function failure12(reason, message) {
19159
19542
  return { ok: false, reason, message: message ?? messageFor(reason) };
19160
19543
  }
19161
- function textOrUndefined6(value) {
19544
+ function textOrUndefined7(value) {
19162
19545
  if (typeof value !== "string") return void 0;
19163
19546
  const text = value.trim();
19164
19547
  return text === "" ? void 0 : text;
19165
19548
  }
19166
19549
  function projectKnowledgeConfig(body) {
19167
- const baseUrl = textOrUndefined6(body["base_url"] ?? body["baseUrl"]);
19168
- const apiKey = textOrUndefined6(body["api_key"] ?? body["apiKey"]);
19550
+ const baseUrl = textOrUndefined7(body["base_url"] ?? body["baseUrl"]);
19551
+ const apiKey = textOrUndefined7(body["api_key"] ?? body["apiKey"]);
19169
19552
  if (baseUrl === void 0 || apiKey === void 0) return void 0;
19170
19553
  return {
19171
19554
  ok: true,
19172
19555
  baseUrl,
19173
19556
  apiKey,
19174
- agentId: textOrUndefined6(body["agent_id"] ?? body["agentId"]) ?? "",
19175
- chatModelId: textOrUndefined6(body["chat_model_id"] ?? body["chatModelId"]) ?? "",
19176
- tenantId: textOrUndefined6(body["tenant_id"] ?? body["tenantId"]) ?? "",
19557
+ agentId: textOrUndefined7(body["agent_id"] ?? body["agentId"]) ?? "",
19558
+ chatModelId: textOrUndefined7(body["chat_model_id"] ?? body["chatModelId"]) ?? "",
19559
+ tenantId: textOrUndefined7(body["tenant_id"] ?? body["tenantId"]) ?? "",
19177
19560
  knowledgeBases: projectKnowledgeBases(body["knowledge_bases"] ?? body["knowledgeBases"])
19178
19561
  };
19179
19562
  }
@@ -19184,10 +19567,10 @@ function projectKnowledgeBases(value) {
19184
19567
  for (const item of value) {
19185
19568
  if (item === null || typeof item !== "object") continue;
19186
19569
  const record = item;
19187
- const id = textOrUndefined6(record["id"]);
19570
+ const id = textOrUndefined7(record["id"]);
19188
19571
  if (id === void 0 || seen.has(id)) continue;
19189
19572
  seen.add(id);
19190
- bases.push({ id, name: textOrUndefined6(record["name"]) ?? "" });
19573
+ bases.push({ id, name: textOrUndefined7(record["name"]) ?? "" });
19191
19574
  }
19192
19575
  return bases.length === 0 ? EMPTY_KNOWLEDGE_BASES : bases;
19193
19576
  }
@@ -19220,8 +19603,9 @@ var KnowledgeConfigProvider = class {
19220
19603
  constructor(deps) {
19221
19604
  this.deps = deps;
19222
19605
  }
19223
- cached;
19224
- tokenCached;
19606
+ configCache = new TtlCache({ ttlMs: KNOWLEDGE_CONFIG_TTL_MS });
19607
+ // 令牌缓存比配置缓存短 30s(KNOWLEDGE_TOKEN_CACHE_TTL_MS 已扣掉余量,见其注释)。
19608
+ tokenCache = new TtlCache({ ttlMs: KNOWLEDGE_TOKEN_CACHE_TTL_MS });
19225
19609
  async token(opts) {
19226
19610
  const force = opts?.force === true;
19227
19611
  let sessionCookie;
@@ -19232,9 +19616,9 @@ var KnowledgeConfigProvider = class {
19232
19616
  return failure12("error");
19233
19617
  }
19234
19618
  if (!sessionCookie) return failure12("unauthenticated");
19235
- const hit = this.tokenCached;
19236
- if (!force && hit?.sessionCookie === sessionCookie && this.now() - hit.at < KNOWLEDGE_TOKEN_CACHE_TTL_MS) {
19237
- return hit.view;
19619
+ if (!force) {
19620
+ const hit = this.tokenCache.get(sessionCookie, this.now());
19621
+ if (hit !== void 0) return hit;
19238
19622
  }
19239
19623
  const gateway = this.deps.gateway();
19240
19624
  if (!gateway) return failure12("unreachable");
@@ -19243,8 +19627,8 @@ var KnowledgeConfigProvider = class {
19243
19627
  const raw = await gateway.getMyKnowledgeToken(sessionCookie);
19244
19628
  const envelope = raw;
19245
19629
  const body = envelope?.["data"] && typeof envelope["data"] === "object" ? envelope["data"] : envelope;
19246
- const token = textOrUndefined6(body?.["token"] ?? body?.["knowledge_token"]);
19247
- const audience = textOrUndefined6(body?.["audience"] ?? body?.["aud"]);
19630
+ const token = textOrUndefined7(body?.["token"] ?? body?.["knowledge_token"]);
19631
+ const audience = textOrUndefined7(body?.["audience"] ?? body?.["aud"]);
19248
19632
  const ids = body?.["knowledge_base_ids"] ?? body?.["knowledgeBaseIds"];
19249
19633
  if (!token || audience !== "hiwork-knowledge" || !Array.isArray(ids) || !ids.every((id) => typeof id === "string")) {
19250
19634
  this.deps.log?.("hiwork-core: \u7F51\u5173\u8FD4\u56DE\u7684 knowledge-token \u5F62\u72B6\u65E0\u6548");
@@ -19255,11 +19639,11 @@ var KnowledgeConfigProvider = class {
19255
19639
  const knowledgeBases = Array.isArray(rawBases) ? rawBases.flatMap((item) => {
19256
19640
  if (item === null || typeof item !== "object") return [];
19257
19641
  const obj = item;
19258
- const id = textOrUndefined6(obj["id"]);
19259
- return id ? [{ id, name: textOrUndefined6(obj["name"]) ?? "" }] : [];
19642
+ const id = textOrUndefined7(obj["id"]);
19643
+ return id ? [{ id, name: textOrUndefined7(obj["name"]) ?? "" }] : [];
19260
19644
  }).filter((item) => knowledgeBaseIds.includes(item.id)) : [];
19261
19645
  const view = { ok: true, token, audience, knowledgeBaseIds, knowledgeBases };
19262
- this.tokenCached = { sessionCookie, view, at: this.now() };
19646
+ this.tokenCache.put(sessionCookie, view, this.now());
19263
19647
  return view;
19264
19648
  } catch (error) {
19265
19649
  const mapped = reasonOfErrorCode(error);
@@ -19279,10 +19663,8 @@ var KnowledgeConfigProvider = class {
19279
19663
  }
19280
19664
  if (sessionCookie === void 0 || sessionCookie === "") return failure12("unauthenticated");
19281
19665
  if (!force) {
19282
- const hit = this.cached;
19283
- if (hit !== void 0 && hit.sessionCookie === sessionCookie && this.now() - hit.at < KNOWLEDGE_CONFIG_TTL_MS) {
19284
- return hit.view;
19285
- }
19666
+ const hit = this.configCache.get(sessionCookie, this.now());
19667
+ if (hit !== void 0) return hit;
19286
19668
  }
19287
19669
  const gateway = this.deps.gateway();
19288
19670
  if (gateway === void 0) return failure12("unreachable");
@@ -19305,7 +19687,7 @@ var KnowledgeConfigProvider = class {
19305
19687
  this.deps.log?.("hiwork-core: \u7F51\u5173\u8FD4\u56DE\u7684 knowledge-config \u4E0D\u5B8C\u6574\uFF08\u7F3A base_url / api_key \u4E4B\u4E00\uFF09");
19306
19688
  return failure12("error");
19307
19689
  }
19308
- this.cached = { sessionCookie, view, at: this.now() };
19690
+ this.configCache.put(sessionCookie, view, this.now());
19309
19691
  return view;
19310
19692
  }
19311
19693
  now() {
@@ -19374,7 +19756,7 @@ function messageFor2(reason) {
19374
19756
  function failure13(reason, message) {
19375
19757
  return { ok: false, reason, message: message ?? messageFor2(reason) };
19376
19758
  }
19377
- function textOrUndefined7(value) {
19759
+ function textOrUndefined8(value) {
19378
19760
  if (typeof value !== "string") return void 0;
19379
19761
  const text = value.trim();
19380
19762
  return text === "" ? void 0 : text;
@@ -19383,15 +19765,15 @@ function teamIdsOf(value) {
19383
19765
  if (!Array.isArray(value)) return [];
19384
19766
  const ids = [];
19385
19767
  for (const item of value) {
19386
- const text = textOrUndefined7(item);
19768
+ const text = textOrUndefined8(item);
19387
19769
  if (text !== void 0) ids.push(text);
19388
19770
  }
19389
19771
  return ids;
19390
19772
  }
19391
19773
  function projectMemoryConfig(body) {
19392
- const panelUrl = textOrUndefined7(body["panel_url"] ?? body["panelUrl"]);
19393
- const serviceId = textOrUndefined7(body["service_id"] ?? body["serviceId"]);
19394
- const apiKey = textOrUndefined7(body["api_key"] ?? body["apiKey"] ?? body["userKey"]);
19774
+ const panelUrl = textOrUndefined8(body["panel_url"] ?? body["panelUrl"]);
19775
+ const serviceId = textOrUndefined8(body["service_id"] ?? body["serviceId"]);
19776
+ const apiKey = textOrUndefined8(body["api_key"] ?? body["apiKey"] ?? body["userKey"]);
19395
19777
  if (panelUrl === void 0 || serviceId === void 0 || apiKey === void 0) return void 0;
19396
19778
  return {
19397
19779
  ok: true,
@@ -19429,7 +19811,7 @@ var MemoryConfigProvider = class {
19429
19811
  constructor(deps) {
19430
19812
  this.deps = deps;
19431
19813
  }
19432
- cached;
19814
+ cache = new TtlCache({ ttlMs: MEMORY_CONFIG_TTL_MS });
19433
19815
  async config(opts) {
19434
19816
  const force = opts?.force === true;
19435
19817
  let sessionCookie;
@@ -19441,10 +19823,8 @@ var MemoryConfigProvider = class {
19441
19823
  }
19442
19824
  if (sessionCookie === void 0 || sessionCookie === "") return failure13("unauthenticated");
19443
19825
  if (!force) {
19444
- const hit = this.cached;
19445
- if (hit !== void 0 && hit.sessionCookie === sessionCookie && this.now() - hit.at < MEMORY_CONFIG_TTL_MS) {
19446
- return hit.view;
19447
- }
19826
+ const hit = this.cache.get(sessionCookie, this.now());
19827
+ if (hit !== void 0) return hit;
19448
19828
  }
19449
19829
  const gateway = this.deps.gateway();
19450
19830
  if (gateway === void 0) return failure13("unreachable", MESSAGE_NO_GATEWAY);
@@ -19467,7 +19847,7 @@ var MemoryConfigProvider = class {
19467
19847
  this.deps.log?.("hiwork-core: \u7F51\u5173\u8FD4\u56DE\u7684 memory-config \u4E0D\u5B8C\u6574\uFF08\u7F3A panel_url / service_id / api_key \u4E4B\u4E00\uFF09");
19468
19848
  return failure13("error");
19469
19849
  }
19470
- this.cached = { sessionCookie, view, at: this.now() };
19850
+ this.cache.put(sessionCookie, view, this.now());
19471
19851
  return view;
19472
19852
  }
19473
19853
  now() {
@@ -19658,7 +20038,7 @@ function provideOfficeRelay(ctx, deps) {
19658
20038
  };
19659
20039
  }
19660
20040
 
19661
- // node_modules/.pnpm/@deepseek-ai+dsh-web@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-llm@0.1.7-rc.2_@deepseek-ai+cordis@4.0.4_/node_modules/@deepseek-ai/dsh-web/lib/index.js
20041
+ // node_modules/.pnpm/@deepseek-ai+dsh-web@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4_@deepseek-ai+dsh-llm@0.2.0-rc.1_@deepseek-ai+cordis@4.0.4_/node_modules/@deepseek-ai/dsh-web/lib/index.js
19662
20042
  var WebError = class extends HarnessError {
19663
20043
  };
19664
20044
  var WebRuntime = class extends Service {
@@ -19836,7 +20216,7 @@ function httpOutcomeToError(status, code) {
19836
20216
  if (status === 502 || code === "SEARCH_UPSTREAM_ERROR") return new WebError("\u641C\u7D22\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", HIWORK_SEARCH_UPSTREAM);
19837
20217
  return new WebError("\u8054\u7F51\u641C\u7D22\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", "WEB_PROVIDER_ERROR");
19838
20218
  }
19839
- function textOrUndefined8(value) {
20219
+ function textOrUndefined9(value) {
19840
20220
  if (typeof value !== "string") return void 0;
19841
20221
  const text = value.trim();
19842
20222
  return text === "" ? void 0 : text;
@@ -19848,11 +20228,11 @@ function projectWebSearch(body) {
19848
20228
  for (const item of raw) {
19849
20229
  if (item === null || typeof item !== "object") continue;
19850
20230
  const record = item;
19851
- const url = textOrUndefined8(record["url"]);
20231
+ const url = textOrUndefined9(record["url"]);
19852
20232
  if (url === void 0) continue;
19853
- const title = textOrUndefined8(record["title"]);
19854
- const snippet = textOrUndefined8(record["snippet"]) ?? textOrUndefined8(record["content"]);
19855
- const publishedAt = textOrUndefined8(record["published_at"]);
20233
+ const title = textOrUndefined9(record["title"]);
20234
+ const snippet = textOrUndefined9(record["snippet"]) ?? textOrUndefined9(record["content"]);
20235
+ const publishedAt = textOrUndefined9(record["published_at"]);
19856
20236
  sources.push({
19857
20237
  url,
19858
20238
  ...title !== void 0 ? { title } : {},
@@ -19916,7 +20296,7 @@ function registerManagedSearchProvider(web, deps) {
19916
20296
 
19917
20297
  // src/index.ts
19918
20298
  var name = "hiwork-core";
19919
- var inject = ["settings", "credentials", "workspaceRegistry", "connection", "web", "agents"];
20299
+ var inject = ["settings", "credentials", "workspaceRegistry", "connection", "web", "agents", "systemPrompt"];
19920
20300
  var NS = "ai-router";
19921
20301
  var PI_AI_NS = "llm-pi-ai";
19922
20302
  var MenuConfig = Schema.object({
@@ -19928,9 +20308,12 @@ var MenuConfig = Schema.object({
19928
20308
  var lastErrorSchema = Schema.object({ code: Schema.string().required(), message: Schema.string().required() }).default(void 0);
19929
20309
  var Config = Schema.object({
19930
20310
  baseURL: Schema.string(),
20311
+ // 非 volatile:与 baseURL 同类的组合层固定配置(不进用户表单),出问题经 bundle patch 关闭。
20312
+ identityContext: Schema.boolean().default(true),
19931
20313
  keyId: Schema.string().volatile(),
19932
20314
  username: Schema.string().volatile(),
19933
20315
  displayName: Schema.string().volatile(),
20316
+ sessionExpiresAt: Schema.string().volatile(),
19934
20317
  lastLogin: Schema.object({ at: Schema.string().required(), modelCount: Schema.number().required(), gatewayName: Schema.string().required() }).default(void 0).volatile(),
19935
20318
  lastError: lastErrorSchema.volatile(),
19936
20319
  pendingLogin: Schema.object({ username: Schema.string().required(), passwordRef: Schema.string().required() }).default(void 0).volatile(),
@@ -20285,22 +20668,60 @@ function apply(ctx, config) {
20285
20668
  }
20286
20669
  };
20287
20670
  let inFlight;
20288
- let policyRequested = false;
20289
- const requestClientPolicy = () => {
20290
- if (policyRequested) return;
20291
- const baseURL = current().baseURL;
20292
- if (baseURL === void 0 || baseURL === "") return;
20293
- policyRequested = true;
20294
- void projectLoginMode({
20295
- baseURL,
20296
- credentials: ports.credentials,
20297
- state: ports.state,
20298
- log: (message) => {
20299
- ctx.logger.warn(message);
20300
- }
20301
- }).catch(() => {
20302
- });
20671
+ const sessionSupervisor = new SessionSupervisor({
20672
+ getSession: async () => (await ports.credentials.resolve(SESSION_REF))?.value,
20673
+ getSessionExpiresAt: () => current().sessionExpiresAt,
20674
+ getBaseURL: () => current().baseURL,
20675
+ ping: async (sessionCookie) => {
20676
+ const baseURL = current().baseURL;
20677
+ if (baseURL === void 0 || baseURL === "") throw new Error("baseURL \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u4F1A\u8BDD\u5FC3\u8DF3");
20678
+ const sessions = await gatewayFor(baseURL).listSessions(sessionCookie);
20679
+ const expiresAt = sessions.find((item) => item.current === true && item.expires_at !== void 0)?.expires_at;
20680
+ return expiresAt === void 0 ? {} : { expiresAt };
20681
+ },
20682
+ patchSessionExpiresAt: (iso) => ports.state.patch({ set: { sessionExpiresAt: iso } }),
20683
+ requestRecovery: () => ports.state.patch({ set: { refreshRequest: { at: (/* @__PURE__ */ new Date()).toISOString() } } }),
20684
+ log: (message) => {
20685
+ ctx.logger.warn(message);
20686
+ }
20687
+ });
20688
+ const sessionActivitySink2 = (event) => {
20689
+ if (event.kind === "success") sessionSupervisor.noteAuthedSuccess(event.expiresAt);
20690
+ else sessionSupervisor.reportAuthed401("gateway");
20303
20691
  };
20692
+ ctx.effect(() => {
20693
+ setSessionActivitySink(sessionActivitySink2);
20694
+ sessionSupervisor.start();
20695
+ return () => {
20696
+ sessionSupervisor.stop();
20697
+ clearSessionActivitySink(sessionActivitySink2);
20698
+ };
20699
+ }, "hiwork-core: session supervisor");
20700
+ const policyPoller = createClientPolicyPoller({
20701
+ attempt: async () => {
20702
+ const baseURL = current().baseURL;
20703
+ if (baseURL === void 0 || baseURL === "") return false;
20704
+ let fetched = false;
20705
+ await projectLoginMode({
20706
+ baseURL,
20707
+ credentials: ports.credentials,
20708
+ state: ports.state,
20709
+ fetchPolicy: async (input) => {
20710
+ const policy = await fetchClientPolicy(input);
20711
+ fetched = policy !== void 0;
20712
+ return policy;
20713
+ },
20714
+ log: (message) => {
20715
+ ctx.logger.warn(message);
20716
+ }
20717
+ });
20718
+ return fetched;
20719
+ },
20720
+ log: (message) => {
20721
+ ctx.logger.warn(message);
20722
+ }
20723
+ });
20724
+ let notifySessionProvisioned;
20304
20725
  const dispatchSectionChange = () => {
20305
20726
  const value = current();
20306
20727
  const requested = value.pendingLogin ?? value.browserLoginRequest ?? value.refreshRequest ?? value.logoutRequest;
@@ -20317,6 +20738,7 @@ function apply(ctx, config) {
20317
20738
  ctx.logger.warn(message);
20318
20739
  }
20319
20740
  });
20741
+ if (value.pendingLogin !== void 0 || value.refreshRequest !== void 0) notifySessionProvisioned?.();
20320
20742
  } catch (error) {
20321
20743
  ctx.logger.error("hiwork-core: ai-router section change failed", error);
20322
20744
  if (isHmrNestedWriteError(error)) {
@@ -20339,7 +20761,7 @@ function apply(ctx, config) {
20339
20761
  };
20340
20762
  ctx.effect(() => {
20341
20763
  const consume = () => {
20342
- requestClientPolicy();
20764
+ policyPoller.trigger();
20343
20765
  dispatchSectionChange();
20344
20766
  };
20345
20767
  consume();
@@ -20347,6 +20769,7 @@ function apply(ctx, config) {
20347
20769
  timer.unref?.();
20348
20770
  return () => {
20349
20771
  clearInterval(timer);
20772
+ policyPoller.dispose();
20350
20773
  };
20351
20774
  }, "hiwork-core: login request poll");
20352
20775
  void ctx.root.loader.await().then(() => importLegacyMenuSection({
@@ -20356,7 +20779,7 @@ function apply(ctx, config) {
20356
20779
  })).catch((error) => {
20357
20780
  ctx.logger.warn(`hiwork-core: \u65E7\u7248\u83DC\u5355\u504F\u597D\u8FC1\u79FB\u672A\u6267\u884C\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
20358
20781
  });
20359
- requestClientPolicy();
20782
+ policyPoller.trigger();
20360
20783
  void (async () => {
20361
20784
  for (const delay2 of STALE_BROWSER_LOGIN_RECONCILE_DELAYS_MS) {
20362
20785
  if (delay2 > 0) await new Promise((resolve3) => {
@@ -20561,6 +20984,27 @@ function apply(ctx, config) {
20561
20984
  clearInterval(timer);
20562
20985
  };
20563
20986
  }, "hiwork-core: workbench warmup");
20987
+ notifySessionProvisioned = buildSessionFanout({
20988
+ onPolicyRefresh: () => policyPoller.trigger(),
20989
+ onManagedMcpRefresh: () => {
20990
+ void managedMcp.load().catch((error) => {
20991
+ ctx.logger.warn(`hiwork-core: \u4F1A\u8BDD\u4F9B\u7ED9\u540E\u5237\u65B0\u53D7\u7BA1 MCP \u6E05\u5355\u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
20992
+ });
20993
+ },
20994
+ onManagedSkillsRefresh: () => {
20995
+ void managedSkills.load().catch((error) => {
20996
+ ctx.logger.warn(`hiwork-core: \u4F1A\u8BDD\u4F9B\u7ED9\u540E\u5237\u65B0\u53D7\u7BA1\u6280\u80FD\u6E05\u5355\u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
20997
+ });
20998
+ },
20999
+ onWorkbenchRefresh: () => {
21000
+ void workbench.load().catch((error) => {
21001
+ ctx.logger.warn(`hiwork-core: \u4F1A\u8BDD\u4F9B\u7ED9\u540E\u5237\u65B0\u5DE5\u4F5C\u53F0\u6E05\u5355\u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
21002
+ });
21003
+ },
21004
+ log: (message) => {
21005
+ ctx.logger.warn(message);
21006
+ }
21007
+ });
20564
21008
  const pmsTodoDeps = {
20565
21009
  resolveSession: async () => (await ports.credentials.resolve(SESSION_REF))?.value,
20566
21010
  gateway: () => {
@@ -20599,6 +21043,13 @@ function apply(ctx, config) {
20599
21043
  ctx.logger.warn(message);
20600
21044
  }
20601
21045
  }), "hiwork-core: identity service");
21046
+ ctx.effect(() => registerIdentityContext(ctx.systemPrompt, {
21047
+ current: () => current(),
21048
+ enabled: () => current().identityContext !== false,
21049
+ log: (message) => {
21050
+ ctx.logger.warn(message);
21051
+ }
21052
+ }), "hiwork-core: identity runtime context");
20602
21053
  ctx.effect(() => provideMemoryConfig(ctx, {
20603
21054
  resolveSession: async () => (await ports.credentials.resolve(SESSION_REF))?.value,
20604
21055
  gateway: () => {
@@ -20664,9 +21115,9 @@ function apply(ctx, config) {
20664
21115
  }), "hiwork-core: plugin updates rpc");
20665
21116
  ctx.effect(() => registerIpcMessageHandler({
20666
21117
  source: process,
20667
- complete: (input) => {
21118
+ complete: async (input) => {
20668
21119
  const value = current();
20669
- return completeBrowserLogin(input, {
21120
+ const result = await completeBrowserLogin(input, {
20670
21121
  ...ports,
20671
21122
  value,
20672
21123
  gateway: gatewayFor(value.baseURL ?? ""),
@@ -20674,6 +21125,8 @@ function apply(ctx, config) {
20674
21125
  ctx.logger.warn(message);
20675
21126
  }
20676
21127
  });
21128
+ if (result === null) notifySessionProvisioned?.();
21129
+ return result;
20677
21130
  },
20678
21131
  setDevice: (info) => {
20679
21132
  setDeviceInfo(info);