fix: 0.2.0 把注入打进原生 system 槽,子代理走同一条 assemble
openai-completions / anthropic-messages 不再塞 last user;子代理 applyChildComposition 后走同一套 fold 与满权限钉钉。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
e1d8e59087
commit
0a04bee3a4
4 files changed
+402
-29
No files matched your search
@@ -24,6 +24,9 @@ _archive/
|
||||
|
||||
# 嵌入前的提示词原文
|
||||
lib/default-prompt-inject.md
|
||||
lib/default-prompt-inject.md.bak
|
||||
lib/default-prompt-inject.zh.md
|
||||
lib/default-prompt-inject.en.md
|
||||
default-prompt-inject.md
|
||||
|
||||
# 本机官方安装包、解包目录、拆出的压缩包
|
||||
|
||||
+305
-14
@@ -2520,6 +2520,24 @@ const COMPLETE_PROMPT_WATERFALL_INJECT =
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};";
|
||||
|
||||
// 折进 persona 后 complete 段本身已含注入,必须放在最前;不能再用 PREV 的 [...tail, base]。
|
||||
const COMPLETE_PROMPT_WATERFALL_INJECT_HEAD =
|
||||
"\t\tconst transformed = await this.ctx.waterfall(scopeTarget(this, scope), \"system-prompt/assemble\", assembly, context, () => Promise.resolve(assembly));\n" +
|
||||
"\t\tconst dshPurgeSections = (transformed.sections || []).filter((s) => String(s && s.name || \"\").startsWith(\"dsh-purge\") && String(s.text || \"\").length > 0);\n" +
|
||||
"\t\tconst dshPurgeRest = (transformed.sections || []).filter((s) => !String(s && s.name || \"\").startsWith(\"dsh-purge\"));\n" +
|
||||
"\t\tif (completeSection === void 0 && !runtimeContextSuppressed) return { ...transformed, sections: [...dshPurgeSections, ...dshPurgeRest] }; // [dsh-purge] fold-complete inject first\n" +
|
||||
"\t\treturn {\n" +
|
||||
"\t\t\t...transformed,\n" +
|
||||
"\t\t\tsections: completeSection === void 0 ? [...dshPurgeSections, ...dshPurgeRest] : (function (complete, purge, rest, assembled) {\n" +
|
||||
"\t\t\t\tconst name = String(complete && complete.name || \"\");\n" +
|
||||
"\t\t\t\tconst after = (assembled || []).find((s) => String(s && s.name || \"\") === name);\n" +
|
||||
"\t\t\t\tconst base = after ? { ...after } : { ...complete };\n" +
|
||||
"\t\t\t\tconst tail = rest.filter((s) => String(s?.name || \"\") !== name);\n" +
|
||||
"\t\t\t\treturn [...purge, base, ...tail]; // [dsh-purge] fold-complete inject first\n" +
|
||||
"\t\t\t})(completeSection, dshPurgeSections, dshPurgeRest, transformed.sections),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};";
|
||||
|
||||
function clearShellNetworkBan(pattern) {
|
||||
return pattern
|
||||
.replace(/\n[ \t]+\* You don't have access to the internet via this tool\./, "")
|
||||
@@ -3818,6 +3836,57 @@ const NEW_TOOL_PATCHES = [
|
||||
],
|
||||
markers: ["[dsh-purge] subagent scope lock neutralized"],
|
||||
},
|
||||
{
|
||||
id: 92,
|
||||
name: "SUBAGENT_SAME_INJECT_PATH",
|
||||
layer: "代码",
|
||||
layer_en: "Code",
|
||||
desc: "子代理创建时立刻挂 assemble 注入,并继承父会话权限 / wrap child assemble at spawn and inherit parent permission preset",
|
||||
file: ["subagent", "subagent-types"],
|
||||
optional: true,
|
||||
skipIfMarked: true,
|
||||
replacements: [
|
||||
{
|
||||
pattern:
|
||||
"\tif (composition.toolFilter !== void 0) childCtx.tools.restrict(composition.toolFilter);\n" +
|
||||
"}",
|
||||
replace:
|
||||
"\tif (composition.toolFilter !== void 0) childCtx.tools.restrict(composition.toolFilter);\n" +
|
||||
"\ttry {\n" +
|
||||
"\t\tconst prepare = globalThis.__dshPurgePrepareAssemble;\n" +
|
||||
"\t\tif (typeof prepare === \"function\") prepare(childCtx);\n" +
|
||||
"\t} catch {}\n" +
|
||||
"} // [dsh-purge-92] child assemble same as parent",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" if (composition.toolFilter !== undefined)\n" +
|
||||
" childCtx.tools.restrict(composition.toolFilter);\n" +
|
||||
"}",
|
||||
replace:
|
||||
" if (composition.toolFilter !== undefined)\n" +
|
||||
" childCtx.tools.restrict(composition.toolFilter);\n" +
|
||||
" try {\n" +
|
||||
" const prepare = globalThis.__dshPurgePrepareAssemble;\n" +
|
||||
" if (typeof prepare === \"function\") prepare(childCtx);\n" +
|
||||
" } catch {}\n" +
|
||||
"} // [dsh-purge-92] child assemble same as parent",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
"\t\tpermissionPreset: preset === \"auto\" || preset === \"danger-full-access\" ? preset : void 0,",
|
||||
replace:
|
||||
"\t\tpermissionPreset: preset || \"danger-full-access\", // [dsh-purge-92] child inherits parent access",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" permissionPreset: preset === 'auto' || preset === 'danger-full-access' ? preset : undefined,",
|
||||
replace:
|
||||
" permissionPreset: preset || 'danger-full-access', // [dsh-purge-92] child inherits parent access",
|
||||
},
|
||||
],
|
||||
markers: ["[dsh-purge-92] child assemble same as parent"],
|
||||
},
|
||||
{
|
||||
// 0.1.5 schema 不认 text;社区预设仍带 text 时挂载失败。
|
||||
id: 38, name: "PERSONA_TEXT_PREFIX_ALIAS", layer: "代码", layer_en: "Code",
|
||||
@@ -3866,16 +3935,16 @@ const NEW_TOOL_PATCHES = [
|
||||
markers: ["[dsh-purge] mnemon summary off-notice"],
|
||||
},
|
||||
{
|
||||
// complete:true 会在 waterfall 之后只留完整段。操作者段是 waterfall 里加上的,
|
||||
// 必须从 waterfall 的结果里抄,不能从 waterfall 之前的段落里抄。
|
||||
// complete:true 会在 waterfall 之后只留完整段。注入已折进 persona 时,
|
||||
// 必须用瀑布之后的 complete 段,并放到最前。
|
||||
id: 40, name: "COMPLETE_PROMPT_KEEP_INJECT", layer: "0.1.5 兼容", layer_en: "0.1.5",
|
||||
desc: "complete 预设仍保留 dsh-purge 注入 / keep inject after complete prompt",
|
||||
desc: "complete 预设把已折进 persona 的注入放最前 / keep folded inject first after complete prompt",
|
||||
file: ["system-prompt", "system-prompt-nested"],
|
||||
replacements: [
|
||||
{
|
||||
// 缩进不固定。0.2 有的构建用 tab,有的用空格;只认 tab 时匹配为 0,重启校验会说没写进宿主。
|
||||
pattern: /[ \t]*const transformed = await this\.ctx\.waterfall\(scopeTarget\(this, scope\), "system-prompt\/assemble", assembly, context, \(\) => Promise\.resolve\(assembly\)\);\n[ \t]*if \(completeSection === void 0 && !runtimeContextSuppressed\) return transformed;\n[ \t]*return \{\n[ \t]*\.\.\.transformed,\n[ \t]*sections: completeSection === void 0 \? transformed\.sections : \[completeSection\],\n[ \t]*contexts: runtimeContextSuppressed \? \[\] : transformed\.contexts\n[ \t]*\};/,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
@@ -3892,7 +3961,7 @@ const NEW_TOOL_PATCHES = [
|
||||
"\t\t\t})(completeSection, dshPurgeInject),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};",
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
@@ -3907,7 +3976,7 @@ const NEW_TOOL_PATCHES = [
|
||||
"\t\t\t})(completeSection, dshPurgeSections),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};",
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
@@ -3923,11 +3992,15 @@ const NEW_TOOL_PATCHES = [
|
||||
"\t\t\t})(completeSection, dshPurgeSections),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};",
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern: COMPLETE_PROMPT_WATERFALL_INJECT_PREV,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
@@ -3946,7 +4019,7 @@ const NEW_TOOL_PATCHES = [
|
||||
"\t\t\t})(completeSection, dshPurgeSections, dshPurgeRest, transformed.sections),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};",
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
@@ -3963,16 +4036,13 @@ const NEW_TOOL_PATCHES = [
|
||||
"\t\t\t})(completeSection, dshPurgeSections, dshPurgeRest),\n" +
|
||||
"\t\t\tcontexts: runtimeContextSuppressed ? [] : transformed.contexts\n" +
|
||||
"\t\t};",
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT,
|
||||
replace: COMPLETE_PROMPT_WATERFALL_INJECT_HEAD,
|
||||
},
|
||||
],
|
||||
forbid: ["dsh-purge:committed"],
|
||||
markersAny: true,
|
||||
markers: [
|
||||
"complete prompt keeps waterfall inject",
|
||||
"complete prompt keeps inject + agent-instructions",
|
||||
"complete prompt keeps inject + assembled complete",
|
||||
"inject last after closer",
|
||||
"fold-complete inject first",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -5782,6 +5852,227 @@ const NEW_TOOL_PATCHES = [
|
||||
],
|
||||
markers: ["[dsh-purge-89] system field extract"],
|
||||
},
|
||||
{
|
||||
id: 90,
|
||||
name: "OPENAI_NATIVE_SYSTEM_ROLE",
|
||||
layer: "代码",
|
||||
layer_en: "Code",
|
||||
desc: "openai-completions 把 systemPrompt 折进 messages[0].role=system(chat/completions 原生槽),不再塞进 last user / fold systemPrompt into native role:system for /chat/completions",
|
||||
file: ["pi-ai-completions"],
|
||||
optional: true,
|
||||
skipIfMarked: true,
|
||||
replacements: [
|
||||
{
|
||||
pattern:
|
||||
"export const stream = (model, context, options) => {\n" +
|
||||
" const stream = new AssistantMessageEventStream();\n" +
|
||||
" const normalizedContext = resolveTranscript(context, getCompat(model).supportsMidConvoSystemMessages);",
|
||||
replace:
|
||||
"export const stream = (model, context, options) => {\n" +
|
||||
" const stream = new AssistantMessageEventStream();\n" +
|
||||
" const promptText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) ? context.systemPrompt : ((typeof context.system === \"string\" && context.system.length > 0) ? context.system : \"\");\n" +
|
||||
" const seeded = (promptText && context.messages?.[0]?.role !== \"system\") ? { ...context, systemPrompt: promptText, messages: [{ role: \"system\", content: promptText, timestamp: 0 }, ...context.messages] } : context;\n" +
|
||||
" const normalizedContext = { ...seeded, ...resolveTranscript(seeded, getCompat(model).supportsMidConvoSystemMessages) }; // [dsh-purge-90] native system role",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
"export function convertMessages(model, context, compat, options) {\n" +
|
||||
" const normalizedContext = resolveTranscript(context, compat.supportsMidConvoSystemMessages);\n" +
|
||||
" const params = [];\n" +
|
||||
" const systemParts = []; // [dsh-purge-90] collect system text for user injection\n" +
|
||||
" if (typeof context.system === \"string\" && context.system.length > 0) systemParts.push(sanitizeSurrogates(context.system));\n" +
|
||||
" const normalizeToolCallId = (id) => {",
|
||||
replace:
|
||||
"export function convertMessages(model, context, compat, options) {\n" +
|
||||
" const promptText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) ? context.systemPrompt : ((typeof context.system === \"string\" && context.system.length > 0) ? context.system : \"\");\n" +
|
||||
" const seeded = (promptText && context.messages?.[0]?.role !== \"system\") ? { ...context, messages: [{ role: \"system\", content: promptText, timestamp: 0 }, ...context.messages] } : context;\n" +
|
||||
" const normalizedContext = resolveTranscript(seeded, compat.supportsMidConvoSystemMessages); // [dsh-purge-90] native system role\n" +
|
||||
" const params = [];\n" +
|
||||
" const normalizeToolCallId = (id) => {",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
"export function convertMessages(model, context, compat, options) {\n" +
|
||||
" const normalizedContext = resolveTranscript(context, compat.supportsMidConvoSystemMessages);\n" +
|
||||
" const params = [];\n" +
|
||||
" const normalizeToolCallId = (id) => {",
|
||||
replace:
|
||||
"export function convertMessages(model, context, compat, options) {\n" +
|
||||
" const promptText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) ? context.systemPrompt : ((typeof context.system === \"string\" && context.system.length > 0) ? context.system : \"\");\n" +
|
||||
" const seeded = (promptText && context.messages?.[0]?.role !== \"system\") ? { ...context, messages: [{ role: \"system\", content: promptText, timestamp: 0 }, ...context.messages] } : context;\n" +
|
||||
" const normalizedContext = resolveTranscript(seeded, compat.supportsMidConvoSystemMessages); // [dsh-purge-90] native system role\n" +
|
||||
" const params = [];\n" +
|
||||
" const normalizeToolCallId = (id) => {",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" const text = i === 0 ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg);\n" +
|
||||
" if (text.length > 0) {\n" +
|
||||
" systemParts.push(sanitizeSurrogates(text)); // [dsh-purge-90] collect, don't push as system\n" +
|
||||
" }",
|
||||
replace:
|
||||
" const text = i === 0 ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg);\n" +
|
||||
" if (text.length > 0) {\n" +
|
||||
" params.push({ role: instructionRole, content: sanitizeSurrogates(text).replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim() }); // [dsh-purge-90] native system role\n" +
|
||||
" }",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" const text = i === 0 ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg);\n" +
|
||||
" if (text.length > 0) {\n" +
|
||||
" params.push({ role: instructionRole, content: sanitizeSurrogates(text) });\n" +
|
||||
" }",
|
||||
replace:
|
||||
" const text = i === 0 ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg);\n" +
|
||||
" if (text.length > 0) {\n" +
|
||||
" params.push({ role: instructionRole, content: sanitizeSurrogates(text).replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim() }); // [dsh-purge-90] native system role\n" +
|
||||
" }",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" lastRole = msg.role;\n" +
|
||||
" }\n" +
|
||||
" if (systemParts.length > 0) { // [dsh-purge-90] inject once\n" +
|
||||
" const systemText = systemParts.join(\"\\n\\n\").replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim();\n" +
|
||||
" const lastUser = params.findLastIndex(m => m.role === \"user\");\n" +
|
||||
" if (lastUser !== -1) {\n" +
|
||||
" const uc = params[lastUser].content;\n" +
|
||||
" if (typeof uc === \"string\") params[lastUser].content = systemText + \"\\n\\n\" + uc;\n" +
|
||||
" else if (Array.isArray(uc)) params[lastUser].content = [{ type: \"text\", text: systemText }, ...uc];\n" +
|
||||
" } else {\n" +
|
||||
" params.push({ role: \"user\", content: systemText });\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
replace:
|
||||
" lastRole = msg.role;\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" lastRole = msg.role;\n" +
|
||||
" }\n" +
|
||||
" if (systemParts.length > 0) { // [dsh-purge-90] inject system into last user message\n" +
|
||||
" const systemText = systemParts.join(\"\\n\\n\").replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim();\n" +
|
||||
" const lastUser = params.findLastIndex(m => m.role === \"user\");\n" +
|
||||
" if (lastUser !== -1) {\n" +
|
||||
" const uc = params[lastUser].content;\n" +
|
||||
" if (typeof uc === \"string\") params[lastUser].content = systemText + \"\\n\\n\" + uc;\n" +
|
||||
" else if (Array.isArray(uc)) params[lastUser].content = [{ type: \"text\", text: systemText }, ...uc];\n" +
|
||||
" } else {\n" +
|
||||
" params.push({ role: \"user\", content: systemText });\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
replace:
|
||||
" lastRole = msg.role;\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
},
|
||||
],
|
||||
markers: ["[dsh-purge-90] native system role"],
|
||||
},
|
||||
{
|
||||
id: 91,
|
||||
name: "ANTHROPIC_NATIVE_PARAMS_SYSTEM",
|
||||
layer: "代码",
|
||||
layer_en: "Code",
|
||||
desc: "anthropic-messages 把 systemPrompt 写入顶层 params.system(/messages 原生槽),不再塞进 last user / write systemPrompt to native params.system for /messages",
|
||||
file: ["pi-ai-anthropic-messages"],
|
||||
optional: true,
|
||||
skipIfMarked: true,
|
||||
replacements: [
|
||||
{
|
||||
pattern:
|
||||
"export const stream = (model, context, options) => {\n" +
|
||||
" const stream = new AssistantMessageEventStream();\n" +
|
||||
" const normalizedContext = resolveTranscript(context, getAnthropicCompat(model).supportsMidConvoSystemMessages);",
|
||||
replace:
|
||||
"export const stream = (model, context, options) => {\n" +
|
||||
" const stream = new AssistantMessageEventStream();\n" +
|
||||
" const promptText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) ? context.systemPrompt : ((typeof context.system === \"string\" && context.system.length > 0) ? context.system : \"\");\n" +
|
||||
" const seeded = (promptText && context.messages?.[0]?.role !== \"system\") ? { ...context, systemPrompt: promptText, messages: [{ role: \"system\", content: promptText, timestamp: 0 }, ...context.messages] } : context;\n" +
|
||||
" const normalizedContext = { ...seeded, ...resolveTranscript(seeded, getAnthropicCompat(model).supportsMidConvoSystemMessages) }; // [dsh-purge-91] native params.system",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" const initialSystemMessage = getInitialSystemMessage(context.messages);\n" +
|
||||
" const initialSystemText = (typeof context.system === \"string\" && context.system.length > 0) // [dsh-purge-91] fallback to context.system (#89 compat)\n" +
|
||||
" ? context.system\n" +
|
||||
" : (initialSystemMessage ? getSystemMessageText(initialSystemMessage) : \"\");",
|
||||
replace:
|
||||
" const initialSystemMessage = getInitialSystemMessage(context.messages);\n" +
|
||||
" const initialSystemText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) // [dsh-purge-91] native params.system\n" +
|
||||
" ? context.systemPrompt\n" +
|
||||
" : ((typeof context.system === \"string\" && context.system.length > 0)\n" +
|
||||
" ? context.system\n" +
|
||||
" : (initialSystemMessage ? getSystemMessageText(initialSystemMessage) : \"\"));",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" const initialSystemMessage = getInitialSystemMessage(context.messages);\n" +
|
||||
" const initialSystemText = initialSystemMessage ? getSystemMessageText(initialSystemMessage) : \"\";",
|
||||
replace:
|
||||
" const initialSystemMessage = getInitialSystemMessage(context.messages);\n" +
|
||||
" const initialSystemText = (typeof context.systemPrompt === \"string\" && context.systemPrompt.length > 0) // [dsh-purge-91] native params.system\n" +
|
||||
" ? context.systemPrompt\n" +
|
||||
" : ((typeof context.system === \"string\" && context.system.length > 0)\n" +
|
||||
" ? context.system\n" +
|
||||
" : (initialSystemMessage ? getSystemMessageText(initialSystemMessage) : \"\"));",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" // [dsh-purge-91] system injected into last user message (0.1.7 style)",
|
||||
replace:
|
||||
" if (initialSystemText) { // [dsh-purge-91] native params.system\n" +
|
||||
" const systemText = sanitizeSurrogates(initialSystemText).replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim();\n" +
|
||||
" params.system = [\n" +
|
||||
" {\n" +
|
||||
" type: \"text\",\n" +
|
||||
" text: systemText,\n" +
|
||||
" ...(cacheControl ? { cache_control: cacheControl } : {}),\n" +
|
||||
" },\n" +
|
||||
" ];\n" +
|
||||
" }",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" if (initialSystemText) { // [dsh-purge-91] inject once\n" +
|
||||
" const systemText = sanitizeSurrogates(initialSystemText).replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim();\n" +
|
||||
" const lastUser = params.messages.findLastIndex(m => m.role === \"user\");\n" +
|
||||
" if (lastUser !== -1) {\n" +
|
||||
" const uc = params.messages[lastUser].content;\n" +
|
||||
" if (typeof uc === \"string\") params.messages[lastUser].content = systemText + \"\\n\\n\" + uc;\n" +
|
||||
" else if (Array.isArray(uc)) params.messages[lastUser].content = [{ type: \"text\", text: systemText }, ...uc];\n" +
|
||||
" } else {\n" +
|
||||
" params.messages.push({ role: \"user\", content: [{ type: \"text\", text: systemText }] });\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
replace:
|
||||
" return params;",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
" if (initialSystemText) { // [dsh-purge-91] inject system into last user message\n" +
|
||||
" const systemText = sanitizeSurrogates(initialSystemText).replace(/<\\|im_start\\|>\\s*system\\s*:\\s*/gi, \"\").replace(/<\\/?project_instructions>/gi, \"\").replace(/<\\|im_end\\|>/g, \"\").trim();\n" +
|
||||
" const lastUser = params.messages.findLastIndex(m => m.role === \"user\");\n" +
|
||||
" if (lastUser !== -1) {\n" +
|
||||
" const uc = params.messages[lastUser].content;\n" +
|
||||
" if (typeof uc === \"string\") params.messages[lastUser].content = systemText + \"\\n\\n\" + uc;\n" +
|
||||
" else if (Array.isArray(uc)) params.messages[lastUser].content = [{ type: \"text\", text: systemText }, ...uc];\n" +
|
||||
" } else {\n" +
|
||||
" params.messages.push({ role: \"user\", content: [{ type: \"text\", text: systemText }] });\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" return params;",
|
||||
replace:
|
||||
" return params;",
|
||||
},
|
||||
],
|
||||
markers: ["[dsh-purge-91] native params.system"],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
+66
-12
@@ -127,6 +127,67 @@ function ensureFullInject(sections, inject) {
|
||||
];
|
||||
}
|
||||
|
||||
const IDENTITY_CARD_PREFIX = "WHO YOU ARE (highest priority";
|
||||
|
||||
function isPersonaFoldTarget(name, preferPrefix) {
|
||||
if (preferPrefix) return PERSONA_PREFIX_NAMES.has(name);
|
||||
return PERSONA_PREFIX_NAMES.has(name) || PERSONA_LEGACY_NAMES.has(name);
|
||||
}
|
||||
|
||||
function stripIdentityPrefix(text) {
|
||||
let rest = String(text || "").trim();
|
||||
if (rest.startsWith(IDENTITY_CARD_PREFIX)) {
|
||||
const split = rest.indexOf("\n\n");
|
||||
rest = split >= 0 ? rest.slice(split + 2).trim() : "";
|
||||
}
|
||||
if (rest.startsWith(OLD_IDENTITY_LOCK)) {
|
||||
rest = rest.slice(OLD_IDENTITY_LOCK.length).trim();
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
function hoistFoldedPersona(sections, hasPrefix) {
|
||||
const list = Array.isArray(sections) ? sections.slice() : [];
|
||||
const idx = list.findIndex((section) => isPersonaFoldTarget(String(section?.name || ""), hasPrefix));
|
||||
if (idx <= 0) return list;
|
||||
const [hit] = list.splice(idx, 1);
|
||||
return [hit, ...list];
|
||||
}
|
||||
|
||||
/** 1.1.34:把 inject 写进 persona-prefix 正文开头,不再另开 dsh-purge 段。 */
|
||||
function foldInjectIntoPersona(sections, injectText) {
|
||||
const inject = typeof injectText === "string" ? injectText : "";
|
||||
const list = Array.isArray(sections) ? sections : [];
|
||||
const names = list.map((section) => String(section?.name || ""));
|
||||
const hasPrefix = names.some((name) => PERSONA_PREFIX_NAMES.has(name));
|
||||
let folded = false;
|
||||
const out = list.map((section) => {
|
||||
const name = String(section?.name || "");
|
||||
if (!isPersonaFoldTarget(name, hasPrefix)) return section;
|
||||
folded = true;
|
||||
const prior = stripIdentityPrefix(String(section.text || ""));
|
||||
if (!inject) return { ...section, text: prior, order: GLOBAL_PROMPT_ORDER };
|
||||
if (prior.includes(inject)) return { ...section, order: GLOBAL_PROMPT_ORDER, interpolate: false };
|
||||
return {
|
||||
...section,
|
||||
text: prior ? `${inject}\n\n${prior}` : inject,
|
||||
order: GLOBAL_PROMPT_ORDER,
|
||||
interpolate: false,
|
||||
};
|
||||
});
|
||||
if (folded) return hoistFoldedPersona(out, hasPrefix);
|
||||
if (!inject) return out;
|
||||
return hoistFoldedPersona([
|
||||
{
|
||||
name: "deployment:persona-prefix",
|
||||
text: inject,
|
||||
order: GLOBAL_PROMPT_ORDER,
|
||||
interpolate: false,
|
||||
},
|
||||
...out,
|
||||
], true);
|
||||
}
|
||||
|
||||
/** 插件写死的执行句,不是用户的规则集。任何模式都不注入。 */
|
||||
function isHardcodedExecRule(section) {
|
||||
if (section?.name !== "dsh-purge:rules") return false;
|
||||
@@ -395,8 +456,8 @@ function isRedteamSubagent(context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 红队 persona 只留调度正文,不再把 inject / 目标卡 / 角色折进去。
|
||||
* inject 和 cap 由 rewritePromptAssembly 置顶;卡和角色用不带 dsh-purge 前缀的段,避免 host 过滤器提到 cap 前面。
|
||||
* 红队 persona 只留调度正文。inject 由 foldInjectIntoPersona 写进 prefix 开头;
|
||||
* 卡和角色用不带 dsh-purge 前缀的段,避免 host 过滤器提到 cap 前面。
|
||||
*/
|
||||
function attachRedteamOpsOnly(sections) {
|
||||
let attached = false;
|
||||
@@ -689,15 +750,7 @@ export function rewritePromptAssembly(assembled, fallbackInject = "", options =
|
||||
sections = insertRoleSection(insertCardSection(withOps, opts.cardText), opts.roleText);
|
||||
}
|
||||
const body = overlayOfficialIdentity(sanitizeAssemblySections(sections));
|
||||
const head = [];
|
||||
if (String(injectText || "").trim()) {
|
||||
head.push({
|
||||
name: "dsh-purge",
|
||||
text: injectText,
|
||||
order: -100000,
|
||||
interpolate: false,
|
||||
});
|
||||
}
|
||||
const folded = foldInjectIntoPersona(body, injectText);
|
||||
const tail = [];
|
||||
if (String(protocol || "").trim()) {
|
||||
tail.push({
|
||||
@@ -707,7 +760,8 @@ export function rewritePromptAssembly(assembled, fallbackInject = "", options =
|
||||
interpolate: false,
|
||||
});
|
||||
}
|
||||
const outSections = ensureFullInject([...head, ...body, ...tail], injectText);
|
||||
const withoutCapDup = folded.filter((section) => section?.name !== CAPABILITY_SECTION);
|
||||
const outSections = ensureFullInject([...withoutCapDup, ...tail], injectText);
|
||||
return {
|
||||
...assembled,
|
||||
sections: outSections,
|
||||
|
||||
+28
-3
@@ -494,9 +494,12 @@ function rewriteLiveAssembly(config, assembled, context, decision = { mode: "ful
|
||||
dropWorkspaceBaseline,
|
||||
});
|
||||
const closerFollow = closerFollowBytes(rewritten?.sections);
|
||||
let subagent = false;
|
||||
try { subagent = isChildAgentContext(context); } catch { subagent = false; }
|
||||
purgeLog("assemble.rewrite", {
|
||||
enabled,
|
||||
redteam,
|
||||
sub: subagent,
|
||||
mode: decision?.mode || "full",
|
||||
task: decision?.taskKind || "general",
|
||||
last: decision?.lastRole || "unknown",
|
||||
@@ -509,7 +512,21 @@ function rewriteLiveAssembly(config, assembled, context, decision = { mode: "ful
|
||||
strip: config.stripMnemon !== false,
|
||||
policy,
|
||||
dropWorkspaceBaseline,
|
||||
injectHead: Array.isArray(rewritten?.sections) && rewritten.sections[0]?.name === "dsh-purge",
|
||||
injectHead: Array.isArray(rewritten?.sections) && (() => {
|
||||
const first = rewritten.sections[0];
|
||||
const firstText = String(first?.text || "");
|
||||
const name = String(first?.name || "");
|
||||
if (name === "dsh-purge") return true;
|
||||
if (!inject) return false;
|
||||
return /persona-prefix$|^persona$|^deployment:persona$/.test(name) && firstText.includes(inject);
|
||||
})(),
|
||||
folded: Array.isArray(rewritten?.sections) && (() => {
|
||||
const first = rewritten.sections[0];
|
||||
const name = String(first?.name || "");
|
||||
if (!inject) return false;
|
||||
return /persona-prefix$|^persona$|^deployment:persona$/.test(name)
|
||||
&& String(first?.text || "").includes(inject);
|
||||
})(),
|
||||
injectTail: Array.isArray(rewritten?.sections) && rewritten.sections.length > 0
|
||||
&& rewritten.sections[rewritten.sections.length - 1]?.name === "dsh-purge",
|
||||
chatml: Array.isArray(rewritten?.sections) && /<project_instructions>|<\|im_start\|>/i.test(String(rewritten.sections[0]?.text || "")),
|
||||
@@ -563,7 +580,6 @@ function permissionPresetsOf(ctx) {
|
||||
}
|
||||
|
||||
function shouldKeepFreshFullAccess(session, svc) {
|
||||
if (session?.header?.origin === "subagent") return false;
|
||||
const state = typeof svc.permissionState === "function" ? svc.permissionState(session) : null;
|
||||
const preset = state?.preset;
|
||||
if (preset === "danger-full-access" || preset === "auto" || preset === "custom") return false;
|
||||
@@ -761,9 +777,18 @@ function installIdentityOverride(ctx, config = {}) {
|
||||
if (!ctx.__dshPurgeSessionHook) {
|
||||
ctx.__dshPurgeSessionHook = true;
|
||||
try {
|
||||
ctx.on("session/created", () => {
|
||||
ctx.on("session/created", (session) => {
|
||||
try { refreshHungOperator(core.findDshHome()); } catch { /* 主目录还没有提示词 */ }
|
||||
wrapAssemble("sessionCreated");
|
||||
try {
|
||||
if (session?.header?.origin !== "subagent") return;
|
||||
const prepare = globalThis.__dshPurgePrepareAssemble;
|
||||
if (typeof prepare !== "function") return;
|
||||
const agents = ctx.agents || (typeof ctx.get === "function" ? ctx.get("agents") : null);
|
||||
const child = typeof agents?.get === "function" ? agents.get(session.id) : null;
|
||||
if (child?.ctx) prepare(child.ctx);
|
||||
else prepare(ctx);
|
||||
} catch { /* 子会话此时可能还没有 agent */ }
|
||||
});
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user