agentp 2.0.0 → 2.1.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/CONTRIBUTING.md +7 -5
- package/README.md +129 -27
- package/bin/agentp +224 -48
- package/bin/ocmux +965 -290
- package/bin/tgagentp +121 -130
- package/docs/specification.md +2 -1
- package/docs/specification_v2.md +188 -583
- package/lib/ocmux.js +26 -289
- package/lib/opencode.js +186 -43
- package/lib/project-state.js +8 -0
- package/lib/tui-cmd.js +12 -5
- package/lib/tui-registry.js +313 -0
- package/package.json +1 -1
package/bin/agentp
CHANGED
|
@@ -18,9 +18,9 @@ const {
|
|
|
18
18
|
selectSession,
|
|
19
19
|
sortSessionsByRecency,
|
|
20
20
|
interruptSession,
|
|
21
|
+
getActiveSessions,
|
|
21
22
|
} = require('../lib/opencode');
|
|
22
23
|
const projectState = require('../lib/project-state');
|
|
23
|
-
const ocmuxLib = require('../lib/ocmux');
|
|
24
24
|
|
|
25
25
|
const TGAGENTP_PORT_FILE = '/tmp/tgagentp-port';
|
|
26
26
|
|
|
@@ -168,7 +168,7 @@ function parseDeferredTicketInput(text) {
|
|
|
168
168
|
if (!data || typeof data.path !== 'string' || !data.path) return null;
|
|
169
169
|
const rest = afterPrefix.slice(jsonEnd);
|
|
170
170
|
const followupText = rest.trim() ? stripFollowupRow(rest.trimStart()) : '';
|
|
171
|
-
|
|
171
|
+
const ticket = {
|
|
172
172
|
ctime: typeof data.ctime === 'string' ? data.ctime : null,
|
|
173
173
|
path: data.path,
|
|
174
174
|
defer: typeof data.defer === 'number' && data.defer > 0 ? data.defer : 0,
|
|
@@ -177,6 +177,10 @@ function parseDeferredTicketInput(text) {
|
|
|
177
177
|
cancelled: data.cancelled === true,
|
|
178
178
|
followupText,
|
|
179
179
|
};
|
|
180
|
+
if (Array.isArray(data.sessionIds)) {
|
|
181
|
+
ticket.sessionIds = data.sessionIds.filter(x => typeof x === 'string' && x);
|
|
182
|
+
}
|
|
183
|
+
return ticket;
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
function parseDeferredTicket(text) {
|
|
@@ -193,6 +197,7 @@ function formatTicket(ticket, elapsed, compact) {
|
|
|
193
197
|
const fields = { ctime: ticket.ctime, path: ticket.path };
|
|
194
198
|
if (ticket.server) fields.server = ticket.server;
|
|
195
199
|
if (ticket.sessionId) fields.sessionId = ticket.sessionId;
|
|
200
|
+
if (Array.isArray(ticket.sessionIds) && ticket.sessionIds.length > 0) fields.sessionIds = ticket.sessionIds;
|
|
196
201
|
if (elapsed !== undefined) fields.elapsed = elapsed;
|
|
197
202
|
if (ticket.defer > 0) fields.defer = ticket.defer;
|
|
198
203
|
// Always present so it can be flipped to true and piped back to cancel.
|
|
@@ -204,8 +209,12 @@ function formatTicket(ticket, elapsed, compact) {
|
|
|
204
209
|
// the ticket. Prints a message and exits 0.
|
|
205
210
|
async function cancelTicket(ticket) {
|
|
206
211
|
let interrupted = false;
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
const ids = Array.isArray(ticket.sessionIds) && ticket.sessionIds.length > 0
|
|
213
|
+
? ticket.sessionIds
|
|
214
|
+
: (ticket.sessionId ? [ticket.sessionId] : []);
|
|
215
|
+
if (ticket.server && ids.length > 0) {
|
|
216
|
+
const interruptedAll = await Promise.all(ids.map(id => interruptSession(ticket.server, id)));
|
|
217
|
+
interrupted = interruptedAll.some(Boolean);
|
|
209
218
|
}
|
|
210
219
|
try { fs.unlinkSync(ticket.path); } catch {}
|
|
211
220
|
try { fs.unlinkSync(ticket.path + '.lock'); } catch {}
|
|
@@ -269,7 +278,9 @@ async function resolveTargetSession(serverBase, sessionName, newSession, opts =
|
|
|
269
278
|
}
|
|
270
279
|
|
|
271
280
|
if (sessions && sessions.length > 0) {
|
|
272
|
-
|
|
281
|
+
// Only "main" sessions (no parentID) are considered as implicit targets —
|
|
282
|
+
// child/subagent sessions are work products, not conversation targets.
|
|
283
|
+
const sorted = sortSessionsByRecency(sessions).filter(s => s.id && !s.parentID);
|
|
273
284
|
if (sorted.length > 0) return sorted[0].id;
|
|
274
285
|
}
|
|
275
286
|
|
|
@@ -414,6 +425,114 @@ function outputReadyResult(filePath, result, qaMode, unusedFollowupText, header)
|
|
|
414
425
|
process.exit(0);
|
|
415
426
|
}
|
|
416
427
|
|
|
428
|
+
// ── Broadcast (ocmux "Space" multi-session send) ───────────────────
|
|
429
|
+
|
|
430
|
+
// One answer section: a heading with the session name and a separator below it.
|
|
431
|
+
function buildBroadcastSection(title, id, answer) {
|
|
432
|
+
let s = `\n💬 ${title} [${id}]\n${'━'.repeat(40)}\n`;
|
|
433
|
+
s += answer;
|
|
434
|
+
if (!answer.endsWith('\n')) s += '\n';
|
|
435
|
+
return s;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Send a prompt to every session in `ids` and collect the answers.
|
|
439
|
+
// deps.getActiveSessions(server) -> Set of busy session ids
|
|
440
|
+
// deps.getSession(server, id) -> session object (title)
|
|
441
|
+
// deps.sendToSession(server, id, text) -> answer text
|
|
442
|
+
// deps.readBroadcast() -> current .ocmux.json broadcast list (null/[] = cancelled)
|
|
443
|
+
// Waits for each session to become idle before sending; stops as soon as the
|
|
444
|
+
// broadcast list disappears (ocmux exited broadcast mode early).
|
|
445
|
+
async function runBroadcastSend(server, ids, prompt, deps) {
|
|
446
|
+
const { getActiveSessions, getSession, sendToSession, readBroadcast, getPrompt, pollMs = 1000, busyTimeoutMs = 15 * 60 * 1000 } = deps;
|
|
447
|
+
const initial = new Set(ids.map(String));
|
|
448
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
449
|
+
const cancelled = () => {
|
|
450
|
+
try { return !isBroadcastActive(readBroadcast(), initial); } catch { return false; }
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// Race a sent session's answer against ocmux leaving broadcast mode. We do
|
|
454
|
+
// not interrupt the model: we merely stop waiting/discard its later answer,
|
|
455
|
+
// exactly as the UI contract promises.
|
|
456
|
+
const awaitAnswerOrCancel = (promise) => new Promise((resolve) => {
|
|
457
|
+
let done = false;
|
|
458
|
+
const finish = (value) => {
|
|
459
|
+
if (done) return;
|
|
460
|
+
done = true;
|
|
461
|
+
clearInterval(timer);
|
|
462
|
+
resolve(value);
|
|
463
|
+
};
|
|
464
|
+
const timer = setInterval(() => {
|
|
465
|
+
if (cancelled()) finish({ cancelled: true });
|
|
466
|
+
}, pollMs);
|
|
467
|
+
promise.then(answer => finish({ answer }), err => finish({ error: err }));
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const runOne = async (id) => {
|
|
471
|
+
let title = id;
|
|
472
|
+
try {
|
|
473
|
+
const s = await getSession(server, id);
|
|
474
|
+
title = (s && (s.title || s.id)) || id;
|
|
475
|
+
} catch {
|
|
476
|
+
return { missing: { id, title, error: 'Unable to read session', timestamp: new Date().toISOString() } };
|
|
477
|
+
}
|
|
478
|
+
// Wait for the session to become idle (skip prompts into a busy session).
|
|
479
|
+
const deadline = Date.now() + busyTimeoutMs;
|
|
480
|
+
let busy = true;
|
|
481
|
+
while (busy && Date.now() < deadline) {
|
|
482
|
+
if (cancelled()) {
|
|
483
|
+
return { missing: { id, title, error: 'Broadcast cancelled while waiting for idle', timestamp: new Date().toISOString() } };
|
|
484
|
+
}
|
|
485
|
+
const active = await getActiveSessions(server);
|
|
486
|
+
if (!active.has(String(id))) busy = false;
|
|
487
|
+
else await sleep(pollMs);
|
|
488
|
+
}
|
|
489
|
+
if (busy) {
|
|
490
|
+
return { missing: { id, title, error: 'Session stayed busy until timeout', timestamp: new Date().toISOString() } };
|
|
491
|
+
}
|
|
492
|
+
try {
|
|
493
|
+
const result = await awaitAnswerOrCancel(sendToSession(server, id, getPrompt ? getPrompt(id, prompt) : prompt));
|
|
494
|
+
if (result.cancelled) {
|
|
495
|
+
return { missing: { id, title, error: 'Broadcast cancelled before this session replied', timestamp: new Date().toISOString() } };
|
|
496
|
+
}
|
|
497
|
+
if (result.error) {
|
|
498
|
+
return { missing: { id, title, error: result.error.message || String(result.error), timestamp: new Date().toISOString() } };
|
|
499
|
+
}
|
|
500
|
+
return { result: { id, title, answer: result.answer } };
|
|
501
|
+
} catch (err) {
|
|
502
|
+
return { missing: { id, title, error: err.message || String(err), timestamp: new Date().toISOString() } };
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// Run independently so one busy/model-limited session never prevents all
|
|
507
|
+
// other selected sessions from receiving the prompt.
|
|
508
|
+
const settled = await Promise.all(ids.map(runOne));
|
|
509
|
+
const results = settled.filter(x => x.result).map(x => x.result);
|
|
510
|
+
const missing = settled.filter(x => x.missing).map(x => x.missing);
|
|
511
|
+
return { results, missing, requested: ids.length };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// A broadcast list is still active while it is an array that contains every
|
|
515
|
+
// session we were asked to reach.
|
|
516
|
+
function isBroadcastActive(broadcast, initial) {
|
|
517
|
+
return Array.isArray(broadcast)
|
|
518
|
+
&& broadcast.length >= 2
|
|
519
|
+
&& [...initial].every((id) => broadcast.includes(id));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Assemble the final broadcast output: heading per session + missing note.
|
|
523
|
+
function formatBroadcastResults({ results, missing, requested }) {
|
|
524
|
+
let out = `🌐 Broadcast to ${requested} sessions\n`;
|
|
525
|
+
for (const r of results) out += buildBroadcastSection(r.title, r.id, r.answer);
|
|
526
|
+
if (missing.length > 0) {
|
|
527
|
+
out += '\n⚠️ Prompt not completed in the following sessions:\n';
|
|
528
|
+
for (const m of missing) {
|
|
529
|
+
out += `- 💬 ${m.title} [${m.id}]\n - Error: ${m.error || 'Unknown'}\n`;
|
|
530
|
+
if (m.timestamp) out += ` - At: ${m.timestamp}\n`;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return out;
|
|
534
|
+
}
|
|
535
|
+
|
|
417
536
|
// Structured header (project path + session id/title) prepended to --qa output,
|
|
418
537
|
// so a piped answer is self-describing about where it came from.
|
|
419
538
|
async function buildSessionHeader(server, sessionId) {
|
|
@@ -434,7 +553,7 @@ async function buildSessionHeader(server, sessionId) {
|
|
|
434
553
|
|
|
435
554
|
// Header for deferred-ticket retrieval (ticket carries server + sessionId).
|
|
436
555
|
async function ticketHeader(ticket, qaMode) {
|
|
437
|
-
if (qaMode && ticket && ticket.server && ticket.sessionId) {
|
|
556
|
+
if (qaMode && ticket && ticket.server && ticket.sessionId && !(Array.isArray(ticket.sessionIds) && ticket.sessionIds.length >= 2)) {
|
|
438
557
|
try { return await buildSessionHeader(ticket.server, ticket.sessionId); } catch {}
|
|
439
558
|
}
|
|
440
559
|
return '';
|
|
@@ -457,7 +576,7 @@ const FINAL_ROW = ' —————————————————\n';
|
|
|
457
576
|
// Write the prompt to a temp file, lock the output file, and spawn a detached
|
|
458
577
|
// `--defer-child` process that runs the prompt and writes the answer. Returns
|
|
459
578
|
// the ticket describing this deferred run.
|
|
460
|
-
function spawnDeferredChild({ promptText, serverBase, sessionName, newSession, qaMode, tgMode, flushMode, targetSessionId, deferSeconds }) {
|
|
579
|
+
function spawnDeferredChild({ promptText, serverBase, sessionName, newSession, qaMode, tgMode, flushMode, targetSessionId, targetSessionIds, deferSeconds }) {
|
|
461
580
|
const tempPath = generateTempFilename();
|
|
462
581
|
const promptTempPath = tempPath + '.prompt';
|
|
463
582
|
|
|
@@ -471,6 +590,7 @@ function spawnDeferredChild({ promptText, serverBase, sessionName, newSession, q
|
|
|
471
590
|
defer: deferSeconds || 0,
|
|
472
591
|
server: serverBase,
|
|
473
592
|
sessionId: targetSessionId,
|
|
593
|
+
sessionIds: targetSessionIds,
|
|
474
594
|
cancelled: false,
|
|
475
595
|
};
|
|
476
596
|
|
|
@@ -488,7 +608,10 @@ function spawnDeferredChild({ promptText, serverBase, sessionName, newSession, q
|
|
|
488
608
|
childArgs.push('--session', sessionName);
|
|
489
609
|
if (newSession) childArgs.push('--new');
|
|
490
610
|
}
|
|
491
|
-
childArgs.push('--resolved-session-id', targetSessionId);
|
|
611
|
+
if (targetSessionId) childArgs.push('--resolved-session-id', targetSessionId);
|
|
612
|
+
if (Array.isArray(targetSessionIds) && targetSessionIds.length > 0) {
|
|
613
|
+
childArgs.push('--resolved-session-ids', JSON.stringify(targetSessionIds));
|
|
614
|
+
}
|
|
492
615
|
childArgs.push(serverBase);
|
|
493
616
|
|
|
494
617
|
const child = child_process.spawn(process.execPath, childArgs, {
|
|
@@ -522,6 +645,7 @@ async function main() {
|
|
|
522
645
|
let outputFile = null;
|
|
523
646
|
let sessionName = null;
|
|
524
647
|
let resolvedSessionId = null;
|
|
648
|
+
let resolvedSessionIds = null;
|
|
525
649
|
let newSession = false;
|
|
526
650
|
// tgMode: false = disabled, 'auto' = auto-detect, true = explicit --tg
|
|
527
651
|
let tgMode = null;
|
|
@@ -624,6 +748,16 @@ async function main() {
|
|
|
624
748
|
console.error('Error: --resolved-session-id requires a session id');
|
|
625
749
|
process.exit(1);
|
|
626
750
|
}
|
|
751
|
+
} else if (args[i] === '--resolved-session-ids') {
|
|
752
|
+
i++;
|
|
753
|
+
try {
|
|
754
|
+
const ids = JSON.parse(args[i]);
|
|
755
|
+
if (!Array.isArray(ids) || ids.length < 2 || ids.some(id => typeof id !== 'string' || !id)) throw new Error('bad ids');
|
|
756
|
+
resolvedSessionIds = ids;
|
|
757
|
+
} catch {
|
|
758
|
+
console.error('Error: --resolved-session-ids requires a JSON array of at least two session ids');
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
627
761
|
} else if (args[i] === '--new') {
|
|
628
762
|
newSession = true;
|
|
629
763
|
} else if (args[i] === '--getLast') {
|
|
@@ -844,15 +978,22 @@ async function main() {
|
|
|
844
978
|
}
|
|
845
979
|
|
|
846
980
|
// This is a regular prompt — create deferred execution
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
981
|
+
const broadcastIds = (resolvedSessionIds && resolvedSessionIds.length >= 2)
|
|
982
|
+
? resolvedSessionIds
|
|
983
|
+
: ((ctx && ctx.state && Array.isArray(ctx.state.broadcast) && ctx.state.broadcast.length >= 2)
|
|
984
|
+
? ctx.state.broadcast
|
|
985
|
+
: null);
|
|
986
|
+
let targetSessionId = null;
|
|
987
|
+
if (!broadcastIds) {
|
|
988
|
+
try {
|
|
989
|
+
targetSessionId = await resolveTargetSession(serverBase, sessionName, newSession, {
|
|
990
|
+
directory: projectDir,
|
|
991
|
+
preferredSession,
|
|
992
|
+
});
|
|
993
|
+
} catch (err) {
|
|
994
|
+
console.error(err.message);
|
|
995
|
+
process.exit(1);
|
|
996
|
+
}
|
|
856
997
|
}
|
|
857
998
|
|
|
858
999
|
const newTicket = spawnDeferredChild({
|
|
@@ -864,6 +1005,7 @@ async function main() {
|
|
|
864
1005
|
tgMode,
|
|
865
1006
|
flushMode,
|
|
866
1007
|
targetSessionId,
|
|
1008
|
+
targetSessionIds: broadcastIds,
|
|
867
1009
|
deferSeconds,
|
|
868
1010
|
});
|
|
869
1011
|
|
|
@@ -938,15 +1080,20 @@ async function main() {
|
|
|
938
1080
|
}
|
|
939
1081
|
|
|
940
1082
|
// Regular prompt: submit via a detached child and wait for the answer.
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1083
|
+
const broadcastIds = (ctx && ctx.state && Array.isArray(ctx.state.broadcast) && ctx.state.broadcast.length >= 2)
|
|
1084
|
+
? ctx.state.broadcast
|
|
1085
|
+
: null;
|
|
1086
|
+
let targetSessionId = null;
|
|
1087
|
+
if (!broadcastIds) {
|
|
1088
|
+
try {
|
|
1089
|
+
targetSessionId = await resolveTargetSession(serverBase, sessionName, newSession, {
|
|
1090
|
+
directory: projectDir,
|
|
1091
|
+
preferredSession,
|
|
1092
|
+
});
|
|
1093
|
+
} catch (err) {
|
|
1094
|
+
console.error(err.message);
|
|
1095
|
+
process.exit(1);
|
|
1096
|
+
}
|
|
950
1097
|
}
|
|
951
1098
|
|
|
952
1099
|
const newTicket = spawnDeferredChild({
|
|
@@ -958,6 +1105,7 @@ async function main() {
|
|
|
958
1105
|
tgMode,
|
|
959
1106
|
flushMode,
|
|
960
1107
|
targetSessionId,
|
|
1108
|
+
targetSessionIds: broadcastIds,
|
|
961
1109
|
deferSeconds: 0,
|
|
962
1110
|
});
|
|
963
1111
|
|
|
@@ -993,34 +1141,62 @@ async function main() {
|
|
|
993
1141
|
// Wrap main processing in try-catch for deferChildMode to write errors to output file
|
|
994
1142
|
let tgError = null;
|
|
995
1143
|
async function runMain() {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1144
|
+
// Broadcast mode: ocmux's Space key selected multiple sessions in
|
|
1145
|
+
// .ocmux.json — send the prompt to all of them.
|
|
1146
|
+
const broadcastIds = (ctx && ctx.state && Array.isArray(ctx.state.broadcast) && ctx.state.broadcast.length >= 2)
|
|
1147
|
+
? ctx.state.broadcast
|
|
1148
|
+
: null;
|
|
1000
1149
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1150
|
+
let sessionId = null;
|
|
1151
|
+
let answer;
|
|
1152
|
+
if (broadcastIds) {
|
|
1153
|
+
const bres = await runBroadcastSend(serverBase, broadcastIds, actualPromptText, {
|
|
1154
|
+
getActiveSessions,
|
|
1155
|
+
getSession,
|
|
1156
|
+
sendToSession,
|
|
1157
|
+
getPrompt: (id, text) => {
|
|
1158
|
+
try {
|
|
1159
|
+
const ann = ctx && ctx.statefile && projectState.readAnnotations(ctx.statefile)[id];
|
|
1160
|
+
return ann ? ann + '\n\n' + text : text;
|
|
1161
|
+
} catch {
|
|
1162
|
+
return text;
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
readBroadcast: () => (ctx && ctx.statefile ? projectState.readProjectState(ctx.statefile).broadcast : null),
|
|
1166
|
+
});
|
|
1167
|
+
answer = formatBroadcastResults(bres);
|
|
1168
|
+
} else {
|
|
1169
|
+
sessionId = resolvedSessionId || await resolveTargetSession(serverBase, sessionName, newSession, {
|
|
1170
|
+
directory: projectDir,
|
|
1171
|
+
preferredSession,
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// Prepend the session's annotation (if any) to the prompt. TUI routing is
|
|
1175
|
+
// owned by explicit ocmux session switches, not by prompt submission.
|
|
1176
|
+
let prompt = actualPromptText;
|
|
1177
|
+
try {
|
|
1178
|
+
if (ctx && ctx.statefile) {
|
|
1179
|
+
const ann = projectState.readAnnotations(ctx.statefile)[sessionId];
|
|
1180
|
+
if (ann) prompt = ann + '\n\n' + actualPromptText;
|
|
1181
|
+
}
|
|
1182
|
+
} catch {
|
|
1183
|
+
// Annotation lookup is best-effort.
|
|
1008
1184
|
}
|
|
1009
|
-
if (projectDir) ocmuxLib.focusWindowByDir(projectDir);
|
|
1010
|
-
} catch {
|
|
1011
|
-
// tmux focus is best-effort; never fail the prompt because of it
|
|
1012
|
-
}
|
|
1013
1185
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1186
|
+
try {
|
|
1187
|
+
answer = await sendToSession(serverBase, sessionId, prompt);
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
throw new Error(`Error sending prompt: ${err.message}`);
|
|
1190
|
+
}
|
|
1019
1191
|
}
|
|
1020
1192
|
|
|
1021
1193
|
// Build the message to send to Telegram (QA pair with rulers)
|
|
1022
1194
|
let tgMessage = answer;
|
|
1023
|
-
if (qaMode) {
|
|
1195
|
+
if (qaMode && broadcastIds) {
|
|
1196
|
+
tgMessage = HUMAN_ROW + actualPromptText + AGENT_ROW + answer;
|
|
1197
|
+
if (!answer.endsWith('\n')) tgMessage += '\n';
|
|
1198
|
+
tgMessage += FINAL_ROW;
|
|
1199
|
+
} else if (qaMode && !broadcastIds) {
|
|
1024
1200
|
let header = '';
|
|
1025
1201
|
try { header = await buildSessionHeader(serverBase, sessionId); } catch {}
|
|
1026
1202
|
tgMessage = header + HUMAN_ROW + actualPromptText + AGENT_ROW + answer;
|
|
@@ -1105,4 +1281,4 @@ if (require.main === module) {
|
|
|
1105
1281
|
});
|
|
1106
1282
|
}
|
|
1107
1283
|
|
|
1108
|
-
module.exports = { main, notifyAgentpGateway, readAllStdin, parseDeferredTicket, parseDeferredTicketInput, formatTicket, waitForDeferredResult, waitForDeferredResultForever };
|
|
1284
|
+
module.exports = { main, notifyAgentpGateway, readAllStdin, parseDeferredTicket, parseDeferredTicketInput, formatTicket, waitForDeferredResult, waitForDeferredResultForever, runBroadcastSend, buildBroadcastSection, formatBroadcastResults, isBroadcastActive };
|