@songsid/agend 2.1.6 → 2.1.7-beta.1
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/dist/channel/adapters/discord.d.ts +1 -0
- package/dist/channel/adapters/discord.js +25 -0
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +13 -0
- package/dist/channel/adapters/telegram.js +44 -3
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +4 -0
- package/dist/daemon.js +32 -25
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +23 -8
- package/dist/fleet-manager.js +101 -28
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +2 -1
- package/dist/instance-lifecycle.js +10 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +5 -1
- package/dist/turn-reply-guard.js +6 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/package.json +1 -1
package/dist/channel/types.d.ts
CHANGED
|
@@ -115,6 +115,10 @@ export interface ChannelAdapter extends EventEmitter {
|
|
|
115
115
|
* threads are their own channel; Telegram reactions key on the supergroup
|
|
116
116
|
* chat_id and ignore it). */
|
|
117
117
|
react(chatId: string, messageId: string, emoji: string, threadId?: string): Promise<void>;
|
|
118
|
+
/** Remove the bot's own reaction from a message. Optional so third-party
|
|
119
|
+
* adapters keep working — callers fall back to a plain add when absent.
|
|
120
|
+
* threadId follows the same routing rule as react. */
|
|
121
|
+
unreact?(chatId: string, messageId: string, emoji: string, threadId?: string): Promise<void>;
|
|
118
122
|
sendApproval(prompt: PermissionPrompt, callback: (decision: "approve" | "approve_always" | "deny") => void, signal?: AbortSignal, threadId?: string): Promise<ApprovalHandle>;
|
|
119
123
|
downloadAttachment(fileId: string): Promise<string>;
|
|
120
124
|
handlePairing(chatId: string, userId: string): Promise<string>;
|
package/dist/daemon.js
CHANGED
|
@@ -37,9 +37,15 @@ const CROSS_INSTANCE_TOOLS = new Set(["send_to_instance", "list_instances", "sta
|
|
|
37
37
|
const SCHEDULE_TOOLS = new Set(["create_schedule", "list_schedules", "update_schedule", "delete_schedule"]);
|
|
38
38
|
const DECISION_TOOLS = new Set(["post_decision", "list_decisions", "update_decision"]);
|
|
39
39
|
const TASK_TOOL = "task";
|
|
40
|
-
// Tools whose success proves the agent
|
|
41
|
-
// of these succeeded, a dead-MCP proxy reply would
|
|
42
|
-
const
|
|
40
|
+
// Tools whose success proves the agent performed an outbound action this turn.
|
|
41
|
+
// While any of these succeeded, a dead-MCP proxy reply would duplicate output.
|
|
42
|
+
const TURN_OUTBOUND_TOOLS = new Set([
|
|
43
|
+
"reply", "react", "edit_message", "send_to_instance", "report_result",
|
|
44
|
+
"request_information", "delegate_task", "broadcast",
|
|
45
|
+
]);
|
|
46
|
+
// These actions reach the human-facing channel and satisfy reply-completion:
|
|
47
|
+
// a deliberate reaction or edit is a valid response even without new text.
|
|
48
|
+
const TURN_COMPLETION_TOOLS = new Set(["reply", "react", "edit_message"]);
|
|
43
49
|
const REPLY_DROP_WARNING_COOLDOWN_MS = 5 * 60_000;
|
|
44
50
|
const REPLY_RECOVERY_PROMPT = "[system:reply-required] The previous human-facing turn ended without a successfully delivered reply. Do not redo the work. Use the reply tool exactly once now to send the user a concise conclusion. If no substantive answer is needed, send a brief acknowledgement. Do not reply to this system instruction except through the reply tool.";
|
|
45
51
|
/** Point a resumed CLI at its one backend-native instruction source. */
|
|
@@ -495,6 +501,15 @@ const INPUT_TRANSIENT_WAIT_MS = 10 * 60_000;
|
|
|
495
501
|
const INPUT_TRANSIENT_POLL_MS = 250;
|
|
496
502
|
/** Max "stranded text → submit → wait for prompt → re-check" rounds per delivery; each may send one recovery Enter. */
|
|
497
503
|
const STRANDED_INPUT_MAX_ROUNDS = 3;
|
|
504
|
+
/** Build a status holder from channel metadata, keeping chat and thread apart. */
|
|
505
|
+
function channelStatus(meta) {
|
|
506
|
+
const chatId = meta.chat_id;
|
|
507
|
+
const messageId = meta.message_id;
|
|
508
|
+
if (!chatId || !messageId)
|
|
509
|
+
return undefined;
|
|
510
|
+
const threadId = meta.thread_id || undefined;
|
|
511
|
+
return threadId ? { chatId, messageId, threadId } : { chatId, messageId };
|
|
512
|
+
}
|
|
498
513
|
const FIRST_ENTER_SETTLE_MS = 1_750;
|
|
499
514
|
const FIRST_DELIVERY_WINDOW_MS = 5_000;
|
|
500
515
|
/** After busy native-queue paste+Enter, wait before checking the pane for silent loss. */
|
|
@@ -3211,7 +3226,7 @@ export class Daemon extends EventEmitter {
|
|
|
3211
3226
|
const turn = this.turnReplyGuard.snapshot();
|
|
3212
3227
|
if (!turn || this.isPaused)
|
|
3213
3228
|
return;
|
|
3214
|
-
if (turn.
|
|
3229
|
+
if (turn.completionDelivered) {
|
|
3215
3230
|
if (turn.phase === "recovering") {
|
|
3216
3231
|
this.emit("reply_drop_recovered", {
|
|
3217
3232
|
name: this.name,
|
|
@@ -3269,9 +3284,9 @@ export class Daemon extends EventEmitter {
|
|
|
3269
3284
|
return;
|
|
3270
3285
|
}
|
|
3271
3286
|
}
|
|
3272
|
-
// Preserve the dead-MCP proxy path and its explicit opt-in.
|
|
3273
|
-
//
|
|
3274
|
-
//
|
|
3287
|
+
// Preserve the dead-MCP proxy path and its explicit opt-in. Any verified
|
|
3288
|
+
// outbound action suppresses a duplicate proxy; only a delivered
|
|
3289
|
+
// human-facing channel action satisfies the completion guard below.
|
|
3275
3290
|
if (!turn.outboundDelivered && this.config.mcp_proxy_reply === true
|
|
3276
3291
|
&& mcpServerState(this.instanceDir).state === "dead") {
|
|
3277
3292
|
this.turnReplyGuard.complete(turn.generation);
|
|
@@ -3331,7 +3346,7 @@ export class Daemon extends EventEmitter {
|
|
|
3331
3346
|
const current = this.turnReplyGuard.snapshot();
|
|
3332
3347
|
if (!current || current.generation !== turn.generation || current.phase !== "recovering")
|
|
3333
3348
|
return;
|
|
3334
|
-
if (current.
|
|
3349
|
+
if (current.completionDelivered) {
|
|
3335
3350
|
this.turnReplyGuard.complete(turn.generation);
|
|
3336
3351
|
this.emit("reply_drop_recovered", {
|
|
3337
3352
|
name: this.name,
|
|
@@ -4204,11 +4219,7 @@ export class Daemon extends EventEmitter {
|
|
|
4204
4219
|
this.recordRecentUserMessage(content, meta);
|
|
4205
4220
|
const formatted = "[STEERING — mid-task course correction. Fold this into the CURRENT work if one is active.]\n"
|
|
4206
4221
|
+ this.formatInboundMessage(content, meta);
|
|
4207
|
-
const
|
|
4208
|
-
const messageId = meta.message_id;
|
|
4209
|
-
const status = (chatId && messageId)
|
|
4210
|
-
? { chatId: meta.thread_id || chatId, messageId }
|
|
4211
|
-
: undefined;
|
|
4222
|
+
const status = channelStatus(meta);
|
|
4212
4223
|
this.steerLock = this.steerLock.then(async () => {
|
|
4213
4224
|
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
4214
4225
|
return;
|
|
@@ -4246,11 +4257,7 @@ export class Daemon extends EventEmitter {
|
|
|
4246
4257
|
this.recordRecentUserMessage(content, meta);
|
|
4247
4258
|
const formatted = "[BTW — side question from the user. Answer it separately without changing or interrupting the CURRENT work.]\n"
|
|
4248
4259
|
+ this.formatInboundMessage(content, meta);
|
|
4249
|
-
const
|
|
4250
|
-
const messageId = meta.message_id;
|
|
4251
|
-
const status = (chatId && messageId)
|
|
4252
|
-
? { chatId: meta.thread_id || chatId, messageId }
|
|
4253
|
-
: undefined;
|
|
4260
|
+
const status = channelStatus(meta);
|
|
4254
4261
|
this.steerLock = this.steerLock.then(async () => {
|
|
4255
4262
|
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
4256
4263
|
return;
|
|
@@ -4327,7 +4334,9 @@ export class Daemon extends EventEmitter {
|
|
|
4327
4334
|
this.logger.warn({ depth: this.pasteQueueDepth }, "Message delivery queue backing up");
|
|
4328
4335
|
}
|
|
4329
4336
|
if (wasQueued && chatId && messageId) {
|
|
4330
|
-
|
|
4337
|
+
const queuedStatus = channelStatus(meta);
|
|
4338
|
+
if (queuedStatus)
|
|
4339
|
+
this.emit("message_queued", queuedStatus);
|
|
4331
4340
|
}
|
|
4332
4341
|
this.pasteLock = this.pasteLock.then(async () => {
|
|
4333
4342
|
try {
|
|
@@ -4346,9 +4355,7 @@ export class Daemon extends EventEmitter {
|
|
|
4346
4355
|
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
4347
4356
|
return;
|
|
4348
4357
|
}
|
|
4349
|
-
const status = (
|
|
4350
|
-
? { chatId: meta.thread_id || chatId, messageId }
|
|
4351
|
-
: undefined;
|
|
4358
|
+
const status = channelStatus(meta);
|
|
4352
4359
|
// A fresh delivery begins a fresh turn — its bubble must not inherit
|
|
4353
4360
|
// the previous turn's tool list.
|
|
4354
4361
|
this.resetToolProgress();
|
|
@@ -6050,14 +6057,14 @@ export class Daemon extends EventEmitter {
|
|
|
6050
6057
|
// an unmapped socket is retained for legacy/tests where mcp_ready was not
|
|
6051
6058
|
// observed before the first tool call.
|
|
6052
6059
|
const sourceSession = this.socketSessionNames.get(socket);
|
|
6053
|
-
const replyAttempt =
|
|
6054
|
-
? this.turnReplyGuard.beginToolAttempt(tool === "reply")
|
|
6060
|
+
const replyAttempt = TURN_OUTBOUND_TOOLS.has(tool) && (!sourceSession || sourceSession === this.name)
|
|
6061
|
+
? this.turnReplyGuard.beginToolAttempt(tool === "reply", TURN_COMPLETION_TOOLS.has(tool))
|
|
6055
6062
|
: null;
|
|
6056
6063
|
// For now, log and respond. Full adapter routing will be wired in fleet manager.
|
|
6057
6064
|
const respond = (result, error) => {
|
|
6058
6065
|
// A message that verifiably went out stands down the dead-MCP proxy reply
|
|
6059
6066
|
// for this turn: the agent proved it can still speak for itself.
|
|
6060
|
-
if (!error && result != null &&
|
|
6067
|
+
if (!error && result != null && TURN_OUTBOUND_TOOLS.has(tool)) {
|
|
6061
6068
|
this.turnReplyGuard.settleToolAttempt(replyAttempt, true);
|
|
6062
6069
|
}
|
|
6063
6070
|
const sent = this.ipcServer?.send(socket, { requestId, result, error }) ?? false;
|