pi-shadow-mind 0.1.5

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.
Files changed (51) hide show
  1. package/DESIGN.md +493 -0
  2. package/README.md +58 -0
  3. package/dist/config.d.ts +18 -0
  4. package/dist/config.js +132 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/entity-store.d.ts +22 -0
  7. package/dist/entity-store.js +71 -0
  8. package/dist/entity-store.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +8537 -0
  11. package/dist/index.js.map +7 -0
  12. package/dist/management-tools.d.ts +4 -0
  13. package/dist/management-tools.js +130 -0
  14. package/dist/management-tools.js.map +1 -0
  15. package/dist/protocol.d.ts +5 -0
  16. package/dist/protocol.js +34 -0
  17. package/dist/protocol.js.map +1 -0
  18. package/dist/random.d.ts +2 -0
  19. package/dist/random.js +14 -0
  20. package/dist/random.js.map +1 -0
  21. package/dist/registry.d.ts +9 -0
  22. package/dist/registry.js +139 -0
  23. package/dist/registry.js.map +1 -0
  24. package/dist/report-batcher.d.ts +15 -0
  25. package/dist/report-batcher.js +49 -0
  26. package/dist/report-batcher.js.map +1 -0
  27. package/dist/runtime.d.ts +36 -0
  28. package/dist/runtime.js +274 -0
  29. package/dist/runtime.js.map +1 -0
  30. package/dist/scheduler.d.ts +10 -0
  31. package/dist/scheduler.js +46 -0
  32. package/dist/scheduler.js.map +1 -0
  33. package/dist/shadow-runner.d.ts +93 -0
  34. package/dist/shadow-runner.js +251 -0
  35. package/dist/shadow-runner.js.map +1 -0
  36. package/dist/shutdown-drain.d.ts +12 -0
  37. package/dist/shutdown-drain.js +22 -0
  38. package/dist/shutdown-drain.js.map +1 -0
  39. package/dist/summaries.d.ts +9 -0
  40. package/dist/summaries.js +56 -0
  41. package/dist/summaries.js.map +1 -0
  42. package/dist/trajectory.d.ts +7 -0
  43. package/dist/trajectory.js +33 -0
  44. package/dist/trajectory.js.map +1 -0
  45. package/dist/types.d.ts +64 -0
  46. package/dist/types.js +2 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/validation.d.ts +8 -0
  49. package/dist/validation.js +18 -0
  50. package/dist/validation.js.map +1 -0
  51. package/package.json +52 -0
