agentp 0.8.0 → 0.9.0

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/lib/opencode.js CHANGED
@@ -11,7 +11,7 @@ function getAuthHeaders() {
11
11
  return { 'Authorization': `Basic ${encoded}` };
12
12
  }
13
13
 
14
- function makeRequest(options, data) {
14
+ function makeRequest(options, data, cancelRef) {
15
15
  return new Promise((resolve, reject) => {
16
16
  const req = http.request(options, (res) => {
17
17
  let body = '';
@@ -25,6 +25,7 @@ function makeRequest(options, data) {
25
25
  });
26
26
  });
27
27
  req.on('error', reject);
28
+ if (cancelRef) cancelRef.current = req;
28
29
  if (data) req.write(data);
29
30
  req.end();
30
31
  });
@@ -181,15 +182,16 @@ async function listSessions(server, directory) {
181
182
  return JSON.parse(body);
182
183
  }
183
184
 
184
- // Send a message directly to a specific session via the session API.
185
+ // Send a message directly to a specific session via the synchronous session API.
185
186
  // Returns the collected text from all text parts in the response.
186
187
  // Optionally specify an agent to handle the message.
187
- async function sendToSession(server, sessionId, text, agent) {
188
+ // Optional cancelRef allows aborting the HTTP request (for /cancel support).
189
+ async function sendToSession(server, sessionId, text, agent, cancelRef) {
188
190
  const url = `${server}/session/${encodeURIComponent(sessionId)}/message`;
189
191
  const bodyObj = { parts: [{ type: 'text', text }] };
190
192
  if (agent) bodyObj.agent = agent;
191
193
  const body = JSON.stringify(bodyObj);
192
- const { status, body: responseBody } = await makeRequest(buildJsonRequest(url, 'POST', body), body);
194
+ const { status, body: responseBody } = await makeRequest(buildJsonRequest(url, 'POST', body), body, cancelRef);
193
195
  if (status !== 200) throw new Error(`Failed to send to session: ${status}`);
194
196
  const result = JSON.parse(responseBody);
195
197
  const texts = (result.parts || [])
@@ -209,9 +211,12 @@ async function createSession(server, title) {
209
211
  }
210
212
 
211
213
  // Update a session's properties (e.g. title).
212
- async function updateSession(server, sessionId, title) {
214
+ async function updateSession(server, sessionId, title, agent) {
215
+ const bodyObj = {};
216
+ if (title != null) bodyObj.title = title;
217
+ if (agent != null) bodyObj.agent = agent;
213
218
  const url = `${server}/session/${encodeURIComponent(sessionId)}`;
214
- const body = JSON.stringify({ title });
219
+ const body = JSON.stringify(bodyObj);
215
220
  const { status, body: responseBody } = await makeRequest(buildJsonRequest(url, 'PATCH', body), body);
216
221
  if (status !== 200) throw new Error(`Failed to update session: ${status}`);
217
222
  return JSON.parse(responseBody);
@@ -276,7 +281,7 @@ async function respondToPermission(server, sessionId, permissionId, response, re
276
281
 
277
282
  // Listen for session events via SSE, collecting assistant text parts and detecting
278
283
  // permission requests. Resolves with the full collected text when the session goes idle.
279
- // callbacks: { onText(chunk), onPermission(permission) }
284
+ // callbacks: { onText(chunk), onPermission(permission), onThinking(chunk), onConnected() }
280
285
  // cancelRef — allows aborting the SSE stream via req.destroy()
281
286
  function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
282
287
  process.stderr.write(` [SSE] connecting to ${server}/event for session ${sessionId}\n`);
@@ -297,9 +302,22 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
297
302
  return;
298
303
  }
299
304
  process.stderr.write(` [SSE] connected for session ${sessionId}\n`);
305
+ if (callbacks.onConnected) callbacks.onConnected();
300
306
  let buffer = '';
301
307
  const userMessageIDs = new Set();
302
308
  let collected = '';
309
+ let resolved = false;
310
+
311
+ // Safety timeout: resolve with whatever we have after 90s
312
+ const safetyTimer = setTimeout(() => {
313
+ if (resolved) return;
314
+ resolved = true;
315
+ process.stderr.write(` [SSE] safety timeout (90s) for session ${sessionId}, collected ${collected.length} chars\n`);
316
+ cleanup();
317
+ req.destroy();
318
+ res.destroy();
319
+ resolve(collected);
320
+ }, 90000);
303
321
 
304
322
  res.on('data', (chunk) => {
305
323
  buffer += chunk.toString();
@@ -325,8 +343,8 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
325
343
  process.stderr.write(` [SSE] event type=${event.type} sessionID=${props.sessionID} waitSession=${sessionId}\n`);
326
344
  }
327
345
 
328
- // Filter by sessionID when the event carries one
329
- if (props.sessionID && props.sessionID !== sessionId) continue;
346
+ // Filter by sessionID when the event carries one (string-compare to handle type mismatches)
347
+ if (props.sessionID && String(props.sessionID) !== String(sessionId)) continue;
330
348
 
331
349
  if (event.type === 'message.updated' && props.info?.role === 'user') {
332
350
  userMessageIDs.add(props.info.id);
@@ -355,6 +373,9 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
355
373
 
356
374
  // Completion signals
357
375
  if (event.type === 'session.idle') {
376
+ clearTimeout(safetyTimer);
377
+ if (resolved) return;
378
+ resolved = true;
358
379
  cleanup();
359
380
  req.destroy();
360
381
  res.destroy();
@@ -362,6 +383,9 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
362
383
  return;
363
384
  }
364
385
  if (event.type === 'session.status' && props.status?.type === 'idle') {
386
+ clearTimeout(safetyTimer);
387
+ if (resolved) return;
388
+ resolved = true;
365
389
  cleanup();
366
390
  req.destroy();
367
391
  res.destroy();
@@ -390,9 +414,21 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
390
414
  });
391
415
 
392
416
  res.on('end', () => {
417
+ clearTimeout(safetyTimer);
418
+ if (resolved) return;
419
+ resolved = true;
393
420
  cleanup();
394
421
  resolve(collected);
395
422
  });
423
+
424
+ res.on('error', (err) => {
425
+ clearTimeout(safetyTimer);
426
+ if (resolved) return;
427
+ resolved = true;
428
+ cleanup();
429
+ process.stderr.write(` [SSE] response stream error for session ${sessionId}: ${err.message}\n`);
430
+ resolve(collected); // resolve with what we have
431
+ });
396
432
  });
397
433
 
398
434
  const cleanup = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Pipe prompts into a running OpenCode TUI session and stream the final answer",
5
5
  "bin": {
6
6
  "agentp": "bin/agentp",