agentp 0.7.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/AGENTS.md +45 -16
- package/CHANGELOG.md +10 -0
- package/README.md +65 -10
- package/bin/agentp +142 -28
- package/bin/ocmux +24 -6
- package/bin/tgagentp +364 -125
- package/debugging_tg_integration.md +5594 -0
- package/lib/opencode.js +55 -12
- package/package.json +1 -1
- package/tgagentp.log +2156 -0
- package/status_report.md +0 -85
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
|
-
|
|
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(
|
|
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);
|
|
@@ -222,13 +227,16 @@ async function updateSession(server, sessionId, title) {
|
|
|
222
227
|
async function selectSession(server, sessionId) {
|
|
223
228
|
const encoded = encodeURIComponent(sessionId);
|
|
224
229
|
const attempts = [
|
|
225
|
-
{ url: `${server}/session/${encoded}/select`, body: '{}' },
|
|
226
230
|
{ url: `${server}/tui/select-session`, body: JSON.stringify({ sessionID: sessionId }) },
|
|
231
|
+
{ url: `${server}/session/${encoded}/select`, body: '{}' },
|
|
227
232
|
];
|
|
228
233
|
for (const { url, body } of attempts) {
|
|
229
234
|
try {
|
|
230
|
-
await makeRequest(buildJsonRequest(url, 'POST', body), body);
|
|
231
|
-
|
|
235
|
+
const { status } = await makeRequest(buildJsonRequest(url, 'POST', body), body);
|
|
236
|
+
// /session/:id/select returns 200 with HTML even for bad IDs (TUI page),
|
|
237
|
+
// but doesn't actually navigate. Only consider it a success if the
|
|
238
|
+
// response looks like JSON (i.e. not HTML).
|
|
239
|
+
if (status === 200) return;
|
|
232
240
|
} catch {
|
|
233
241
|
// try next approach
|
|
234
242
|
}
|
|
@@ -273,7 +281,7 @@ async function respondToPermission(server, sessionId, permissionId, response, re
|
|
|
273
281
|
|
|
274
282
|
// Listen for session events via SSE, collecting assistant text parts and detecting
|
|
275
283
|
// permission requests. Resolves with the full collected text when the session goes idle.
|
|
276
|
-
// callbacks: { onText(chunk), onPermission(permission) }
|
|
284
|
+
// callbacks: { onText(chunk), onPermission(permission), onThinking(chunk), onConnected() }
|
|
277
285
|
// cancelRef — allows aborting the SSE stream via req.destroy()
|
|
278
286
|
function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
279
287
|
process.stderr.write(` [SSE] connecting to ${server}/event for session ${sessionId}\n`);
|
|
@@ -294,9 +302,22 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
294
302
|
return;
|
|
295
303
|
}
|
|
296
304
|
process.stderr.write(` [SSE] connected for session ${sessionId}\n`);
|
|
305
|
+
if (callbacks.onConnected) callbacks.onConnected();
|
|
297
306
|
let buffer = '';
|
|
298
307
|
const userMessageIDs = new Set();
|
|
299
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);
|
|
300
321
|
|
|
301
322
|
res.on('data', (chunk) => {
|
|
302
323
|
buffer += chunk.toString();
|
|
@@ -322,8 +343,8 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
322
343
|
process.stderr.write(` [SSE] event type=${event.type} sessionID=${props.sessionID} waitSession=${sessionId}\n`);
|
|
323
344
|
}
|
|
324
345
|
|
|
325
|
-
// Filter by sessionID when the event carries one
|
|
326
|
-
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;
|
|
327
348
|
|
|
328
349
|
if (event.type === 'message.updated' && props.info?.role === 'user') {
|
|
329
350
|
userMessageIDs.add(props.info.id);
|
|
@@ -352,6 +373,9 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
352
373
|
|
|
353
374
|
// Completion signals
|
|
354
375
|
if (event.type === 'session.idle') {
|
|
376
|
+
clearTimeout(safetyTimer);
|
|
377
|
+
if (resolved) return;
|
|
378
|
+
resolved = true;
|
|
355
379
|
cleanup();
|
|
356
380
|
req.destroy();
|
|
357
381
|
res.destroy();
|
|
@@ -359,6 +383,9 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
359
383
|
return;
|
|
360
384
|
}
|
|
361
385
|
if (event.type === 'session.status' && props.status?.type === 'idle') {
|
|
386
|
+
clearTimeout(safetyTimer);
|
|
387
|
+
if (resolved) return;
|
|
388
|
+
resolved = true;
|
|
362
389
|
cleanup();
|
|
363
390
|
req.destroy();
|
|
364
391
|
res.destroy();
|
|
@@ -376,6 +403,10 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
376
403
|
if (callbacks.onText) callbacks.onText(part.text);
|
|
377
404
|
collected += part.text;
|
|
378
405
|
}
|
|
406
|
+
|
|
407
|
+
if ((part.type === 'reasoning' || part.type === 'thinking') && part.text != null && part.text !== '') {
|
|
408
|
+
if (callbacks.onThinking) callbacks.onThinking(part.text);
|
|
409
|
+
}
|
|
379
410
|
} catch (e) {
|
|
380
411
|
// Malformed SSE event — skip
|
|
381
412
|
}
|
|
@@ -383,9 +414,21 @@ function listenForSessionEvents(server, sessionId, callbacks, cancelRef) {
|
|
|
383
414
|
});
|
|
384
415
|
|
|
385
416
|
res.on('end', () => {
|
|
417
|
+
clearTimeout(safetyTimer);
|
|
418
|
+
if (resolved) return;
|
|
419
|
+
resolved = true;
|
|
386
420
|
cleanup();
|
|
387
421
|
resolve(collected);
|
|
388
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
|
+
});
|
|
389
432
|
});
|
|
390
433
|
|
|
391
434
|
const cleanup = () => {
|