@slock-ai/daemon 0.23.0 → 0.25.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/dist/chat-bridge.js +18 -50
- package/dist/index.js +110 -92
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -229,58 +229,26 @@ Use your Read tool to view this image.` }]
|
|
|
229
229
|
}
|
|
230
230
|
);
|
|
231
231
|
server.tool(
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
-
},
|
|
237
|
-
async ({ block }) => {
|
|
232
|
+
"check_messages",
|
|
233
|
+
"Check for new messages without waiting. Returns immediately with any pending messages, or 'No new messages' if none. Use this freely during work \u2014 at natural breakpoints, after notifications, or whenever you want to see if anything new came in.",
|
|
234
|
+
{},
|
|
235
|
+
async () => {
|
|
238
236
|
try {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
const params2 = new URLSearchParams();
|
|
247
|
-
if (block) {
|
|
248
|
-
params2.set("block", "true");
|
|
249
|
-
params2.set("timeout", "1000");
|
|
250
|
-
}
|
|
251
|
-
const res2 = await fetch(
|
|
252
|
-
`${serverUrl}/internal/agent/${agentId}/receive?${params2}`,
|
|
253
|
-
{ method: "GET", headers: commonHeaders }
|
|
254
|
-
);
|
|
255
|
-
if (!res2.ok) {
|
|
256
|
-
const errData = await res2.json().catch(() => ({}));
|
|
257
|
-
return { content: [{ type: "text", text: `Error: ${errData.error || res2.statusText}` }] };
|
|
258
|
-
}
|
|
259
|
-
const data2 = await res2.json();
|
|
260
|
-
if (data2.messages?.length > 0) {
|
|
261
|
-
return { content: [{ type: "text", text: formatMessages(data2.messages) }] };
|
|
262
|
-
}
|
|
263
|
-
return {
|
|
264
|
-
content: [{ type: "text", text: "No new messages. Take a rest \u2014 new messages will be delivered to you directly. Do not call receive_message or take any further action until notified." }]
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
const thisPoll = Math.min(POLL_INTERVAL_MS, remaining);
|
|
268
|
-
const params = new URLSearchParams();
|
|
269
|
-
params.set("block", "true");
|
|
270
|
-
params.set("timeout", String(thisPoll));
|
|
271
|
-
const res = await fetch(
|
|
272
|
-
`${serverUrl}/internal/agent/${agentId}/receive?${params}`,
|
|
273
|
-
{ method: "GET", headers: commonHeaders }
|
|
274
|
-
);
|
|
275
|
-
if (!res.ok) {
|
|
276
|
-
const errData = await res.json().catch(() => ({}));
|
|
277
|
-
return { content: [{ type: "text", text: `Error: ${errData.error || res.statusText}` }] };
|
|
278
|
-
}
|
|
279
|
-
const data = await res.json();
|
|
280
|
-
if (data.messages?.length > 0) {
|
|
281
|
-
return { content: [{ type: "text", text: formatMessages(data.messages) }] };
|
|
282
|
-
}
|
|
237
|
+
const res = await fetch(
|
|
238
|
+
`${serverUrl}/internal/agent/${agentId}/receive`,
|
|
239
|
+
{ method: "GET", headers: commonHeaders }
|
|
240
|
+
);
|
|
241
|
+
if (!res.ok) {
|
|
242
|
+
const errData = await res.json().catch(() => ({}));
|
|
243
|
+
return { content: [{ type: "text", text: `Error: ${errData.error || res.statusText}` }] };
|
|
283
244
|
}
|
|
245
|
+
const data = await res.json();
|
|
246
|
+
if (data.messages?.length > 0) {
|
|
247
|
+
return { content: [{ type: "text", text: formatMessages(data.messages) }] };
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
content: [{ type: "text", text: "No new messages." }]
|
|
251
|
+
};
|
|
284
252
|
} catch (err) {
|
|
285
253
|
return {
|
|
286
254
|
content: [{ type: "text", text: `Error: ${err.message}` }]
|
package/dist/index.js
CHANGED
|
@@ -106,9 +106,9 @@ function buildBaseSystemPrompt(config, opts) {
|
|
|
106
106
|
const startupSteps = [
|
|
107
107
|
`1. **Read MEMORY.md** (in your cwd). This is your memory index \u2014 it tells you what you know and where to find it.`,
|
|
108
108
|
`2. Follow the instructions in MEMORY.md to read any other memory files you need (e.g. channel summaries, role definitions, user preferences).`,
|
|
109
|
-
`3.
|
|
109
|
+
`3. Stop and wait. New messages will be delivered to you automatically via stdin.`,
|
|
110
110
|
`4. When you receive a message, process it and reply with ${t("send_message")}.`,
|
|
111
|
-
`5.
|
|
111
|
+
`5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them.`
|
|
112
112
|
];
|
|
113
113
|
let prompt = `You are "${config.displayName || config.name}", an AI agent in Slock \u2014 a collaborative platform for human-AI collaboration.
|
|
114
114
|
|
|
@@ -120,7 +120,7 @@ You are a **long-running, persistent agent**. You are NOT a one-shot assistant \
|
|
|
120
120
|
|
|
121
121
|
You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
122
122
|
|
|
123
|
-
1. **${t("
|
|
123
|
+
1. **${t("check_messages")}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
|
|
124
124
|
2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
|
|
125
125
|
3. **${t("list_server")}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
|
|
126
126
|
4. **${t("read_history")}** \u2014 Read past messages from a channel or DM.
|
|
@@ -130,6 +130,7 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
130
130
|
8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
131
131
|
9. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
132
132
|
10. **${t("upload_file")}** \u2014 Upload an image file to attach to a message. Returns an attachment ID to pass to send_message.
|
|
133
|
+
11. **${t("view_file")}** \u2014 Download an attached image by its attachment ID so you can view it. Use when messages contain image attachments.
|
|
133
134
|
|
|
134
135
|
CRITICAL RULES:
|
|
135
136
|
${criticalRules.join("\n")}
|
|
@@ -333,13 +334,12 @@ You may develop a specialized role over time through your interactions. Embrace
|
|
|
333
334
|
|
|
334
335
|
While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens, you will receive a system notification like:
|
|
335
336
|
|
|
336
|
-
\`[System notification: You have N new message(s) waiting. Call
|
|
337
|
+
\`[System notification: You have N new message(s) waiting. Call check_messages to read them when you're ready.]\`
|
|
337
338
|
|
|
338
339
|
How to handle these:
|
|
339
|
-
-
|
|
340
|
-
-
|
|
341
|
-
-
|
|
342
|
-
- These notifications are batched (you won't get one per message), so the count tells you how many are waiting.`;
|
|
340
|
+
- Call \`${t("check_messages")}()\` to check for new messages. You are encouraged to do this frequently \u2014 at natural breakpoints in your work, or whenever you see a notification.
|
|
341
|
+
- If the new message is higher priority, you may pivot to it. If not, continue your current work.
|
|
342
|
+
- \`check_messages\` returns instantly with any pending messages (or "no new messages"). It is always safe to call.`;
|
|
343
343
|
}
|
|
344
344
|
if (config.description) {
|
|
345
345
|
prompt += `
|
|
@@ -485,6 +485,7 @@ var ClaudeDriver = class {
|
|
|
485
485
|
if (name === "mcp__chat__update_task_status") return "Updating task status\u2026";
|
|
486
486
|
if (name === "mcp__chat__list_server") return "Listing server\u2026";
|
|
487
487
|
if (name === "mcp__chat__read_history") return "Reading history\u2026";
|
|
488
|
+
if (name === "mcp__chat__check_messages") return "Checking messages\u2026";
|
|
488
489
|
if (name.startsWith("mcp__chat__")) return "";
|
|
489
490
|
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
490
491
|
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
@@ -716,11 +717,10 @@ var CodexDriver = class {
|
|
|
716
717
|
return buildBaseSystemPrompt(config, {
|
|
717
718
|
toolPrefix: "",
|
|
718
719
|
extraCriticalRules: [
|
|
719
|
-
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
720
|
-
"- ALWAYS call receive_message(block=true) after completing any task \u2014 this keeps you listening for new messages."
|
|
720
|
+
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
721
721
|
],
|
|
722
722
|
postStartupNotes: [
|
|
723
|
-
"**IMPORTANT**: Your process exits after each turn completes. You will be automatically restarted when new messages arrive.
|
|
723
|
+
"**IMPORTANT**: Your process exits after each turn completes. You will be automatically restarted when new messages arrive. Complete all your work, then stop \u2014 new messages will wake you up."
|
|
724
724
|
],
|
|
725
725
|
includeStdinNotificationSection: false
|
|
726
726
|
});
|
|
@@ -735,6 +735,7 @@ var CodexDriver = class {
|
|
|
735
735
|
if (name === `${this.mcpToolPrefix}update_task_status`) return "Updating task status\u2026";
|
|
736
736
|
if (name === `${this.mcpToolPrefix}list_server`) return "Listing server\u2026";
|
|
737
737
|
if (name === `${this.mcpToolPrefix}read_history`) return "Reading history\u2026";
|
|
738
|
+
if (name === `${this.mcpToolPrefix}check_messages`) return "Checking messages\u2026";
|
|
738
739
|
if (name.startsWith(this.mcpToolPrefix)) return "";
|
|
739
740
|
if (name === "shell" || name === "command_execution") return "Running command\u2026";
|
|
740
741
|
if (name === "file_change") return "Editing file\u2026";
|
|
@@ -801,6 +802,7 @@ function toLocalTime(iso) {
|
|
|
801
802
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
802
803
|
}
|
|
803
804
|
var MAX_TRAJECTORY_TEXT = 2e3;
|
|
805
|
+
var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
804
806
|
var AgentProcessManager = class {
|
|
805
807
|
agents = /* @__PURE__ */ new Map();
|
|
806
808
|
agentsStarting = /* @__PURE__ */ new Set();
|
|
@@ -868,11 +870,13 @@ Use read_history to catch up, or respond to the message above first.`;
|
|
|
868
870
|
}
|
|
869
871
|
prompt += `
|
|
870
872
|
|
|
871
|
-
Respond as appropriate \u2014 reply using send_message, or take action as needed.
|
|
873
|
+
Respond as appropriate \u2014 reply using send_message, or take action as needed. Complete ALL your work before stopping.
|
|
874
|
+
|
|
875
|
+
IMPORTANT: If the message requires multi-step work (e.g. research, code changes, testing), complete ALL steps before stopping. Sending a progress update does NOT mean your task is done \u2014 only stop when you have NO more work to do. New messages will be delivered to you automatically via stdin.`;
|
|
872
876
|
if (driver.supportsStdinNotification) {
|
|
873
877
|
prompt += `
|
|
874
878
|
|
|
875
|
-
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call
|
|
879
|
+
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
876
880
|
}
|
|
877
881
|
} else if (isResume && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
878
882
|
prompt = `You have unread messages from while you were offline:`;
|
|
@@ -882,18 +886,18 @@ Note: While you are busy, you may receive [System notification: ...] messages. F
|
|
|
882
886
|
}
|
|
883
887
|
prompt += `
|
|
884
888
|
|
|
885
|
-
Use read_history to catch up on important channels, then
|
|
889
|
+
Use read_history to catch up on important channels, then stop. New messages will be delivered to you automatically.`;
|
|
886
890
|
if (driver.supportsStdinNotification) {
|
|
887
891
|
prompt += `
|
|
888
892
|
|
|
889
|
-
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call
|
|
893
|
+
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
890
894
|
}
|
|
891
895
|
} else if (isResume) {
|
|
892
|
-
prompt = `No new messages while you were away.
|
|
896
|
+
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. New messages will be delivered to you automatically via stdin.`;
|
|
893
897
|
if (driver.supportsStdinNotification) {
|
|
894
898
|
prompt += `
|
|
895
899
|
|
|
896
|
-
Note: While you are busy, you may receive [System notification: ...] messages about new messages. Finish your current step, then call
|
|
900
|
+
Note: While you are busy, you may receive [System notification: ...] messages about new messages. Finish your current step, then call check_messages to check for messages.`;
|
|
897
901
|
}
|
|
898
902
|
} else {
|
|
899
903
|
prompt = driver.buildSystemPrompt(config, agentId);
|
|
@@ -910,12 +914,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
910
914
|
process: proc,
|
|
911
915
|
driver,
|
|
912
916
|
inbox: [],
|
|
913
|
-
pendingReceive: null,
|
|
914
917
|
config,
|
|
915
918
|
sessionId: config.sessionId || null,
|
|
916
|
-
|
|
919
|
+
isIdle: false,
|
|
917
920
|
notificationTimer: null,
|
|
918
|
-
pendingNotificationCount: 0
|
|
921
|
+
pendingNotificationCount: 0,
|
|
922
|
+
activityHeartbeat: null,
|
|
923
|
+
lastActivity: "",
|
|
924
|
+
lastActivityDetail: ""
|
|
919
925
|
};
|
|
920
926
|
this.agents.set(agentId, agentProcess);
|
|
921
927
|
this.agentsStarting.delete(agentId);
|
|
@@ -943,31 +949,30 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
943
949
|
if (this.agents.has(agentId)) {
|
|
944
950
|
const ap = this.agents.get(agentId);
|
|
945
951
|
if (ap.process !== proc) return;
|
|
946
|
-
if (ap.pendingReceive) {
|
|
947
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
948
|
-
ap.pendingReceive.resolve([]);
|
|
949
|
-
}
|
|
950
952
|
if (ap.notificationTimer) {
|
|
951
953
|
clearTimeout(ap.notificationTimer);
|
|
952
954
|
}
|
|
955
|
+
if (ap.activityHeartbeat) {
|
|
956
|
+
clearInterval(ap.activityHeartbeat);
|
|
957
|
+
}
|
|
953
958
|
this.agents.delete(agentId);
|
|
954
959
|
if (code === 0) {
|
|
955
960
|
this.idleAgentConfigs.set(agentId, {
|
|
956
961
|
config: { ...ap.config, sessionId: ap.sessionId },
|
|
957
962
|
sessionId: ap.sessionId
|
|
958
963
|
});
|
|
959
|
-
this.
|
|
964
|
+
this.broadcastActivity(agentId, "online", "Process idle");
|
|
960
965
|
} else {
|
|
961
966
|
this.idleAgentConfigs.delete(agentId);
|
|
962
967
|
const reason = code === null ? "killed by signal" : `exit code ${code}`;
|
|
963
968
|
console.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
964
969
|
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
965
|
-
this.
|
|
970
|
+
this.broadcastActivity(agentId, "offline", `Crashed (${reason})`);
|
|
966
971
|
}
|
|
967
972
|
}
|
|
968
973
|
});
|
|
969
974
|
this.sendToServer({ type: "agent:status", agentId, status: "active" });
|
|
970
|
-
this.
|
|
975
|
+
this.broadcastActivity(agentId, "working", "Starting\u2026");
|
|
971
976
|
} catch (err) {
|
|
972
977
|
this.agentsStarting.delete(agentId);
|
|
973
978
|
throw err;
|
|
@@ -977,18 +982,17 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
977
982
|
this.idleAgentConfigs.delete(agentId);
|
|
978
983
|
const ap = this.agents.get(agentId);
|
|
979
984
|
if (!ap) return;
|
|
980
|
-
if (ap.pendingReceive) {
|
|
981
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
982
|
-
ap.pendingReceive.resolve([]);
|
|
983
|
-
}
|
|
984
985
|
if (ap.notificationTimer) {
|
|
985
986
|
clearTimeout(ap.notificationTimer);
|
|
986
987
|
}
|
|
988
|
+
if (ap.activityHeartbeat) {
|
|
989
|
+
clearInterval(ap.activityHeartbeat);
|
|
990
|
+
}
|
|
987
991
|
this.agents.delete(agentId);
|
|
988
992
|
ap.process.kill("SIGTERM");
|
|
989
993
|
if (!silent) {
|
|
990
994
|
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
991
|
-
this.
|
|
995
|
+
this.broadcastActivity(agentId, "offline", "Stopped");
|
|
992
996
|
}
|
|
993
997
|
if (wait) {
|
|
994
998
|
await new Promise((resolve) => {
|
|
@@ -1023,15 +1027,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1023
1027
|
}
|
|
1024
1028
|
return;
|
|
1025
1029
|
}
|
|
1026
|
-
if (ap.
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
ap.inbox.push(message);
|
|
1030
|
+
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
1031
|
+
ap.isIdle = false;
|
|
1032
|
+
this.broadcastActivity(agentId, "working", "Message received");
|
|
1033
|
+
this.deliverMessageViaStdin(agentId, ap, message);
|
|
1034
|
+
return;
|
|
1032
1035
|
}
|
|
1036
|
+
ap.inbox.push(message);
|
|
1033
1037
|
if (!ap.driver.supportsStdinNotification) return;
|
|
1034
|
-
if (ap.isInReceiveMessage) return;
|
|
1035
1038
|
if (!ap.sessionId) return;
|
|
1036
1039
|
ap.pendingNotificationCount++;
|
|
1037
1040
|
if (!ap.notificationTimer) {
|
|
@@ -1169,11 +1172,37 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1169
1172
|
return { content, binary: false };
|
|
1170
1173
|
}
|
|
1171
1174
|
// Private methods
|
|
1175
|
+
/**
|
|
1176
|
+
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
1177
|
+
* both the status (for the dot indicator) and trajectory entries (for the activity log).
|
|
1178
|
+
*/
|
|
1179
|
+
broadcastActivity(agentId, activity, detail, extraTrajectory = []) {
|
|
1180
|
+
const ap = this.agents.get(agentId);
|
|
1181
|
+
const entries = [...extraTrajectory];
|
|
1182
|
+
const hasToolStart = entries.some((e) => e.kind === "tool_start");
|
|
1183
|
+
if (!hasToolStart) {
|
|
1184
|
+
entries.push({ kind: "status", activity, detail });
|
|
1185
|
+
}
|
|
1186
|
+
this.sendToServer({ type: "agent:activity", agentId, activity, detail, entries });
|
|
1187
|
+
if (ap) {
|
|
1188
|
+
ap.lastActivity = activity;
|
|
1189
|
+
ap.lastActivityDetail = detail;
|
|
1190
|
+
if (activity === "working" || activity === "thinking") {
|
|
1191
|
+
if (!ap.activityHeartbeat) {
|
|
1192
|
+
ap.activityHeartbeat = setInterval(() => {
|
|
1193
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: ap.lastActivity, detail: ap.lastActivityDetail });
|
|
1194
|
+
}, ACTIVITY_HEARTBEAT_MS);
|
|
1195
|
+
}
|
|
1196
|
+
} else {
|
|
1197
|
+
if (ap.activityHeartbeat) {
|
|
1198
|
+
clearInterval(ap.activityHeartbeat);
|
|
1199
|
+
ap.activityHeartbeat = null;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1172
1204
|
/** Handle a single ParsedEvent from any runtime driver */
|
|
1173
1205
|
handleParsedEvent(agentId, event, driver) {
|
|
1174
|
-
const trajectory = [];
|
|
1175
|
-
let activity = "";
|
|
1176
|
-
let detail = "";
|
|
1177
1206
|
const ap = this.agents.get(agentId);
|
|
1178
1207
|
switch (event.kind) {
|
|
1179
1208
|
case "session_init":
|
|
@@ -1182,76 +1211,48 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1182
1211
|
break;
|
|
1183
1212
|
case "thinking": {
|
|
1184
1213
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
if (ap) ap.
|
|
1214
|
+
const extra = text ? [{ kind: "thinking", text }] : [];
|
|
1215
|
+
this.broadcastActivity(agentId, "thinking", "", extra);
|
|
1216
|
+
if (ap) ap.isIdle = false;
|
|
1188
1217
|
break;
|
|
1189
1218
|
}
|
|
1190
1219
|
case "text": {
|
|
1191
1220
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
if (ap) ap.isInReceiveMessage = false;
|
|
1221
|
+
this.broadcastActivity(agentId, "thinking", "", [{ kind: "text", text }]);
|
|
1222
|
+
if (ap) ap.isIdle = false;
|
|
1195
1223
|
break;
|
|
1196
1224
|
}
|
|
1197
1225
|
case "tool_call": {
|
|
1198
1226
|
const toolName = event.name;
|
|
1199
1227
|
const inputSummary = driver.summarizeToolInput(toolName, event.input);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
activity = "online";
|
|
1204
|
-
if (ap) {
|
|
1205
|
-
ap.isInReceiveMessage = true;
|
|
1206
|
-
ap.pendingNotificationCount = 0;
|
|
1207
|
-
if (ap.notificationTimer) {
|
|
1208
|
-
clearTimeout(ap.notificationTimer);
|
|
1209
|
-
ap.notificationTimer = null;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
} else {
|
|
1213
|
-
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1214
|
-
activity = "working";
|
|
1215
|
-
detail = "Checking messages\u2026";
|
|
1216
|
-
if (ap) ap.isInReceiveMessage = false;
|
|
1217
|
-
}
|
|
1218
|
-
} else if (toolName === `${driver.mcpToolPrefix}send_message`) {
|
|
1219
|
-
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1220
|
-
activity = "working";
|
|
1221
|
-
detail = "Sending message\u2026";
|
|
1222
|
-
if (ap) ap.isInReceiveMessage = false;
|
|
1223
|
-
} else {
|
|
1224
|
-
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1225
|
-
activity = "working";
|
|
1226
|
-
detail = driver.toolDisplayName(toolName);
|
|
1227
|
-
if (ap) ap.isInReceiveMessage = false;
|
|
1228
|
-
}
|
|
1228
|
+
const detail = toolName === `${driver.mcpToolPrefix}check_messages` ? "Checking messages\u2026" : toolName === `${driver.mcpToolPrefix}send_message` ? "Sending message\u2026" : driver.toolDisplayName(toolName);
|
|
1229
|
+
this.broadcastActivity(agentId, "working", detail, [{ kind: "tool_start", toolName, toolInput: inputSummary }]);
|
|
1230
|
+
if (ap) ap.isIdle = false;
|
|
1229
1231
|
break;
|
|
1230
1232
|
}
|
|
1231
1233
|
case "turn_end":
|
|
1232
|
-
activity = "online";
|
|
1233
1234
|
if (ap) {
|
|
1234
|
-
ap.isInReceiveMessage = false;
|
|
1235
1235
|
if (event.sessionId) ap.sessionId = event.sessionId;
|
|
1236
|
+
if (ap.inbox.length > 0 && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
1237
|
+
const nextMessage = ap.inbox.shift();
|
|
1238
|
+
this.broadcastActivity(agentId, "working", "Message received");
|
|
1239
|
+
this.deliverMessageViaStdin(agentId, ap, nextMessage);
|
|
1240
|
+
} else {
|
|
1241
|
+
ap.isIdle = true;
|
|
1242
|
+
this.broadcastActivity(agentId, "online", "Idle");
|
|
1243
|
+
}
|
|
1236
1244
|
}
|
|
1237
1245
|
if (event.sessionId) {
|
|
1238
1246
|
this.sendToServer({ type: "agent:session", agentId, sessionId: event.sessionId });
|
|
1239
1247
|
}
|
|
1240
1248
|
break;
|
|
1241
|
-
case "error":
|
|
1242
|
-
|
|
1249
|
+
case "error": {
|
|
1250
|
+
const currentActivity = ap?.lastActivity || "working";
|
|
1251
|
+
const currentDetail = ap?.lastActivityDetail || "";
|
|
1252
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: currentActivity, detail: currentDetail, entries: [{ kind: "text", text: `Error: ${event.message}` }] });
|
|
1243
1253
|
break;
|
|
1244
|
-
}
|
|
1245
|
-
if (activity) {
|
|
1246
|
-
this.sendToServer({ type: "agent:activity", agentId, activity, detail });
|
|
1247
|
-
const hasToolStart = trajectory.some((e) => e.kind === "tool_start");
|
|
1248
|
-
if (!hasToolStart) {
|
|
1249
|
-
trajectory.push({ kind: "status", activity, detail });
|
|
1250
1254
|
}
|
|
1251
1255
|
}
|
|
1252
|
-
if (trajectory.length > 0) {
|
|
1253
|
-
this.sendToServer({ type: "agent:trajectory", agentId, entries: trajectory });
|
|
1254
|
-
}
|
|
1255
1256
|
}
|
|
1256
1257
|
/** Send a batched notification to the agent via stdin about pending messages */
|
|
1257
1258
|
sendStdinNotification(agentId) {
|
|
@@ -1261,15 +1262,32 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1261
1262
|
ap.pendingNotificationCount = 0;
|
|
1262
1263
|
ap.notificationTimer = null;
|
|
1263
1264
|
if (count === 0) return;
|
|
1264
|
-
if (ap.
|
|
1265
|
+
if (ap.isIdle) return;
|
|
1265
1266
|
if (!ap.sessionId) return;
|
|
1266
|
-
const notification = `[System notification: You have ${count} new message${count > 1 ? "s" : ""} waiting. Call
|
|
1267
|
+
const notification = `[System notification: You have ${count} new message${count > 1 ? "s" : ""} waiting. Call check_messages to read ${count > 1 ? "them" : "it"} when you're ready.]`;
|
|
1267
1268
|
console.log(`[Agent ${agentId}] Sending stdin notification: ${count} message(s)`);
|
|
1268
1269
|
const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId);
|
|
1269
1270
|
if (encoded) {
|
|
1270
1271
|
ap.process.stdin?.write(encoded + "\n");
|
|
1271
1272
|
}
|
|
1272
1273
|
}
|
|
1274
|
+
/** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
|
|
1275
|
+
deliverMessageViaStdin(agentId, ap, message) {
|
|
1276
|
+
const channelLabel = message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
|
|
1277
|
+
const senderPrefix = message.sender_type === "agent" ? "(agent) " : "";
|
|
1278
|
+
const time = message.timestamp ? ` (${toLocalTime(message.timestamp)})` : "";
|
|
1279
|
+
const formatted = `[${channelLabel}]${time} ${senderPrefix}@${message.sender_name}: ${message.content}`;
|
|
1280
|
+
const prompt = `New message received:
|
|
1281
|
+
|
|
1282
|
+
${formatted}
|
|
1283
|
+
|
|
1284
|
+
Respond as appropriate. Complete all your work before stopping.`;
|
|
1285
|
+
const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId);
|
|
1286
|
+
if (encoded) {
|
|
1287
|
+
console.log(`[Agent ${agentId}] Delivering message via stdin from @${message.sender_name}`);
|
|
1288
|
+
ap.process.stdin?.write(encoded + "\n");
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1273
1291
|
/** List ONE level of a directory — directories returned without children (lazy-loaded on demand) */
|
|
1274
1292
|
async listDirectoryChildren(dir, rootDir) {
|
|
1275
1293
|
let entries;
|