pi-harness-runtime 1.1.231 → 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 +34 -0
  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 +19 -12
  43. package/packages/jev-judge/dist/continuation-controller.d.ts.map +1 -1
  44. package/packages/jev-judge/dist/continuation-controller.js +111 -54
  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 +128 -63
  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,48 +97,100 @@ 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;
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;
116
+ }
117
+
118
+ /**
119
+ * Public method to evaluate and execute continuation
120
+ * Called by external handlers (e.g., agent_settled)
121
+ * Checks blockers and pause BEFORE selecting a task
122
+ */
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
130
+ const availability = await this.todoProvider.availability();
131
+ if (!availability.available) {
132
+ debugLog("TodoContinuation", "Provider unavailable:", availability.error);
133
+ return { action: "stop", reason: availability.error ?? "provider unavailable" };
134
+ }
135
+
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
+ }
149
+
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` };
154
+ }
155
+
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;
123
164
  }
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;
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}` };
137
169
  }
138
- });
170
+
171
+ return { action: "continue", nextItemId: item.id };
172
+ }
139
173
 
140
- debugLog("TodoContinuation", "Lifecycle hooks registered");
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 };
190
+ }
191
+
192
+ // 7. No work remaining
193
+ return { action: "stop", reason: "No pending tasks" };
141
194
  }
142
195
 
143
196
  /**
@@ -208,13 +261,19 @@ export class TodoContinuationController {
208
261
  /**
209
262
  * Continue a specific todo item
210
263
  */
211
- private async continueItem(item: TodoItem): Promise<void> {
212
- if (this.state.steerAttempts >= this.config.maxSteerAttempts) {
213
- 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);
214
273
  return;
215
274
  }
216
275
 
217
- this.state.steerAttempts++;
276
+ this.state.steerAttemptsForCurrentItem++;
218
277
  this.state.continuationQueued = true;
219
278
 
220
279
  // Build continuation message
@@ -227,7 +286,7 @@ export class TodoContinuationController {
227
286
 
228
287
  // Send continuation message via Pi API
229
288
  try {
230
- 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" });
231
290
  } catch (err) {
232
291
  debugLog("TodoContinuation", "Failed to send continuation:", String(err));
233
292
  this.state.continuationQueued = false;
@@ -238,12 +297,18 @@ export class TodoContinuationController {
238
297
  * Steer to continue a todo item
239
298
  */
240
299
  async steerToContinue(itemId: number): Promise<void> {
241
- if (this.state.steerAttempts >= this.config.maxSteerAttempts) {
242
- 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);
243
308
  return;
244
309
  }
245
310
 
246
- this.state.steerAttempts++;
311
+ this.state.steerAttemptsForCurrentItem++;
247
312
  this.state.continuationQueued = true;
248
313
 
249
314
  const snapshot = await this.todoProvider.getSnapshot();
@@ -259,7 +324,7 @@ export class TodoContinuationController {
259
324
 
260
325
  // Send continuation message via Pi API
261
326
  try {
262
- 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" });
263
328
  } catch (err) {
264
329
  debugLog("TodoContinuation", "Failed to send steer:", String(err));
265
330
  this.state.continuationQueued = false;
@@ -287,7 +352,7 @@ export class TodoContinuationController {
287
352
  /**
288
353
  * Reset state for new session
289
354
  */
290
- private reset(): void {
355
+ _reset(): void {
291
356
  this.state = {
292
357
  sessionId: `session-${Date.now()}`,
293
358
  generation: 0,
@@ -298,7 +363,8 @@ export class TodoContinuationController {
298
363
  continuationQueued: false,
299
364
  userPaused: false,
300
365
  lastSteerMessage: null,
301
- steerAttempts: 0,
366
+ steerAttemptsForCurrentItem: 0,
367
+ lastItemContinued: null,
302
368
  lastEvaluationResult: null,
303
369
  };
304
370
  }
@@ -306,7 +372,7 @@ export class TodoContinuationController {
306
372
  /**
307
373
  * Cleanup on shutdown
308
374
  */
309
- private cleanup(): void {
375
+ _cleanup(): void {
310
376
  debugLog("TodoContinuation", "Cleaning up");
311
377
  this.state.continuationQueued = false;
312
378
  this.state.generation++;
@@ -325,13 +391,12 @@ export class TodoContinuationController {
325
391
  }
326
392
 
327
393
  /**
328
- * 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
329
396
  */
330
397
  export function createTodoContinuation(
331
- pi: ExtensionAPI,
398
+ ctx: TodoContext,
332
399
  config?: ContinuationConfig
333
400
  ): TodoContinuationController {
334
- const controller = new TodoContinuationController(pi, config);
335
- controller.register();
336
- return controller;
401
+ return new TodoContinuationController(ctx, config);
337
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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.231",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/provider-selector",
3
- "version": "1.1.231",
3
+ "version": "1.1.233",
4
4
  "description": "Multi-Provider Selector — route requests by cost, quality, and latency (RFC-0012)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/providers",
3
- "version": "1.1.231",
3
+ "version": "1.1.233",
4
4
  "description": "pi-harness-runtime — providers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",