@@ -0,0 +1,274 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { buildSessionContext, } from "@earendil-works/pi-coding-agent";
3
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
4
+ import { Text } from "@earendil-works/pi-tui";
5
+ import { ConfigStore } from "./config.js";
6
+ import { EntityStore } from "./entity-store.js";
7
+ import { registerManagementTools } from "./management-tools.js";
8
+ import { ShadowRegistry } from "./registry.js";
9
+ import { ReportBatcher, formatReportBatch } from "./report-batcher.js";
10
+ import { createRandom } from "./random.js";
11
+ import { decideHeartbeat } from "./scheduler.js";
12
+ import { ShadowRunner, resolveShadowTools } from "./shadow-runner.js";
13
+ import { waitForSettled } from "./shutdown-drain.js";
14
+ export class ShadowMindRuntime {
15
+ pi;
16
+ agentDir = getAgentDir();
17
+ configStore = new ConfigStore(this.agentDir);
18
+ registry = new ShadowRegistry(this.agentDir);
19
+ entityStore = new EntityStore(this.registry, this.configStore.configPath);
20
+ runner = new ShadowRunner();
21
+ active = new Map();
22
+ recentEvents = [];
23
+ batcher;
24
+ epoch = 0;
25
+ modelCalls = 0;
26
+ paused = false;
27
+ panelVisible = false;
28
+ latestContext;
29
+ diagnostics = [];
30
+ shadowCount = 0;
31
+ random = Math.random;
32
+ constructor(pi) {
33
+ this.pi = pi;
34
+ this.batcher = new ReportBatcher(this.configStore.current.resultBatchWindowMs, (reports) => this.deliverReports(reports));
35
+ }
36
+ register() {
37
+ registerManagementTools(this.pi, this.entityStore, () => this.configStore.current);
38
+ this.registerEvents();
39
+ this.registerUi();
40
+ }
41
+ registerEvents() {
42
+ this.pi.on("session_start", async (_event, ctx) => {
43
+ this.latestContext = ctx;
44
+ this.modelCalls = 0;
45
+ await this.configStore.initialize();
46
+ this.random = createRandom(this.configStore.current.randomSeed);
47
+ await this.registry.initialize();
48
+ await this.refresh(ctx);
49
+ this.record("session-config", { randomSeed: this.configStore.current.randomSeed ?? "random" });
50
+ this.updateStatus(ctx);
51
+ });
52
+ this.pi.on("input", (event, ctx) => {
53
+ this.latestContext = ctx;
54
+ if (event.source === "extension")
55
+ return;
56
+ this.epoch += 1;
57
+ this.abortAll("new-user-input");
58
+ });
59
+ this.pi.on("after_provider_response", (_event, ctx) => {
60
+ this.latestContext = ctx;
61
+ this.modelCalls += 1;
62
+ });
63
+ this.pi.on("turn_end", async (_event, ctx) => {
64
+ this.latestContext = ctx;
65
+ await this.onHeartbeat(ctx);
66
+ });
67
+ this.pi.on("session_shutdown", async (event, ctx) => {
68
+ this.latestContext = ctx;
69
+ if (event.reason === "quit" && (ctx.mode === "print" || ctx.mode === "json")) {
70
+ await this.drainHeadless(ctx);
71
+ }
72
+ this.epoch += 1;
73
+ this.abortAll("session-shutdown");
74
+ ctx.ui.setStatus("shadow-mind", undefined);
75
+ ctx.ui.setWidget("shadow-mind-panel", undefined);
76
+ });
77
+ }
78
+ registerUi() {
79
+ this.pi.registerCommand("shadow", {
80
+ description: "Show Shadow Mind status, or pause/resume it",
81
+ handler: async (args, ctx) => {
82
+ this.latestContext = ctx;
83
+ const command = args.trim().toLowerCase();
84
+ if (command === "pause") {
85
+ this.paused = true;
86
+ this.abortAll("paused");
87
+ ctx.ui.notify("Shadow Mind paused", "info");
88
+ }
89
+ else if (command === "resume") {
90
+ this.paused = false;
91
+ ctx.ui.notify("Shadow Mind resumed", "info");
92
+ }
93
+ else if (command === "status") {
94
+ await this.refresh(ctx);
95
+ ctx.ui.notify(this.statusLines().join("\n"), this.diagnostics.length ? "warning" : "info");
96
+ }
97
+ else if (command === "hide") {
98
+ this.panelVisible = false;
99
+ ctx.ui.setWidget("shadow-mind-panel", undefined);
100
+ }
101
+ else {
102
+ await this.refresh(ctx);
103
+ this.panelVisible = !this.panelVisible;
104
+ }
105
+ this.updateStatus(ctx);
106
+ },
107
+ });
108
+ this.pi.registerMessageRenderer("shadow-report", (message, _options, theme) => {
109
+ const content = typeof message.content === "string" ? message.content : "Shadow report";
110
+ const prefix = theme.fg("accent", "🐙 shadow · ");
111
+ return new Text(`${prefix}${content}`, 0, 0);
112
+ });
113
+ }
114
+ async onHeartbeat(ctx) {
115
+ await this.refresh(ctx);
116
+ if (this.paused || !ctx.model) {
117
+ this.record("heartbeat-skipped", { reason: this.paused ? "paused" : "no-model", modelCalls: this.modelCalls });
118
+ return;
119
+ }
120
+ const snapshot = await this.registry.load();
121
+ const fullModelId = `${ctx.model.provider}/${ctx.model.id}`;
122
+ const decision = decideHeartbeat({
123
+ heartbeatProbability: this.configStore.current.heartbeatProbability,
124
+ availableSlots: Math.max(0, this.configStore.current.maxParallelShadows - this.active.size),
125
+ shadows: snapshot.shadows,
126
+ activeShadowIds: new Set([...this.active.values()].map(({ shadow }) => shadow.id)),
127
+ mainModelId: fullModelId,
128
+ random: this.random,
129
+ });
130
+ this.record("heartbeat", {
131
+ modelCalls: this.modelCalls,
132
+ roll: decision.heartbeatRoll,
133
+ candidates: decision.candidates,
134
+ activated: decision.activated.map(({ shadow, roll }) => ({ id: shadow.id, roll })),
135
+ ...(decision.modelFiltered.length ? { modelFiltered: decision.modelFiltered } : {}),
136
+ ...(decision.runningExcluded.length ? { runningExcluded: decision.runningExcluded } : {}),
137
+ });
138
+ if (!decision.activated.length)
139
+ return;
140
+ const context = buildSessionContext(ctx.sessionManager.getEntries(), ctx.sessionManager.getLeafId());
141
+ const availableTools = new Set(this.pi.getAllTools().map((tool) => tool.name));
142
+ for (const { shadow } of decision.activated) {
143
+ this.launchShadow(ctx, shadow, ctx.model, fullModelId, context, availableTools);
144
+ }
145
+ }
146
+ launchShadow(ctx, shadow, mainModel, fullModelId, context, availableTools) {
147
+ const runId = randomUUID();
148
+ const runEpoch = this.epoch;
149
+ const { tools, missing } = resolveShadowTools(shadow.tools, availableTools);
150
+ this.active.set(runId, { shadow, epoch: runEpoch });
151
+ this.record("run-start", {
152
+ runId,
153
+ shadowId: shadow.id,
154
+ model: shadow.runWithModel ?? this.configStore.current.defaultShadowModel ?? fullModelId,
155
+ ...(missing.length ? { missingTools: missing } : {}),
156
+ });
157
+ this.updateStatus(ctx);
158
+ void this.runner.run({
159
+ shadow: structuredClone(shadow),
160
+ config: structuredClone(this.configStore.current),
161
+ epoch: runEpoch,
162
+ runId,
163
+ cwd: ctx.cwd,
164
+ agentDir: this.agentDir,
165
+ mainSystemPrompt: ctx.getSystemPrompt(),
166
+ messages: context.messages,
167
+ mainModel,
168
+ tools,
169
+ resolveModel: (id) => resolveModel(ctx, id),
170
+ modelAuthOk: (model) => ctx.modelRegistry.hasConfiguredAuth(model) || ctx.modelRegistry.isUsingOAuth(model),
171
+ mainThinkingLevel: ctx.thinkingLevel,
172
+ onReport: (report) => this.acceptReport(report),
173
+ }).then((result) => this.handleRunEnd(runId, shadow, result));
174
+ }
175
+ handleRunEnd(runId, shadow, result) {
176
+ this.active.delete(runId);
177
+ this.record("run-end", { runId, shadowId: shadow.id, ...result });
178
+ if (this.latestContext)
179
+ this.updateStatus(this.latestContext);
180
+ }
181
+ acceptReport(report) {
182
+ if (report.epoch !== this.epoch)
183
+ return;
184
+ this.batcher.add(report);
185
+ }
186
+ async deliverReports(reports) {
187
+ const current = reports.filter((report) => report.epoch === this.epoch);
188
+ if (!current.length)
189
+ return;
190
+ const content = formatReportBatch(current);
191
+ this.record("report-delivered", { runIds: current.map((report) => report.runId), count: current.length });
192
+ const idle = this.latestContext?.isIdle() ?? true;
193
+ this.pi.sendMessage({
194
+ customType: "shadow-report",
195
+ content,
196
+ display: true,
197
+ details: { reports: current.map(({ shadowId, runId }) => ({ shadowId, runId })) },
198
+ }, { triggerTurn: true, deliverAs: idle ? "followUp" : "steer" });
199
+ }
200
+ async refresh(ctx) {
201
+ const config = await this.configStore.reload();
202
+ const registry = await this.registry.load();
203
+ this.batcher.setWindow(config.config.resultBatchWindowMs);
204
+ this.shadowCount = registry.shadows.length;
205
+ this.diagnostics = [
206
+ ...(config.error ? [`config: ${config.error}`] : []),
207
+ ...registry.diagnostics.map((item) => `${item.filePath}: ${item.message}`),
208
+ ];
209
+ this.updateStatus(ctx);
210
+ }
211
+ abortAll(reason) {
212
+ this.runner.abortAll();
213
+ this.batcher.clear();
214
+ this.record("runs-aborted", { reason, count: this.active.size });
215
+ }
216
+ async drainHeadless(ctx) {
217
+ if (this.active.size === 0 && !this.batcher.hasPending)
218
+ return;
219
+ const timeoutMs = this.configStore.current.headlessDrainTimeoutSeconds * 1000;
220
+ this.record("headless-drain-start", { timeoutMs, active: this.active.size });
221
+ const result = await waitForSettled({
222
+ timeoutMs,
223
+ isSettled: () => this.active.size === 0
224
+ && !this.batcher.hasPending
225
+ && ctx.isIdle()
226
+ && !ctx.hasPendingMessages(),
227
+ });
228
+ this.record(result.settled ? "headless-drain-complete" : "headless-drain-timeout", {
229
+ durationMs: result.durationMs,
230
+ active: this.active.size,
231
+ });
232
+ if (!result.settled)
233
+ this.abortAll("headless-drain-timeout");
234
+ }
235
+ record(kind, data) {
236
+ const event = { at: new Date().toISOString(), kind, epoch: this.epoch, data };
237
+ this.recentEvents.push(event);
238
+ if (this.recentEvents.length > 20)
239
+ this.recentEvents.shift();
240
+ this.pi.appendEntry("shadow-mind-event", event);
241
+ }
242
+ updateStatus(ctx) {
243
+ ctx.ui.setStatus("shadow-mind", `🐙 ${this.active.size}${this.paused ? " paused" : ""}${this.diagnostics.length || this.hasRecentRunErrors() ? " !" : ""}`);
244
+ if (this.panelVisible)
245
+ ctx.ui.setWidget("shadow-mind-panel", this.statusLines(), { placement: "aboveEditor" });
246
+ }
247
+ hasRecentRunErrors() {
248
+ return this.recentEvents.slice(-3).some((event) => event.kind === "run-end" && (event.data?.reason === "error" || event.data?.reason === "timeout"));
249
+ }
250
+ statusLines() {
251
+ const config = this.configStore.current;
252
+ return [
253
+ `🐙 Shadow Mind · ${this.paused ? "paused" : "active"} · running ${this.active.size}/${config.maxParallelShadows}`,
254
+ `heartbeat ${formatNumber(config.heartbeatProbability)} · batch ${config.resultBatchWindowMs}ms · timeout ${config.defaultShadowTimeoutSeconds}s · drain ${config.headlessDrainTimeoutSeconds}s · thinking ${config.defaultThinkingLevel}`,
255
+ `definitions: ${this.shadowCount} valid · ${this.diagnostics.length} invalid`,
256
+ ...this.recentEvents.slice(-5).map((event) => {
257
+ const failed = event.kind === "run-end" && (event.data?.reason === "error" || event.data?.reason === "timeout");
258
+ const detail = failed ? ` ${event.data?.error ?? event.data?.reason}` : "";
259
+ return `${event.at.slice(11, 19)} ${event.kind}${detail}`;
260
+ }),
261
+ "Commands: /shadow pause | resume | status | hide",
262
+ ];
263
+ }
264
+ }
265
+ function resolveModel(ctx, fullId) {
266
+ const separator = fullId.indexOf("/");
267
+ if (separator <= 0 || separator === fullId.length - 1)
268
+ return undefined;
269
+ return ctx.modelRegistry.find(fullId.slice(0, separator), fullId.slice(separator + 1));
270
+ }
271
+ function formatNumber(value) {
272
+ return Number(value.toFixed(3)).toString();
273
+ }
274
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { HeartbeatDecision, ShadowDefinition } from "./types.js";
2
+ export declare function decideHeartbeat(options: {
3
+ heartbeatProbability: number;
4
+ availableSlots: number;
5
+ shadows: readonly ShadowDefinition[];
6
+ activeShadowIds: ReadonlySet<string>;
7
+ mainModelId: string;
8
+ random?: () => number;
9
+ }): HeartbeatDecision;
10
+ export declare function matchesModel(shadow: ShadowDefinition, fullModelId: string): boolean;
@@ -0,0 +1,46 @@
1
+ export function decideHeartbeat(options) {
2
+ const random = options.random ?? Math.random;
3
+ const heartbeatRoll = random();
4
+ if (heartbeatRoll >= options.heartbeatProbability || options.availableSlots <= 0) {
5
+ return { heartbeatRoll, activated: [], candidates: [], modelFiltered: [], runningExcluded: [] };
6
+ }
7
+ const modelFiltered = [];
8
+ const runningExcluded = [];
9
+ const rolls = options.shadows
10
+ .filter((shadow) => {
11
+ if (!shadow.enabled)
12
+ return false;
13
+ if (options.activeShadowIds.has(shadow.id)) {
14
+ runningExcluded.push(shadow.id);
15
+ return false;
16
+ }
17
+ if (!matchesModel(shadow, options.mainModelId)) {
18
+ modelFiltered.push(shadow.id);
19
+ return false;
20
+ }
21
+ return true;
22
+ })
23
+ .map((shadow) => ({ shadow, roll: random() }));
24
+ const hits = rolls.filter(({ shadow, roll }) => roll < shadow.activationProbability);
25
+ const selected = sample(hits, Math.min(options.availableSlots, hits.length), random);
26
+ const selectedIds = new Set(selected.map(({ shadow }) => shadow.id));
27
+ return {
28
+ heartbeatRoll,
29
+ activated: selected,
30
+ candidates: rolls.map(({ shadow, roll }) => ({ shadowId: shadow.id, roll, selected: selectedIds.has(shadow.id) })),
31
+ modelFiltered,
32
+ runningExcluded,
33
+ };
34
+ }
35
+ export function matchesModel(shadow, fullModelId) {
36
+ return shadow.activeForModels.includes("*") || shadow.activeForModels.includes(fullModelId);
37
+ }
38
+ function sample(values, count, random) {
39
+ const copy = [...values];
40
+ for (let index = copy.length - 1; index > 0; index -= 1) {
41
+ const swap = Math.floor(random() * (index + 1));
42
+ [copy[index], copy[swap]] = [copy[swap], copy[index]];
43
+ }
44
+ return copy.slice(0, count);
45
+ }
46
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { createAgentSession } from "@earendil-works/pi-coding-agent";
2
+ import type { Model } from "@earendil-works/pi-ai";
3
+ import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
4
+ import type { ShadowConfig, ShadowDefinition, ShadowReport } from "./types.js";
5
+ /**
6
+ * Resolve the final tool allowlist for a Shadow run against the main session's
7
+ * tool registry, per the design contract:
8
+ *
9
+ * - the final set is `readOnlyTools defaults + whitelist + report_to_main`;
10
+ * - names that do not exist in the main session are dropped (ignored) and
11
+ * returned as `missing`, while the rest are provided normally;
12
+ * - the built-in `report_to_main` is always kept.
13
+ */
14
+ export declare function resolveShadowTools(whitelist: readonly string[], available: ReadonlySet<string>, defaults?: readonly string[]): {
15
+ tools: string[];
16
+ missing: string[];
17
+ };
18
+ export interface ShadowRunRequest {
19
+ shadow: ShadowDefinition;
20
+ config: ShadowConfig;
21
+ epoch: number;
22
+ runId: string;
23
+ cwd: string;
24
+ agentDir: string;
25
+ mainSystemPrompt: string;
26
+ messages: readonly Record<string, unknown>[];
27
+ mainModel: Model<any>;
28
+ /** Final tool allowlist, already resolved against the main session registry. */
29
+ tools: string[];
30
+ resolveModel(fullModelId: string): Model<any> | undefined;
31
+ /** Optional auth check for explicitly configured shadow models (runtime supplies it). */
32
+ modelAuthOk?(model: Model<any>): boolean;
33
+ /** The main session's effective thinking level at activation, used as the final fallback. */
34
+ mainThinkingLevel?: ThinkingLevel;
35
+ onReport(report: ShadowReport): void;
36
+ }
37
+ export interface ToolStat {
38
+ tool: string;
39
+ calls: number;
40
+ failures: number;
41
+ }
42
+ export interface ShadowRunResult {
43
+ reason: "report" | "silent" | "timeout" | "aborted" | "error";
44
+ error?: string;
45
+ durationMs: number;
46
+ toolNames: string[];
47
+ /** Requested tools that did not materialize in the shadow session. */
48
+ missingTools: string[];
49
+ toolCalls: number;
50
+ toolFailures: number;
51
+ /** Per-tool usage summary (called tools only). */
52
+ toolStats: ToolStat[];
53
+ /** The thinking level actually used for this run (after fallback resolution). */
54
+ thinkingLevel?: string;
55
+ sessionFile?: string;
56
+ }
57
+ type ShadowSession = Awaited<ReturnType<typeof createAgentSession>>["session"];
58
+ /** Unify the three run() return paths so result shape changes touch one place. */
59
+ export declare function buildRunResult(options: {
60
+ reason: ShadowRunResult["reason"];
61
+ error?: string;
62
+ durationMs: number;
63
+ session?: ShadowSession;
64
+ /** Message count before the shadow's own run started; only later tool results are counted. */
65
+ baseMessageCount: number;
66
+ missingTools: string[];
67
+ thinkingLevel?: string;
68
+ }): ShadowRunResult;
69
+ export declare class ShadowRunner {
70
+ private readonly controllers;
71
+ run(request: ShadowRunRequest): Promise<ShadowRunResult>;
72
+ /** Build the shadow AgentSession (loader, report tool, allowlist) and validate it. */
73
+ private bootstrapSession;
74
+ abort(runId: string): void;
75
+ abortAll(): void;
76
+ }
77
+ export declare function toolMetrics(messages: readonly {
78
+ role?: string;
79
+ toolName?: string;
80
+ isError?: boolean;
81
+ }[]): {
82
+ toolCalls: number;
83
+ toolFailures: number;
84
+ toolStats: ToolStat[];
85
+ };
86
+ /**
87
+ * Resolve the thinking level for this run: shadow config → plugin default →
88
+ * the main session's effective level. The first candidate the model supports
89
+ * (per its thinkingLevelMap; null marks unsupported, a missing key or map means
90
+ * provider default, i.e. supported) wins. Fails only when none are supported.
91
+ */
92
+ export declare function resolveRunThinkingLevel(model: Model<any>, request: ShadowRunRequest): ThinkingLevel;
93
+ export {};