dsh-tiddlywiki 0.16.14 → 0.16.15

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/README.md CHANGED
@@ -35,6 +35,7 @@
35
35
 
36
36
  > 最近几个主要版本的一句话更新记录(完整变更见 git log / Releases)。
37
37
 
38
+ - **v0.16.15**(2026-09-07):**修:agent 写笔记不指定内容类型 → 显示解析错乱;现在自动默认 `text/markdown`**。`tiddlywiki_put` / `tiddlywiki_batch_put` 组装 tiddler 时从不设 `type`,TW 把无 type 条目按 wikitext(`text/vnd.tiddlywiki`)解析,agent 写的 Markdown 正文(`##`、`**`、列表…)全部原样显示。修复:两工具在写入前对**未指定类型**的条目自动补 `type: text/markdown`(`$:/` 系统条目除外——config/插件内部件不能被强标 markdown;显式传 `fields.type` 仍然生效,可写 `text/vnd.tiddlywiki`);工具输出新增「类型: …(未指定,已默认 markdown)」让 agent 可见。**顺带修掉一个隐患**:工具描述与 README 里的 `fields` 示例原来是 `{"type":"meeting","date":…}`——`type` 是 TW 的**内容类型保留字段**,agent 照抄示例会把内容类型写成 `meeting`、渲染直接坏掉——示例改为 `{"date":…}` 并在描述/注入提示词里明确警告「业务分类放 tags,不要放 fields.type」。注入提示词新增「**内容类型约定**」段(默认 markdown、如何写 wikitext、fields.type 警告)。host 改动:`npm run build:host` + **重启 dsh web** 后生效(提示词/工具都是启动时装配)。
38
39
  - **v0.16.14**(2026-09-07):**修:「知识库」Tab 仍显示「佚失条目」——根因是浏览器端 TW 根本看不到 `$:/temp`**。v0.16.13 的过滤/自愈治标不治本:TW 浏览器端同步机制天生排除 volatile 条目——服务端 recipe 列表默认 `[all[tiddlers]!is[system]]`(`get-tiddlers-json.js`),tiddlyweb adaptor 的请求过滤器又显式 `-[prefix[$:/temp/]]`(`tiddlywebadaptor.js getSkinnyTiddlers`),lazyLoad 只补「已知 skinny」不拉「完全缺失」——所以 iframe 里的 TW 永远拿不到 `$:/temp/dsh/session-summary/<会话ID>`,`#<标题>` hash 直达必然渲染「佚失条目」,客户端再怎么自动重新生成也无济于事(服务端有、浏览器没有)。修复(纯客户端,host 无变化):iframe **不带 hash** 挂载(注入落地前盖加载浮层,不闪现默认页),就绪后由客户端**注入**——同源 `/get` 读回汇总字段 → `iframe.contentWindow.$tw.wiki.addTiddler(...)` 写进 iframe 的 TW store → 设 `contentWindow.location.hash` 走 TW 原生 hash 导航(与中央面板 `openTiddler` 同一机制,落地重试绕开 boot 时序竞争);自愈升级:30s 探测在「服务端丢失(TW 重启)→ 重新生成」之外,新增「服务端仍在而 iframe store 丢失(iframe 自身重载)→ 重新注入并导航」,不打扰用户在 iframe 内的浏览。汇总条目仍只存在于服务端内存 + iframe 内存——**不落盘、不进 git、重启即消失(设计不变)**。刷新页面即生效。
39
40
  - **v0.16.13**(2026-09-07):**修:会话「知识库」Tab 出现「佚失条目」**。① 汇总内容里的死链/垃圾条目:会话日志常混入模型的示例/残缺写法(`#…`、`#标题`、带反引号的截断链接),旧代码会把它们原样收进汇总并写成 `[[…`]]` 这种坏 wikitext,TW 渲染成「佚失条目」——现在采集时统一过滤(控制字符 / `]` `|` 反引号 `#` / `…` 占位 / 超长标题一律丢弃)。② 汇总条目本身是 volatile 的 `$:/temp`,**TW 重启即消失**,若 Tab 一直开着,iframe 会停在 TW 的「佚失条目」页——客户端新增自愈:每 30s 探测一次 `/get`,条目不存在就自动重新生成(连续 3 次仍缺失则交还手动「🔄 刷新」)。刷新页面 + 重启 dsh web 后,重新打开 Tab 即生成干净汇总。
