diff --git a/.dsh-plugin/README.md b/.dsh-plugin/README.md index 0782f0c..6dcd53c 100644 --- a/.dsh-plugin/README.md +++ b/.dsh-plugin/README.md @@ -16,6 +16,10 @@ SCD 管理但不可恢复,就 `agent.steer(...)` 一条纠正消息,让 Agen `PreCompact` 的一半不在此移植;压缩后由 DSH 自身的 `AGENTS.md` 基线机制 重新注入指令。 +纠正消息使用 Session v4 的 `source: { kind: "plugin:thinloop-continuity" }`, +由来源字段标明 Thinloop;旧的 `{ kind: "plugin", plugin: "thinloop-continuity" }` +会被 v4 会话写入校验拒绝。 + ## 挂载层:宿主级用户 patch,而非 agent preset 推荐把插件挂载到 **home 级用户 patch 层** `$DSH_HOME/cordis.patch.yml` diff --git a/.dsh-plugin/continuity.mjs b/.dsh-plugin/continuity.mjs index fd31744..5ef64cb 100644 --- a/.dsh-plugin/continuity.mjs +++ b/.dsh-plugin/continuity.mjs @@ -26,11 +26,7 @@ import { RELATIVE_STATE_PATH, validateState } from "../hooks/validate-state.mjs" export const name = "thinloop-continuity"; export const inject = []; -// A corrective user-role message matching the runtime `UserMessage` shape -// ({ id, role: 'user', content: [{ type: 'text', text }], source }). Provenance -// is the plugin itself — the same `MessageSourceMap['plugin']` shape the -// official `dsh-hooks-codex` Stop bridge steers with — so the transcript keeps -// attributing the correction to Thinloop instead of spoofing the human user. +// Session v4 requires a producer-owned source kind for corrective messages. function correctiveMessage(text) { return { id: `thinloop-continuity-${Date.now().toString(36)}-${Math.random() @@ -38,7 +34,7 @@ function correctiveMessage(text) { .slice(2)}`, role: "user", content: [{ type: "text", text }], - source: { kind: "plugin", plugin: "thinloop-continuity" }, + source: { kind: "plugin:thinloop-continuity" }, }; } diff --git a/tests/plugin-compatibility.test.mjs b/tests/plugin-compatibility.test.mjs index a59412c..92e14f8 100644 --- a/tests/plugin-compatibility.test.mjs +++ b/tests/plugin-compatibility.test.mjs @@ -201,9 +201,8 @@ test( assert.equal(typeof plugin.apply, "function"); const source = fs.readFileSync(pluginUrl, "utf8"); - // Steered corrections must attribute to the plugin, matching the official - // dsh-hooks-codex Stop bridge, instead of spoofing the human user. - assert.match(source, /kind: "plugin", plugin: "thinloop-continuity"/); + // Session v4 sources identify the producer directly. + assert.match(source, /kind: "plugin:thinloop-continuity"/); assert.doesNotMatch(source, /kind: "user"/); // The gate must share one validator with the subprocess hooks. assert.match(source, /from "\.\.\/hooks\/validate-state\.mjs"/); diff --git a/tests/validate-state.test.mjs b/tests/validate-state.test.mjs index c5f8287..f050284 100644 --- a/tests/validate-state.test.mjs +++ b/tests/validate-state.test.mjs @@ -148,6 +148,7 @@ test("DSH bounds unchanged invalid-state steering per agent and rearms after rec assert.equal(messages.length, 0); await listener({ agent }); assert.equal(messages.length, 1); + assert.deepEqual(messages[0].source, { kind: "plugin:thinloop-continuity" }); assert.match(messages[0].content[0].text, /empty section: Evidence/); await listener({ agent }); await listener({ agent });