pi-shadow-mind 0.1.7 → 0.1.9

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 CHANGED
@@ -233,7 +233,7 @@ Main system prompt
233
233
  → one user message: plain-text Main trajectory + Shadow protocol + Shadow definition + kickoff
234
234
  ```
235
235
 
236
- 公共协议保持最小,只说明:当前实例是一次性的并行认知核心;消息历史已经过净化;它可以依照自身职责独立观察或执行工作;需要向 Main 同步结果时调用 `report_to_main`,该调用会立即结束当前 loop;没有需要同步的内容时可以正常结束。具体关注点、任务线和表达方式全部由 Shadow Markdown 决定。
236
+ 公共协议保持最小,只说明:当前实例是一次性的并行认知核心;消息历史已经过净化;它可以依照自身职责独立观察或执行工作;需要向 Main 同步结果时调用 `report_to_main`,该调用会立即结束当前 loop;没有需要同步的内容时可以正常结束。kickoff 首先要求判断轨迹是否与当前 Shadow 职责相关:无关时只回复固定词 `NOT_RELEVANT` 并立即结束,不调用任何工具;相关时才继续执行职责。具体关注点、任务线和表达方式全部由 Shadow Markdown 决定。
237
237
 
238
238
  轨迹范围覆盖当前 Main Session 从开始到激活时刻的全部历史,而不只包含当前用户 epoch。这样 Shadow 能看到早期用户约束和已经形成的会话决策。epoch 只用于判断 Shadow 的迟到结果能否介入,不参与轨迹裁剪。
239
239
 
package/README.md CHANGED
@@ -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.7
26
+ pi install npm:pi-shadow-mind@0.1.9
27
27
  # or, after unpacking the ZIP
28
28
  pi install ./pi-shadow-mind-0.1.0
29
29
  ```
package/dist/index.js CHANGED
@@ -7918,6 +7918,35 @@ function sample(values, count, random) {
7918
7918
  return copy.slice(0, count);
7919
7919
  }
7920
7920
 
7921
+ // src/session-lifetime.ts
7922
+ var STALE_CONTEXT_MARKER = "This extension ctx is stale after session replacement or reload";
7923
+ var SessionLifetime = class {
7924
+ active = false;
7925
+ activate() {
7926
+ this.active = true;
7927
+ }
7928
+ deactivate() {
7929
+ this.active = false;
7930
+ }
7931
+ get isActive() {
7932
+ return this.active;
7933
+ }
7934
+ run(action) {
7935
+ if (!this.active) return false;
7936
+ try {
7937
+ action();
7938
+ return true;
7939
+ } catch (error) {
7940
+ if (!isStaleExtensionContextError(error)) throw error;
7941
+ this.active = false;
7942
+ return false;
7943
+ }
7944
+ }
7945
+ };
7946
+ function isStaleExtensionContextError(error) {
7947
+ return error instanceof Error && error.message.includes(STALE_CONTEXT_MARKER);
7948
+ }
7949
+
7921
7950
  // src/shadow-runner.ts
7922
7951
  import { mkdir as mkdir3 } from "node:fs/promises";
7923
7952
  import { fileURLToPath } from "node:url";
@@ -7938,7 +7967,10 @@ var RUNTIME_PROTOCOL = `You are a Shadow Mind running beside the main agent.
7938
7967
  Work independently on the responsibility below using the supplied, sanitized main-session trajectory.
7939
7968
  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
7969
  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
- Calling report_to_main ends this run immediately. If there is nothing useful to report, finish silently.`;
7970
+ Calling report_to_main ends this run immediately. If relevant work produces nothing worth reporting, finish silently.`;
7971
+ var KICKOFF = `First decide whether the trajectory is relevant to this Shadow Mind's responsibility.
7972
+ If it is unrelated, reply exactly NOT_RELEVANT and stop immediately. Do not call any tool, including report_to_main.
7973
+ If it is relevant, perform the Shadow Mind's responsibility now. Call report_to_main only when the main agent should receive a result.`;
7942
7974
  function buildShadowSystemPrompt(mainSystemPrompt) {
7943
7975
  return mainSystemPrompt;
7944
7976
  }
@@ -7951,7 +7983,7 @@ ${RUNTIME_PROTOCOL}
7951
7983
  ${shadow.prompt}
7952
7984
  </shadow-mind>
7953
7985
 
7954
- Begin this Shadow Mind run now. Inspect the trajectory and act only when useful.`;
7986
+ ${KICKOFF}`;
7955
7987
  }
7956
7988
 
7957
7989
  // src/summaries.ts