40
41
  - **v0.16.12**(2026-09-07):**深度代码优化(内部重构,行为不变)**。① **配置链修复**:`ui.sendToAgent`/`ui.allArticles` 默认值不再被 cordis 宽松类型吞掉(改为显式逐层合并),`uiLanguage` 不再被配置覆盖层丢弃;设置页「常规配置」补齐「发送给 Agent」开关/token/endpoint 字段(此前只在代码里生效、设置页没有)。② **去重**:新增 `src/host/http.ts`(路由/管理后台共用的 body 读取 + JSON 响应,删掉 3 份重复实现)与 `src/client/endpoints.ts`(`/status` 端点六处合一);`seeds.ts` 引入 `defineSeed` 工厂(每个 seed 的 id/标题/描述三处重复 + try/catch 样板全部收敛,7 项注册表缩到声明式);`tools.ts` 抽出 `requireWiki`(8 处重复守卫)与 `gitStatusBits`(git 状态文案两处合一);事件名常量统一(`NOTE_STATE_EVENT`/`PANEL_RELOAD_EVENT`)。③ **泄漏修复**:快速笔记卡片标签编辑器与「知识库」FAB 的 document 级监听器在卸载时正确移除;TW 面板的 40×150ms 哈希等待链在卸载后停止(不再驱动已移除 iframe);编辑器弹窗拖拽改用 Pointer Capture(鼠标在 iframe 上松手不再残留 window 监听);保存按钮/同步控制器加防连点与卸载后停轮询。④ **清理**:删除约 40 行无引用 CSS 与 2 个死导出(`mountNoteWidget`/`PANEL_VIEW_SELECTOR`);`admin.ts` 去掉无效的动态 import(消除构建警告);会话「知识库」汇总的笔记状态改为 8 路并发查询。已过 `typecheck` / `build` / `selftest`(含 `session/summary` 端到端用例)。
@@ -109,7 +110,7 @@ dsh plugin --profile web add link:/path/to/your/dsh-tiddlywiki
109
110
  | `tiddlywiki_recent` | `limit?`, `since?` | 最近修改的笔记(倒序),开工快速了解近期动态 |
110
111
  | `tiddlywiki_list_tags` | — | 现有非系统 tag 及各自计数(按使用次数降序) |
111
112
  | `tiddlywiki_get` | `title` | 读单个 tiddler 全文 |
112
- | `tiddlywiki_put` | `title`, `text`, `tags?`, `fields?` | 写/覆盖 tiddler;`fields` 可带业务字段(如 `{"type":"meeting","date":"2026-09-02"}`) |
113
+ | `tiddlywiki_put` | `title`, `text`, `tags?`, `fields?` | 写/覆盖 tiddler;**未指定内容类型时自动默认 `text/markdown`**(`$:/` 系统条目除外,v0.16.15);`fields` 可带业务字段(如 `{"date":"2026-09-02"}`)——⚠️ `fields.type` 是 TW 内容类型保留字段,勿写业务分类值(业务分类放 tags) |
113
114
  | `tiddlywiki_batch_put` | `items[]`, `overwrite?` | 批量写入;`overwrite=false` 跳过已存在标题 |
114
115
  | `tiddlywiki_rename` | `oldTitle`, `newTitle`, `updateRefs?` | 重命名 + 尽量更新其他 tiddler 里的 `[[旧]]`/`{{旧}}` 引用 |
115
116
  | `tiddlywiki_delete` | `title` | 删除 tiddler(幂等) |
