pi-harness-runtime 1.1.232 → 1.1.233

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 (115) hide show
  1. package/index.ts +10 -7
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/cookie-sanitizer/package.json +1 -1
  24. package/packages/cost-optimizer/package.json +1 -1
  25. package/packages/dependency-analyzer/package.json +1 -1
  26. package/packages/django-plugin/package.json +1 -1
  27. package/packages/doc-generator/package.json +1 -1
  28. package/packages/e2e-tester/package.json +1 -1
  29. package/packages/evaluation-engine/package.json +1 -1
  30. package/packages/evaluation-runner/package.json +1 -1
  31. package/packages/event-bus/package.json +1 -1
  32. package/packages/event-store/package.json +1 -1
  33. package/packages/experience-replay/package.json +1 -1
  34. package/packages/feedback-collector/package.json +1 -1
  35. package/packages/file-copy-helper/package.json +1 -1
  36. package/packages/framework-detector/package.json +1 -1
  37. package/packages/framework-plugin-sdk/package.json +1 -1
  38. package/packages/frappe-plugin/package.json +1 -1
  39. package/packages/generic-web-plugin/package.json +1 -1
  40. package/packages/health-monitor/package.json +1 -1
  41. package/packages/intent-analyzer/package.json +1 -1
  42. package/packages/jev-judge/dist/continuation-controller.d.ts +14 -12
  43. package/packages/jev-judge/dist/continuation-controller.d.ts.map +1 -1
  44. package/packages/jev-judge/dist/continuation-controller.js +97 -65
  45. package/packages/jev-judge/dist/continuation-controller.js.map +1 -1
  46. package/packages/jev-judge/dist/todo-provider.d.ts +16 -1
  47. package/packages/jev-judge/dist/todo-provider.d.ts.map +1 -1
  48. package/packages/jev-judge/dist/todo-provider.js +22 -11
  49. package/packages/jev-judge/dist/todo-provider.js.map +1 -1
  50. package/packages/jev-judge/dist/typesafe-provider.d.ts.map +1 -1
  51. package/packages/jev-judge/dist/typesafe-provider.js +12 -3
  52. package/packages/jev-judge/dist/typesafe-provider.js.map +1 -1
  53. package/packages/jev-judge/package.json +1 -1
  54. package/packages/jev-judge/src/continuation-controller.ts +111 -77
  55. package/packages/jev-judge/src/todo-provider.ts +40 -21
  56. package/packages/jev-judge/src/typesafe-provider.ts +12 -3
  57. package/packages/knowledge-graph/package.json +1 -1
  58. package/packages/knowledge-retrieval/package.json +1 -1
  59. package/packages/laravel-plugin/package.json +1 -1
  60. package/packages/learning-engine/package.json +1 -1
  61. package/packages/mcp-adapter/package.json +1 -1
  62. package/packages/memory-engine/package.json +1 -1
  63. package/packages/milestone-manager/package.json +1 -1
  64. package/packages/model-registry/package.json +1 -1
  65. package/packages/nextjs-plugin/package.json +1 -1
  66. package/packages/notification/package.json +1 -1
  67. package/packages/observability/package.json +1 -1
  68. package/packages/okf-indexer/package.json +1 -1
  69. package/packages/paperclip-integration/package.json +1 -1
  70. package/packages/performance-optimizer/package.json +1 -1
  71. package/packages/privilege-broker/package.json +1 -1
  72. package/packages/project-analyzer/package.json +1 -1
  73. package/packages/project-bootstrap/package.json +1 -1
  74. package/packages/projection-engine/package.json +1 -1
  75. package/packages/prompt-compiler/package.json +1 -1
  76. package/packages/prompt-versioning/package.json +1 -1
  77. package/packages/provider-adapter-sdk/package.json +1 -1
  78. package/packages/provider-router/package.json +1 -1
  79. package/packages/provider-selector/package.json +1 -1
  80. package/packages/providers/package.json +1 -1
  81. package/packages/qdrant-skills/package.json +1 -1
  82. package/packages/quota-manager/package.json +1 -1
  83. package/packages/rate-limiter/package.json +1 -1
  84. package/packages/react-vite-plugin/package.json +1 -1
  85. package/packages/release-manager/package.json +1 -1
  86. package/packages/requirement-compiler/package.json +1 -1
  87. package/packages/runtime/package.json +1 -1
  88. package/packages/scheduler/package.json +1 -1
  89. package/packages/scheduler-adapter/package.json +1 -1
  90. package/packages/session/package.json +1 -1
  91. package/packages/session-api/package.json +1 -1
  92. package/packages/session-export/package.json +1 -1
  93. package/packages/shared-context/package.json +1 -1
  94. package/packages/skill-mcp-client/package.json +1 -1
  95. package/packages/skill-registry/package.json +1 -1
  96. package/packages/skills/package.json +1 -1
  97. package/packages/sprint-planner/package.json +1 -1
  98. package/packages/subscription-engine/package.json +1 -1
  99. package/packages/task-compiler/package.json +1 -1
  100. package/packages/telegram/package.json +1 -1
  101. package/packages/tencentdb-memory/package.json +1 -1
  102. package/packages/tencentdb-sync/package.json +1 -1
  103. package/packages/test-data-generator/package.json +1 -1
  104. package/packages/test-generator/package.json +1 -1
  105. package/packages/todo-bd-sync/package.json +1 -1
  106. package/packages/token-estimation/package.json +1 -1
  107. package/packages/token-optimizer/package.json +1 -1
  108. package/packages/trajectory/package.json +1 -1
  109. package/packages/tui/package.json +1 -1
  110. package/packages/types/package.json +1 -1
  111. package/packages/webmcp-tester/package.json +1 -1
  112. package/packages/workflow-events/package.json +1 -1
  113. package/packages/workspace-scanner/package.json +1 -1
  114. package/packages/worktree/package.json +1 -1
  115. package/packages/write-review/package.json +1 -1
