pi-shadow-mind 0.1.19 → 0.1.21
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-Evolution.md +24 -0
- package/DESIGN.md +17 -8
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/dist/index.js +417 -37
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,11 +6,8 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/runtime.ts
|
|
8
8
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
9
|
-
import {
|
|
10
|
-
buildSessionContext
|
|
11
|
-
} from "@earendil-works/pi-coding-agent";
|
|
12
9
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
10
|
+
import { Text as Text2 } from "@earendil-works/pi-tui";
|
|
14
11
|
|
|
15
12
|
// src/final-response-queue.ts
|
|
16
13
|
var FinalResponseQueue = class {
|
|
@@ -29,6 +26,7 @@ var FinalResponseQueue = class {
|
|
|
29
26
|
this.pending.length = 0;
|
|
30
27
|
}
|
|
31
28
|
pump() {
|
|
29
|
+
if (this.options.canLaunch && !this.options.canLaunch()) return;
|
|
32
30
|
while (this.options.activeCount() < this.options.maxParallel() && this.pending.length > 0) {
|
|
33
31
|
const epoch = this.options.currentEpoch();
|
|
34
32
|
const activeShadowIds = this.options.activeShadowIds();
|
|
@@ -52,6 +50,7 @@ var CompletionReview = class {
|
|
|
52
50
|
maxParallel: () => this.host.maxParallel(),
|
|
53
51
|
activeCount: () => this.host.activeCount(),
|
|
54
52
|
activeShadowIds: () => this.host.activeShadowIds(),
|
|
53
|
+
canLaunch: () => this.host.canLaunch ? this.host.canLaunch() : true,
|
|
55
54
|
launch: ({ job, run }) => host.launch(job, run)
|
|
56
55
|
});
|
|
57
56
|
}
|
|
@@ -78,7 +77,8 @@ var CompletionReview = class {
|
|
|
78
77
|
requestId: request.id,
|
|
79
78
|
epoch: request.epoch,
|
|
80
79
|
remaining: jobs.length,
|
|
81
|
-
reports: []
|
|
80
|
+
reports: [],
|
|
81
|
+
shadowIds: jobs.map((job) => job.shadowId)
|
|
82
82
|
};
|
|
83
83
|
this.queue.enqueue(
|
|
84
84
|
jobs.map((job) => {
|
|
@@ -102,7 +102,9 @@ var CompletionReview = class {
|
|
|
102
102
|
this.state.remaining -= 1;
|
|
103
103
|
if (this.state.remaining > 0) return;
|
|
104
104
|
const reports = this.state.reports;
|
|
105
|
+
const shadowIds = this.state.shadowIds;
|
|
105
106
|
this.retire(requestId);
|
|
107
|
+
this.host.onReviewCompleted?.(shadowIds);
|
|
106
108
|
if (reports.length > 0) this.host.deliver(reports);
|
|
107
109
|
}
|
|
108
110
|
cancel(request) {
|
|
@@ -145,6 +147,12 @@ function isNonEmptyString(value) {
|
|
|
145
147
|
function isStringArray(value) {
|
|
146
148
|
return Array.isArray(value) && value.every((item) => isNonEmptyString(item));
|
|
147
149
|
}
|
|
150
|
+
function cleanStringArray(value, fallback, name) {
|
|
151
|
+
if (value === void 0) return [...fallback];
|
|
152
|
+
if (!isStringArray(value))
|
|
153
|
+
throw new Error(`${name} must be an array of non-empty strings`);
|
|
154
|
+
return [...new Set(value.map((item) => item.trim()))];
|
|
155
|
+
}
|
|
148
156
|
function isThinkingLevel(value) {
|
|
149
157
|
return typeof value === "string" && THINKING_LEVELS.has(value);
|
|
150
158
|
}
|
|
@@ -152,6 +160,7 @@ function isThinkingLevel(value) {
|
|
|
152
160
|
// src/config.ts
|
|
153
161
|
var DEFAULT_CONFIG = {
|
|
154
162
|
heartbeatProbability: 1 / 3,
|
|
163
|
+
heartbeatTools: [],
|
|
155
164
|
maxParallelShadows: 2,
|
|
156
165
|
defaultShadowTimeoutSeconds: 300,
|
|
157
166
|
headlessDrainTimeoutSeconds: 120,
|
|
@@ -164,6 +173,7 @@ function parseConfig(input) {
|
|
|
164
173
|
}
|
|
165
174
|
const value = input;
|
|
166
175
|
const probability = numberInRange(value.heartbeat_probability, 0, 1, DEFAULT_CONFIG.heartbeatProbability, "heartbeat_probability");
|
|
176
|
+
const heartbeatTools = cleanStringArray(value.heartbeat_tools, DEFAULT_CONFIG.heartbeatTools, "heartbeat_tools");
|
|
167
177
|
const parallel = positiveInteger(value.max_parallel_shadows, DEFAULT_CONFIG.maxParallelShadows, "max_parallel_shadows");
|
|
168
178
|
const timeout = positiveNumber(value.default_shadow_timeout_seconds, DEFAULT_CONFIG.defaultShadowTimeoutSeconds, "default_shadow_timeout_seconds");
|
|
169
179
|
const drainTimeout = positiveNumber(value.headless_drain_timeout_seconds, DEFAULT_CONFIG.headlessDrainTimeoutSeconds, "headless_drain_timeout_seconds");
|
|
@@ -176,6 +186,7 @@ function parseConfig(input) {
|
|
|
176
186
|
}
|
|
177
187
|
return {
|
|
178
188
|
heartbeatProbability: probability,
|
|
189
|
+
heartbeatTools,
|
|
179
190
|
maxParallelShadows: parallel,
|
|
180
191
|
defaultShadowTimeoutSeconds: timeout,
|
|
181
192
|
headlessDrainTimeoutSeconds: drainTimeout,
|
|
@@ -188,6 +199,7 @@ function parseConfig(input) {
|
|
|
188
199
|
function serializeConfig(config) {
|
|
189
200
|
return `${JSON.stringify({
|
|
190
201
|
heartbeat_probability: config.heartbeatProbability,
|
|
202
|
+
...config.heartbeatTools && config.heartbeatTools.length ? { heartbeat_tools: config.heartbeatTools } : {},
|
|
191
203
|
max_parallel_shadows: config.maxParallelShadows,
|
|
192
204
|
default_shadow_timeout_seconds: config.defaultShadowTimeoutSeconds,
|
|
193
205
|
headless_drain_timeout_seconds: config.headlessDrainTimeoutSeconds,
|
|
@@ -6968,7 +6980,7 @@ function parseShadowMarkdown(source, filePath) {
|
|
|
6968
6980
|
"activation_probability"
|
|
6969
6981
|
),
|
|
6970
6982
|
trigger: triggerArray(value.trigger, ["heartbeat"], "trigger"),
|
|
6971
|
-
activeForModels:
|
|
6983
|
+
activeForModels: cleanStringArray(
|
|
6972
6984
|
value.active_for_models,
|
|
6973
6985
|
["*"],
|
|
6974
6986
|
"active_for_models"
|
|
@@ -6979,7 +6991,16 @@ function parseShadowMarkdown(source, filePath) {
|
|
|
6979
6991
|
value.timeout_seconds,
|
|
6980
6992
|
"timeout_seconds"
|
|
6981
6993
|
),
|
|
6982
|
-
|
|
6994
|
+
finalResponseRounds: optionalNonNegativeInteger(
|
|
6995
|
+
value.final_response_rounds,
|
|
6996
|
+
"final_response_rounds"
|
|
6997
|
+
),
|
|
6998
|
+
activationTools: cleanStringArray(
|
|
6999
|
+
value.activation_tools,
|
|
7000
|
+
[],
|
|
7001
|
+
"activation_tools"
|
|
7002
|
+
),
|
|
7003
|
+
tools: cleanStringArray(value.tools, [], "tools"),
|
|
6983
7004
|
prompt,
|
|
6984
7005
|
filePath
|
|
6985
7006
|
};
|
|
@@ -7010,15 +7031,15 @@ function optionalPositiveNumber(value, name) {
|
|
|
7010
7031
|
throw new Error(`${name} must be positive`);
|
|
7011
7032
|
return value;
|
|
7012
7033
|
}
|
|
7013
|
-
function
|
|
7014
|
-
if (value === void 0) return
|
|
7015
|
-
if (!
|
|
7016
|
-
throw new Error(`${name} must be
|
|
7017
|
-
return
|
|
7034
|
+
function optionalNonNegativeInteger(value, name) {
|
|
7035
|
+
if (value === void 0) return void 0;
|
|
7036
|
+
if (!isFiniteNumber(value) || !Number.isInteger(value) || value < 0)
|
|
7037
|
+
throw new Error(`${name} must be a non-negative integer`);
|
|
7038
|
+
return value;
|
|
7018
7039
|
}
|
|
7019
7040
|
function triggerArray(value, fallback, name) {
|
|
7020
7041
|
const values = typeof value === "string" ? [value] : value;
|
|
7021
|
-
const items =
|
|
7042
|
+
const items = cleanStringArray(values, fallback, name);
|
|
7022
7043
|
const allowed = new Set(SHADOW_TRIGGERS);
|
|
7023
7044
|
const invalid = items.filter((item) => !allowed.has(item));
|
|
7024
7045
|
if (invalid.length)
|
|
@@ -7104,6 +7125,8 @@ function serializeShadow(shadow) {
|
|
|
7104
7125
|
...shadow.runWithModel ? { run_with_model: shadow.runWithModel } : {},
|
|
7105
7126
|
...shadow.thinkingLevel ? { thinking_level: shadow.thinkingLevel } : {},
|
|
7106
7127
|
...shadow.timeoutSeconds !== void 0 ? { timeout_seconds: shadow.timeoutSeconds } : {},
|
|
7128
|
+
...shadow.finalResponseRounds !== void 0 ? { final_response_rounds: shadow.finalResponseRounds } : {},
|
|
7129
|
+
...shadow.activationTools && shadow.activationTools.length ? { activation_tools: shadow.activationTools } : {},
|
|
7107
7130
|
tools: shadow.tools ?? []
|
|
7108
7131
|
};
|
|
7109
7132
|
return `---
|
|
@@ -7114,7 +7137,9 @@ ${(shadow.prompt ?? "").trim()}
|
|
|
7114
7137
|
`;
|
|
7115
7138
|
}
|
|
7116
7139
|
function describeShadow(shadow) {
|
|
7117
|
-
|
|
7140
|
+
const actTools = shadow.activationTools.length ? ` act_tools=${shadow.activationTools.join(",")}` : "";
|
|
7141
|
+
const finalRounds = shadow.finalResponseRounds ? ` final_rounds=${shadow.finalResponseRounds}` : "";
|
|
7142
|
+
return `${shadow.enabled ? "enabled" : "disabled"} ${shadow.id} (${shadow.name}) p=${shadow.activationProbability} trigger=${shadow.trigger.join(",")} models=${shadow.activeForModels.join(",")}${finalRounds}${actTools} tools=${shadow.tools.join(",") || "default"} file=${basename2(shadow.filePath)}`;
|
|
7118
7143
|
}
|
|
7119
7144
|
function definedOnly(value) {
|
|
7120
7145
|
return Object.fromEntries(
|
|
@@ -7122,6 +7147,27 @@ function definedOnly(value) {
|
|
|
7122
7147
|
);
|
|
7123
7148
|
}
|
|
7124
7149
|
|
|
7150
|
+
// src/final-response-budget.ts
|
|
7151
|
+
var FinalResponseBudget = class {
|
|
7152
|
+
counts = /* @__PURE__ */ new Map();
|
|
7153
|
+
canRun(shadow) {
|
|
7154
|
+
const limit = shadow.finalResponseRounds ?? 0;
|
|
7155
|
+
if (limit <= 0) return true;
|
|
7156
|
+
return this.used(shadow.id) < limit;
|
|
7157
|
+
}
|
|
7158
|
+
commit(shadowIds) {
|
|
7159
|
+
for (const id of shadowIds) {
|
|
7160
|
+
this.counts.set(id, this.used(id) + 1);
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
used(shadowId) {
|
|
7164
|
+
return this.counts.get(shadowId) ?? 0;
|
|
7165
|
+
}
|
|
7166
|
+
reset() {
|
|
7167
|
+
this.counts.clear();
|
|
7168
|
+
}
|
|
7169
|
+
};
|
|
7170
|
+
|
|
7125
7171
|
// src/management-tools.ts
|
|
7126
7172
|
import { Type } from "typebox";
|
|
7127
7173
|
var ID = Type.String({ pattern: "^[a-z0-9][a-z0-9_-]*$" });
|
|
@@ -7149,6 +7195,8 @@ var SHADOW_FIELDS = {
|
|
|
7149
7195
|
run_with_model: Type.Optional(Type.String()),
|
|
7150
7196
|
thinking_level: Type.Optional(THINKING),
|
|
7151
7197
|
timeout_seconds: Type.Optional(Type.Number({ exclusiveMinimum: 0 })),
|
|
7198
|
+
final_response_rounds: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
7199
|
+
activation_tools: Type.Optional(Type.Array(Type.String())),
|
|
7152
7200
|
tools: Type.Optional(Type.Array(Type.String())),
|
|
7153
7201
|
prompt: Type.Optional(Type.String())
|
|
7154
7202
|
};
|
|
@@ -7270,6 +7318,7 @@ function configWriteTool(store, getConfig) {
|
|
|
7270
7318
|
heartbeat_probability: Type.Optional(
|
|
7271
7319
|
Type.Number({ minimum: 0, maximum: 1 })
|
|
7272
7320
|
),
|
|
7321
|
+
heartbeat_tools: Type.Optional(Type.Array(Type.String())),
|
|
7273
7322
|
max_parallel_shadows: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
7274
7323
|
default_shadow_timeout_seconds: Type.Optional(
|
|
7275
7324
|
Type.Number({ exclusiveMinimum: 0 })
|
|
@@ -7289,6 +7338,7 @@ function configWriteTool(store, getConfig) {
|
|
|
7289
7338
|
const raw = params;
|
|
7290
7339
|
const next = parseConfig({
|
|
7291
7340
|
heartbeat_probability: raw.heartbeat_probability ?? current.heartbeatProbability,
|
|
7341
|
+
heartbeat_tools: raw.heartbeat_tools ?? current.heartbeatTools,
|
|
7292
7342
|
max_parallel_shadows: raw.max_parallel_shadows ?? current.maxParallelShadows,
|
|
7293
7343
|
default_shadow_timeout_seconds: raw.default_shadow_timeout_seconds ?? current.defaultShadowTimeoutSeconds,
|
|
7294
7344
|
headless_drain_timeout_seconds: raw.headless_drain_timeout_seconds ?? current.headlessDrainTimeoutSeconds,
|
|
@@ -7336,6 +7386,8 @@ function toPatch(raw) {
|
|
|
7336
7386
|
runWithModel: raw.run_with_model,
|
|
7337
7387
|
thinkingLevel: raw.thinking_level,
|
|
7338
7388
|
timeoutSeconds: raw.timeout_seconds,
|
|
7389
|
+
finalResponseRounds: raw.final_response_rounds,
|
|
7390
|
+
activationTools: raw.activation_tools,
|
|
7339
7391
|
tools: raw.tools,
|
|
7340
7392
|
prompt: raw.prompt
|
|
7341
7393
|
};
|
|
@@ -7385,6 +7437,147 @@ function formatReportBatch(reports) {
|
|
|
7385
7437
|
${report.content}`).join("\n\n");
|
|
7386
7438
|
}
|
|
7387
7439
|
|
|
7440
|
+
// src/report-history.ts
|
|
7441
|
+
var ReportHistory = class {
|
|
7442
|
+
constructor(limit = 5) {
|
|
7443
|
+
this.limit = limit;
|
|
7444
|
+
if (!Number.isInteger(limit) || limit < 0) throw new Error("Report limit must be a non-negative integer");
|
|
7445
|
+
}
|
|
7446
|
+
reports = [];
|
|
7447
|
+
add(reports, deliveredAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
7448
|
+
for (const report of reports) {
|
|
7449
|
+
this.reports = this.reports.filter(({ runId }) => runId !== report.runId);
|
|
7450
|
+
this.reports.push({ ...report, deliveredAt });
|
|
7451
|
+
if (this.reports.length > this.limit) this.reports.shift();
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7454
|
+
/** Newest delivery first; callers receive an isolated snapshot. */
|
|
7455
|
+
list() {
|
|
7456
|
+
return this.reports.map((report) => ({ ...report })).reverse();
|
|
7457
|
+
}
|
|
7458
|
+
clear() {
|
|
7459
|
+
this.reports = [];
|
|
7460
|
+
}
|
|
7461
|
+
};
|
|
7462
|
+
|
|
7463
|
+
// src/report-viewer.ts
|
|
7464
|
+
import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
7465
|
+
var ReportViewer = class {
|
|
7466
|
+
constructor(reports, theme, host) {
|
|
7467
|
+
this.theme = theme;
|
|
7468
|
+
this.host = host;
|
|
7469
|
+
this.body = new Text(reports.map(
|
|
7470
|
+
(report) => `${report.shadowName} \xB7 ${report.deliveredAt}
|
|
7471
|
+
run ${report.runId}
|
|
7472
|
+
${report.content.replace(/\r\n?/g, "\n")}`
|
|
7473
|
+
).join("\n\n"), 0, 0);
|
|
7474
|
+
this.actions = [
|
|
7475
|
+
["escape", () => host.close()],
|
|
7476
|
+
["up", () => this.scroll(-1)],
|
|
7477
|
+
["down", () => this.scroll(1)],
|
|
7478
|
+
["pageUp", () => this.scroll(-this.pageSize)],
|
|
7479
|
+
["pageDown", () => this.scroll(this.pageSize)],
|
|
7480
|
+
["home", () => {
|
|
7481
|
+
this.offset = 0;
|
|
7482
|
+
}],
|
|
7483
|
+
["end", () => {
|
|
7484
|
+
this.offset = this.maxOffset();
|
|
7485
|
+
}]
|
|
7486
|
+
];
|
|
7487
|
+
}
|
|
7488
|
+
body;
|
|
7489
|
+
offset = 0;
|
|
7490
|
+
contentHeight = 0;
|
|
7491
|
+
pageSize = 1;
|
|
7492
|
+
actions;
|
|
7493
|
+
render(width) {
|
|
7494
|
+
const height = Math.max(1, Math.min(Math.floor(this.host.rows() * 0.8), this.host.rows() - 2));
|
|
7495
|
+
this.pageSize = Math.max(1, height - 2);
|
|
7496
|
+
const lines = this.body.render(Math.max(1, width));
|
|
7497
|
+
this.contentHeight = lines.length;
|
|
7498
|
+
this.offset = Math.min(this.offset, this.maxOffset());
|
|
7499
|
+
const page = lines.slice(this.offset, this.offset + this.pageSize);
|
|
7500
|
+
if (height < 3) return page.slice(0, height).map((line) => truncateToWidth(line, width));
|
|
7501
|
+
return [
|
|
7502
|
+
this.theme.fg("accent", truncateToWidth("Recent Shadow reports \xB7 newest first", width)),
|
|
7503
|
+
...page,
|
|
7504
|
+
this.theme.fg("dim", truncateToWidth(
|
|
7505
|
+
`Esc close \xB7 \u2191\u2193 PgUp/PgDn Home/End \xB7 ${this.offset + 1}\u2013${Math.min(this.offset + this.pageSize, lines.length)}/${lines.length}`,
|
|
7506
|
+
width
|
|
7507
|
+
))
|
|
7508
|
+
];
|
|
7509
|
+
}
|
|
7510
|
+
handleInput(data) {
|
|
7511
|
+
const action = this.actions.find(([key]) => matchesKey(data, key));
|
|
7512
|
+
if (!action) return;
|
|
7513
|
+
action[1]();
|
|
7514
|
+
this.host.requestRender();
|
|
7515
|
+
}
|
|
7516
|
+
invalidate() {
|
|
7517
|
+
this.body.invalidate();
|
|
7518
|
+
}
|
|
7519
|
+
maxOffset() {
|
|
7520
|
+
return Math.max(0, this.contentHeight - this.pageSize);
|
|
7521
|
+
}
|
|
7522
|
+
scroll(lines) {
|
|
7523
|
+
this.offset = Math.max(0, Math.min(this.maxOffset(), this.offset + lines));
|
|
7524
|
+
}
|
|
7525
|
+
};
|
|
7526
|
+
|
|
7527
|
+
// src/report-browser.ts
|
|
7528
|
+
var ReportBrowser = class {
|
|
7529
|
+
history = new ReportHistory();
|
|
7530
|
+
closeViewer;
|
|
7531
|
+
add(reports) {
|
|
7532
|
+
this.history.add(reports);
|
|
7533
|
+
}
|
|
7534
|
+
reset() {
|
|
7535
|
+
this.closeViewer?.();
|
|
7536
|
+
this.history.clear();
|
|
7537
|
+
}
|
|
7538
|
+
async handleCommand(command, ctx) {
|
|
7539
|
+
const [name, ...args] = command.split(/\s+/);
|
|
7540
|
+
if (name !== "reports") return false;
|
|
7541
|
+
if (args.length && args.join(" ") !== "hide") {
|
|
7542
|
+
ctx.ui.notify("Usage: /shadow reports [hide]", "warning");
|
|
7543
|
+
return true;
|
|
7544
|
+
}
|
|
7545
|
+
if (args[0] === "hide" || this.closeViewer) {
|
|
7546
|
+
this.closeViewer?.();
|
|
7547
|
+
return true;
|
|
7548
|
+
}
|
|
7549
|
+
if (ctx.mode !== "tui") {
|
|
7550
|
+
if (ctx.hasUI) ctx.ui.notify("The report viewer requires TUI mode. Delivered reports remain in the conversation.", "warning");
|
|
7551
|
+
return true;
|
|
7552
|
+
}
|
|
7553
|
+
const reports = this.history.list();
|
|
7554
|
+
if (!reports.length) {
|
|
7555
|
+
ctx.ui.notify("No reports delivered in this session yet.", "info");
|
|
7556
|
+
return true;
|
|
7557
|
+
}
|
|
7558
|
+
const view = { close: () => {
|
|
7559
|
+
} };
|
|
7560
|
+
try {
|
|
7561
|
+
await ctx.ui.custom((tui, theme, _keys, done) => {
|
|
7562
|
+
view.close = () => {
|
|
7563
|
+
if (this.closeViewer !== view.close) return;
|
|
7564
|
+
this.closeViewer = void 0;
|
|
7565
|
+
done();
|
|
7566
|
+
};
|
|
7567
|
+
this.closeViewer = view.close;
|
|
7568
|
+
return new ReportViewer(reports, theme, {
|
|
7569
|
+
rows: () => tui.terminal.rows,
|
|
7570
|
+
requestRender: () => tui.requestRender(),
|
|
7571
|
+
close: view.close
|
|
7572
|
+
});
|
|
7573
|
+
}, { overlay: true, overlayOptions: { width: "90%", maxHeight: "80%", margin: 1 } });
|
|
7574
|
+
} finally {
|
|
7575
|
+
if (this.closeViewer === view.close) this.closeViewer = void 0;
|
|
7576
|
+
}
|
|
7577
|
+
return true;
|
|
7578
|
+
}
|
|
7579
|
+
};
|
|
7580
|
+
|
|
7388
7581
|
// src/random.ts
|
|
7389
7582
|
function createRandom(seed) {
|
|
7390
7583
|
if (seed === void 0) return Math.random;
|
|
@@ -7398,9 +7591,143 @@ function createRandom(seed) {
|
|
|
7398
7591
|
};
|
|
7399
7592
|
}
|
|
7400
7593
|
|
|
7594
|
+
// src/acp-projection.ts
|
|
7595
|
+
import {
|
|
7596
|
+
buildContextEntries,
|
|
7597
|
+
buildSessionContext,
|
|
7598
|
+
sessionEntryToContextMessages
|
|
7599
|
+
} from "@earendil-works/pi-coding-agent";
|
|
7600
|
+
|
|
7601
|
+
// src/acp-state.ts
|
|
7602
|
+
import { readFileSync } from "node:fs";
|
|
7603
|
+
function isRecord(value) {
|
|
7604
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7605
|
+
}
|
|
7606
|
+
function readAcpBlocks(sessionFile) {
|
|
7607
|
+
if (!sessionFile) return [];
|
|
7608
|
+
try {
|
|
7609
|
+
const state = JSON.parse(readFileSync(`${sessionFile}.acp.json`, "utf8"));
|
|
7610
|
+
if (!isRecord(state) || !Array.isArray(state.blocks)) return [];
|
|
7611
|
+
const blocks = [];
|
|
7612
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7613
|
+
for (const block of state.blocks) {
|
|
7614
|
+
if (!isRecord(block)) return [];
|
|
7615
|
+
if (block.active === false) continue;
|
|
7616
|
+
if (block.active !== true || typeof block.blockId !== "string" || !block.blockId || typeof block.summary !== "string" || !block.summary.trim() || !Array.isArray(block.effectiveMessageIds) || !block.effectiveMessageIds.every((id) => typeof id === "string" && id.length > 0) || seen.has(block.blockId)) return [];
|
|
7617
|
+
seen.add(block.blockId);
|
|
7618
|
+
blocks.push({
|
|
7619
|
+
blockId: block.blockId,
|
|
7620
|
+
summary: block.summary,
|
|
7621
|
+
effectiveMessageIds: [...block.effectiveMessageIds]
|
|
7622
|
+
});
|
|
7623
|
+
}
|
|
7624
|
+
return blocks;
|
|
7625
|
+
} catch {
|
|
7626
|
+
return [];
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
|
|
7630
|
+
// src/acp-projection.ts
|
|
7631
|
+
function buildShadowSessionContext(entries, leafId, sessionFile) {
|
|
7632
|
+
const context = buildSessionContext(entries, leafId);
|
|
7633
|
+
const blocks = readAcpBlocks(sessionFile);
|
|
7634
|
+
if (!blocks.length) return context;
|
|
7635
|
+
const visible = buildContextEntries(entries, leafId).flatMap(
|
|
7636
|
+
(entry) => sessionEntryToContextMessages(entry).map((message) => ({ id: entry.id, message }))
|
|
7637
|
+
);
|
|
7638
|
+
return { ...context, messages: projectMessages(visible, blocks) };
|
|
7639
|
+
}
|
|
7640
|
+
function toolCalls(message) {
|
|
7641
|
+
return message.role === "assistant" ? message.content.filter((part) => part.type === "toolCall") : [];
|
|
7642
|
+
}
|
|
7643
|
+
function coverageIds({ id, message }) {
|
|
7644
|
+
const calls = toolCalls(message);
|
|
7645
|
+
return calls.length > 1 ? calls.map((call) => `${id}#${call.id}`) : [id];
|
|
7646
|
+
}
|
|
7647
|
+
function summaryMessage(block) {
|
|
7648
|
+
return {
|
|
7649
|
+
role: "compactionSummary",
|
|
7650
|
+
summary: `[ACP block ${block.blockId} \u2014 ${block.effectiveMessageIds.length} folded messages]
|
|
7651
|
+
${block.summary}`,
|
|
7652
|
+
tokensBefore: 0,
|
|
7653
|
+
timestamp: 0
|
|
7654
|
+
};
|
|
7655
|
+
}
|
|
7656
|
+
function projectMessages(visible, blocks) {
|
|
7657
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
7658
|
+
visible.forEach((item, index) => {
|
|
7659
|
+
for (const id of coverageIds(item)) indexById.set(id, index);
|
|
7660
|
+
});
|
|
7661
|
+
const covered = /* @__PURE__ */ new Set();
|
|
7662
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
7663
|
+
for (const block of blocks) {
|
|
7664
|
+
const positions = block.effectiveMessageIds.flatMap((id) => {
|
|
7665
|
+
const index = indexById.get(id);
|
|
7666
|
+
return index === void 0 ? [] : [index];
|
|
7667
|
+
});
|
|
7668
|
+
if (!positions.length) continue;
|
|
7669
|
+
const first = positions.reduce((left, right) => Math.min(left, right));
|
|
7670
|
+
anchors.set(first, [...anchors.get(first) ?? [], block]);
|
|
7671
|
+
for (const id of block.effectiveMessageIds) covered.add(id);
|
|
7672
|
+
}
|
|
7673
|
+
if (!covered.size) return visible.map(({ message }) => message);
|
|
7674
|
+
const firstUser = visible.findIndex(
|
|
7675
|
+
({ message }) => message.role === "user" || message.role === "custom"
|
|
7676
|
+
);
|
|
7677
|
+
const messages = [];
|
|
7678
|
+
visible.forEach((item, index) => {
|
|
7679
|
+
for (const block of anchors.get(index) ?? []) messages.push(summaryMessage(block));
|
|
7680
|
+
const retained = index === firstUser ? item.message : retainUncovered(item, covered);
|
|
7681
|
+
if (retained) messages.push(retained);
|
|
7682
|
+
});
|
|
7683
|
+
return removeOrphanedTools(messages);
|
|
7684
|
+
}
|
|
7685
|
+
function retainUncovered(item, covered) {
|
|
7686
|
+
const ids = coverageIds(item);
|
|
7687
|
+
if (!ids.some((id) => covered.has(id))) return item.message;
|
|
7688
|
+
if (ids.every((id) => covered.has(id))) return void 0;
|
|
7689
|
+
const message = item.message;
|
|
7690
|
+
if (message.role !== "assistant") return message;
|
|
7691
|
+
return {
|
|
7692
|
+
...message,
|
|
7693
|
+
content: message.content.filter(
|
|
7694
|
+
(part) => part.type !== "toolCall" || !covered.has(`${item.id}#${part.id}`)
|
|
7695
|
+
)
|
|
7696
|
+
};
|
|
7697
|
+
}
|
|
7698
|
+
function removeOrphanedTools(messages) {
|
|
7699
|
+
const resultIds = new Set(messages.flatMap(
|
|
7700
|
+
(message) => message.role === "toolResult" ? [message.toolCallId] : []
|
|
7701
|
+
));
|
|
7702
|
+
const paired = messages.flatMap((message) => {
|
|
7703
|
+
const calls = toolCalls(message);
|
|
7704
|
+
if (!calls.length || message.role !== "assistant") return [message];
|
|
7705
|
+
const retained = calls.filter((call) => call.name === "compress" || resultIds.has(call.id));
|
|
7706
|
+
if (!retained.length) return [];
|
|
7707
|
+
const ids = new Set(retained.map((call) => call.id));
|
|
7708
|
+
return [{ ...message, content: message.content.filter((part) => part.type !== "toolCall" || ids.has(part.id)) }];
|
|
7709
|
+
});
|
|
7710
|
+
const callIds = new Set(paired.flatMap((message) => toolCalls(message).map((call) => call.id)));
|
|
7711
|
+
return paired.filter((message) => message.role !== "toolResult" || callIds.has(message.toolCallId));
|
|
7712
|
+
}
|
|
7713
|
+
|
|
7401
7714
|
// src/scheduler.ts
|
|
7402
|
-
function
|
|
7403
|
-
|
|
7715
|
+
function extractToolNames(toolResults) {
|
|
7716
|
+
const names = /* @__PURE__ */ new Set();
|
|
7717
|
+
for (const item of toolResults) {
|
|
7718
|
+
if (!item || typeof item !== "object") continue;
|
|
7719
|
+
const name = item.toolName;
|
|
7720
|
+
if (typeof name === "string" && name.trim().length > 0) {
|
|
7721
|
+
names.add(name.trim());
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
return names;
|
|
7725
|
+
}
|
|
7726
|
+
function shouldEvaluateHeartbeat(toolsOrResults, heartbeatTools) {
|
|
7727
|
+
const toolNames = toolsOrResults instanceof Set ? toolsOrResults : extractToolNames(toolsOrResults);
|
|
7728
|
+
if (toolNames.size === 0) return false;
|
|
7729
|
+
if (!heartbeatTools || heartbeatTools.length === 0) return true;
|
|
7730
|
+
return heartbeatTools.some((tool2) => toolNames.has(tool2));
|
|
7404
7731
|
}
|
|
7405
7732
|
function shouldEvaluateFinalResponse(messages) {
|
|
7406
7733
|
const message = messages.at(-1);
|
|
@@ -7423,11 +7750,14 @@ function decideHeartbeat(options) {
|
|
|
7423
7750
|
activated: [],
|
|
7424
7751
|
candidates: [],
|
|
7425
7752
|
modelFiltered: [],
|
|
7426
|
-
runningExcluded: []
|
|
7753
|
+
runningExcluded: [],
|
|
7754
|
+
roundFiltered: [],
|
|
7755
|
+
toolFiltered: []
|
|
7427
7756
|
};
|
|
7428
7757
|
}
|
|
7429
7758
|
const modelFiltered = [];
|
|
7430
7759
|
const runningExcluded = [];
|
|
7760
|
+
const toolFiltered = [];
|
|
7431
7761
|
const rolls = options.shadows.filter((shadow) => {
|
|
7432
7762
|
if (!shadow.enabled || !hasTrigger(shadow, "heartbeat")) return false;
|
|
7433
7763
|
if (options.activeShadowIds.has(shadow.id)) {
|
|
@@ -7438,6 +7768,10 @@ function decideHeartbeat(options) {
|
|
|
7438
7768
|
modelFiltered.push(shadow.id);
|
|
7439
7769
|
return false;
|
|
7440
7770
|
}
|
|
7771
|
+
if (!matchesActivationTools(shadow, options.executedTools)) {
|
|
7772
|
+
toolFiltered.push(shadow.id);
|
|
7773
|
+
return false;
|
|
7774
|
+
}
|
|
7441
7775
|
return true;
|
|
7442
7776
|
}).map((shadow) => ({ shadow, roll: random() }));
|
|
7443
7777
|
const hits = rolls.filter(
|
|
@@ -7458,11 +7792,14 @@ function decideHeartbeat(options) {
|
|
|
7458
7792
|
selected: selectedIds.has(shadow.id)
|
|
7459
7793
|
})),
|
|
7460
7794
|
modelFiltered,
|
|
7461
|
-
runningExcluded
|
|
7795
|
+
runningExcluded,
|
|
7796
|
+
roundFiltered: [],
|
|
7797
|
+
toolFiltered
|
|
7462
7798
|
};
|
|
7463
7799
|
}
|
|
7464
7800
|
function decideFinalResponse(options) {
|
|
7465
7801
|
const modelFiltered = [];
|
|
7802
|
+
const roundFiltered = [];
|
|
7466
7803
|
const candidates = options.shadows.filter((shadow) => {
|
|
7467
7804
|
if (!shadow.enabled || !hasTrigger(shadow, "final_response"))
|
|
7468
7805
|
return false;
|
|
@@ -7470,6 +7807,10 @@ function decideFinalResponse(options) {
|
|
|
7470
7807
|
modelFiltered.push(shadow.id);
|
|
7471
7808
|
return false;
|
|
7472
7809
|
}
|
|
7810
|
+
if (options.canRunFinalResponse && !options.canRunFinalResponse(shadow)) {
|
|
7811
|
+
roundFiltered.push(shadow.id);
|
|
7812
|
+
return false;
|
|
7813
|
+
}
|
|
7473
7814
|
return true;
|
|
7474
7815
|
}).map((shadow) => ({ shadow, roll: 0 }));
|
|
7475
7816
|
return {
|
|
@@ -7480,12 +7821,21 @@ function decideFinalResponse(options) {
|
|
|
7480
7821
|
selected: true
|
|
7481
7822
|
})),
|
|
7482
7823
|
modelFiltered,
|
|
7483
|
-
runningExcluded: []
|
|
7824
|
+
runningExcluded: [],
|
|
7825
|
+
roundFiltered
|
|
7484
7826
|
};
|
|
7485
7827
|
}
|
|
7486
7828
|
function matchesModel(shadow, fullModelId) {
|
|
7487
7829
|
return shadow.activeForModels.includes("*") || shadow.activeForModels.includes(fullModelId);
|
|
7488
7830
|
}
|
|
7831
|
+
function matchesActivationTools(shadow, executedTools) {
|
|
7832
|
+
if (shadow.activationTools.length === 0) {
|
|
7833
|
+
return true;
|
|
7834
|
+
}
|
|
7835
|
+
if (!executedTools) return false;
|
|
7836
|
+
const executedSet = executedTools instanceof Set ? executedTools : new Set(executedTools);
|
|
7837
|
+
return shadow.activationTools.some((tool2) => executedSet.has(tool2));
|
|
7838
|
+
}
|
|
7489
7839
|
function hasTrigger(shadow, trigger) {
|
|
7490
7840
|
return shadow.trigger.includes(trigger);
|
|
7491
7841
|
}
|
|
@@ -7716,7 +8066,7 @@ function serializeTrajectory(messages) {
|
|
|
7716
8066
|
continue;
|
|
7717
8067
|
}
|
|
7718
8068
|
if (message.role === "compactionSummary" || message.role === "branchSummary") {
|
|
7719
|
-
appendText(lines, "SUMMARY", message.content);
|
|
8069
|
+
appendText(lines, "SUMMARY", message.summary ?? message.content);
|
|
7720
8070
|
}
|
|
7721
8071
|
}
|
|
7722
8072
|
return `<main-agent-trajectory>
|
|
@@ -8380,16 +8730,20 @@ var ShadowMindRuntime = class {
|
|
|
8380
8730
|
runner = new ShadowRunner();
|
|
8381
8731
|
usageStore = new UsageStore(this.agentDir);
|
|
8382
8732
|
active = /* @__PURE__ */ new Map();
|
|
8733
|
+
finalResponseBudget = new FinalResponseBudget();
|
|
8383
8734
|
completionReview = new CompletionReview({
|
|
8384
8735
|
currentEpoch: () => this.epoch,
|
|
8385
8736
|
maxParallel: () => this.configStore.current.maxParallelShadows,
|
|
8386
8737
|
activeCount: () => this.active.size,
|
|
8387
8738
|
activeShadowIds: () => new Set([...this.active.values()].map(({ shadow }) => shadow.id)),
|
|
8739
|
+
canLaunch: () => !this.batcher.hasPending,
|
|
8388
8740
|
launch: (pending, review) => this.launchShadow({ ...pending, completionReview: review }),
|
|
8389
|
-
deliver: (reports) => void this.deliverReports(reports)
|
|
8741
|
+
deliver: (reports) => void this.deliverReports(reports),
|
|
8742
|
+
onReviewCompleted: (shadowIds) => this.finalResponseBudget.commit(shadowIds)
|
|
8390
8743
|
});
|
|
8391
8744
|
recentEvents = [];
|
|
8392
8745
|
recentRuns = [];
|
|
8746
|
+
reports = new ReportBrowser();
|
|
8393
8747
|
batcher;
|
|
8394
8748
|
sessionLifetime = new SessionLifetime();
|
|
8395
8749
|
epoch = 0;
|
|
@@ -8417,8 +8771,10 @@ var ShadowMindRuntime = class {
|
|
|
8417
8771
|
this.latestContext = ctx;
|
|
8418
8772
|
this.modelCalls = 0;
|
|
8419
8773
|
this.completedWithFinalText = false;
|
|
8774
|
+
this.finalResponseBudget.reset();
|
|
8420
8775
|
this.sessionUsage = zeroUsage();
|
|
8421
8776
|
this.recentRuns.length = 0;
|
|
8777
|
+
this.reports.reset();
|
|
8422
8778
|
await this.configStore.initialize();
|
|
8423
8779
|
await this.usageStore.initialize();
|
|
8424
8780
|
this.random = createRandom(this.configStore.current.randomSeed);
|
|
@@ -8433,6 +8789,7 @@ var ShadowMindRuntime = class {
|
|
|
8433
8789
|
this.latestContext = ctx;
|
|
8434
8790
|
if (event.source === "extension") return;
|
|
8435
8791
|
this.completedWithFinalText = false;
|
|
8792
|
+
this.finalResponseBudget.reset();
|
|
8436
8793
|
this.epoch += 1;
|
|
8437
8794
|
this.abortAll("new-user-input");
|
|
8438
8795
|
});
|
|
@@ -8442,14 +8799,16 @@ var ShadowMindRuntime = class {
|
|
|
8442
8799
|
});
|
|
8443
8800
|
this.pi.on("turn_end", async (event, ctx) => {
|
|
8444
8801
|
this.latestContext = ctx;
|
|
8445
|
-
|
|
8802
|
+
const toolResults = event.toolResults ?? [];
|
|
8803
|
+
const executedTools = extractToolNames(toolResults);
|
|
8804
|
+
if (executedTools.size === 0) {
|
|
8446
8805
|
this.record("heartbeat-skipped", {
|
|
8447
8806
|
reason: "no-tool-activity",
|
|
8448
8807
|
modelCalls: this.modelCalls
|
|
8449
8808
|
});
|
|
8450
8809
|
return;
|
|
8451
8810
|
}
|
|
8452
|
-
await this.onHeartbeat(ctx);
|
|
8811
|
+
await this.onHeartbeat(ctx, executedTools);
|
|
8453
8812
|
});
|
|
8454
8813
|
this.pi.on("agent_end", (event, ctx) => {
|
|
8455
8814
|
this.latestContext = ctx;
|
|
@@ -8481,6 +8840,7 @@ var ShadowMindRuntime = class {
|
|
|
8481
8840
|
if (!result.settled) this.active.clear();
|
|
8482
8841
|
}
|
|
8483
8842
|
await this.usageStore.flush();
|
|
8843
|
+
this.reports.reset();
|
|
8484
8844
|
ctx.ui.setStatus("shadow-mind", void 0);
|
|
8485
8845
|
ctx.ui.setWidget("shadow-mind-panel", void 0);
|
|
8486
8846
|
this.sessionLifetime.deactivate();
|
|
@@ -8504,6 +8864,7 @@ var ShadowMindRuntime = class {
|
|
|
8504
8864
|
this.setPaused(!this.paused, ctx);
|
|
8505
8865
|
return;
|
|
8506
8866
|
}
|
|
8867
|
+
if (await this.reports.handleCommand(command, ctx)) return;
|
|
8507
8868
|
if (command === "status") {
|
|
8508
8869
|
await this.refresh(ctx);
|
|
8509
8870
|
ctx.ui.notify(
|
|
@@ -8534,12 +8895,20 @@ var ShadowMindRuntime = class {
|
|
|
8534
8895
|
(message, _options, theme) => {
|
|
8535
8896
|
const content = typeof message.content === "string" ? message.content : "Shadow report";
|
|
8536
8897
|
const prefix = theme.fg("accent", "\u{1F419} shadow \xB7 ");
|
|
8537
|
-
return new
|
|
8898
|
+
return new Text2(`${prefix}${content}`, 0, 0);
|
|
8538
8899
|
}
|
|
8539
8900
|
);
|
|
8540
8901
|
}
|
|
8541
|
-
async onHeartbeat(ctx) {
|
|
8902
|
+
async onHeartbeat(ctx, executedTools) {
|
|
8542
8903
|
const snapshot = await this.refresh(ctx);
|
|
8904
|
+
const config = this.configStore.current;
|
|
8905
|
+
if (!shouldEvaluateHeartbeat(executedTools, config.heartbeatTools)) {
|
|
8906
|
+
this.record("heartbeat-skipped", {
|
|
8907
|
+
reason: "tool-filtered",
|
|
8908
|
+
modelCalls: this.modelCalls
|
|
8909
|
+
});
|
|
8910
|
+
return;
|
|
8911
|
+
}
|
|
8543
8912
|
if (this.paused || !ctx.model) {
|
|
8544
8913
|
this.record("heartbeat-skipped", {
|
|
8545
8914
|
reason: this.paused ? "paused" : "no-model",
|
|
@@ -8549,17 +8918,18 @@ var ShadowMindRuntime = class {
|
|
|
8549
8918
|
}
|
|
8550
8919
|
const fullModelId = `${ctx.model.provider}/${ctx.model.id}`;
|
|
8551
8920
|
const decision = decideHeartbeat({
|
|
8552
|
-
heartbeatProbability:
|
|
8921
|
+
heartbeatProbability: config.heartbeatProbability,
|
|
8553
8922
|
availableSlots: Math.max(
|
|
8554
8923
|
0,
|
|
8555
|
-
|
|
8924
|
+
config.maxParallelShadows - this.active.size
|
|
8556
8925
|
),
|
|
8557
8926
|
shadows: snapshot.shadows,
|
|
8558
8927
|
activeShadowIds: new Set(
|
|
8559
8928
|
[...this.active.values()].map(({ shadow }) => shadow.id)
|
|
8560
8929
|
),
|
|
8561
8930
|
mainModelId: fullModelId,
|
|
8562
|
-
random: this.random
|
|
8931
|
+
random: this.random,
|
|
8932
|
+
executedTools
|
|
8563
8933
|
});
|
|
8564
8934
|
this.record("heartbeat", {
|
|
8565
8935
|
modelCalls: this.modelCalls,
|
|
@@ -8570,12 +8940,14 @@ var ShadowMindRuntime = class {
|
|
|
8570
8940
|
roll
|
|
8571
8941
|
})),
|
|
8572
8942
|
...decision.modelFiltered.length ? { modelFiltered: decision.modelFiltered } : {},
|
|
8573
|
-
...decision.runningExcluded.length ? { runningExcluded: decision.runningExcluded } : {}
|
|
8943
|
+
...decision.runningExcluded.length ? { runningExcluded: decision.runningExcluded } : {},
|
|
8944
|
+
...decision.toolFiltered.length ? { toolFiltered: decision.toolFiltered } : {}
|
|
8574
8945
|
});
|
|
8575
8946
|
if (!decision.activated.length) return;
|
|
8576
|
-
const context =
|
|
8947
|
+
const context = buildShadowSessionContext(
|
|
8577
8948
|
ctx.sessionManager.getEntries(),
|
|
8578
|
-
ctx.sessionManager.getLeafId()
|
|
8949
|
+
ctx.sessionManager.getLeafId(),
|
|
8950
|
+
ctx.sessionManager.getSessionFile()
|
|
8579
8951
|
);
|
|
8580
8952
|
const availableTools = new Set(
|
|
8581
8953
|
this.pi.getAllTools().map((tool2) => tool2.name)
|
|
@@ -8592,6 +8964,10 @@ var ShadowMindRuntime = class {
|
|
|
8592
8964
|
}
|
|
8593
8965
|
}
|
|
8594
8966
|
async onFinalResponse(ctx) {
|
|
8967
|
+
if (this.batcher.hasPending) {
|
|
8968
|
+
await this.batcher.flush();
|
|
8969
|
+
return;
|
|
8970
|
+
}
|
|
8595
8971
|
const request = this.completionReview.begin(this.epoch);
|
|
8596
8972
|
const snapshot = await this.refresh(ctx);
|
|
8597
8973
|
if (!this.completionReview.isCurrent(request)) {
|
|
@@ -8609,21 +8985,24 @@ var ShadowMindRuntime = class {
|
|
|
8609
8985
|
const fullModelId = `${mainModel.provider}/${mainModel.id}`;
|
|
8610
8986
|
const decision = decideFinalResponse({
|
|
8611
8987
|
shadows: snapshot.shadows,
|
|
8612
|
-
mainModelId: fullModelId
|
|
8988
|
+
mainModelId: fullModelId,
|
|
8989
|
+
canRunFinalResponse: (shadow) => this.finalResponseBudget.canRun(shadow)
|
|
8613
8990
|
});
|
|
8614
8991
|
this.record("final-response", {
|
|
8615
8992
|
candidates: decision.candidates,
|
|
8616
8993
|
activated: decision.activated.map(({ shadow }) => shadow.id),
|
|
8617
8994
|
...decision.modelFiltered.length ? { modelFiltered: decision.modelFiltered } : {},
|
|
8618
|
-
...decision.runningExcluded.length ? { runningExcluded: decision.runningExcluded } : {}
|
|
8995
|
+
...decision.runningExcluded.length ? { runningExcluded: decision.runningExcluded } : {},
|
|
8996
|
+
...decision.roundFiltered.length ? { roundFiltered: decision.roundFiltered } : {}
|
|
8619
8997
|
});
|
|
8620
8998
|
if (!decision.activated.length) {
|
|
8621
8999
|
this.completionReview.schedule(request, []);
|
|
8622
9000
|
return;
|
|
8623
9001
|
}
|
|
8624
|
-
const context =
|
|
9002
|
+
const context = buildShadowSessionContext(
|
|
8625
9003
|
ctx.sessionManager.getEntries(),
|
|
8626
|
-
ctx.sessionManager.getLeafId()
|
|
9004
|
+
ctx.sessionManager.getLeafId(),
|
|
9005
|
+
ctx.sessionManager.getSessionFile()
|
|
8627
9006
|
);
|
|
8628
9007
|
const availableTools = new Set(
|
|
8629
9008
|
this.pi.getAllTools().map((tool2) => tool2.name)
|
|
@@ -8735,7 +9114,7 @@ var ShadowMindRuntime = class {
|
|
|
8735
9114
|
count: current.length
|
|
8736
9115
|
});
|
|
8737
9116
|
const idle = this.latestContext?.isIdle() ?? true;
|
|
8738
|
-
this.sessionLifetime.run(() => {
|
|
9117
|
+
const delivered = this.sessionLifetime.run(() => {
|
|
8739
9118
|
this.pi.sendMessage(
|
|
8740
9119
|
{
|
|
8741
9120
|
customType: "shadow-report",
|
|
@@ -8751,6 +9130,7 @@ var ShadowMindRuntime = class {
|
|
|
8751
9130
|
{ triggerTurn: true, deliverAs: idle ? "followUp" : "steer" }
|
|
8752
9131
|
);
|
|
8753
9132
|
});
|
|
9133
|
+
if (delivered) this.reports.add(current);
|
|
8754
9134
|
}
|
|
8755
9135
|
async refresh(ctx) {
|
|
8756
9136
|
const config = await this.configStore.reload();
|
|
@@ -8852,7 +9232,7 @@ var ShadowMindRuntime = class {
|
|
|
8852
9232
|
const detail = failed ? ` ${event.data?.error ?? event.data?.reason}` : "";
|
|
8853
9233
|
return `${new Date(event.at).toLocaleTimeString("en-GB", { hour12: false })} ${event.kind}${detail}`;
|
|
8854
9234
|
}),
|
|
8855
|
-
"Shortcut: Alt+S toggle \xB7 Commands: /shadow toggle | pause | resume | status | hide"
|
|
9235
|
+
"Shortcut: Alt+S toggle \xB7 Commands: /shadow toggle | pause | resume | status | hide | reports"
|
|
8856
9236
|
];
|
|
8857
9237
|
}
|
|
8858
9238
|
};
|