@hachej/boring-agent 0.1.33 → 0.1.35

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.
@@ -280,47 +280,81 @@ function matchExpectations(opts, actual) {
280
280
  }
281
281
  async function runOnce(app, opts) {
282
282
  const querySuffix = formatQuerySuffix(opts.query);
283
- await app.inject({
284
- method: "POST",
285
- url: `/api/v1/agent/sessions${querySuffix}`,
286
- headers: opts.headers,
287
- payload: { id: opts.sessionId }
288
- });
289
283
  const userMessage = opts.systemPrompt ? `[SYSTEM]
290
284
  ${opts.systemPrompt}
291
285
  [/SYSTEM]
292
286
 
293
287
  ${opts.prompt}` : opts.prompt;
294
- let res;
295
288
  try {
296
- res = await withTimeout(
289
+ const res = await withTimeout(
297
290
  app.inject({
298
291
  method: "POST",
299
- url: `/api/v1/agent/chat${querySuffix}`,
292
+ url: `/api/v1/agent/pi-chat/${encodeURIComponent(opts.sessionId)}/prompt${querySuffix}`,
300
293
  headers: opts.headers,
301
294
  payload: {
302
- sessionId: opts.sessionId,
303
295
  message: userMessage,
296
+ clientNonce: `eval-${opts.sessionId}`,
304
297
  model: opts.model
305
298
  }
306
299
  }),
307
300
  opts.timeoutMs,
308
301
  `chat request exceeded ${opts.timeoutMs}ms`
309
302
  );
303
+ if (res.statusCode !== 202 && res.statusCode !== 200) {
304
+ throw new Error(
305
+ `Pi chat prompt returned ${res.statusCode}: ${res.body.slice(0, 256)}`
306
+ );
307
+ }
308
+ const snapshot = await withTimeout(
309
+ waitForSettledState(app, opts, querySuffix),
310
+ opts.timeoutMs,
311
+ `chat turn did not settle within ${opts.timeoutMs}ms`
312
+ );
313
+ return capturePiChatSnapshot(snapshot);
310
314
  } finally {
311
315
  void app.inject({
312
316
  method: "DELETE",
313
- url: `/api/v1/agent/sessions/${encodeURIComponent(opts.sessionId)}${querySuffix}`,
317
+ url: `/api/v1/agent/pi-chat/sessions/${encodeURIComponent(opts.sessionId)}${querySuffix}`,
314
318
  headers: opts.headers
315
319
  }).catch(() => {
316
320
  });
317
321
  }
318
- if (res.statusCode !== 200) {
319
- throw new Error(
320
- `chat returned ${res.statusCode}: ${res.body.slice(0, 256)}`
321
- );
322
+ }
323
+ var STATE_POLL_INTERVAL_MS = 250;
324
+ async function waitForSettledState(app, opts, querySuffix) {
325
+ for (; ; ) {
326
+ const state = await app.inject({
327
+ method: "GET",
328
+ url: `/api/v1/agent/pi-chat/${encodeURIComponent(opts.sessionId)}/state${querySuffix}`,
329
+ headers: opts.headers
330
+ });
331
+ if (state.statusCode !== 200) {
332
+ throw new Error(`Pi chat state returned ${state.statusCode}: ${state.body.slice(0, 256)}`);
333
+ }
334
+ const snapshot = JSON.parse(state.body);
335
+ if (snapshot.status !== "streaming") return snapshot;
336
+ await new Promise((resolve) => setTimeout(resolve, STATE_POLL_INTERVAL_MS));
337
+ }
338
+ }
339
+ function capturePiChatSnapshot(snapshot) {
340
+ const toolCalls = [];
341
+ const textParts = [];
342
+ const snapshotError = snapshot.error;
343
+ const errorText = typeof snapshotError?.message === "string" ? snapshotError.message : void 0;
344
+ const messages = Array.isArray(snapshot.messages) ? snapshot.messages : [];
345
+ for (const message of messages) {
346
+ if (typeof message !== "object" || message === null) continue;
347
+ const parts = Array.isArray(message.parts) ? message.parts : [];
348
+ for (const part of parts) {
349
+ if (typeof part !== "object" || part === null) continue;
350
+ const rec = part;
351
+ if (rec.type === "text" && typeof rec.text === "string") textParts.push(rec.text);
352
+ if (rec.type === "tool-call" && typeof rec.toolName === "string") {
353
+ toolCalls.push({ tool: rec.toolName, params: typeof rec.input === "object" && rec.input !== null ? rec.input : {} });
354
+ }
355
+ }
322
356
  }
323
- return parseSseStream(res.body);
357
+ return { toolCalls, text: textParts.join(""), errorText };
324
358
  }
325
359
  function formatQuerySuffix(query) {
326
360
  if (!query) return "";
@@ -332,63 +366,6 @@ function formatQuerySuffix(query) {
332
366
  const encoded = params.toString();
333
367
  return encoded ? `?${encoded}` : "";
334
368
  }
335
- function parseSseStream(body) {
336
- const toolCalls = [];
337
- const textParts = [];
338
- const errorParts = [];
339
- let usage;
340
- for (const line of body.split("\n")) {
341
- const trimmed = line.trim();
342
- if (!trimmed.startsWith("data:")) continue;
343
- const payload = trimmed.slice("data:".length).trim();
344
- if (!payload || payload === "[DONE]") continue;
345
- let chunk;
346
- try {
347
- chunk = JSON.parse(payload);
348
- } catch {
349
- continue;
350
- }
351
- const type = chunk.type;
352
- if (type === "tool-input-available") {
353
- const toolName = chunk.toolName;
354
- if (typeof toolName !== "string") continue;
355
- const input = chunk.input;
356
- const params = input && typeof input === "object" && !Array.isArray(input) ? input : {};
357
- toolCalls.push({ tool: toolName, params });
358
- } else if (type === "text-delta") {
359
- const delta = chunk.delta;
360
- if (typeof delta === "string") textParts.push(delta);
361
- } else if (type === "data-usage") {
362
- const data = chunk.data;
363
- if (data && typeof data === "object") {
364
- const obj = data;
365
- if (typeof obj.input === "number" && typeof obj.output === "number") {
366
- usage = { input: obj.input, output: obj.output };
367
- }
368
- }
369
- } else if (type === "error") {
370
- const text = stringifyStreamError(chunk);
371
- if (text) errorParts.push(text);
372
- }
373
- }
374
- return {
375
- toolCalls,
376
- text: textParts.join(""),
377
- usage,
378
- errorText: errorParts.length ? errorParts.join("\n") : void 0
379
- };
380
- }
381
- function stringifyStreamError(chunk) {
382
- for (const key of ["errorText", "message", "error"]) {
383
- const value = chunk[key];
384
- if (typeof value === "string" && value.trim()) return value;
385
- if (value && typeof value === "object") {
386
- const message = value.message;
387
- if (typeof message === "string" && message.trim()) return message;
388
- }
389
- }
390
- return "unknown stream error";
391
- }
392
369
  function withTimeout(p, ms, label) {
393
370
  return new Promise((resolve, reject) => {
394
371
  const timer = setTimeout(() => reject(new Error(label)), ms);