@@ -1,5 +1,4 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { TodoItem } from "./todo-provider.js";
1
+ import type { TodoItem, TodoContext } from "./todo-provider.js";
3
2
  import { createTodoProvider } from "./todo-provider.js";
4
3
 
5
4
  /**
@@ -45,7 +44,8 @@ export interface ContinuationState {
45
44
  continuationQueued: boolean;
46
45
  userPaused: boolean;
47
46
  lastSteerMessage: string | null;
48
- steerAttempts: number;
47
+ steerAttemptsForCurrentItem: number;
48
+ lastItemContinued: number | null;
49
49
  lastEvaluationResult: string | null;
50
50
  }
51
51
 
@@ -65,7 +65,7 @@ export interface ContinuationResult {
65
65
  * Evaluates todo state on agent_settled and determines next action.
66
66
  */
67
67
  export class TodoContinuationController {
68
- private pi: ExtensionAPI;
68
+ private ctx: TodoContext;
69
69
  private config: Required<ContinuationConfig>;
70
70
  private todoProvider: ReturnType<typeof createTodoProvider>;
71
71
 
@@ -79,13 +79,14 @@ export class TodoContinuationController {
79
79
  continuationQueued: false,
80
80
  userPaused: false,
81
81
  lastSteerMessage: null,
82
- steerAttempts: 0,
82
+ steerAttemptsForCurrentItem: 0,
83
+ lastItemContinued: null,
83
84
  lastEvaluationResult: null,
84
85
  };
85
86
 
86
- constructor(pi: ExtensionAPI, config?: ContinuationConfig) {
87
- this.pi = pi;
88
- this.todoProvider = createTodoProvider(pi);
87
+ constructor(ctx: TodoContext, config?: ContinuationConfig) {
88
+ this.ctx = ctx;
89
+ this.todoProvider = createTodoProvider(ctx);
89
90
  this.config = {
90
91
  jevFallbackMinWaitMs: 5 * 60 * 1000, // 5 minutes
91
92
  routineThreshold: 0.7,
@@ -96,78 +97,99 @@ export class TodoContinuationController {
96
97
  }
97
98
 
98
99
  /**
99
- * Register lifecycle hooks
100
+ * Check if continuation is allowed (not paused, session idle)
100
101
  */
101
- register(): void {
102
- debugLog("TodoContinuation", "Registering hooks");
103
-
104
- // Session start - reset state
105
- this.pi.on("session_start", () => {
106
- this.reset();
107
- debugLog("TodoContinuation", "Session started");
108
- });
109
-
110
- // Agent settled - evaluate continuation
111
- this.pi.on("agent_settled", () => {
112
- this.evaluateContinuation().catch(() => {
113
- // Silently handle errors
114
- });
115
- });
116
-
117
- // User input - reset pause state
118
- this.pi.on("message_start", (event) => {
119
- const msg = event.message as { role?: string };
120
- if (msg?.role === "user") {
121
- this.state.userPaused = false;
122
- this.state.lastSteerMessage = null;
123
- }
124
- });
125
-
126
- // Session shutdown - cleanup
127
- this.pi.on("session_shutdown", () => {
128
- this.cleanup();
129
- });
130
-
131
- // Tool call - track mutations that might invalidate state
132
- this.pi.on("tool_call", (event) => {
133
- // Increment generation on edits
134
- if (["write", "edit", "bash"].includes(event.toolName)) {
135
- this.state.generation++;
136
- this.state.continuationQueued = false;
137
- }
138
- });
139
-
140
- debugLog("TodoContinuation", "Lifecycle hooks registered");
102
+ private canContinue(): boolean {
103
+ if (this.state.userPaused) {
104
+ debugLog("TodoContinuation", "User paused, not continuing");
105
+ return false;
106
+ }
107
+ if (this.ctx.isIdle?.() === false) {
108
+ debugLog("TodoContinuation", "Session not idle, not continuing");
109
+ return false;
110
+ }
111
+ if (this.ctx.hasPendingMessages?.()) {
112
+ debugLog("TodoContinuation", "Pending messages, not continuing");
113
+ return false;
114
+ }
115
+ return true;
141
116
  }
142
117
 
143
118
  /**
144
119
  * Public method to evaluate and execute continuation
145
120
  * Called by external handlers (e.g., agent_settled)
121
+ * Checks blockers and pause BEFORE selecting a task
146
122
  */
147
123
  async decide(): Promise<ContinuationResult> {
124
+ // 1. Check if continuation is allowed
125
+ if (!this.canContinue()) {
126
+ return { action: "stop", reason: "paused or session not idle" };
127
+ }
128
+
129
+ // 2. Check provider availability
148
130
  const availability = await this.todoProvider.availability();
149
131
  if (!availability.available) {
150
132
  debugLog("TodoContinuation", "Provider unavailable:", availability.error);
151
- return { action: "stop", reason: availability.error };
133
+ return { action: "stop", reason: availability.error ?? "provider unavailable" };
152
134
  }
153
135
 
154
- const inProgress = await this.todoProvider.getInProgress();
155
- const pending = await this.todoProvider.getPending();
136
+ // 3. Get todo state (throws on branch read failure)
137
+ let inProgress: TodoItem[];
138
+ let pending: TodoItem[];
139
+ let blocked: TodoItem[];
140
+ try {
141
+ inProgress = await this.todoProvider.getInProgress();
142
+ pending = await this.todoProvider.getPending();
143
+ blocked = await this.todoProvider.getBlocked();
144
+ } catch (err) {
145
+ const msg = err instanceof Error ? err.message : String(err);
146
+ debugLog("TodoContinuation", "Failed to read todos:", msg);
147
+ return { action: "stop", reason: `todo read failed: ${msg}` };
148
+ }
156
149
 
157
- if (inProgress.length > 0) {
158
- return { action: "continue", nextItemId: inProgress[0].id };
150
+ // 4. Check for blocked items FIRST (before selecting any task)
151
+ if (blocked.length > 0) {
152
+ debugLog("TodoContinuation", "Tasks blocked:", blocked.length);
153
+ return { action: "blocked", question: `${blocked.length} task(s) blocked` };
159
154
  }
160
155
 
161
- if (pending.length > 0) {
162
- return { action: "continue", nextItemId: pending[0].id };
156
+ // 5. Continue in-progress work if any
157
+ if (inProgress.length > 0) {
158
+ const item = inProgress[0];
159
+
160
+ // Reset counter if switching to a different task
161
+ if (this.state.lastItemContinued !== item.id) {
162
+ this.state.steerAttemptsForCurrentItem = 0;
163
+ this.state.lastItemContinued = item.id;
164
+ }
165
+
166
+ // Check per-task attempt limit
167
+ if (this.state.steerAttemptsForCurrentItem >= this.config.maxSteerAttempts) {
168
+ return { action: "stop", reason: `max attempts (${this.config.maxSteerAttempts}) reached for task ${item.id}` };
169
+ }
170
+
171
+ return { action: "continue", nextItemId: item.id };
163
172
  }
164
173
 
165
- // No work remaining - but check for blocked items
166
- const blocked = await this.todoProvider.getBlocked();
167
- if (blocked.length > 0) {
168
- return { action: "blocked", question: `${blocked.length} task(s) blocked` };
174
+ // 6. Continue pending work if any
175
+ if (pending.length > 0) {
176
+ const item = pending[0];
177
+
178
+ // Reset counter if switching to a different task
179
+ if (this.state.lastItemContinued !== item.id) {
180
+ this.state.steerAttemptsForCurrentItem = 0;
181
+ this.state.lastItemContinued = item.id;
182
+ }
183
+
184
+ // Check per-task attempt limit
185
+ if (this.state.steerAttemptsForCurrentItem >= this.config.maxSteerAttempts) {
186
+ return { action: "stop", reason: `max attempts (${this.config.maxSteerAttempts}) reached for task ${item.id}` };
187
+ }
188
+
189
+ return { action: "continue", nextItemId: item.id };
169
190
  }
170
191
 
192
+ // 7. No work remaining
171
193
  return { action: "stop", reason: "No pending tasks" };
172
194
  }
173
195
 
@@ -239,13 +261,19 @@ export class TodoContinuationController {
239
261
  /**
240
262
  * Continue a specific todo item
241
263
  */
242
- private async continueItem(item: TodoItem): Promise<void> {
243
- if (this.state.steerAttempts >= this.config.maxSteerAttempts) {
244
- debugLog("TodoContinuation", "Max steer attempts reached");
264
+ async continueItem(item: TodoItem): Promise<void> {
265
+ // Increment attempts for this specific item
266
+ if (this.state.lastItemContinued !== item.id) {
267
+ this.state.steerAttemptsForCurrentItem = 0;
268
+ this.state.lastItemContinued = item.id;
269
+ }
270
+
271
+ if (this.state.steerAttemptsForCurrentItem >= this.config.maxSteerAttempts) {
272
+ debugLog("TodoContinuation", "Max steer attempts reached for item", item.id);
245
273
  return;
246
274
  }
247
275
 
248
- this.state.steerAttempts++;
276
+ this.state.steerAttemptsForCurrentItem++;
249
277
  this.state.continuationQueued = true;
250
278
 
251
279
  // Build continuation message
@@ -258,7 +286,7 @@ export class TodoContinuationController {
258
286
 
259
287
  // Send continuation message via Pi API
260
288
  try {
261
- await (this.pi as ExtensionAPI & { sendUserMessage: (content: string, opts?: { deliverAs?: string }) => Promise<void> }).sendUserMessage(message, { deliverAs: "steer" });
289
+ await this.ctx.sendUserMessage?.(message, { deliverAs: "steer" });
262
290
  } catch (err) {
263
291
  debugLog("TodoContinuation", "Failed to send continuation:", String(err));
264
292
  this.state.continuationQueued = false;
@@ -269,12 +297,18 @@ export class TodoContinuationController {
269
297
  * Steer to continue a todo item
270
298
  */
271
299
  async steerToContinue(itemId: number): Promise<void> {
272
- if (this.state.steerAttempts >= this.config.maxSteerAttempts) {
273
- debugLog("TodoContinuation", "Max steer attempts reached");
300
+ // Increment attempts for this specific item
301
+ if (this.state.lastItemContinued !== itemId) {
302
+ this.state.steerAttemptsForCurrentItem = 0;
303
+ this.state.lastItemContinued = itemId;
304
+ }
305
+
306
+ if (this.state.steerAttemptsForCurrentItem >= this.config.maxSteerAttempts) {
307
+ debugLog("TodoContinuation", "Max steer attempts reached for item", itemId);
274
308
  return;
275
309
  }
276
310
 
277
- this.state.steerAttempts++;
311
+ this.state.steerAttemptsForCurrentItem++;
278
312
  this.state.continuationQueued = true;
279
313
 
280
314
  const snapshot = await this.todoProvider.getSnapshot();
@@ -290,7 +324,7 @@ export class TodoContinuationController {
290
324
 
291
325
  // Send continuation message via Pi API
292
326
  try {
293
- await (this.pi as ExtensionAPI & { sendUserMessage: (content: string, opts?: { deliverAs?: string }) => Promise<void> }).sendUserMessage(message, { deliverAs: "steer" });
327
+ await this.ctx.sendUserMessage?.(message, { deliverAs: "steer" });
294
328
  } catch (err) {
295
329
  debugLog("TodoContinuation", "Failed to send steer:", String(err));
296
330
  this.state.continuationQueued = false;
@@ -318,7 +352,7 @@ export class TodoContinuationController {
318
352
  /**
319
353
  * Reset state for new session
320
354
  */
321
- private reset(): void {
355
+ _reset(): void {
322
356
  this.state = {
323
357
  sessionId: `session-${Date.now()}`,
324
358
  generation: 0,
@@ -329,7 +363,8 @@ export class TodoContinuationController {
329
363
  continuationQueued: false,
330
364
  userPaused: false,
331
365
  lastSteerMessage: null,
332
- steerAttempts: 0,
366
+ steerAttemptsForCurrentItem: 0,
367
+ lastItemContinued: null,
333
368
  lastEvaluationResult: null,
334
369
  };
335
370
  }
@@ -337,7 +372,7 @@ export class TodoContinuationController {
337
372
  /**
338
373
  * Cleanup on shutdown
339
374
  */
340
- private cleanup(): void {
375
+ _cleanup(): void {
341
376
  debugLog("TodoContinuation", "Cleaning up");
342
377
  this.state.continuationQueued = false;
343
378
  this.state.generation++;
@@ -356,13 +391,12 @@ export class TodoContinuationController {
356
391
  }
357
392
 
358
393
  /**
359
- * Create and register a todo continuation controller
394
+ * Create a todo continuation controller
395
+ * Note: Caller is responsible for registering event handlers with the proper context
360
396
  */
361
397
  export function createTodoContinuation(
362
- pi: ExtensionAPI,
398
+ ctx: TodoContext,
363
399
  config?: ContinuationConfig
364
400
  ): TodoContinuationController {
365
- const controller = new TodoContinuationController(pi, config);
366
- controller.register();
367
- return controller;
401
+ return new TodoContinuationController(ctx, config);
368
402
  }
@@ -7,6 +7,20 @@
7
7
 
8
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
9
 
10
+ /**
11
+ * Minimal context interface for todo provider
12
+ * Contains only the properties needed to read session branch and send messages
13
+ */
14
+ export interface TodoContext {
15
+ sessionManager: {
16
+ getSessionId(): string;
17
+ getBranch(): Iterable<unknown>;
18
+ };
19
+ isIdle?: () => boolean;
20
+ hasPendingMessages?: () => boolean;
21
+ sendUserMessage?: (content: string, opts?: { deliverAs?: string }) => Promise<void>;
22
+ }
23
+
10
24
  /**
11
25
  * Todo item status
12
26
  */
@@ -175,22 +189,15 @@ export class MockTodoProvider implements TodoProvider {
175
189
  }
176
190
  }
177
191
 
178
- /**
179
- * Extended ExtensionAPI type with sessionManager (not in base type but available at runtime)
180
- */
181
- interface PiExtensionAPI extends ExtensionAPI {
182
- sessionManager: {
183
- getSessionId(): string;
184
- getBranch(): Iterable<unknown>;
185
- };
186
- }
192
+
187
193
 
188
194
  /**
189
195
  * Create a todo provider that reads from rpiv-todo state via session branch
190
196
  */
191
- export function createTodoProvider(pi?: ExtensionAPI): TodoProvider {
192
- if (pi && "sessionManager" in pi) {
193
- return new PiTodoProvider(pi as PiExtensionAPI);
197
+ export function createTodoProvider(ctx?: TodoContext | ExtensionAPI): TodoProvider {
198
+ // Accept either TodoContext or ExtensionAPI with sessionManager
199
+ if (ctx && "sessionManager" in ctx) {
200
+ return new PiTodoProvider(ctx as TodoContext);
194
201
  }
195
202
  return new PiUnavailableTodoProvider();
196
203
  }
@@ -200,15 +207,16 @@ export function createTodoProvider(pi?: ExtensionAPI): TodoProvider {
200
207
  * Replicates rpiv-todo's replayFromBranch logic to access todo state
201
208
  */
202
209
  class PiTodoProvider implements TodoProvider {
203
- private pi: PiExtensionAPI;
210
+ private ctx: TodoContext;
204
211
  private _revision = 0;
212
+ private _lastError: string | null = null;
205
213
 
206
- constructor(pi: PiExtensionAPI) {
207
- this.pi = pi;
214
+ constructor(ctx: TodoContext) {
215
+ this.ctx = ctx;
208
216
  }
209
217
 
210
218
  async availability(): Promise<TodoAvailability> {
211
- if (!this.pi.sessionManager) {
219
+ if (!this.ctx.sessionManager) {
212
220
  return {
213
221
  available: false,
214
222
  error: "Session manager not available",
@@ -229,13 +237,15 @@ class PiTodoProvider implements TodoProvider {
229
237
 
230
238
  /**
231
239
  * Get todo state from session branch (replicates rpiv-todo's replayFromBranch)
240
+ * Returns null if branch read fails (don't silently return empty state)
232
241
  */
233
- private getTodoState(): { tasks: TodoItem[]; nextId: number } {
242
+ private getTodoState(): { tasks: TodoItem[]; nextId: number } | null {
234
243
  let fromEntries: { tasks: TodoItem[]; nextId: number } | undefined;
235
244
  let fromToolResults: { tasks: TodoItem[]; nextId: number } | undefined;
236
245
 
237
246
  try {
238
- for (const entry of this.pi.sessionManager.getBranch()) {
247
+ const branch = this.ctx.sessionManager.getBranch();
248
+ for (const entry of branch) {
239
249
  const e = entry as {
240
250
  type?: string;
241
251
  customType?: string;
@@ -259,19 +269,28 @@ class PiTodoProvider implements TodoProvider {
259
269
  fromToolResults = { tasks: (msg.details as { tasks: TodoItem[] }).tasks.map(t => ({ ...t })), nextId: (msg.details as { nextId: number }).nextId };
260
270
  }
261
271
  } catch (err) {
262
- // Fall through to EMPTY_STATE on error
272
+ // Return null on error - don't silently return empty state
273
+ this._lastError = err instanceof Error ? err.message : String(err);
274
+ return null;
263
275
  }
264
276
 
265
277
  // Prefer custom entries (more authoritative), fall back to tool results
266
- return fromEntries ?? fromToolResults ?? { tasks: [], nextId: 1 };
278
+ this._lastError = null;
279
+ return fromEntries ?? fromToolResults ?? null;
267
280
  }
268
281
 
269
282
  async getSnapshot(): Promise<TodoSnapshot> {
270
283
  this._revision++;
271
284
  const state = this.getTodoState();
285
+
286
+ // If branch read failed, throw to indicate unavailable state
287
+ if (state === null) {
288
+ throw new Error(`Todo state unavailable: ${this._lastError ?? "branch read failed"}`);
289
+ }
290
+
272
291
  return {
273
292
  items: state.tasks,
274
- sessionId: this.pi.sessionManager.getSessionId() ?? "unknown",
293
+ sessionId: this.ctx.sessionManager.getSessionId() ?? "unknown",
275
294
  revision: this._revision,
276
295
  createdAt: new Date(),
277
296
  };
@@ -258,12 +258,21 @@ export class TypeSafeJudge {
258
258
 
259
259
  switch (answer.type) {
260
260
  case "noul":
261
- // FIX: Use answer.noul (not probability) per TypeSafe API spec
261
+ // FIX: Validate noul value is present and in range [0, 1]
262
+ if (answer.noul === undefined || answer.noul === null) {
263
+ throw new Error(`TypeSafe API: missing noul value for question "${questionId}"`);
264
+ }
265
+ if (typeof answer.noul !== "number" || isNaN(answer.noul)) {
266
+ throw new Error(`TypeSafe API: invalid noul value for question "${questionId}": ${JSON.stringify(answer.noul)}`);
267
+ }
268
+ if (answer.noul < 0 || answer.noul > 1) {
269
+ throw new Error(`TypeSafe API: noul value out of range [0,1] for question "${questionId}": ${answer.noul}`);
270
+ }
262
271
  response = {
263
272
  type: "noul",
264
- noul: answer.noul ?? 0.5,
273
+ noul: answer.noul,
265
274
  };
266
- probability = answer.noul ?? 0.5;
275
+ probability = answer.noul;
267
276
  break;
268
277
 
269
278
  case "choice":
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/knowledge-graph",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Knowledge Graph extraction and provenance linking for SKILL.md files",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/knowledge-retrieval",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Knowledge retrieval via TencentDB-Agent-Memory MCP (RFC-0105)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/laravel-plugin",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Laravel framework analysis — artisan commands, Eloquent models, Blade",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/learning-engine",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Learning engine for extracting lessons from runtime execution (RFC-0058)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/mcp-adapter",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "type": "module",
5
5
  "description": "MCP server adapter for pi-harness — exposes harness capabilities as MCP tools/resources/prompts (RFC-0068)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/memory-engine",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Memory engine with OKF integration for durable knowledge management (RFC-0060)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/milestone-manager",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Track milestones and alerts when items are at risk",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/model-registry",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Model Registry for pi-harness-runtime — centralized model metadata with pricing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/nextjs-plugin",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Next.js framework detection and analysis for pi-harness-runtime (RFC-0062)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/notification",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "pi-harness-runtime — notification",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/observability",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Comprehensive observability system with structured logging, tracing, metrics, and alerting",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/okf-indexer",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "OKF document indexer for SKILL.md files (RFC-0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/paperclip-integration",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Paperclip API integration: poll agent inbox, route tasks to pi.dev, Telegram alerts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/performance-optimizer",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Performance optimization analyzer for pi-harness runtime (RFC-0056)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/privilege-broker",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Privilege Broker — capability registry and command executor for autonomous runtime (RFC-0101 §7)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/project-analyzer",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Project analysis system for pi-harness-runtime - detects frameworks, rules, and project structure",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/project-bootstrap",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Project scaffolding from typed ProjectSpec",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/projection-engine",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/prompt-compiler",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Deterministic prompt compiler for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/prompt-versioning",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "type": "module",
5
5
  "description": "Version control for prompts with rollback support (RFC-0019)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/provider-adapter-sdk",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "SDK for building, testing, and registering AI provider adapters",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/provider-router",
3
- "version": "1.1.232",
3
+ "version": "1.1.233",
4
4
  "description": "Enhanced Provider Router for pi-harness-runtime — intelligent routing with capabilities, costs, and quotas",
5
5
  "type": "module",
6
6
  "main": "./dist/provider-router.js",