@@ -8314,6 +8346,7 @@ var ShadowMindRuntime = class {
8314
8346
  active = /* @__PURE__ */ new Map();
8315
8347
  recentEvents = [];
8316
8348
  batcher;
8349
+ sessionLifetime = new SessionLifetime();
8317
8350
  epoch = 0;
8318
8351
  modelCalls = 0;
8319
8352
  paused = false;
@@ -8329,6 +8362,7 @@ var ShadowMindRuntime = class {
8329
8362
  }
8330
8363
  registerEvents() {
8331
8364
  this.pi.on("session_start", async (_event, ctx) => {
8365
+ this.sessionLifetime.activate();
8332
8366
  this.latestContext = ctx;
8333
8367
  this.modelCalls = 0;
8334
8368
  await this.configStore.initialize();
@@ -8361,6 +8395,7 @@ var ShadowMindRuntime = class {
8361
8395
  this.abortAll("session-shutdown");
8362
8396
  ctx.ui.setStatus("shadow-mind", void 0);
8363
8397
  ctx.ui.setWidget("shadow-mind-panel", void 0);
8398
+ this.sessionLifetime.deactivate();
8364
8399
  });
8365
8400
  }
8366
8401
  registerUi() {
@@ -8453,12 +8488,20 @@ var ShadowMindRuntime = class {
8453
8488
  modelAuthOk: (model) => ctx.modelRegistry.hasConfiguredAuth(model) || ctx.modelRegistry.isUsingOAuth(model),
8454
8489
  mainThinkingLevel: ctx.thinkingLevel,
8455
8490
  onReport: (report) => this.acceptReport(report)
8456
- }).then((result) => this.handleRunEnd(runId, shadow, result));
8491
+ }).then((result) => this.handleRunEnd(runId, shadow, result)).catch((error) => {
8492
+ this.active.delete(runId);
8493
+ this.record("run-end", {
8494
+ runId,
8495
+ shadowId: shadow.id,
8496
+ reason: "error",
8497
+ error: error instanceof Error ? error.message : String(error)
8498
+ });
8499
+ });
8457
8500
  }
8458
8501
  handleRunEnd(runId, shadow, result) {
8459
8502
  this.active.delete(runId);
8460
8503
  this.record("run-end", { runId, shadowId: shadow.id, ...result });
8461
- if (this.latestContext) this.updateStatus(this.latestContext);
8504
+ if (this.latestContext && this.sessionLifetime.isActive) this.updateStatus(this.latestContext);
8462
8505
  }
8463
8506
  acceptReport(report) {
8464
8507
  if (report.epoch !== this.epoch) return;
@@ -8470,12 +8513,14 @@ var ShadowMindRuntime = class {
8470
8513
  const content = formatReportBatch(current);
8471
8514
  this.record("report-delivered", { runIds: current.map((report) => report.runId), count: current.length });
8472
8515
  const idle = this.latestContext?.isIdle() ?? true;
8473
- this.pi.sendMessage({
8474
- customType: "shadow-report",
8475
- content,
8476
- display: true,
8477
- details: { reports: current.map(({ shadowId, runId }) => ({ shadowId, runId })) }
8478
- }, { triggerTurn: true, deliverAs: idle ? "followUp" : "steer" });
8516
+ this.sessionLifetime.run(() => {
8517
+ this.pi.sendMessage({
8518
+ customType: "shadow-report",
8519
+ content,
8520
+ display: true,
8521
+ details: { reports: current.map(({ shadowId, runId }) => ({ shadowId, runId })) }
8522
+ }, { triggerTurn: true, deliverAs: idle ? "followUp" : "steer" });
8523
+ });
8479
8524
  }
8480
8525
  async refresh(ctx) {
8481
8526
  const config = await this.configStore.reload();
@@ -8511,11 +8556,13 @@ var ShadowMindRuntime = class {
8511
8556
  const event = { at: (/* @__PURE__ */ new Date()).toISOString(), kind, epoch: this.epoch, data };
8512
8557
  this.recentEvents.push(event);
8513
8558
  if (this.recentEvents.length > 20) this.recentEvents.shift();
8514
- this.pi.appendEntry("shadow-mind-event", event);
8559
+ this.sessionLifetime.run(() => this.pi.appendEntry("shadow-mind-event", event));
8515
8560
  }
8516
8561
  updateStatus(ctx) {
8517
- ctx.ui.setStatus("shadow-mind", `\u{1F419} ${this.active.size}${this.paused ? " paused" : ""}${this.diagnostics.length || this.hasRecentRunErrors() ? " !" : ""}`);
8518
- if (this.panelVisible) ctx.ui.setWidget("shadow-mind-panel", this.statusLines(), { placement: "aboveEditor" });
8562
+ this.sessionLifetime.run(() => {
8563
+ ctx.ui.setStatus("shadow-mind", `\u{1F419} ${this.active.size}${this.paused ? " paused" : ""}${this.diagnostics.length || this.hasRecentRunErrors() ? " !" : ""}`);
8564
+ if (this.panelVisible) ctx.ui.setWidget("shadow-mind-panel", this.statusLines(), { placement: "aboveEditor" });
8565
+ });
8519
8566
  }
8520
8567
  hasRecentRunErrors() {
8521
8568
  return this.recentEvents.slice(-3).some((event) => event.kind === "run-end" && (event.data?.reason === "error" || event.data?.reason === "timeout"));