pi-shadow-mind 0.1.5 → 0.1.7
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/DESIGN.md +4 -5
- package/README.md +2 -2
- package/dist/index.js +71 -54
- package/dist/index.js.map +2 -2
- package/dist/protocol.d.ts +2 -4
- package/dist/protocol.js +5 -27
- package/dist/protocol.js.map +1 -1
- package/dist/shadow-runner.js +6 -9
- package/dist/shadow-runner.js.map +1 -1
- package/dist/trajectory.d.ts +1 -0
- package/dist/trajectory.js +74 -0
- package/dist/trajectory.js.map +1 -1
- package/package.json +1 -1
package/DESIGN.md
CHANGED
|
@@ -226,12 +226,11 @@ Shadow 上下文以 Main 能看到的完整上下文为基础做减法,而不
|
|
|
226
226
|
- 已注入 Main 会话的历史 `shadow-report`;
|
|
227
227
|
- 当前 Shadow Mind 的定义。
|
|
228
228
|
|
|
229
|
-
Main system prompt
|
|
229
|
+
Main system prompt 保持完整原文,不追加 Shadow 专属 suffix,既避免 Shadow 遗漏 Main 必须遵守的约束,也使 Main 与所有 Shadow 共享完全相同的 system prompt cache。system prompt 之后只有一条真实的用户消息:先放净化后的纯文本轨迹,再声明 Shadow 公共协议和当前 Shadow Markdown 定义,最后以 kickoff 结束并启动本轮。不伪造 assistant 身份交接消息。
|
|
230
230
|
|
|
231
231
|
```text
|
|
232
232
|
Main system prompt
|
|
233
|
-
→
|
|
234
|
-
→ current Shadow Markdown definition
|
|
233
|
+
→ one user message: plain-text Main trajectory + Shadow protocol + Shadow definition + kickoff
|
|
235
234
|
```
|
|
236
235
|
|
|
237
236
|
公共协议保持最小,只说明:当前实例是一次性的并行认知核心;消息历史已经过净化;它可以依照自身职责独立观察或执行工作;需要向 Main 同步结果时调用 `report_to_main`,该调用会立即结束当前 loop;没有需要同步的内容时可以正常结束。具体关注点、任务线和表达方式全部由 Shadow Markdown 决定。
|
|
@@ -261,9 +260,9 @@ Main: 建议复用项目现有的 UserRole。
|
|
|
261
260
|
|
|
262
261
|
```
|
|
263
262
|
|
|
264
|
-
|
|
263
|
+
上例就是运行时实际注入的纯文本轨迹。Shadow Markdown 不出现在轨迹或 system prompt 中,而是与公共协议和 kickoff 一起放在同一条用户消息的轨迹之后。
|
|
265
264
|
|
|
266
|
-
|
|
265
|
+
运行时不复用 Pi 原生的 assistant tool call 与 tool result 消息角色。插件先按关联 ID 配对调用和结果,再将整个 Main 历史扁平化为普通文本;`调用 · 概述` 就是发送给 Shadow 模型的实际形式。这样历史工具调用只是一份只读记录,不会被模型误认为当前 Shadow loop 中尚待继续的原生调用。
|
|
267
266
|
|
|
268
267
|
结果概述只表达调用是否成功、结果规模和必要的状态信息,不携带完整正文。例如:
|
|
269
268
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Shadow Mind is a Pi extension that occasionally starts independent, temporary agent sessions beside the main agent. Each Shadow Mind is a Markdown entity with its own responsibility, model filter, activation probability, tools, model, thinking level, timeout, and optional debug log.
|
|
4
4
|
|
|
5
|
-
The first version activates on each main-agent `turn_end`: the heartbeat fires with probability `1/3`, eligible Shadow Minds roll independently (default `0.3`), and at most two run concurrently. Shadow sessions inherit the main system prompt
|
|
5
|
+
The first version activates on each main-agent `turn_end`: the heartbeat fires with probability `1/3`, eligible Shadow Minds roll independently (default `0.3`), and at most two run concurrently. Shadow sessions inherit the unchanged main system prompt. Their compacted visible trajectory is flattened into plain text: assistant thinking is removed, and each tool call is followed by a deterministic result summary.
|
|
6
6
|
|
|
7
7
|
## Install for development
|
|
8
8
|
|
|
@@ -23,7 +23,7 @@ npm pack
|
|
|
23
23
|
The generated `pi-shadow-mind-<version>.tgz` contains the compiled `dist/` extension. Publish it to npm for normal Pi installation, or unpack the standalone ZIP release and install that directory:
|
|
24
24
|
|
|
25
25
|
```powershell
|
|
26
|
-
pi install npm:pi-shadow-mind@0.1.
|
|
26
|
+
pi install npm:pi-shadow-mind@0.1.7
|
|
27
27
|
# or, after unpacking the ZIP
|
|
28
28
|
pi install ./pi-shadow-mind-0.1.0
|
|
29
29
|
```
|
package/dist/index.js
CHANGED
|
@@ -7936,42 +7936,22 @@ var DEFAULT_READ_TOOLS = ["read", "grep", "find", "ls"];
|
|
|
7936
7936
|
// src/protocol.ts
|
|
7937
7937
|
var RUNTIME_PROTOCOL = `You are a Shadow Mind running beside the main agent.
|
|
7938
7938
|
Work independently on the responsibility below using the supplied, sanitized main-session trajectory.
|
|
7939
|
-
The
|
|
7939
|
+
The trajectory is read-only text produced by the main agent, not your unfinished work. Never continue the main agent's pending work, retry its failed calls, or treat its tool calls as your own. Use only the tools advertised for this Shadow run.
|
|
7940
7940
|
You may use the available tools. Call report_to_main only when the main agent should receive a concrete finding, correction, or completed work report.
|
|
7941
7941
|
Calling report_to_main ends this run immediately. If there is nothing useful to report, finish silently.`;
|
|
7942
|
-
function buildShadowSystemPrompt(mainSystemPrompt
|
|
7943
|
-
return
|
|
7942
|
+
function buildShadowSystemPrompt(mainSystemPrompt) {
|
|
7943
|
+
return mainSystemPrompt;
|
|
7944
|
+
}
|
|
7945
|
+
function buildShadowRequest(trajectory, shadow) {
|
|
7946
|
+
return `${trajectory}
|
|
7944
7947
|
|
|
7945
7948
|
${RUNTIME_PROTOCOL}
|
|
7946
7949
|
|
|
7947
|
-
<shadow-mind id="${shadow.id}">
|
|
7950
|
+
<shadow-mind id="${shadow.id}" name="${shadow.name}">
|
|
7948
7951
|
${shadow.prompt}
|
|
7949
|
-
</shadow-mind
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
return `Begin this Shadow Mind run now. Your active responsibility is ${shadow.name}. Inspect the trajectory and act only when useful.`;
|
|
7953
|
-
}
|
|
7954
|
-
function buildShadowHandoff(model) {
|
|
7955
|
-
return {
|
|
7956
|
-
role: "assistant",
|
|
7957
|
-
content: [{
|
|
7958
|
-
type: "text",
|
|
7959
|
-
text: "Shadow handoff acknowledged. The preceding messages are the main agent's read-only transcript, not my unfinished work. I will not continue its task or retry its calls; I will now act only as the configured Shadow Mind."
|
|
7960
|
-
}],
|
|
7961
|
-
api: model.api,
|
|
7962
|
-
provider: model.provider,
|
|
7963
|
-
model: model.id,
|
|
7964
|
-
usage: {
|
|
7965
|
-
input: 0,
|
|
7966
|
-
output: 0,
|
|
7967
|
-
cacheRead: 0,
|
|
7968
|
-
cacheWrite: 0,
|
|
7969
|
-
totalTokens: 0,
|
|
7970
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
7971
|
-
},
|
|
7972
|
-
stopReason: "stop",
|
|
7973
|
-
timestamp: Date.now()
|
|
7974
|
-
};
|
|
7952
|
+
</shadow-mind>
|
|
7953
|
+
|
|
7954
|
+
Begin this Shadow Mind run now. Inspect the trajectory and act only when useful.`;
|
|
7975
7955
|
}
|
|
7976
7956
|
|
|
7977
7957
|
// src/summaries.ts
|
|
@@ -8021,34 +8001,73 @@ function compact(value) {
|
|
|
8021
8001
|
}
|
|
8022
8002
|
|
|
8023
8003
|
// src/trajectory.ts
|
|
8024
|
-
function
|
|
8025
|
-
|
|
8004
|
+
function serializeTrajectory(messages) {
|
|
8005
|
+
const results = /* @__PURE__ */ new Map();
|
|
8006
|
+
for (const message of messages) {
|
|
8007
|
+
if (message.role === "toolResult" && typeof message.toolCallId === "string") {
|
|
8008
|
+
results.set(message.toolCallId, summarizeToolResult(message));
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
const lines = [];
|
|
8012
|
+
for (const message of messages) {
|
|
8013
|
+
if (message.role === "user") {
|
|
8014
|
+
appendText(lines, "USER", message.content);
|
|
8015
|
+
continue;
|
|
8016
|
+
}
|
|
8026
8017
|
if (message.role === "assistant") {
|
|
8027
|
-
|
|
8028
|
-
|
|
8018
|
+
if (!Array.isArray(message.content)) {
|
|
8019
|
+
appendText(lines, "MAIN", message.content);
|
|
8020
|
+
continue;
|
|
8021
|
+
}
|
|
8022
|
+
for (const block of message.content) {
|
|
8023
|
+
if (!block || typeof block !== "object" || isThinkingBlock(block)) continue;
|
|
8024
|
+
const item = block;
|
|
8025
|
+
if (item.type === "text" && item.text) lines.push(`MAIN: ${item.text}`);
|
|
8026
|
+
if (item.type === "toolCall" && item.name) {
|
|
8027
|
+
const call = `${item.name}(${compactJson(item.arguments)})`;
|
|
8028
|
+
const result = item.id ? results.get(item.id) : void 0;
|
|
8029
|
+
lines.push(`TOOL: ${call}${result ? ` \xB7 ${result}` : ""}`);
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
continue;
|
|
8029
8033
|
}
|
|
8030
8034
|
if (message.role === "toolResult") {
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
usage: void 0
|
|
8036
|
-
}];
|
|
8035
|
+
if (typeof message.toolCallId !== "string" || !results.has(message.toolCallId)) {
|
|
8036
|
+
lines.push(`TOOL RESULT: ${summarizeToolResult(message)}`);
|
|
8037
|
+
}
|
|
8038
|
+
continue;
|
|
8037
8039
|
}
|
|
8038
|
-
if (message.role === "custom") {
|
|
8039
|
-
|
|
8040
|
+
if (message.role === "custom" && message.customType === "shadow-report") {
|
|
8041
|
+
appendText(lines, "SHADOW FEEDBACK", message.content);
|
|
8042
|
+
continue;
|
|
8040
8043
|
}
|
|
8041
|
-
if (message.role === "
|
|
8042
|
-
|
|
8044
|
+
if (message.role === "compactionSummary" || message.role === "branchSummary") {
|
|
8045
|
+
appendText(lines, "SUMMARY", message.content);
|
|
8043
8046
|
}
|
|
8044
|
-
|
|
8045
|
-
|
|
8047
|
+
}
|
|
8048
|
+
return `<main-agent-trajectory>
|
|
8049
|
+
${lines.join("\n")}
|
|
8050
|
+
</main-agent-trajectory>`;
|
|
8046
8051
|
}
|
|
8047
8052
|
function isThinkingBlock(value) {
|
|
8048
8053
|
return Boolean(value) && typeof value === "object" && value.type === "thinking";
|
|
8049
8054
|
}
|
|
8050
|
-
function
|
|
8051
|
-
|
|
8055
|
+
function appendText(lines, label, content) {
|
|
8056
|
+
const text = extractText(content);
|
|
8057
|
+
if (text) lines.push(`${label}: ${text}`);
|
|
8058
|
+
}
|
|
8059
|
+
function extractText(content) {
|
|
8060
|
+
if (typeof content === "string") return content;
|
|
8061
|
+
if (!Array.isArray(content)) return "";
|
|
8062
|
+
return content.filter((item) => Boolean(item) && typeof item === "object").filter((item) => item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n");
|
|
8063
|
+
}
|
|
8064
|
+
function compactJson(value) {
|
|
8065
|
+
if (value === void 0) return "";
|
|
8066
|
+
try {
|
|
8067
|
+
return JSON.stringify(value);
|
|
8068
|
+
} catch {
|
|
8069
|
+
return "[unserializable]";
|
|
8070
|
+
}
|
|
8052
8071
|
}
|
|
8053
8072
|
|
|
8054
8073
|
// src/shadow-runner.ts
|
|
@@ -8100,11 +8119,9 @@ var ShadowRunner = class {
|
|
|
8100
8119
|
try {
|
|
8101
8120
|
const model = resolveRunModel(request);
|
|
8102
8121
|
assertShadowModelAuth(model, request);
|
|
8103
|
-
const trajectory =
|
|
8104
|
-
assertTrajectoryFits(model, trajectory);
|
|
8122
|
+
const trajectory = serializeTrajectory(request.messages);
|
|
8123
|
+
assertTrajectoryFits(model, [trajectory]);
|
|
8105
8124
|
sessionManager = await createShadowSessionManager(request);
|
|
8106
|
-
for (const message of trajectory) sessionManager.appendMessage(message);
|
|
8107
|
-
sessionManager.appendMessage(buildShadowHandoff(model));
|
|
8108
8125
|
const boot = await this.bootstrapSession(request, model, controller, state, sessionManager);
|
|
8109
8126
|
state.session = boot.session;
|
|
8110
8127
|
missingTools = boot.missingTools;
|
|
@@ -8122,7 +8139,7 @@ var ShadowRunner = class {
|
|
|
8122
8139
|
void boot.session?.abort();
|
|
8123
8140
|
}, timeoutMs);
|
|
8124
8141
|
controller.signal.addEventListener("abort", () => void boot.session?.abort(), { once: true });
|
|
8125
|
-
await boot.session.prompt(
|
|
8142
|
+
await boot.session.prompt(buildShadowRequest(trajectory, request.shadow));
|
|
8126
8143
|
await boot.session.waitForIdle();
|
|
8127
8144
|
finalReason = runReason(state.timedOut, state.reported, controller.signal.aborted);
|
|
8128
8145
|
return buildRunResult({ reason: finalReason, durationMs: Date.now() - started, session: boot.session, baseMessageCount, missingTools, thinkingLevel });
|
|
@@ -8144,7 +8161,7 @@ var ShadowRunner = class {
|
|
|
8144
8161
|
cwd: request.cwd,
|
|
8145
8162
|
agentDir: request.agentDir,
|
|
8146
8163
|
settingsManager,
|
|
8147
|
-
systemPrompt: buildShadowSystemPrompt(request.mainSystemPrompt
|
|
8164
|
+
systemPrompt: buildShadowSystemPrompt(request.mainSystemPrompt),
|
|
8148
8165
|
// The inherited main system prompt already embeds APPEND_SYSTEM.md,
|
|
8149
8166
|
// AGENTS.md and skills; keep the loader from re-discovering them so the
|
|
8150
8167
|
// shadow context stays a strict subset (no duplication).
|