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.
Files changed (100) hide show
  1. package/README.md +76 -67
  2. package/agents/code-reviewer-docs.md +11 -1
  3. package/agents/code-reviewer-guidelines.md +11 -1
  4. package/agents/code-reviewer-quality.md +11 -1
  5. package/agents/code-reviewer-security.md +11 -1
  6. package/agents/code-reviewer-spec.md +11 -1
  7. package/agents/git-expert.md +42 -3
  8. package/agents/github-expert.md +11 -0
  9. package/extensions/acpx-provider/runtime-models.ts +18 -6
  10. package/extensions/cli-provider/agents/claude.ts +3 -2
  11. package/extensions/cli-provider/agents/cursor.ts +3 -5
  12. package/extensions/cli-provider/agents/gemini.ts +3 -2
  13. package/extensions/cli-provider/discover.ts +7 -7
  14. package/extensions/cli-provider/index.ts +5 -0
  15. package/extensions/cli-provider/runtime-models.ts +18 -6
  16. package/extensions/cli-provider/session-reaper.ts +5 -7
  17. package/extensions/cli-provider/sessions.ts +4 -0
  18. package/extensions/cli-provider/shared/discover-cache.ts +17 -176
  19. package/extensions/coms/coms-p2p.ts +1719 -423
  20. package/extensions/coms/coms-shared.ts +261 -39
  21. package/extensions/coms/coms-wrapper.ts +100 -62
  22. package/extensions/coms/index.ts +8 -4
  23. package/extensions/coms/mixed-turn.ts +26 -0
  24. package/extensions/coms/probe-socket.ts +42 -0
  25. package/extensions/coms/themeMap.ts +0 -1
  26. package/extensions/image-gen/image-gen.ts +19 -1
  27. package/extensions/orchestrator/async-agents.ts +563 -102
  28. package/extensions/orchestrator/async-capability.ts +8 -8
  29. package/extensions/orchestrator/cron.ts +15 -9
  30. package/extensions/orchestrator/dreaming.ts +32 -25
  31. package/extensions/orchestrator/enforcement-helpers.ts +459 -17
  32. package/extensions/orchestrator/enforcement-rules.ts +5 -4
  33. package/extensions/orchestrator/enforcement.ts +80 -27
  34. package/extensions/orchestrator/extended-autocomplete.ts +7 -0
  35. package/extensions/orchestrator/git-helpers.ts +2 -2
  36. package/extensions/orchestrator/index.ts +78 -10
  37. package/extensions/orchestrator/markdown-transformer.ts +313 -0
  38. package/extensions/orchestrator/memory-embeddings.ts +36 -34
  39. package/extensions/orchestrator/memory-provenance.ts +29 -25
  40. package/extensions/orchestrator/memory-scoring.ts +23 -18
  41. package/extensions/orchestrator/memory-tools.ts +10 -3
  42. package/extensions/orchestrator/overlay-dashboard.ts +17 -5
  43. package/extensions/orchestrator/parse-model-override.ts +29 -0
  44. package/extensions/orchestrator/pi-config-review-state.ts +57 -33
  45. package/extensions/orchestrator/project-settings.ts +333 -127
  46. package/extensions/orchestrator/resolve-agent-model.ts +24 -13
  47. package/extensions/orchestrator/review-porcelain.ts +4 -0
  48. package/extensions/orchestrator/review-ui.ts +63 -15
  49. package/extensions/orchestrator/rule-placeholders.ts +411 -2
  50. package/extensions/orchestrator/rules.ts +80 -132
  51. package/extensions/orchestrator/settings-tui-helpers.ts +380 -0
  52. package/extensions/orchestrator/settings-tui-submenus.ts +647 -0
  53. package/extensions/orchestrator/settings-tui.ts +636 -0
  54. package/extensions/orchestrator/state-jsonl.ts +431 -0
  55. package/extensions/orchestrator/subagent-tool.ts +157 -59
  56. package/extensions/orchestrator/sync-limit.ts +23 -0
  57. package/extensions/orchestrator/task-lifecycle.ts +77 -67
  58. package/extensions/orchestrator/utils.ts +8 -5
  59. package/extensions/pidash/pidash.ts +193 -156
  60. package/extensions/pidiff/pidiff.ts +96 -38
  61. package/extensions/pidiff/spawn-lock.ts +62 -0
  62. package/extensions/pitasks/index.ts +430 -0
  63. package/extensions/pitasks/reminders.ts +37 -0
  64. package/extensions/pitasks/task-store.ts +467 -0
  65. package/extensions/pitasks/task-tools.ts +375 -0
  66. package/extensions/pitasks/task-widget.ts +181 -0
  67. package/extensions/pitasks/types.ts +38 -0
  68. package/extensions/providers/acpx-driver.ts +12 -2
  69. package/extensions/providers/built-in-drivers.ts +6 -6
  70. package/extensions/providers/claude-driver.ts +1 -1
  71. package/extensions/providers/cursor-cli-driver.ts +1 -1
  72. package/extensions/providers/gemini-driver.ts +1 -1
  73. package/extensions/providers/index.ts +144 -26
  74. package/extensions/providers/initialized-guard.ts +27 -0
  75. package/extensions/providers/restore-default-model.ts +596 -0
  76. package/extensions/providers/session-shutdown.ts +39 -0
  77. package/extensions/shared/coms-active.ts +23 -0
  78. package/extensions/shared/create-runtime-provider.ts +2 -0
  79. package/extensions/shared/daemon-manager.ts +0 -8
  80. package/extensions/shared/file-logger.ts +132 -25
  81. package/extensions/shared/logger.ts +50 -0
  82. package/extensions/shared/models-dev.ts +421 -0
  83. package/extensions/shared/oneshot.ts +116 -0
  84. package/extensions/shared/resolve-binary.ts +191 -0
  85. package/extensions/shared/types.ts +3 -0
  86. package/package.json +9 -3
  87. package/prompts/coderabbit-rate-limit.md +2 -0
  88. package/rules/00-orchestrator-core.md +3 -0
  89. package/rules/10-agent-routing.md +12 -1
  90. package/rules/20-code-review-loop.md +52 -25
  91. package/rules/30-prompt-templates.md +4 -0
  92. package/rules/35-memory.md +3 -3
  93. package/rules/40-critical-rules.md +3 -0
  94. package/rules/55-coms-protocol.md +84 -18
  95. package/rules/60-task-tracking.md +2 -0
  96. package/settings-keys.json +563 -0
  97. package/templates/coms-feature-manager-prompt.md +4 -6
  98. package/extensions/coms/coms-net-server.ts +0 -1642
  99. package/extensions/coms/coms-net-wrapper.ts +0 -521
  100. 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 { substituteRulePlaceholders } from "./rule-placeholders.js";
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
- // Track the user message ID that last triggered task-focus enforcement.
39
- // Don't re-fire for the same user message (prevents loops from followUp turns).
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
- const telemetryPath = path.join(getProjectDataDir(cwd), "memory-telemetry.jsonl");
53
- const entry = JSON.stringify({
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
- fs.appendFileSync(telemetryPath, entry + "\n", "utf-8");
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
- const telemetryPath = path.join(getProjectDataDir(cwd), "memory-telemetry.jsonl");
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
- fs.appendFileSync(telemetryPath, entry + "\n", "utf-8");
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) { console.debug("[rules] rebuildAndOrganize on session_start failed:", e?.message || e); }
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) { console.debug("[rules] embedding bootstrap failed:", e?.message?.slice(0, 100)); }
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) { console.debug("[rules] rebuildAndOrganize on agent_start failed:", e?.message || e); }
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") console.debug("[rules] failed to read", dir, e?.message?.slice(0, 100));
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
- console.debug(`[rules] ${fileName} skipped (${Math.round(stat.size / 1024)}KB > 128KB limit)`);
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
- console.debug(`[rules] ${fileName} failed from ${candidates[i]}:`, e?.message?.slice(0, 100));
172
+ log.debug(`${fileName} failed from ${candidates[i]}:`, e?.message?.slice(0, 100));
172
173
  }
