Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dsh-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
8 changes: 2 additions & 6 deletions .dsh-plugin/continuity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,15 @@ 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()
.toString(36)
.slice(2)}`,
role: "user",
content: [{ type: "text", text }],
source: { kind: "plugin", plugin: "thinloop-continuity" },
source: { kind: "plugin:thinloop-continuity" },
};
}

Expand Down
5 changes: 2 additions & 3 deletions tests/plugin-compatibility.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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"/);
Expand Down
1 change: 1 addition & 0 deletions tests/validate-state.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
Loading