package/lib/index.js CHANGED
@@ -3718,6 +3718,12 @@ function defineTool(options) {
3718
3718
  * 首页据此把这类笔记单独列在「Agent 区块」并从主标签列表排除。
3719
3719
  */
3720
3720
  const AGENT_WRITTEN_TAG = "agent-written";
3721
+ /**
3722
+ * Agent 写入的默认内容类型。agent 正文按约定是 Markdown,而 TW 对无 type 的
3723
+ * tiddler 按 wikitext(text/vnd.tiddlywiki)解析——`##`/`**` 之类原样显示,显示
3724
+ * 解析全坏(v0.16.15 起 put/batch_put 自动补该默认;wiki 已启用 tiddlywiki/markdown)。
3725
+ */
3726
+ const DEFAULT_NOTE_TYPE = "text/markdown";
3721
3727
  function snippetOf(text, max = 160) {
3722
3728
  const flat = text.replace(/\s+/g, " ").trim();
3723
3729
  return flat.length <= max ? flat : `${flat.slice(0, max)}…`;
@@ -3781,6 +3787,20 @@ function finalTagsForWrite(title, existing, tags) {
3781
3787
  if (tags.includes("agent-written")) return tags;
3782
3788
  return [...tags, AGENT_WRITTEN_TAG];
3783
3789
  }
3790
+ /**
3791
+ * Compute the final content type for a write (mutates the tiddler in place):
3792
+ * - explicit `type` (normally via fields) wins — untouched;
3793
+ * - `$:/` system tiddlers keep TW's own default (config/plugin internals must
3794
+ * not be force-marked as markdown);
3795
+ * - everything else defaults to Markdown (DEFAULT_NOTE_TYPE) — agent notes are
3796
+ * written in Markdown and TW would otherwise parse them as wikitext.
3797
+ */
3798
+ function finalTypeForWrite(title, tiddler) {
3799
+ if (typeof tiddler.type === "string" && tiddler.type.length > 0) return { defaulted: false };
3800
+ if (title.startsWith("$:/")) return { defaulted: false };
3801
+ tiddler.type = DEFAULT_NOTE_TYPE;
3802
+ return { defaulted: true };
3803
+ }
3784
3804
  function registerTiddlywikiTools(ctx, deps) {
3785
3805
  const disposers = [];
3786
3806
  const register = (tool) => {
@@ -3988,7 +4008,7 @@ function registerTiddlywikiTools(ctx, deps) {
3988
4008
  }));
3989
4009
  register(defineTool({
3990
4010
  name: "tiddlywiki_put",
3991
- description: "写入(新建或覆盖)一个 TiddlyWiki tiddler。同名覆盖;tags 为标签数组,fields 为附加自定义字段(json 对象,会写入 tiddler 字段)。写入后触发自动 commit。新建(title 不存在)时自动补打 agent-written 标签标记「由 Agent 撰写」,无需手动添加。",
4011
+ description: "写入(新建或覆盖)一个 TiddlyWiki tiddler。同名覆盖;tags 为标签数组,fields 为附加自定义字段(json 对象,会写入 tiddler 字段)。写入后触发自动 commit。新建(title 不存在)时自动补打 agent-written 标签标记「由 Agent 撰写」,无需手动添加。未指定内容类型时自动默认 text/markdown($:/ 系统条目除外);要写原生 wikitext 需显式在 fields 传 {\"type\":\"text/vnd.tiddlywiki\"}。⚠️ fields.type 是 TW 的内容类型保留字段,不要把业务分类值(如 \"meeting\")写进去——业务分类请放 tags。",
3992
4012
  parameters: {
3993
4013
  title: {
3994
4014
  type: "string",
@@ -3997,7 +4017,7 @@ function registerTiddlywikiTools(ctx, deps) {
3997
4017
  },
3998
4018
  text: {
3999
4019
  type: "string",
4000
- description: "tiddler 全文(wiki 文本)",
4020
+ description: "tiddler 全文(默认按 Markdown 解析)",
4001
4021
  required: true
4002
4022
  },
4003
4023
  tags: {
@@ -4007,7 +4027,7 @@ function registerTiddlywikiTools(ctx, deps) {
4007
4027
  },
4008
4028
  fields: {
4009
4029
  type: "json",
4010
- description: "附加自定义字段,如 {\"type\":\"meeting\",\"date\":\"2026-09-02\"}(可选)"
4030
+ description: "附加自定义字段,如 {\"date\":\"2026-09-02\"}(可选)。注意:fields.type 是 TW 内容类型(保留字段,默认已自动补 text/markdown),不要写业务分类值"
4011
4031
  }
4012
4032
  },
4013
4033
  output: {
@@ -4015,6 +4035,7 @@ function registerTiddlywikiTools(ctx, deps) {
4015
4035
  render: (_args, value) => {
4016
4036
  const lines = [`已写入 tiddler「${value.title}」`];
4017
4037
  if (value.tags.length > 0) lines.push(`标签: ${value.tags.join(", ")}`);
4038
+ if (value.type !== null) lines.push(`类型: ${value.type}${value.typeDefaulted === true ? "(未指定,已默认 markdown)" : ""}`);
4018
4039
  if (value.fields !== null) {
4019
4040
  const entries = Object.entries(value.fields);
4020
4041
  if (entries.length > 0) lines.push(`字段: ${entries.map(([k, v]) => `${k}=${String(v)}`).join(", ")}`);
@@ -4035,19 +4056,22 @@ function registerTiddlywikiTools(ctx, deps) {
4035
4056
  };
4036
4057
  if (tags.length > 0) tiddler.tags = tags;
4037
4058
  if (args.fields !== void 0 && typeof args.fields === "object" && args.fields !== null) Object.assign(tiddler, args.fields);
4059
+ const { defaulted } = finalTypeForWrite(args.title, tiddler);
4038
4060
  await wiki.put(tiddler);
4039
4061
  deps.autoCommit();
4040
4062
  return {
4041
4063
  ok: true,
4042
4064
  title: args.title,
4043
4065
  tags,
4066
+ type: typeof tiddler.type === "string" ? tiddler.type : null,
4067
+ ...defaulted ? { typeDefaulted: true } : {},
4044
4068
  fields: args.fields ?? null
4045
4069
  };
4046
4070
  }
4047
4071
  }));
4048
4072
  register(defineTool({
4049
4073
  name: "tiddlywiki_batch_put",
4050
- description: "批量写入/覆盖多个 TiddlyWiki tiddler(一次工具调用)。overwrite=false 时跳过已存在的标题;返回逐条结果。写入后触发自动 commit。新建(title 不存在)的条目会自动补打 agent-written 标签,无需手动添加。",
4074
+ description: "批量写入/覆盖多个 TiddlyWiki tiddler(一次工具调用)。overwrite=false 时跳过已存在的标题;返回逐条结果。写入后触发自动 commit。新建(title 不存在)的条目会自动补打 agent-written 标签,无需手动添加。未指定内容类型(fields.type)的条目自动默认 text/markdown($:/ 系统条目除外)。",
4051
4075
  parameters: {
4052
4076
  items: {
4053
4077
  type: "array",
@@ -4063,7 +4087,7 @@ function registerTiddlywikiTools(ctx, deps) {
4063
4087
  },
4064
4088
  text: {
4065
4089
  type: "string",
4066
- description: "全文(wiki 文本)",
4090
+ description: "全文(默认按 Markdown 解析)",
4067
4091
  required: true
4068
4092
  },
4069
4093
  tags: {
@@ -4073,7 +4097,7 @@ function registerTiddlywikiTools(ctx, deps) {
4073
4097
  },
4074
4098
  fields: {
4075
4099
  type: "json",
4076
- description: "附加自定义字段(可选)"
4100
+ description: "附加自定义字段(可选)。注意:fields.type 是 TW 内容类型(保留字段,默认已自动补 text/markdown),不要写业务分类值"
4077
4101
  }
4078
4102
  }
4079
4103
  }
@@ -4127,6 +4151,7 @@ function registerTiddlywikiTools(ctx, deps) {
4127
4151
  };
4128
4152
  if (tags.length > 0) tiddler.tags = tags;
4129
4153
  if (item.fields !== void 0 && typeof item.fields === "object" && item.fields !== null) Object.assign(tiddler, item.fields);
4154
+ finalTypeForWrite(item.title, tiddler);
4130
4155
  await wiki.put(tiddler);
4131
4156
  written++;
4132
4157
  results.push({
@@ -4607,6 +4632,8 @@ pull 冲突后:先 \`tiddlywiki_git_resolve files=[冲突文件] strategy=keep
4607
4632
 
4608
4633
  **Agent 笔记标签约定**:\`tiddlywiki_put\` / \`tiddlywiki_batch_put\` 新建笔记时,插件会自动补打 \`agent-written\` 标签(标记「由 Agent 撰写」),无需手动添加,也不要手动移除它(除非用户明确要求)。首页会把 Agent 笔记单独列在「Agent 区块」,主标签列表只统计人类笔记。若某篇 Agent 笔记后续被人类编辑过,请在该笔记上补打 \`human-edited\` 标签,首页会把它归入「Agent + 人工」档。覆盖写入已有的(人类)笔记时不会自动加 agent-written,请保持笔记原本的归属。
4609
4634
 
4635
+ **内容类型约定**:agent 笔记正文默认用 **Markdown** 写;\`tiddlywiki_put\` / \`tiddlywiki_batch_put\` 未指定内容类型时自动按 \`text/markdown\` 写入(\`$:/\` 系统条目除外),无需手动指定。要写 TW 原生 wikitext 才需要在 fields 里显式传 \`{"type":"text/vnd.tiddlywiki"}\`。⚠️ \`fields.type\` 是 TW 的**内容类型**保留字段——不要把业务分类值(如 \`"meeting"\`)写进去(会破坏渲染),业务分类请放 \`tags\`。
4636
+
4610
4637
  **引用 wiki 笔记用可点击链接**:在回复流中引用某篇笔记时,用格式 \`[标题](/dsh-tiddlywiki/tw/#标题)\` 输出(标题含空格/特殊字符时做 URL 编码,如 \`A%20B\`;中文标题可直接写)。这类链接会被界面自动接管:点击后打开中央 TW 面板并跳转到该笔记的原生页面。回复里也优先用这个链接格式代替纯文本标题,让用户能一键跳到 wiki。`;
4611
4638
  /**
4612
4639
  * Mount the host half.