pi-orchestrator-config 4.2.0 → 4.3.2
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/README.md +76 -67
- package/agents/code-reviewer-docs.md +11 -1
- package/agents/code-reviewer-guidelines.md +11 -1
- package/agents/code-reviewer-quality.md +11 -1
- package/agents/code-reviewer-security.md +11 -1
- package/agents/code-reviewer-spec.md +11 -1
- package/agents/git-expert.md +42 -3
- package/agents/github-expert.md +11 -0
- package/extensions/acpx-provider/runtime-models.ts +18 -6
- package/extensions/cli-provider/agents/claude.ts +3 -2
- package/extensions/cli-provider/agents/cursor.ts +3 -5
- package/extensions/cli-provider/agents/gemini.ts +3 -2
- package/extensions/cli-provider/discover.ts +7 -7
- package/extensions/cli-provider/index.ts +5 -0
- package/extensions/cli-provider/runtime-models.ts +18 -6
- package/extensions/cli-provider/session-reaper.ts +5 -7
- package/extensions/cli-provider/sessions.ts +4 -0
- package/extensions/cli-provider/shared/discover-cache.ts +17 -176
- package/extensions/coms/coms-p2p.ts +1719 -423
- package/extensions/coms/coms-shared.ts +261 -39
- package/extensions/coms/coms-wrapper.ts +100 -62
- package/extensions/coms/index.ts +8 -4
- package/extensions/coms/mixed-turn.ts +26 -0
- package/extensions/coms/probe-socket.ts +42 -0
- package/extensions/coms/themeMap.ts +0 -1
- package/extensions/image-gen/image-gen.ts +19 -1
- package/extensions/orchestrator/async-agents.ts +563 -102
- package/extensions/orchestrator/async-capability.ts +8 -8
- package/extensions/orchestrator/cron.ts +15 -9
- package/extensions/orchestrator/dreaming.ts +32 -25
- package/extensions/orchestrator/enforcement-helpers.ts +459 -17
- package/extensions/orchestrator/enforcement-rules.ts +5 -4
- package/extensions/orchestrator/enforcement.ts +80 -27
- package/extensions/orchestrator/extended-autocomplete.ts +7 -0
- package/extensions/orchestrator/git-helpers.ts +2 -2
- package/extensions/orchestrator/index.ts +78 -10
- package/extensions/orchestrator/markdown-transformer.ts +313 -0
- package/extensions/orchestrator/memory-embeddings.ts +36 -34
- package/extensions/orchestrator/memory-provenance.ts +29 -25
- package/extensions/orchestrator/memory-scoring.ts +23 -18
- package/extensions/orchestrator/memory-tools.ts +10 -3
- package/extensions/orchestrator/overlay-dashboard.ts +17 -5
- package/extensions/orchestrator/parse-model-override.ts +29 -0
- package/extensions/orchestrator/pi-config-review-state.ts +57 -33
- package/extensions/orchestrator/project-settings.ts +333 -127
- package/extensions/orchestrator/resolve-agent-model.ts +24 -13
- package/extensions/orchestrator/review-porcelain.ts +4 -0
- package/extensions/orchestrator/review-ui.ts +63 -15
- package/extensions/orchestrator/rule-placeholders.ts +411 -2
- package/extensions/orchestrator/rules.ts +80 -132
- package/extensions/orchestrator/settings-tui-helpers.ts +380 -0
- package/extensions/orchestrator/settings-tui-submenus.ts +647 -0
- package/extensions/orchestrator/settings-tui.ts +636 -0
- package/extensions/orchestrator/state-jsonl.ts +431 -0
- package/extensions/orchestrator/subagent-tool.ts +157 -59
- package/extensions/orchestrator/sync-limit.ts +23 -0
- package/extensions/orchestrator/task-lifecycle.ts +77 -67
- package/extensions/orchestrator/utils.ts +8 -5
- package/extensions/pidash/pidash.ts +193 -156
- package/extensions/pidiff/pidiff.ts +96 -38
- package/extensions/pidiff/spawn-lock.ts +62 -0
- package/extensions/pitasks/index.ts +430 -0
- package/extensions/pitasks/reminders.ts +37 -0
- package/extensions/pitasks/task-store.ts +467 -0
- package/extensions/pitasks/task-tools.ts +375 -0
- package/extensions/pitasks/task-widget.ts +181 -0
- package/extensions/pitasks/types.ts +38 -0
- package/extensions/providers/acpx-driver.ts +12 -2
- package/extensions/providers/built-in-drivers.ts +6 -6
- package/extensions/providers/claude-driver.ts +1 -1
- package/extensions/providers/cursor-cli-driver.ts +1 -1
- package/extensions/providers/gemini-driver.ts +1 -1
- package/extensions/providers/index.ts +144 -26
- package/extensions/providers/initialized-guard.ts +27 -0
- package/extensions/providers/restore-default-model.ts +596 -0
- package/extensions/providers/session-shutdown.ts +39 -0
- package/extensions/shared/coms-active.ts +23 -0
- package/extensions/shared/create-runtime-provider.ts +2 -0
- package/extensions/shared/daemon-manager.ts +0 -8
- package/extensions/shared/file-logger.ts +132 -25
- package/extensions/shared/logger.ts +50 -0
- package/extensions/shared/models-dev.ts +421 -0
- package/extensions/shared/oneshot.ts +116 -0
- package/extensions/shared/resolve-binary.ts +191 -0
- package/extensions/shared/types.ts +3 -0
- package/package.json +9 -3
- package/prompts/coderabbit-rate-limit.md +2 -0
- package/rules/00-orchestrator-core.md +3 -0
- package/rules/10-agent-routing.md +12 -1
- package/rules/20-code-review-loop.md +52 -25
- package/rules/30-prompt-templates.md +4 -0
- package/rules/35-memory.md +3 -3
- package/rules/40-critical-rules.md +3 -0
- package/rules/55-coms-protocol.md +84 -18
- package/rules/60-task-tracking.md +2 -0
- package/settings-keys.json +563 -0
- package/templates/coms-feature-manager-prompt.md +4 -6
- package/extensions/coms/coms-net-server.ts +0 -1642
- package/extensions/coms/coms-net-wrapper.ts +0 -521
- package/extensions/coms/coms-net.ts +0 -1589
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* retrieval telemetry, and vector-based contextual memory injection.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { createLogger } from "../shared/logger.js";
|
|
10
|
+
|
|
11
|
+
const log = createLogger("rules");
|
|
9
12
|
import * as fs from "node:fs";
|
|
10
13
|
import * as os from "node:os";
|
|
11
14
|
import * as path from "node:path";
|
|
@@ -13,10 +16,26 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
13
16
|
import { formatDuration } from "./async-agents.js";
|
|
14
17
|
import { buildSituationReport, estimateMemoryBudget, rebuildAndOrganize } from "./situation-report.js";
|
|
15
18
|
import { classifyQueryClass, getQueryClassBias } from "./memory-query-class.js";
|
|
16
|
-
import { getSetting } from "./project-settings.js";
|
|
17
|
-
import {
|
|
19
|
+
import { getSetting, SETTINGS_KEYS } from "./project-settings.js";
|
|
20
|
+
import { assembleRuleText, isSettingTruthy } from "./rule-placeholders.js";
|
|
21
|
+
import { isComsActive } from "../shared/coms-active.js";
|
|
22
|
+
import { JsonlAppendLog } from "./state-jsonl.js";
|
|
18
23
|
import { getProjectDataDir } from "./utils.js";
|
|
19
24
|
|
|
25
|
+
/** Per-cwd telemetry log cache — avoids re-creating on every call. */
|
|
26
|
+
const telemetryLogCache = new Map<string, JsonlAppendLog<Record<string, unknown>>>();
|
|
27
|
+
|
|
28
|
+
function getTelemetryLog(cwd: string): JsonlAppendLog<Record<string, unknown>> {
|
|
29
|
+
const dataDir = getProjectDataDir(cwd);
|
|
30
|
+
const cached = telemetryLogCache.get(dataDir);
|
|
31
|
+
if (cached) return cached;
|
|
32
|
+
const log = new JsonlAppendLog<Record<string, unknown>>(
|
|
33
|
+
path.join(dataDir, "memory-telemetry.jsonl"),
|
|
34
|
+
);
|
|
35
|
+
telemetryLogCache.set(dataDir, log);
|
|
36
|
+
return log;
|
|
37
|
+
}
|
|
38
|
+
|
|
20
39
|
/** Social closer gate — skip expensive vector search for trivial messages */
|
|
21
40
|
const SOCIAL_CLOSERS = new Set([
|
|
22
41
|
"ok", "yes", "no", "thanks", "thank you", "got it", "sure",
|
|
@@ -35,13 +54,8 @@ function isSocialCloser(text: string): boolean {
|
|
|
35
54
|
/** Track last injected memories for usage detection in turn_end */
|
|
36
55
|
let lastInjectedMemories: { text: string; category: string; similarity: number }[] = [];
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
let lastTaskFocusUserMsgId: string | null = null;
|
|
41
|
-
let taskFocusPending = false;
|
|
42
|
-
let taskFocusPendingMsgId: string | null = null;
|
|
43
|
-
|
|
44
|
-
/** Log what memories were auto-injected for retrieval telemetry */
|
|
57
|
+
/** Log what memories were auto-injected for retrieval telemetry.
|
|
58
|
+
* Uses JsonlAppendLog — seq/ts added automatically, size-based truncation built-in. */
|
|
45
59
|
function logMemoryInjection(
|
|
46
60
|
cwd: string,
|
|
47
61
|
prompt: string,
|
|
@@ -49,37 +63,27 @@ function logMemoryInjection(
|
|
|
49
63
|
queryClass?: string,
|
|
50
64
|
): void {
|
|
51
65
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ts: new Date().toISOString(),
|
|
66
|
+
getTelemetryLog(cwd).append({
|
|
67
|
+
event: "inject",
|
|
55
68
|
prompt: prompt.slice(0, 200),
|
|
56
69
|
queryClass: queryClass ?? "general",
|
|
57
70
|
injected: injected.map(m => ({ text: m.text.slice(0, 100), category: m.category, similarity: m.similarity })),
|
|
58
71
|
});
|
|
59
|
-
|
|
60
|
-
// Cap file at 500KB
|
|
61
|
-
const stat = fs.statSync(telemetryPath);
|
|
62
|
-
if (stat.size > 512000) {
|
|
63
|
-
const lines = fs.readFileSync(telemetryPath, "utf-8").split("\n").filter(Boolean);
|
|
64
|
-
fs.writeFileSync(telemetryPath, lines.slice(-200).join("\n") + "\n", "utf-8");
|
|
65
|
-
}
|
|
66
|
-
} catch (e: any) { console.debug("[rules] telemetry write failed:", e?.message?.slice(0, 100)); }
|
|
72
|
+
} catch (e: any) { log.debug("telemetry write failed:", e?.message?.slice(0, 100)); }
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
/** Log whether injected memories were referenced in the LLM response
|
|
75
|
+
/** Log whether injected memories were referenced in the LLM response.
|
|
76
|
+
* Uses JsonlAppendLog — seq/ts added automatically. */
|
|
70
77
|
function logMemoryUsage(cwd: string, injected: { text: string; category: string; similarity: number }[], used: { text: string; category: string; similarity: number }[]): void {
|
|
71
78
|
try {
|
|
72
|
-
|
|
73
|
-
const entry = JSON.stringify({
|
|
74
|
-
ts: new Date().toISOString(),
|
|
79
|
+
getTelemetryLog(cwd).append({
|
|
75
80
|
event: "usage",
|
|
76
81
|
injectedCount: injected.length,
|
|
77
82
|
usedCount: used.length,
|
|
78
83
|
usageRate: injected.length > 0 ? +(used.length / injected.length).toFixed(2) : 0,
|
|
79
84
|
used: used.map(m => ({ text: m.text.slice(0, 100), category: m.category })),
|
|
80
85
|
});
|
|
81
|
-
|
|
82
|
-
} catch (e: any) { console.debug("[rules] telemetry usage write failed:", e?.message?.slice(0, 100)); }
|
|
86
|
+
} catch (e: any) { log.debug("telemetry usage write failed:", e?.message?.slice(0, 100)); }
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
export function registerRules(
|
|
@@ -93,13 +97,10 @@ export function registerRules(
|
|
|
93
97
|
// Run full rebuild on session start (once per session, not every turn)
|
|
94
98
|
pi.on("session_start", async (_event, ctx) => {
|
|
95
99
|
rebuildDone = false;
|
|
96
|
-
lastTaskFocusUserMsgId = null;
|
|
97
|
-
taskFocusPending = false;
|
|
98
|
-
taskFocusPendingMsgId = null;
|
|
99
100
|
try {
|
|
100
101
|
rebuildAndOrganize(ctx.cwd);
|
|
101
102
|
rebuildDone = true;
|
|
102
|
-
} catch (e: any) {
|
|
103
|
+
} catch (e: any) { log.debug("rebuildAndOrganize on session_start failed:", e?.message || e); }
|
|
103
104
|
|
|
104
105
|
// Bootstrap vector embeddings — embed missing entries only (orchestrator only)
|
|
105
106
|
// Skips model init entirely if all entries are already embedded
|
|
@@ -112,7 +113,7 @@ export function registerRules(
|
|
|
112
113
|
// embedMissing checks store first — only inits model if entries are actually missing
|
|
113
114
|
await embedMissing(ctx.cwd, entries);
|
|
114
115
|
}
|
|
115
|
-
} catch (e: any) {
|
|
116
|
+
} catch (e: any) { log.debug("embedding bootstrap failed:", e?.message?.slice(0, 100)); }
|
|
116
117
|
}
|
|
117
118
|
});
|
|
118
119
|
|
|
@@ -122,7 +123,7 @@ export function registerRules(
|
|
|
122
123
|
try {
|
|
123
124
|
rebuildAndOrganize(ctx.cwd);
|
|
124
125
|
rebuildDone = true;
|
|
125
|
-
} catch (e: any) {
|
|
126
|
+
} catch (e: any) { log.debug("rebuildAndOrganize on agent_start failed:", e?.message || e); }
|
|
126
127
|
}
|
|
127
128
|
let extra = "";
|
|
128
129
|
|
|
@@ -146,7 +147,7 @@ export function registerRules(
|
|
|
146
147
|
ruleCandidates.set(f, existing);
|
|
147
148
|
}
|
|
148
149
|
} catch (e: any) {
|
|
149
|
-
if (e?.code !== "ENOENT")
|
|
150
|
+
if (e?.code !== "ENOENT") log.debug("failed to read", dir, e?.message?.slice(0, 100));
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -160,7 +161,7 @@ export function registerRules(
|
|
|
160
161
|
try {
|
|
161
162
|
const stat = fs.statSync(candidates[i]);
|
|
162
163
|
if (stat.size > 128 * 1024) {
|
|
163
|
-
|
|
164
|
+
log.debug(`${fileName} skipped (${Math.round(stat.size / 1024)}KB > 128KB limit)`);
|
|
164
165
|
loaded = true; // Mark as loaded to prevent lower-precedence fallback
|
|
165
166
|
break;
|
|
166
167
|
}
|
|
@@ -168,16 +169,44 @@ export function registerRules(
|
|
|
168
169
|
loaded = true;
|
|
169
170
|
break;
|
|
170
171
|
} catch (e: any) {
|
|
171
|
-
|
|
172
|
+
log.debug(`${fileName} failed from ${candidates[i]}:`, e?.message?.slice(0, 100));
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
if (!loaded) {
|
|
175
|
-
|
|
176
|
+
log.debug(`${fileName}: all candidates failed, skipping`);
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
if (ruleContents.length > 0) {
|
|
179
180
|
const reviewLoopMaxCycles = getSetting(ctx.cwd, "review_loop_max_cycles");
|
|
180
|
-
|
|
181
|
+
log.debug("assembling rules", { count: ruleContents.length, reviewLoopMaxCycles });
|
|
182
|
+
let includedCount = 0;
|
|
183
|
+
let skippedCount = 0;
|
|
184
|
+
const joined = assembleRuleText(ruleContents, {
|
|
185
|
+
resolve: (key) => getSetting(ctx.cwd, key),
|
|
186
|
+
knownKeys: Object.keys(SETTINGS_KEYS),
|
|
187
|
+
featurePredicates: {
|
|
188
|
+
coms_active: () => isComsActive(),
|
|
189
|
+
external_ai_agents: () =>
|
|
190
|
+
isSettingTruthy(getSetting(ctx.cwd, "acpx_agents")) ||
|
|
191
|
+
isSettingTruthy(getSetting(ctx.cwd, "cli_agents")),
|
|
192
|
+
},
|
|
193
|
+
reviewLoopMaxCycles: Number(reviewLoopMaxCycles),
|
|
194
|
+
onWarn: (msg) => log.warn("rule assembly:", msg),
|
|
195
|
+
onSkip: ({ attrs, index }) => {
|
|
196
|
+
skippedCount += 1;
|
|
197
|
+
log.debug("rule skipped by frontmatter gate", { index, attrs });
|
|
198
|
+
},
|
|
199
|
+
onInclude: ({ attrs, index }) => {
|
|
200
|
+
includedCount += 1;
|
|
201
|
+
log.debug("rule included", { index, attrs });
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
log.info("rules assembled", {
|
|
205
|
+
chars: joined.length,
|
|
206
|
+
files: ruleContents.length,
|
|
207
|
+
included: includedCount,
|
|
208
|
+
skipped: skippedCount,
|
|
209
|
+
});
|
|
181
210
|
extra += "\n\n" + joined;
|
|
182
211
|
} else {
|
|
183
212
|
extra +=
|
|
@@ -235,6 +264,7 @@ export function registerRules(
|
|
|
235
264
|
const results = await vectorSearch(ctx.cwd, event.prompt, searchPool, topK);
|
|
236
265
|
const relevant = results.filter(r => r.similarity > 0.65);
|
|
237
266
|
if (relevant.length > 0) {
|
|
267
|
+
log.info("context_injected", "contextual_memories");
|
|
238
268
|
contextMemories = "\n# Contextually Relevant Memories\n\n" +
|
|
239
269
|
"These memories were automatically retrieved based on your current message:\n\n" +
|
|
240
270
|
relevant.map(r => `- [${r.category}] ${r.text} (similarity: ${r.similarity.toFixed(3)})`).join("\n") +
|
|
@@ -242,9 +272,10 @@ export function registerRules(
|
|
|
242
272
|
// Retrieval telemetry — track what was injected
|
|
243
273
|
logMemoryInjection(ctx.cwd, event.prompt, relevant, queryClass);
|
|
244
274
|
lastInjectedMemories = relevant;
|
|
275
|
+
log.info("memory_injected", relevant.length);
|
|
245
276
|
}
|
|
246
277
|
}
|
|
247
|
-
} catch (e: any) {
|
|
278
|
+
} catch (e: any) { log.debug("vector search auto-inject failed:", e?.message?.slice(0, 100)); }
|
|
248
279
|
}
|
|
249
280
|
|
|
250
281
|
// Auto-inject relevant session history (keyword search, zero LLM cost)
|
|
@@ -254,70 +285,23 @@ export function registerRules(
|
|
|
254
285
|
const { searchSessions } = await import("./session-search.js");
|
|
255
286
|
const sessionResults = searchSessions(ctx.cwd, event.prompt, 3);
|
|
256
287
|
if (sessionResults.length > 0) {
|
|
288
|
+
log.info("context_injected", "session_history");
|
|
257
289
|
sessionContext = "\n# Relevant Past Sessions\n\n" +
|
|
258
290
|
"Automatically retrieved from past conversation summaries:\n\n" +
|
|
259
291
|
sessionResults.map(r =>
|
|
260
292
|
`- **${r.timestamp.split("T")[0]}** (${r.sessionId.slice(0, 8)}): ${r.snippet.replace(/[`$]/g, "")}`
|
|
261
293
|
).join("\n") +
|
|
262
294
|
"\n\n";
|
|
263
|
-
// Telemetry — log session injections
|
|
295
|
+
// Telemetry — log session injections via JsonlAppendLog
|
|
264
296
|
try {
|
|
265
|
-
|
|
266
|
-
const entry = JSON.stringify({
|
|
267
|
-
ts: new Date().toISOString(),
|
|
297
|
+
getTelemetryLog(ctx.cwd).append({
|
|
268
298
|
event: "session-inject",
|
|
269
299
|
prompt: event.prompt.slice(0, 200),
|
|
270
300
|
sessionCount: sessionResults.length,
|
|
271
301
|
});
|
|
272
|
-
|
|
273
|
-
const stat = fs.statSync(telemetryPath);
|
|
274
|
-
if (stat.size > 512000) {
|
|
275
|
-
const lines = fs.readFileSync(telemetryPath, "utf-8").split("\n").filter(Boolean);
|
|
276
|
-
fs.writeFileSync(telemetryPath, lines.slice(-200).join("\n") + "\n", "utf-8");
|
|
277
|
-
}
|
|
278
|
-
} catch (e: any) { console.debug("[rules] session telemetry write failed:", e?.message?.slice(0, 100)); }
|
|
279
|
-
}
|
|
280
|
-
} catch (e: any) { console.debug("[rules] session history auto-inject failed:", e?.message?.slice(0, 100)); }
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Task-focus reminder — compose with FRESH task data (flag set at turn_end)
|
|
284
|
-
if (taskFocusPending) {
|
|
285
|
-
taskFocusPending = false;
|
|
286
|
-
try {
|
|
287
|
-
const tasksDir = path.join(ctx.cwd, ".pi", "tasks");
|
|
288
|
-
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
289
|
-
const taskCandidates: string[] = [];
|
|
290
|
-
if (sessionId) taskCandidates.push(path.join(tasksDir, `tasks-${sessionId}.json`));
|
|
291
|
-
taskCandidates.push(path.join(tasksDir, "tasks.json"));
|
|
292
|
-
// Session-first fallback: prefer session-scoped store, only fall back
|
|
293
|
-
// to tasks.json when the session store doesn't exist or has no active tasks.
|
|
294
|
-
// This matches the semantics used by readTaskSummary() and task lifecycle functions.
|
|
295
|
-
let allActiveTasks: Array<{ id: string; status: string; subject: string }> = [];
|
|
296
|
-
for (const taskFile of taskCandidates) {
|
|
297
|
-
try {
|
|
298
|
-
if (!fs.existsSync(taskFile)) continue;
|
|
299
|
-
const data = JSON.parse(fs.readFileSync(taskFile, "utf-8"));
|
|
300
|
-
const tasks = data.tasks || [];
|
|
301
|
-
const active = tasks.filter((t: any) => t.status === "in_progress" || t.status === "pending");
|
|
302
|
-
if (active.length > 0) {
|
|
303
|
-
allActiveTasks = active;
|
|
304
|
-
break; // Use the first store that has active tasks
|
|
305
|
-
}
|
|
306
|
-
} catch { continue; }
|
|
307
|
-
}
|
|
308
|
-
if (allActiveTasks.length > 0) {
|
|
309
|
-
const summary = allActiveTasks
|
|
310
|
-
.slice(0, 3)
|
|
311
|
-
.map((t) => `#${t.id} [${t.status}] ${t.subject}`)
|
|
312
|
-
.join(", ");
|
|
313
|
-
pi.sendMessage({
|
|
314
|
-
customType: "task-focus-enforcement",
|
|
315
|
-
content: `⚠️ You have active tasks — resume your workflow now:\n${summary}${allActiveTasks.length > 3 ? ` (+${allActiveTasks.length - 3} more)` : ""}`,
|
|
316
|
-
display: true,
|
|
317
|
-
}, { triggerTurn: false, deliverAs: "nextTurn" });
|
|
318
|
-
lastTaskFocusUserMsgId = taskFocusPendingMsgId;
|
|
302
|
+
} catch (e: any) { log.debug("session telemetry write failed:", e?.message?.slice(0, 100)); }
|
|
319
303
|
}
|
|
320
|
-
} catch (e: any) {
|
|
304
|
+
} catch (e: any) { log.debug("session history auto-inject failed:", e?.message?.slice(0, 100)); }
|
|
321
305
|
}
|
|
322
306
|
|
|
323
307
|
if (!extra && !memories && !contextMemories && !sessionContext) return;
|
|
@@ -332,6 +316,7 @@ export function registerRules(
|
|
|
332
316
|
// This catches cases where the user's prompt doesn't mention deployment
|
|
333
317
|
// but the AI just modified files that have deployment-related memories.
|
|
334
318
|
pi.on("turn_end", async (_event, ctx) => {
|
|
319
|
+
log.info("turn_end_enter");
|
|
335
320
|
if (process.env.PI_SUBAGENT_CHILD === "1") return;
|
|
336
321
|
|
|
337
322
|
// Retrieval telemetry — detect if injected memories were used in the response
|
|
@@ -349,45 +334,7 @@ export function registerRules(
|
|
|
349
334
|
}
|
|
350
335
|
lastInjectedMemories = [];
|
|
351
336
|
}
|
|
352
|
-
} catch (e: any) {
|
|
353
|
-
|
|
354
|
-
// Task-focus enforcement: if this turn had no tool calls but tasks are active,
|
|
355
|
-
// the LLM likely answered a side question and forgot to resume work.
|
|
356
|
-
// Set a flag here; the actual reminder is composed at before_agent_start
|
|
357
|
-
// with fresh task data (avoids stale/deleted tasks in the message).
|
|
358
|
-
try {
|
|
359
|
-
const turnToolResults = (_event as any).toolResults;
|
|
360
|
-
const hadToolCalls = turnToolResults && Array.isArray(turnToolResults) && turnToolResults.length > 0;
|
|
361
|
-
let userTriggered = false;
|
|
362
|
-
let triggeringMsgId: string | null = null;
|
|
363
|
-
try {
|
|
364
|
-
const branch = ctx.sessionManager.getBranch();
|
|
365
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
366
|
-
const entry = branch[i];
|
|
367
|
-
if (entry.type === "message" && (entry as any).message?.role === "assistant") continue;
|
|
368
|
-
if (entry.type === "custom_message") {
|
|
369
|
-
userTriggered = false;
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
if (entry.type === "message" && (entry as any).message?.role === "user") {
|
|
373
|
-
const msgId = (entry as any).id;
|
|
374
|
-
if (msgId && msgId === lastTaskFocusUserMsgId) {
|
|
375
|
-
userTriggered = false;
|
|
376
|
-
} else {
|
|
377
|
-
userTriggered = true;
|
|
378
|
-
triggeringMsgId = msgId ?? null;
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
} catch { /* best-effort */ }
|
|
385
|
-
|
|
386
|
-
if (!hadToolCalls && userTriggered) {
|
|
387
|
-
taskFocusPending = true;
|
|
388
|
-
taskFocusPendingMsgId = triggeringMsgId;
|
|
389
|
-
}
|
|
390
|
-
} catch (e: any) { console.debug("[rules] task-focus enforcement failed:", e?.message?.slice(0, 100)); }
|
|
337
|
+
} catch (e: any) { log.debug("telemetry usage detection failed:", e?.message?.slice(0, 100)); }
|
|
391
338
|
|
|
392
339
|
// Semantic enforcement: check verifier rules against this turn's tool calls
|
|
393
340
|
try {
|
|
@@ -411,7 +358,7 @@ export function registerRules(
|
|
|
411
358
|
}
|
|
412
359
|
}
|
|
413
360
|
}
|
|
414
|
-
} catch (e: any) {
|
|
361
|
+
} catch (e: any) { log.debug("semantic enforcement failed:", e?.message?.slice(0, 100)); }
|
|
415
362
|
|
|
416
363
|
// Check what files were modified in this turn by looking at tool results
|
|
417
364
|
const toolResults = (_event as any).toolResults;
|
|
@@ -454,8 +401,9 @@ export function registerRules(
|
|
|
454
401
|
}, { triggerTurn: false, deliverAs: "followUp" });
|
|
455
402
|
}
|
|
456
403
|
} catch (e: any) {
|
|
457
|
-
|
|
404
|
+
log.debug("turn_end memory reminder failed:", e?.message?.slice(0, 100));
|
|
458
405
|
}
|
|
406
|
+
log.info("turn_end_exit");
|
|
459
407
|
});
|
|
460
408
|
}
|
|
461
409
|
|
|
@@ -476,6 +424,6 @@ function loadMemoriesWithScoring(
|
|
|
476
424
|
}
|
|
477
425
|
return result + "\n";
|
|
478
426
|
}
|
|
479
|
-
} catch (e: any) {
|
|
427
|
+
} catch (e: any) { log.debug("situation report failed:", e?.message || e); }
|
|
480
428
|
return "";
|
|
481
429
|
}
|