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 +2 -1
- package/lib/index.js +33 -6
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/host/tools.ts +33 -8
- package/src/index.ts +2 -0
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
|
|
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
|
|
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: "附加自定义字段,如 {\"
|
|
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: "
|
|
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.
|