173
174
  }
174
175
  if (!loaded) {
175
- console.debug(`[rules] ${fileName}: all candidates failed, skipping`);
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
- const joined = substituteRulePlaceholders(ruleContents.join("\n\n"), { reviewLoopMaxCycles });
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) { console.debug("[rules] vector search auto-inject failed:", e?.message?.slice(0, 100)); }
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 (same safeguards as logMemoryInjection)
295
+ // Telemetry — log session injections via JsonlAppendLog
264
296
  try {
265
- const telemetryPath = path.join(getProjectDataDir(ctx.cwd), "memory-telemetry.jsonl");
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
- fs.appendFileSync(telemetryPath, entry + "\n", "utf-8");
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) { console.debug("[rules] task-focus reminder failed:", e?.message?.slice(0, 100)); }
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) { console.debug("[rules] telemetry usage detection failed:", e?.message?.slice(0, 100)); }
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) { console.debug("[rules] semantic enforcement failed:", e?.message?.slice(0, 100)); }
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
- console.debug("[rules] turn_end memory reminder failed:", e?.message?.slice(0, 100));
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) { console.debug("[rules] situation report failed:", e?.message || e); }
427
+ } catch (e: any) { log.debug("situation report failed:", e?.message || e); }
480
428
  return "";
481
429
  }