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.
- package/index.ts +34 -0
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/dist/continuation-controller.d.ts +19 -12
- package/packages/jev-judge/dist/continuation-controller.d.ts.map +1 -1
- package/packages/jev-judge/dist/continuation-controller.js +111 -54
- package/packages/jev-judge/dist/continuation-controller.js.map +1 -1
- package/packages/jev-judge/dist/todo-provider.d.ts +16 -1
- package/packages/jev-judge/dist/todo-provider.d.ts.map +1 -1
- package/packages/jev-judge/dist/todo-provider.js +22 -11
- package/packages/jev-judge/dist/todo-provider.js.map +1 -1
- package/packages/jev-judge/dist/typesafe-provider.d.ts.map +1 -1
- package/packages/jev-judge/dist/typesafe-provider.js +12 -3
- package/packages/jev-judge/dist/typesafe-provider.js.map +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/jev-judge/src/continuation-controller.ts +128 -63
- package/packages/jev-judge/src/todo-provider.ts +40 -21
- package/packages/jev-judge/src/typesafe-provider.ts +12 -3
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
82
|
+
steerAttemptsForCurrentItem: 0,
|
|
83
|
+
lastItemContinued: null,
|
|
83
84
|
lastEvaluationResult: null,
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
constructor(
|
|
87
|
-
this.
|
|
88
|
-
this.todoProvider = createTodoProvider(
|
|
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
|
-
*
|
|
100
|
+
* Check if continuation is allowed (not paused, session idle)
|
|
100
101
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
debugLog("TodoContinuation", "Session
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
242
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
398
|
+
ctx: TodoContext,
|
|
332
399
|
config?: ContinuationConfig
|
|
333
400
|
): TodoContinuationController {
|
|
334
|
-
|
|
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(
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
210
|
+
private ctx: TodoContext;
|
|
204
211
|
private _revision = 0;
|
|
212
|
+
private _lastError: string | null = null;
|
|
205
213
|
|
|
206
|
-
constructor(
|
|
207
|
-
this.
|
|
214
|
+
constructor(ctx: TodoContext) {
|
|
215
|
+
this.ctx = ctx;
|
|
208
216
|
}
|
|
209
217
|
|
|
210
218
|
async availability(): Promise<TodoAvailability> {
|
|
211
|
-
if (!this.
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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
|
|
273
|
+
noul: answer.noul,
|
|
265
274
|
};
|
|
266
|
-
probability = answer.noul
|
|
275
|
+
probability = answer.noul;
|
|
267
276
|
break;
|
|
268
277
|
|
|
269
278
|
case "choice":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/provider-router",
|
|
3
|
-
"version": "1.1.
|
|
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",
|