@shgroup/dsh-serenity-hooks 1.26.7 → 1.26.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.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.26.7",
3
+ "version": "1.26.9",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/handyman/session_rebuild/skiff_admin + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)+ Skiff 认知子集角色(实验性)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/index.js CHANGED
@@ -11479,6 +11479,7 @@ x.lex;
11479
11479
  */
11480
11480
  var skiff_debug_exports = /* @__PURE__ */ __exportAll({
11481
11481
  discoverCccs: () => discoverCccs,
11482
+ jscSafeJsonText: () => jscSafeJsonText,
11482
11483
  renderSkiffMarkdown: () => renderSkiffMarkdown,
11483
11484
  skiffDebugPage: () => skiffDebugPage,
11484
11485
  startSkiffDebugServer: () => startSkiffDebugServer,
@@ -11546,7 +11547,7 @@ async function discoverCccs(ctx, defaultRoot) {
11546
11547
  }
11547
11548
  /** 问答页 HTML:CCC 切换器 + 角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ WebUI 链接 */
11548
11549
  function skiffDebugPage(cccs, defaultRoot, webPort) {
11549
- const data = JSON.stringify(cccs).replace(/</g, "\\u003c");
11550
+ const data = jscSafeJsonText(JSON.stringify(cccs).replace(/</g, "\\u003c"));
11550
11551
  return `<!DOCTYPE html>
11551
11552
  <html lang="zh">
11552
11553
  <head>
@@ -11724,8 +11725,103 @@ function escapeHtml(s) {
11724
11725
  })[c] ?? c);
11725
11726
  }
11726
11727
  /**
11728
+ * JSC (Safari/iOS) JSON.parse 快速路径正则兼容化(v1.26.9,S142 调研定稿)。
11729
+ *
11730
+ * 背景:WebKit bug 200190「JavaScriptCore's Regex can't match the content」——JSC 的
11731
+ * JSON.parse 用**内部正则**预校验字符串;内容含**原始** `\u2028`(行分隔符)/`\u2029`
11732
+ * (段分隔符)时正则无法匹配 → 对**合法 JSON** 也抛
11733
+ * `SyntaxError: The string did not match the expected pattern`(sentry-javascript #2487 同源)。
11734
+ * JSON.stringify **不转义** `\u2028`/`\u2029`/`\uFEFF`(它们都是合法 JSON 字符串字符)→
11735
+ * 在 JSON **文本层**把它们替换为 `\uXXXX` 转义序列:JSON.parse 后语义完全一致(还原原字符),
11736
+ * 且 JSC 正则看到的是常规 ASCII 转义(与 JSON.stringify 对控制字符的输出同形态,安全)。
11737
+ *
11738
+ * 3100 问答页客户端 `await res.json()`(acp-http.ts)与页面内嵌 JSON(skiff-debug/acp-http)
11739
+ * 均需此兼容层——iOS Safari 用户实测"复杂回答"触发。
11740
+ *
11741
+ * @param jsonText JSON.stringify 的输出文本;原地等价替换,返回 JSC 安全文本
11742
+ */
11743
+ function jscSafeJsonText(jsonText) {
11744
+ return jsonText.replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/\uFEFF/g, "\\uFEFF");
11745
+ }
11746
+ /**
11747
+ * 提取 `<think>…</think>` 块(v1.26.8:**状态机扫描,弃正则**——用户批评"老用正则不是个办法")。
11748
+ *
11749
+ * 逐字符扫描识别开/闭标签(大小写不敏感;`<think ...>` 允许属性变体;`</think >` 允许尾随空格),
11750
+ * 不依赖正则回溯,天然处理:
11751
+ * - **嵌套 `<think>`**(内层按内容处理,不递归——DeepSeek think 不会嵌套)
11752
+ * - **未闭合 `<think>`**(优雅截断:剩余全部作为 think 内容,不泄漏标记)
11753
+ * - **占位符冲突**(用 \u0001T<idx>\u0001——ASCII 控制字符,正文几乎不可能出现)
11754
+ *
11755
+ * @returns body(占位符替换后的正文)+ thinks(提取的 think 内容数组,保序)
11756
+ */
11757
+ function extractThinkBlocks(raw) {
11758
+ const thinks = [];
11759
+ const parts = [];
11760
+ let i = 0;
11761
+ while (i < raw.length) {
11762
+ const openTag = matchOpenThink(raw, i);
11763
+ if (!openTag) break;
11764
+ parts.push(raw.slice(i, openTag.tagStart));
11765
+ const closeTag = matchCloseThink(raw, openTag.contentStart);
11766
+ if (closeTag < 0) {
11767
+ const inner = raw.slice(openTag.contentStart);
11768
+ thinks.push(inner.trim());
11769
+ parts.push(`\u0001T${thinks.length - 1}\u0001`);
11770
+ i = raw.length;
11771
+ break;
11772
+ }
11773
+ const inner = raw.slice(openTag.contentStart, closeTag);
11774
+ thinks.push(inner.trim());
11775
+ parts.push(`\u0001T${thinks.length - 1}\u0001`);
11776
+ let j = closeTag + 7;
11777
+ while (j < raw.length && (raw[j] === " " || raw[j] === " " || raw[j] === "\n" || raw[j] === "\r")) j++;
11778
+ i = raw[j] === ">" ? j + 1 : closeTag + 8;
11779
+ }
11780
+ if (i < raw.length) parts.push(raw.slice(i));
11781
+ return {
11782
+ body: parts.join(""),
11783
+ thinks
11784
+ };
11785
+ }
11786
+ /** 从 from 起找 `<think` 开标签(大小写不敏感,允许属性);返回标签起点与内容起点,或 null */
11787
+ function matchOpenThink(s, from) {
11788
+ for (let i = from; i <= s.length - 7; i++) {
11789
+ if (s[i] !== "<" || s[i + 1] !== "t" && s[i + 1] !== "T") continue;
11790
+ if (s.slice(i + 1, i + 6).toLowerCase() !== "think") continue;
11791
+ const after = s[i + 6];
11792
+ if (after === ">") return {
11793
+ tagStart: i,
11794
+ contentStart: i + 7
11795
+ };
11796
+ if (after === " " || after === " " || after === "\n" || after === "\r") {
11797
+ const gt = s.indexOf(">", i + 7);
11798
+ if (gt < 0) return null;
11799
+ return {
11800
+ tagStart: i,
11801
+ contentStart: gt + 1
11802
+ };
11803
+ }
11804
+ }
11805
+ return null;
11806
+ }
11807
+ /** 从 contentStart 起找 `</think>` 闭合标签(大小写不敏感,允许 `</think >` 尾随空格);返回闭合标签起点或 -1 */
11808
+ function matchCloseThink(s, contentStart) {
11809
+ for (let i = contentStart; i <= s.length - 8; i++) {
11810
+ if (s[i] !== "<" || s[i + 1] !== "/") continue;
11811
+ if (s.slice(i + 2, i + 7).toLowerCase() !== "think") continue;
11812
+ const after = s[i + 7];
11813
+ if (after === ">") return i;
11814
+ if (after === " " || after === " " || after === "\n" || after === "\r") {
11815
+ let j = i + 8;
11816
+ while (j < s.length && (s[j] === " " || s[j] === " " || s[j] === "\n" || s[j] === "\r")) j++;
11817
+ if (s[j] === ">") return i;
11818
+ }
11819
+ }
11820
+ return -1;
11821
+ }
11822
+ /**
11727
11823
  * Markdown 渲染(v1.25.9,正经库 marked 服务端渲染——替代手写正则渲染器,S142 用户要求):
11728
- * ① 提取 `<think>…</think>` 块(占位符 \u0000T<idx>\u0000
11824
+ * ① 提取 `<think>…</think>` 块(v1.26.8 状态机扫描,占位符 \u0001T<idx>\u0001
11729
11825
  * ② 正文与 think 内容**先 escapeHtml 再 marked.parse**(GFM + breaks)——markdown 语法不受
11730
11826
  * 转义影响,原始 HTML 注入被消除(安全);代码块内 `<` 显示为实体(可接受)
11731
11827
  * ③ think 占位符:默认还原为 `<details class="think">` 折叠卡(🧠 思考过程,默认收起);
@@ -11733,17 +11829,12 @@ function escapeHtml(s) {
11733
11829
  * @param hideThink 为 true 时 `<think>` 内容完全不渲染(public 问答页体验)
11734
11830
  */
11735
11831
  function renderSkiffMarkdown(raw, hideThink = false) {
11736
- const thinks = [];
11737
- const body = raw.replace(/<think>([\s\S]*?)<\/think>/gi, (_m, inner) => {
11738
- const idx = thinks.length;
11739
- thinks.push(String(inner ?? "").trim());
11740
- return `\u0000T${idx}\u0000`;
11741
- });
11832
+ const { body, thinks } = extractThinkBlocks(raw);
11742
11833
  const parsed = f.parse(escapeHtml(body), {
11743
11834
  breaks: true,
11744
11835
  gfm: true
11745
11836
  });
11746
- return (typeof parsed === "string" ? parsed : "").replace(/\u0000T(\d+)\u0000/g, (_m, idx) => {
11837
+ return (typeof parsed === "string" ? parsed : "").replace(/\u0001T(\d+)\u0001/g, (_m, idx) => {
11747
11838
  if (hideThink) return "";
11748
11839
  const inner = thinks[Number(idx)] ?? "";
11749
11840
  const innerHtml = inner === "" ? "" : f.parse(escapeHtml(inner), {
@@ -12060,7 +12151,7 @@ function readBody(req) {
12060
12151
  });
12061
12152
  }
12062
12153
  function sendJson(res, status, payload) {
12063
- const body = JSON.stringify(payload);
12154
+ const body = jscSafeJsonText(JSON.stringify(payload));
12064
12155
  res.writeHead(status, {
12065
12156
  "Content-Type": "application/json; charset=utf-8",
12066
12157
  "Cache-Control": "no-store"
@@ -12459,11 +12550,11 @@ gateKey.focus()
12459
12550
  }
12460
12551
  /** 单容器问答页(v1.26.2→v1.26.6):URL 锁定容器;聊天 UI——消息流 + 连续对话 + key 从列表页记忆(v1.26.6 用户拍板:对话页不再填 key) */
12461
12552
  function publicAskContainerPage(ccc) {
12462
- const data = JSON.stringify({
12553
+ const data = jscSafeJsonText(JSON.stringify({
12463
12554
  name: ccc.name,
12464
12555
  root: ccc.root,
12465
12556
  roles: ccc.roles
12466
- }).replace(/</g, "\\u003c");
12557
+ }).replace(/</g, "\\u003c"));
12467
12558
  return `<!DOCTYPE html>
12468
12559
  <html lang="zh">
12469
12560
  <head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.26.7",
3
+ "version": "1.26.9",
4
4
  "description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
5
5
  "license": "MIT",
6
6
  "repository": {