@ctliz/agent-intercom-claude 0.14.1 → 0.15.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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +14 -3
- package/broker/broker.ts +3 -2
- package/broker/client.ts +5 -2
- package/claude/inbox.ts +3 -1
- package/claude/mcp-protocol.ts +31 -9
- package/claude/named-team-membership.ts +124 -0
- package/claude/named-teams.ts +11 -0
- package/claude/native-bridge.ts +13 -4
- package/claude/reply-context.ts +49 -0
- package/claude/runtime.ts +45 -31
- package/claude/team-guidance.ts +7 -0
- package/claude/worker-daemon.ts +4 -0
- package/commands/intercom-create.md +4 -2
- package/commands/intercom-join.md +1 -1
- package/dist/broker.mjs +3 -2
- package/dist/cci.mjs +23 -8
- package/dist/ccim.mjs +23 -8
- package/dist/claude-server.mjs +241 -85
- package/dist/inbox-monitor.mjs +2 -1
- package/dist/worker-daemon.mjs +11 -5
- package/package.json +1 -1
- package/skills/claude-intercom/SKILL.md +5 -5
- package/types.ts +1 -0
package/dist/claude-server.mjs
CHANGED
|
@@ -635,14 +635,15 @@ function isMessage(value) {
|
|
|
635
635
|
}
|
|
636
636
|
if (message.replyTo !== void 0 || message.expectsReply !== void 0) return false;
|
|
637
637
|
}
|
|
638
|
-
if (!hasExactDataKeys(message.content, ["text"], ["attachments"])) {
|
|
638
|
+
if (!hasExactDataKeys(message.content, ["text"], ["attachments", "team"])) {
|
|
639
639
|
return false;
|
|
640
640
|
}
|
|
641
641
|
const content = message.content;
|
|
642
642
|
if (typeof content.text !== "string") {
|
|
643
643
|
return false;
|
|
644
644
|
}
|
|
645
|
-
if (
|
|
645
|
+
if (content.team !== void 0 && (typeof content.team !== "string" || !/^[A-Za-z][A-Za-z0-9_-]{0,31}$/.test(content.team))) return false;
|
|
646
|
+
if (message.control !== void 0 && (content.text !== "" || content.attachments !== void 0 || content.team !== void 0)) return false;
|
|
646
647
|
return content.attachments === void 0 || isDenseArrayOf(content.attachments, isAttachment);
|
|
647
648
|
}
|
|
648
649
|
function isSessionInfo(value) {
|
|
@@ -1265,6 +1266,7 @@ var IntercomClient = class extends EventEmitter {
|
|
|
1265
1266
|
...options.control === void 0 ? {} : { control: options.control },
|
|
1266
1267
|
content: {
|
|
1267
1268
|
text: options.text,
|
|
1269
|
+
...options.team === void 0 ? {} : { team: options.team },
|
|
1268
1270
|
...options.attachments === void 0 ? {} : { attachments: options.attachments }
|
|
1269
1271
|
}
|
|
1270
1272
|
};
|
|
@@ -1859,6 +1861,7 @@ function toInboxEntry(from, message, now = Date.now()) {
|
|
|
1859
1861
|
fromId: from.id,
|
|
1860
1862
|
...from.name ? { fromName: from.name } : {},
|
|
1861
1863
|
messageId: message.id,
|
|
1864
|
+
...message.content.team ? { team: message.content.team } : {},
|
|
1862
1865
|
...message.replyTo ? { replyTo: message.replyTo } : {},
|
|
1863
1866
|
expectsReply: Boolean(message.expectsReply),
|
|
1864
1867
|
text: message.content.text
|
|
@@ -2188,19 +2191,17 @@ function parseStoredTeam(value) {
|
|
|
2188
2191
|
if (typeof value.createdAt !== "number" || !Number.isSafeInteger(value.createdAt) || value.createdAt <= 0) {
|
|
2189
2192
|
return void 0;
|
|
2190
2193
|
}
|
|
2194
|
+
if (value.memberSessionIds !== void 0 && (!Array.isArray(value.memberSessionIds) || !value.memberSessionIds.every((id) => typeof id === "string" && id.trim() === id && id.length > 0 && !/[\u0000-\u001f\u007f]/.test(id)) || new Set(value.memberSessionIds).size !== value.memberSessionIds.length || !value.memberSessionIds.includes(value.managerSessionId))) return void 0;
|
|
2195
|
+
if (value.work !== void 0 && (typeof value.work !== "string" || !value.work.trim() || value.work.length > 2e3)) return void 0;
|
|
2191
2196
|
return {
|
|
2192
2197
|
name: value.name,
|
|
2193
2198
|
scopeId: value.scopeId,
|
|
2194
2199
|
managerSessionId: value.managerSessionId,
|
|
2195
|
-
createdAt: value.createdAt
|
|
2200
|
+
createdAt: value.createdAt,
|
|
2201
|
+
...value.memberSessionIds === void 0 ? {} : { memberSessionIds: value.memberSessionIds },
|
|
2202
|
+
...value.work === void 0 ? {} : { work: value.work }
|
|
2196
2203
|
};
|
|
2197
2204
|
}
|
|
2198
|
-
function rejectManagedJoin(env = process.env) {
|
|
2199
|
-
if (env.AGENT_INTERCOM_WORKER_ID?.trim() || env.AGENT_INTERCOM_OWNED === "1" || env.AGENT_INTERCOM_TEAM_MANIFEST?.trim()) {
|
|
2200
|
-
return "This session is already a managed member and cannot join another team.";
|
|
2201
|
-
}
|
|
2202
|
-
return void 0;
|
|
2203
|
-
}
|
|
2204
2205
|
function parseTeamName(raw) {
|
|
2205
2206
|
const name = raw.trim();
|
|
2206
2207
|
if (!name || name.includes(" ") || !NAMED_TEAM_NAME_PATTERN.test(name)) {
|
|
@@ -2245,35 +2246,6 @@ function listNamedTeams(agentDir) {
|
|
|
2245
2246
|
}
|
|
2246
2247
|
return teams;
|
|
2247
2248
|
}
|
|
2248
|
-
function findNamedTeam(name, agentDir) {
|
|
2249
|
-
return listNamedTeams(agentDir).find((team) => team.name === name);
|
|
2250
|
-
}
|
|
2251
|
-
function createNamedTeam(input) {
|
|
2252
|
-
const name = parseTeamName(input.name);
|
|
2253
|
-
const managerSessionId = input.managerSessionId.trim();
|
|
2254
|
-
if (!managerSessionId || managerSessionId !== input.managerSessionId || /[\u0000-\u001f\u007f]/.test(managerSessionId)) {
|
|
2255
|
-
throw genericWriteError();
|
|
2256
|
-
}
|
|
2257
|
-
const existing = listNamedTeams(input.agentDir);
|
|
2258
|
-
if (existing.some((team2) => team2.name === name)) {
|
|
2259
|
-
throw new Error(`A named team called ${name} already exists.`);
|
|
2260
|
-
}
|
|
2261
|
-
const scopeId = (input.generateScope ?? (() => generateNamedTeamScope(existing.map((team2) => team2.scopeId))))();
|
|
2262
|
-
if (!isNamedTeamScope(scopeId) || existing.some((team2) => team2.scopeId === scopeId)) {
|
|
2263
|
-
throw genericWriteError();
|
|
2264
|
-
}
|
|
2265
|
-
const team = {
|
|
2266
|
-
name,
|
|
2267
|
-
scopeId,
|
|
2268
|
-
managerSessionId,
|
|
2269
|
-
createdAt: input.now ?? Date.now()
|
|
2270
|
-
};
|
|
2271
|
-
const dir = getIntercomDirPath(input.agentDir ?? getAgentDirPath());
|
|
2272
|
-
ensureIntercomRuntimeDir(dir);
|
|
2273
|
-
const payload = { version: NAMED_TEAMS_VERSION, teams: [...existing, team] };
|
|
2274
|
-
writeDurableJson(teamsFilePath(input.agentDir), payload);
|
|
2275
|
-
return team;
|
|
2276
|
-
}
|
|
2277
2249
|
function formatJoinableNamedTeamList(teams) {
|
|
2278
2250
|
if (teams.length === 0) {
|
|
2279
2251
|
return 'No joinable named teams found.\nCreate one with intercom_join({ name: "billing", create: true }).';
|
|
@@ -2293,6 +2265,147 @@ Role: teammate
|
|
|
2293
2265
|
Display name: ${input.name}`;
|
|
2294
2266
|
}
|
|
2295
2267
|
|
|
2268
|
+
// claude/named-team-membership.ts
|
|
2269
|
+
import { mkdirSync as mkdirSync4, rmSync } from "node:fs";
|
|
2270
|
+
import { join as join8 } from "node:path";
|
|
2271
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2272
|
+
function namedTeamMemberIds(team) {
|
|
2273
|
+
return team.memberSessionIds ?? [team.managerSessionId];
|
|
2274
|
+
}
|
|
2275
|
+
function sessionNamedTeams(sessionId, agentDir) {
|
|
2276
|
+
return listNamedTeams(agentDir).filter((team) => namedTeamMemberIds(team).includes(sessionId));
|
|
2277
|
+
}
|
|
2278
|
+
async function appendNamedTeamMembership(input) {
|
|
2279
|
+
const name = parseTeamName(input.name);
|
|
2280
|
+
const memberIds = [.../* @__PURE__ */ new Set([input.selfId, ...input.members ?? []])];
|
|
2281
|
+
if (memberIds.some((id) => !id || id.trim() !== id || /[\u0000-\u001f\u007f]/.test(id))) {
|
|
2282
|
+
throw new Error("Invalid team member session ID");
|
|
2283
|
+
}
|
|
2284
|
+
if (input.work !== void 0 && (!input.work.trim() || input.work.length > 2e3)) {
|
|
2285
|
+
throw new Error("Work must be a non-empty task description (max 2000 characters)");
|
|
2286
|
+
}
|
|
2287
|
+
const dir = getIntercomDirPath(input.agentDir ?? getAgentDirPath());
|
|
2288
|
+
ensureIntercomRuntimeDir(dir);
|
|
2289
|
+
const lock = join8(dir, "named-teams.lock");
|
|
2290
|
+
const deadline = Date.now() + 5e3;
|
|
2291
|
+
for (; ; ) {
|
|
2292
|
+
try {
|
|
2293
|
+
mkdirSync4(lock, { mode: 448 });
|
|
2294
|
+
break;
|
|
2295
|
+
} catch (error2) {
|
|
2296
|
+
if (error2.code !== "EEXIST") throw error2;
|
|
2297
|
+
if (Date.now() >= deadline) throw new Error("Team registry is busy; retry. If a writer crashed, remove the stale named-teams.lock directory after verifying no writer is active.");
|
|
2298
|
+
await delay(25);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
try {
|
|
2302
|
+
const teams = listNamedTeams(input.agentDir);
|
|
2303
|
+
let team = teams.find((entry) => entry.name === name);
|
|
2304
|
+
if (input.create) {
|
|
2305
|
+
if (team) throw new Error(`A named team called ${name} already exists; join it instead.`);
|
|
2306
|
+
team = { name, scopeId: generateNamedTeamScope(teams.map((entry) => entry.scopeId)), managerSessionId: input.selfId, createdAt: Date.now() };
|
|
2307
|
+
teams.push(team);
|
|
2308
|
+
}
|
|
2309
|
+
if (!team) throw new Error(`Team "${name}" does not exist; create it explicitly.`);
|
|
2310
|
+
if (memberIds.some((id) => id !== input.selfId) && team.managerSessionId !== input.selfId) {
|
|
2311
|
+
throw new Error(`Only the manager of team "${name}" may add other sessions`);
|
|
2312
|
+
}
|
|
2313
|
+
if (input.work !== void 0 && !input.create) {
|
|
2314
|
+
throw new Error("Work is set when creating a team; use a new team for a different task");
|
|
2315
|
+
}
|
|
2316
|
+
const updated = {
|
|
2317
|
+
...team,
|
|
2318
|
+
memberSessionIds: [.../* @__PURE__ */ new Set([...namedTeamMemberIds(team), ...memberIds])],
|
|
2319
|
+
...input.work === void 0 ? {} : { work: input.work.trim() }
|
|
2320
|
+
};
|
|
2321
|
+
writeDurableJson(join8(dir, NAMED_TEAMS_FILE), {
|
|
2322
|
+
version: NAMED_TEAMS_VERSION,
|
|
2323
|
+
teams: teams.map((entry) => entry.name === name ? updated : entry)
|
|
2324
|
+
});
|
|
2325
|
+
return updated;
|
|
2326
|
+
} finally {
|
|
2327
|
+
rmSync(lock, { recursive: true });
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
function requireNamedTeamMembers(name, selfId, peerId, agentDir) {
|
|
2331
|
+
const team = listNamedTeams(agentDir).find((entry) => entry.name === name);
|
|
2332
|
+
if (!team) throw new Error(`Unknown team "${name}"`);
|
|
2333
|
+
const members = namedTeamMemberIds(team);
|
|
2334
|
+
if (!members.includes(selfId) || !members.includes(peerId)) {
|
|
2335
|
+
throw new Error(`Both sessions must belong to team "${name}" before messaging`);
|
|
2336
|
+
}
|
|
2337
|
+
return team;
|
|
2338
|
+
}
|
|
2339
|
+
function resolveNamedMessageTeam(selfId, peerId, requested, agentDir) {
|
|
2340
|
+
if (requested !== void 0) return requireNamedTeamMembers(requested, selfId, peerId, agentDir).name;
|
|
2341
|
+
const mine = sessionNamedTeams(selfId, agentDir);
|
|
2342
|
+
const shared = mine.filter((team) => namedTeamMemberIds(team).includes(peerId));
|
|
2343
|
+
if (shared.length === 1) return shared[0].name;
|
|
2344
|
+
if (shared.length > 1) throw new Error("Multiple shared teams; specify `team` for this task");
|
|
2345
|
+
return void 0;
|
|
2346
|
+
}
|
|
2347
|
+
function namedTeamRoster(team, selfId, sessions) {
|
|
2348
|
+
return {
|
|
2349
|
+
name: team.name,
|
|
2350
|
+
...team.work ? { work: team.work } : {},
|
|
2351
|
+
self: { id: selfId, isManager: selfId === team.managerSessionId },
|
|
2352
|
+
manager: { target: team.managerSessionId, connected: sessions.some((entry) => entry.id === team.managerSessionId) },
|
|
2353
|
+
members: namedTeamMemberIds(team).map((id) => ({
|
|
2354
|
+
id,
|
|
2355
|
+
target: id,
|
|
2356
|
+
name: sessions.find((entry) => entry.id === id)?.name,
|
|
2357
|
+
role: id === team.managerSessionId ? "manager" : "member",
|
|
2358
|
+
connected: sessions.some((entry) => entry.id === id)
|
|
2359
|
+
}))
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
function formatNamedTeamRoster(team) {
|
|
2363
|
+
return [
|
|
2364
|
+
`Team: ${team.name}`,
|
|
2365
|
+
...team.work ? [`Work: ${team.work}`] : [],
|
|
2366
|
+
`You: ${team.self.id}${team.self.isManager ? " [manager]" : ""}`,
|
|
2367
|
+
`Manager: ${team.manager.target}${team.manager.connected ? "" : " [offline]"}`,
|
|
2368
|
+
"Members:",
|
|
2369
|
+
...team.members.map((member) => `- ${member.name || member.id} (${member.id}) [${member.role}]${member.connected ? "" : " [offline]"}`)
|
|
2370
|
+
].join("\n");
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// claude/reply-context.ts
|
|
2374
|
+
function contextId(entry) {
|
|
2375
|
+
return `ctx-${entry.message.id}`;
|
|
2376
|
+
}
|
|
2377
|
+
function askId(entry) {
|
|
2378
|
+
return `ask-${entry.message.id}`;
|
|
2379
|
+
}
|
|
2380
|
+
function replyHint(entry) {
|
|
2381
|
+
const team = entry.message.content.team;
|
|
2382
|
+
return `${team ? ` [Team: ${team}]` : ""} [contextId: ${contextId(entry)}]${entry.message.expectsReply ? ` [askId: ${askId(entry)}]` : ""}`;
|
|
2383
|
+
}
|
|
2384
|
+
function selectReplyContext(entries, asks, input) {
|
|
2385
|
+
if (input.askId && input.contextId) throw new Error("Specify askId or contextId, not both");
|
|
2386
|
+
let target;
|
|
2387
|
+
if (input.askId || input.contextId) {
|
|
2388
|
+
const selected = input.askId ? asks.find((entry) => askId(entry) === input.askId) : entries.find((entry) => contextId(entry) === input.contextId);
|
|
2389
|
+
if (!selected || selected.message.expectsReply && !asks.includes(selected)) throw new Error("Unknown or resolved reply selector; call intercom_pending");
|
|
2390
|
+
if (input.to && selected.from.id !== input.to && selected.from.name?.toLowerCase() !== input.to.toLowerCase()) {
|
|
2391
|
+
throw new Error("Reply sender does not match the selected context");
|
|
2392
|
+
}
|
|
2393
|
+
if (input.which) throw new Error("Do not combine an exact reply selector with which");
|
|
2394
|
+
target = selected;
|
|
2395
|
+
} else {
|
|
2396
|
+
const to = input.to;
|
|
2397
|
+
const candidates = to ? asks.filter((entry) => entry.from.id === to || entry.from.name?.toLowerCase() === to.toLowerCase() || entry.from.id.startsWith(to)) : asks;
|
|
2398
|
+
if (!input.which && new Set(candidates.map((entry) => entry.message.content.team)).size > 1) {
|
|
2399
|
+
throw new Error("Multiple team contexts; specify askId or contextId");
|
|
2400
|
+
}
|
|
2401
|
+
target = selectPendingAsk(asks, input.to, input.which);
|
|
2402
|
+
}
|
|
2403
|
+
if (input.team !== void 0 && input.team !== target.message.content.team) {
|
|
2404
|
+
throw new Error("Reply team must match the original message; omit team to inherit it");
|
|
2405
|
+
}
|
|
2406
|
+
return target;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2296
2409
|
// claude/runtime.ts
|
|
2297
2410
|
function matchesPendingSender(entry, to) {
|
|
2298
2411
|
return entry.from.id === to || entry.from.name?.toLowerCase() === to.toLowerCase() || entry.from.id.startsWith(to);
|
|
@@ -2330,6 +2443,9 @@ function publicPendingEntry(entry, selector) {
|
|
|
2330
2443
|
...entry.from.parentSessionId ? { parent_session_id: entry.from.parentSessionId } : {},
|
|
2331
2444
|
...entry.from.generation ? { generation: entry.from.generation } : {}
|
|
2332
2445
|
},
|
|
2446
|
+
contextId: contextId(entry),
|
|
2447
|
+
...entry.message.expectsReply ? { askId: askId(entry) } : {},
|
|
2448
|
+
...entry.message.content.team ? { team: entry.message.content.team } : {},
|
|
2333
2449
|
received_at: entry.receivedAt,
|
|
2334
2450
|
read: entry.read,
|
|
2335
2451
|
text: entry.message.content.text,
|
|
@@ -2545,7 +2661,7 @@ var ClaudeIntercomRuntime = class {
|
|
|
2545
2661
|
}
|
|
2546
2662
|
scheduleReconnect() {
|
|
2547
2663
|
if (!this.reconnectEnabled || this.reconnectTimer) return;
|
|
2548
|
-
const
|
|
2664
|
+
const delay3 = this.reconnectDelays[Math.min(this.reconnectAttempt, this.reconnectDelays.length - 1)];
|
|
2549
2665
|
this.reconnectTimer = setTimeout(() => {
|
|
2550
2666
|
this.reconnectTimer = null;
|
|
2551
2667
|
void this.connect().then((client) => {
|
|
@@ -2557,7 +2673,7 @@ var ClaudeIntercomRuntime = class {
|
|
|
2557
2673
|
this.reconnectAttempt += 1;
|
|
2558
2674
|
this.scheduleReconnect();
|
|
2559
2675
|
});
|
|
2560
|
-
},
|
|
2676
|
+
}, delay3);
|
|
2561
2677
|
this.reconnectTimer.unref?.();
|
|
2562
2678
|
}
|
|
2563
2679
|
clearReconnectTimer() {
|
|
@@ -2609,25 +2725,24 @@ var ClaudeIntercomRuntime = class {
|
|
|
2609
2725
|
this.reconnectEnabled = true;
|
|
2610
2726
|
await this.connect();
|
|
2611
2727
|
}
|
|
2612
|
-
async join(name, create = false) {
|
|
2613
|
-
const blocked = rejectManagedJoin();
|
|
2614
|
-
if (blocked) return textResult(blocked, { ok: false }, true);
|
|
2728
|
+
async join(name, create = false, members, work) {
|
|
2615
2729
|
try {
|
|
2616
|
-
if (create) {
|
|
2617
|
-
if (typeof name !== "string" || !name.trim()) {
|
|
2618
|
-
return textResult("Creating a team requires a name.", { ok: false }, true);
|
|
2619
|
-
}
|
|
2620
|
-
const team2 = createNamedTeam({ name: parseTeamName(name), managerSessionId: this.identity.sessionId });
|
|
2621
|
-
await this.switchRuntimeScope(team2.scopeId, team2.managerSessionId);
|
|
2622
|
-
return textResult(formatCreateSuccess({ team: team2.name, name: this.identity.name }), { ok: true, team: team2.name, role: "manager" });
|
|
2623
|
-
}
|
|
2624
2730
|
if (!name?.trim()) {
|
|
2731
|
+
if (create || members?.length || work !== void 0) throw new Error("Creating or extending a team requires a name");
|
|
2625
2732
|
return textResult(formatJoinableNamedTeamList(listNamedTeams()));
|
|
2626
2733
|
}
|
|
2627
|
-
const
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2734
|
+
const client = await this.connect();
|
|
2735
|
+
const sessions = await client.listSessions();
|
|
2736
|
+
const memberIds = (members ?? []).map((member) => {
|
|
2737
|
+
const id = resolveSessionTarget(sessions, member);
|
|
2738
|
+
if (!id) throw new Error(`Session "${member}" is not connected`);
|
|
2739
|
+
return id;
|
|
2740
|
+
});
|
|
2741
|
+
const team = await appendNamedTeamMembership({ name: parseTeamName(name), selfId: this.identity.sessionId, members: memberIds, create, work });
|
|
2742
|
+
const roster = namedTeamRoster(team, this.identity.sessionId, sessions);
|
|
2743
|
+
const notice = create ? formatCreateSuccess({ team: team.name, name: this.identity.name }) : formatNamedJoinSuccess({ team: team.name, name: this.identity.name });
|
|
2744
|
+
return textResult(`${notice}
|
|
2745
|
+
Other team memberships are unchanged.`, { ok: true, team: team.name, role: roster.self.isManager ? "manager" : "member", roster });
|
|
2631
2746
|
} catch (error2) {
|
|
2632
2747
|
return textResult(error2 instanceof Error ? error2.message : String(error2), { ok: false }, true);
|
|
2633
2748
|
}
|
|
@@ -2637,7 +2752,7 @@ var ClaudeIntercomRuntime = class {
|
|
|
2637
2752
|
if (waiter) {
|
|
2638
2753
|
const senderTarget = from.name || from.id;
|
|
2639
2754
|
const fromMatches = senderTarget.toLowerCase() === waiter.from.toLowerCase() || from.id === waiter.from;
|
|
2640
|
-
if (fromMatches) {
|
|
2755
|
+
if (fromMatches && message.content.team === waiter.team) {
|
|
2641
2756
|
this.replyWaiters.delete(waiter.replyTo);
|
|
2642
2757
|
clearTimeout(waiter.timeout);
|
|
2643
2758
|
waiter.cleanup?.();
|
|
@@ -2655,7 +2770,7 @@ var ClaudeIntercomRuntime = class {
|
|
|
2655
2770
|
} catch {
|
|
2656
2771
|
}
|
|
2657
2772
|
}
|
|
2658
|
-
waitForReply(from, replyTo, timeoutMs = getAskTimeoutMs(), signal) {
|
|
2773
|
+
waitForReply(from, replyTo, timeoutMs = getAskTimeoutMs(), signal, team) {
|
|
2659
2774
|
return new Promise((resolve3, reject) => {
|
|
2660
2775
|
if (signal?.aborted) {
|
|
2661
2776
|
reject(new Error("intercom_ask cancelled"));
|
|
@@ -2679,7 +2794,7 @@ var ClaudeIntercomRuntime = class {
|
|
|
2679
2794
|
reject(new Error(`No reply from "${from}" within ${Math.round(timeoutMs / 1e3)} seconds`));
|
|
2680
2795
|
}, timeoutMs);
|
|
2681
2796
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2682
|
-
this.replyWaiters.set(replyTo, { from, replyTo, resolve: resolve3, reject, timeout, cleanup });
|
|
2797
|
+
this.replyWaiters.set(replyTo, { from, replyTo, team, resolve: resolve3, reject, timeout, cleanup });
|
|
2683
2798
|
});
|
|
2684
2799
|
}
|
|
2685
2800
|
async resolveTarget(to) {
|
|
@@ -2697,9 +2812,16 @@ cwd: ${this.identity.cwd}`,
|
|
|
2697
2812
|
{ session_id: sessionId, name: this.identity.name, cwd: this.identity.cwd, model: this.identity.model }
|
|
2698
2813
|
);
|
|
2699
2814
|
}
|
|
2700
|
-
async team() {
|
|
2815
|
+
async team(name) {
|
|
2701
2816
|
const client = await this.connect();
|
|
2702
2817
|
const sessions = await client.listSessions();
|
|
2818
|
+
const mine = sessionNamedTeams(this.identity.sessionId);
|
|
2819
|
+
const selected = name ? mine.filter((entry) => entry.name === name) : mine;
|
|
2820
|
+
if (name && !selected.length) return textResult(`You do not belong to team "${name}"`, { ok: false }, true);
|
|
2821
|
+
if (selected.length) {
|
|
2822
|
+
const teams = selected.map((entry) => namedTeamRoster(entry, this.identity.sessionId, sessions));
|
|
2823
|
+
return textResult(teams.map(formatNamedTeamRoster).join("\n\n"), { teams });
|
|
2824
|
+
}
|
|
2703
2825
|
const team = await resolveIntercomTeam({ selfId: client.sessionId ?? this.identity.sessionId, sessions });
|
|
2704
2826
|
return textResult(formatIntercomTeam(team), team);
|
|
2705
2827
|
}
|
|
@@ -2751,28 +2873,34 @@ Pending asks: ${this.unresolvedAsks.size}`,
|
|
|
2751
2873
|
client.updatePresence({ status: summary.trim() || "idle" });
|
|
2752
2874
|
return textResult("Summary updated.", { ok: true, summary });
|
|
2753
2875
|
}
|
|
2754
|
-
async send(to, message, attachments, replyTo) {
|
|
2876
|
+
async send(to, message, attachments, replyTo, requestedTeam) {
|
|
2755
2877
|
const client = await this.connect();
|
|
2756
2878
|
const sendTo = await this.resolveTarget(to);
|
|
2757
|
-
const
|
|
2879
|
+
const source = replyTo ? this.unread.find((entry) => entry.message.id === replyTo && entry.from.id === sendTo) : void 0;
|
|
2880
|
+
if (replyTo && !source) throw new Error("Unknown inbound reply context");
|
|
2881
|
+
if (source && requestedTeam !== void 0 && requestedTeam !== source.message.content.team) throw new Error("Reply team must match the original message");
|
|
2882
|
+
const team = source ? source.message.content.team : resolveNamedMessageTeam(this.identity.sessionId, sendTo, requestedTeam);
|
|
2883
|
+
const result = await client.send(sendTo, { text: message, attachments, replyTo: source?.message.expectsReply ? replyTo : void 0, team });
|
|
2758
2884
|
if (!result.delivered) {
|
|
2759
2885
|
return textResult(`Message to "${to}" was not delivered: ${result.reason ?? "Session may not exist or has disconnected."}`, { ok: false, message_id: result.id, reason: result.reason }, true);
|
|
2760
2886
|
}
|
|
2761
2887
|
if (replyTo) this.unresolvedAsks.delete(replyTo);
|
|
2762
2888
|
return textResult(`Message sent to ${to}.`, { ok: true, message_id: result.id, to });
|
|
2763
2889
|
}
|
|
2764
|
-
async ask(to, message, attachments, timeoutMs = getAskTimeoutMs(), signal) {
|
|
2890
|
+
async ask(to, message, attachments, timeoutMs = getAskTimeoutMs(), signal, requestedTeam) {
|
|
2765
2891
|
const client = await this.connect();
|
|
2766
2892
|
const sendTo = await this.resolveTarget(to);
|
|
2893
|
+
const team = resolveNamedMessageTeam(this.identity.sessionId, sendTo, requestedTeam);
|
|
2767
2894
|
const questionId = randomUUID4();
|
|
2768
|
-
const replyPromise = this.waitForReply(sendTo, questionId, timeoutMs, signal);
|
|
2895
|
+
const replyPromise = this.waitForReply(sendTo, questionId, timeoutMs, signal, team);
|
|
2769
2896
|
void replyPromise.catch(() => void 0);
|
|
2770
2897
|
try {
|
|
2771
2898
|
const result = await client.send(sendTo, {
|
|
2772
2899
|
messageId: questionId,
|
|
2773
2900
|
text: message,
|
|
2774
2901
|
attachments,
|
|
2775
|
-
expectsReply: true
|
|
2902
|
+
expectsReply: true,
|
|
2903
|
+
team
|
|
2776
2904
|
});
|
|
2777
2905
|
if (!result.delivered) {
|
|
2778
2906
|
this.replyWaiters.get(questionId)?.reject(new Error(result.reason ?? "Session may not exist or has disconnected."));
|
|
@@ -2796,12 +2924,12 @@ ${replyText}`, { ok: true, message_id: result.id, reply });
|
|
|
2796
2924
|
}
|
|
2797
2925
|
const pendingAsks = Array.from(this.unresolvedAsks.values()).sort((a, b) => a.receivedAt - b.receivedAt);
|
|
2798
2926
|
const lines = [
|
|
2799
|
-
unreadMessages.length ? unreadMessages.map((entry) => `- ${formatSessionDisplay(entry.from)}: ${entry.message.content.text}${formatAttachments(entry.message.content.attachments)}`).join("\n") : "No unread messages.",
|
|
2927
|
+
unreadMessages.length ? unreadMessages.map((entry) => `- ${formatSessionDisplay(entry.from)}${replyHint(entry)}: ${entry.message.content.text}${formatAttachments(entry.message.content.attachments)}`).join("\n") : "No unread messages.",
|
|
2800
2928
|
pendingAsks.length ? `
|
|
2801
2929
|
Pending asks:
|
|
2802
2930
|
${pendingAsks.map((entry) => {
|
|
2803
2931
|
const selector = pendingSelector(pendingAsks, entry);
|
|
2804
|
-
return `- ${formatSessionDisplay(entry.from)}${selector ? ` [${selector}]` : ""}: ${entry.message.content.text}`;
|
|
2932
|
+
return `- ${formatSessionDisplay(entry.from)}${replyHint(entry)}${selector ? ` [${selector}]` : ""}: ${entry.message.content.text}`;
|
|
2805
2933
|
}).join("\n")}` : ""
|
|
2806
2934
|
].filter(Boolean);
|
|
2807
2935
|
return textResult(lines.join("\n"), {
|
|
@@ -2809,10 +2937,10 @@ ${pendingAsks.map((entry) => {
|
|
|
2809
2937
|
pending_asks: pendingAsks.map((entry) => publicPendingEntry(entry, pendingSelector(pendingAsks, entry)))
|
|
2810
2938
|
});
|
|
2811
2939
|
}
|
|
2812
|
-
async reply(message, to, which) {
|
|
2940
|
+
async reply(message, to, which, askId2, contextId2, team) {
|
|
2813
2941
|
let target;
|
|
2814
2942
|
try {
|
|
2815
|
-
target =
|
|
2943
|
+
target = selectReplyContext(this.unread, Array.from(this.unresolvedAsks.values()), { to, which, askId: askId2, contextId: contextId2, team });
|
|
2816
2944
|
} catch (error2) {
|
|
2817
2945
|
return textResult(error2 instanceof Error ? error2.message : String(error2), { ok: false }, true);
|
|
2818
2946
|
}
|
|
@@ -2824,6 +2952,15 @@ ${pendingAsks.map((entry) => {
|
|
|
2824
2952
|
}
|
|
2825
2953
|
};
|
|
2826
2954
|
|
|
2955
|
+
// claude/team-guidance.ts
|
|
2956
|
+
var TASK_TEAM_GUIDANCE = `Intercom task-team rules:
|
|
2957
|
+
- When the user delegates to named peers and this task has no approved team, ask once whether to form a team with you and those peers. Wait for approval before creating a team or adding peers. An explicit create/join request is approval; never ask again for an approved task or inbound team message.
|
|
2958
|
+
- After approval, discover the connected peers, then intercom_join({ name: "launch", create: true, members: ["front", "writer"], work: "Current task" }) adds everyone in one call. Do not ask the user to join each terminal manually.
|
|
2959
|
+
- Membership is additive: joining another team preserves previous teams. Reuse the approved team for the same task; a different task may need a different team.
|
|
2960
|
+
- Initial contact without a shared team is allowed: omit team for an ungrouped direct message. Unrelated team memberships must not prevent contact or silently create teams.
|
|
2961
|
+
- Include team on task sends/asks, especially when peers share multiple teams. Replies inherit the original message's team via askId or contextId from intercom_pending; never override it using a current team or mix contexts from different tasks.
|
|
2962
|
+
- Use intercom_send for assignments, progress/status requests, notifications and follow-ups. Use intercom_ask only when your next step genuinely depends on the answer; keep only one unresolved ask per recipient.`;
|
|
2963
|
+
|
|
2827
2964
|
// claude/mcp-protocol.ts
|
|
2828
2965
|
var inflightToolCalls = /* @__PURE__ */ new Map();
|
|
2829
2966
|
function asString(value, name) {
|
|
@@ -2832,6 +2969,14 @@ function asString(value, name) {
|
|
|
2832
2969
|
}
|
|
2833
2970
|
return value;
|
|
2834
2971
|
}
|
|
2972
|
+
function optionalString(value, name) {
|
|
2973
|
+
return value === void 0 ? void 0 : asString(value, name);
|
|
2974
|
+
}
|
|
2975
|
+
function memberArray(value) {
|
|
2976
|
+
if (value === void 0) return void 0;
|
|
2977
|
+
if (!Array.isArray(value)) throw new Error("members must be an array of session names or IDs");
|
|
2978
|
+
return Array.from(value, (member) => asString(member, "member"));
|
|
2979
|
+
}
|
|
2835
2980
|
function asBoolean(value, defaultValue) {
|
|
2836
2981
|
return typeof value === "boolean" ? value : defaultValue;
|
|
2837
2982
|
}
|
|
@@ -2882,26 +3027,30 @@ function buildToolDefinitions(runtime) {
|
|
|
2882
3027
|
},
|
|
2883
3028
|
{
|
|
2884
3029
|
name: "intercom_team",
|
|
2885
|
-
description: "Show your
|
|
2886
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
3030
|
+
description: "Show all your named task teams, or inspect one by name. Falls back to managed-team discovery.",
|
|
3031
|
+
inputSchema: { type: "object", properties: { team: { type: "string" } }, additionalProperties: false },
|
|
2887
3032
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
2888
|
-
handler: async () => runtime.team()
|
|
3033
|
+
handler: async (args) => runtime.team(optionalString(args.team, "team"))
|
|
2889
3034
|
},
|
|
2890
3035
|
{
|
|
2891
3036
|
name: "intercom_join",
|
|
2892
|
-
description: "
|
|
3037
|
+
description: "After user approval, create or join a task team. Membership is additive; members lets the manager add connected peers in one call. Omit name to list teams.",
|
|
2893
3038
|
inputSchema: {
|
|
2894
3039
|
type: "object",
|
|
2895
3040
|
properties: {
|
|
2896
3041
|
name: { type: "string", description: "Team name to join. Omit to list joinable teams." },
|
|
2897
|
-
create: { type: "boolean", description: "Create this named team and join as manager. Requires name." }
|
|
3042
|
+
create: { type: "boolean", description: "Create this named team and join as manager. Requires name." },
|
|
3043
|
+
members: { type: "array", items: { type: "string" }, description: "Connected session names or IDs to add; manager only." },
|
|
3044
|
+
work: { type: "string", maxLength: 2e3, description: "Task description when creating a team." }
|
|
2898
3045
|
},
|
|
2899
3046
|
additionalProperties: false
|
|
2900
3047
|
},
|
|
2901
3048
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
2902
3049
|
handler: async (args) => runtime.join(
|
|
2903
|
-
|
|
2904
|
-
args.create === true
|
|
3050
|
+
optionalString(args.name, "name"),
|
|
3051
|
+
args.create === true,
|
|
3052
|
+
memberArray(args.members),
|
|
3053
|
+
optionalString(args.work, "work")
|
|
2905
3054
|
)
|
|
2906
3055
|
},
|
|
2907
3056
|
{
|
|
@@ -2948,13 +3097,14 @@ function buildToolDefinitions(runtime) {
|
|
|
2948
3097
|
properties: {
|
|
2949
3098
|
to: { type: "string" },
|
|
2950
3099
|
message: { type: "string" },
|
|
2951
|
-
attachments: attachmentsSchema
|
|
3100
|
+
attachments: attachmentsSchema,
|
|
3101
|
+
team: { type: "string", description: "Task team; required when multiple teams are shared. Omit for ungrouped contact." }
|
|
2952
3102
|
},
|
|
2953
3103
|
required: ["to", "message"],
|
|
2954
3104
|
additionalProperties: false
|
|
2955
3105
|
},
|
|
2956
3106
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
2957
|
-
handler: async (args) => runtime.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments))
|
|
3107
|
+
handler: async (args) => runtime.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments), void 0, optionalString(args.team, "team"))
|
|
2958
3108
|
},
|
|
2959
3109
|
{
|
|
2960
3110
|
name: "intercom_ask",
|
|
@@ -2965,6 +3115,7 @@ function buildToolDefinitions(runtime) {
|
|
|
2965
3115
|
to: { type: "string" },
|
|
2966
3116
|
message: { type: "string" },
|
|
2967
3117
|
attachments: attachmentsSchema,
|
|
3118
|
+
team: { type: "string", description: "Task team; required when multiple teams are shared." },
|
|
2968
3119
|
timeout_ms: { type: "integer", minimum: 1, maximum: 12e4, description: "Maximum time to wait for a reply before returning an error. Use intercom_send plus intercom_pending for longer work." }
|
|
2969
3120
|
},
|
|
2970
3121
|
required: ["to", "message"],
|
|
@@ -2976,7 +3127,8 @@ function buildToolDefinitions(runtime) {
|
|
|
2976
3127
|
asString(args.message, "message"),
|
|
2977
3128
|
asAttachmentArray(args.attachments),
|
|
2978
3129
|
asOptionalPositiveInteger(args.timeout_ms, "timeout_ms"),
|
|
2979
|
-
signal
|
|
3130
|
+
signal,
|
|
3131
|
+
optionalString(args.team, "team")
|
|
2980
3132
|
)
|
|
2981
3133
|
},
|
|
2982
3134
|
{
|
|
@@ -2992,19 +3144,22 @@ function buildToolDefinitions(runtime) {
|
|
|
2992
3144
|
},
|
|
2993
3145
|
{
|
|
2994
3146
|
name: "intercom_reply",
|
|
2995
|
-
description: "Reply to
|
|
3147
|
+
description: "Reply to an inbound ask or ordinary message using askId or contextId from intercom_pending. Inherits the original team; team cannot override it.",
|
|
2996
3148
|
inputSchema: {
|
|
2997
3149
|
type: "object",
|
|
2998
3150
|
properties: {
|
|
2999
3151
|
message: { type: "string" },
|
|
3000
3152
|
to: { type: "string", description: "Optional sender/session selector; never a message or thread ID." },
|
|
3153
|
+
askId: { type: "string", description: "Stable receiver-local pending ask selector." },
|
|
3154
|
+
contextId: { type: "string", description: "Exact inbound context selector, including ordinary messages." },
|
|
3155
|
+
team: { type: "string", description: "Must match the original message; omit to inherit." },
|
|
3001
3156
|
which: { type: "string", enum: ["oldest", "latest"], description: "Select the oldest or latest ask from the chosen sender." }
|
|
3002
3157
|
},
|
|
3003
3158
|
required: ["message"],
|
|
3004
3159
|
additionalProperties: false
|
|
3005
3160
|
},
|
|
3006
3161
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
3007
|
-
handler: async (args) => runtime.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : void 0, args.which === "oldest" || args.which === "latest" ? args.which : void 0)
|
|
3162
|
+
handler: async (args) => runtime.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : void 0, args.which === "oldest" || args.which === "latest" ? args.which : void 0, optionalString(args.askId, "askId"), optionalString(args.contextId, "contextId"), optionalString(args.team, "team"))
|
|
3008
3163
|
}
|
|
3009
3164
|
];
|
|
3010
3165
|
return tools;
|
|
@@ -3035,7 +3190,8 @@ async function handleMcpRequest(request, runtime) {
|
|
|
3035
3190
|
return ok(request.id, {
|
|
3036
3191
|
protocolVersion: "2025-06-18",
|
|
3037
3192
|
capabilities: { tools: {} },
|
|
3038
|
-
|
|
3193
|
+
instructions: TASK_TEAM_GUIDANCE,
|
|
3194
|
+
serverInfo: { name: "claude-intercom", version: "0.15.0" }
|
|
3039
3195
|
});
|
|
3040
3196
|
case "ping":
|
|
3041
3197
|
return ok(request.id, {});
|
|
@@ -3072,9 +3228,9 @@ async function handleMcpRequest(request, runtime) {
|
|
|
3072
3228
|
// claude/session-lifecycle.ts
|
|
3073
3229
|
import { execFileSync } from "node:child_process";
|
|
3074
3230
|
import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync7, readSync, renameSync as renameSync3, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3075
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
3231
|
+
import { basename as basename2, dirname as dirname4, join as join9 } from "node:path";
|
|
3076
3232
|
import { StringDecoder } from "node:string_decoder";
|
|
3077
|
-
import { setTimeout as
|
|
3233
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
3078
3234
|
function findClaudeHost(pid = process.ppid) {
|
|
3079
3235
|
for (let depth = 0; pid > 1 && depth < 16; depth++) {
|
|
3080
3236
|
let parts;
|
|
@@ -3097,7 +3253,7 @@ function findClaudeHost(pid = process.ppid) {
|
|
|
3097
3253
|
return void 0;
|
|
3098
3254
|
}
|
|
3099
3255
|
function claudeSessionMetadataPath(host) {
|
|
3100
|
-
return
|
|
3256
|
+
return join9(getIntercomDirPath(), `claude-host-${host.pid}.json`);
|
|
3101
3257
|
}
|
|
3102
3258
|
function readClaudeSessionMetadata(path, host) {
|
|
3103
3259
|
try {
|
|
@@ -3183,7 +3339,7 @@ async function waitForClaudeSession(reader) {
|
|
|
3183
3339
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
3184
3340
|
const metadata = reader.read();
|
|
3185
3341
|
if (metadata) return metadata;
|
|
3186
|
-
await
|
|
3342
|
+
await delay2(50);
|
|
3187
3343
|
}
|
|
3188
3344
|
return reader.read();
|
|
3189
3345
|
}
|
package/dist/inbox-monitor.mjs
CHANGED
|
@@ -33,7 +33,8 @@ function formatInboxLine(entry) {
|
|
|
33
33
|
const who = entry.fromName ? `${entry.fromName} (${entry.fromId.slice(0, 8)})` : entry.fromId;
|
|
34
34
|
const kind = entry.expectsReply ? " [asking \u2014 awaiting your reply]" : "";
|
|
35
35
|
const oneLine = entry.text.replace(/\s+/g, " ").trim();
|
|
36
|
-
return `Intercom message from ${who}${kind}: ${oneLine}
|
|
36
|
+
return `Intercom message from ${who}${entry.team ? ` [Team: ${entry.team}]` : ""}${kind}: ${oneLine}
|
|
37
|
+
Reply with intercom_reply({ contextId: "ctx-${entry.messageId}", message: "..." }); inherit the original team.`;
|
|
37
38
|
}
|
|
38
39
|
function inboxBatchFrom(content, alreadyEmitted) {
|
|
39
40
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
package/dist/worker-daemon.mjs
CHANGED
|
@@ -1190,14 +1190,15 @@ function isMessage(value) {
|
|
|
1190
1190
|
}
|
|
1191
1191
|
if (message.replyTo !== void 0 || message.expectsReply !== void 0) return false;
|
|
1192
1192
|
}
|
|
1193
|
-
if (!hasExactDataKeys(message.content, ["text"], ["attachments"])) {
|
|
1193
|
+
if (!hasExactDataKeys(message.content, ["text"], ["attachments", "team"])) {
|
|
1194
1194
|
return false;
|
|
1195
1195
|
}
|
|
1196
1196
|
const content = message.content;
|
|
1197
1197
|
if (typeof content.text !== "string") {
|
|
1198
1198
|
return false;
|
|
1199
1199
|
}
|
|
1200
|
-
if (
|
|
1200
|
+
if (content.team !== void 0 && (typeof content.team !== "string" || !/^[A-Za-z][A-Za-z0-9_-]{0,31}$/.test(content.team))) return false;
|
|
1201
|
+
if (message.control !== void 0 && (content.text !== "" || content.attachments !== void 0 || content.team !== void 0)) return false;
|
|
1201
1202
|
return content.attachments === void 0 || isDenseArrayOf(content.attachments, isAttachment);
|
|
1202
1203
|
}
|
|
1203
1204
|
function isSessionInfo(value) {
|
|
@@ -1820,6 +1821,7 @@ var IntercomClient = class extends EventEmitter {
|
|
|
1820
1821
|
...options.control === void 0 ? {} : { control: options.control },
|
|
1821
1822
|
content: {
|
|
1822
1823
|
text: options.text,
|
|
1824
|
+
...options.team === void 0 ? {} : { team: options.team },
|
|
1823
1825
|
...options.attachments === void 0 ? {} : { attachments: options.attachments }
|
|
1824
1826
|
}
|
|
1825
1827
|
};
|
|
@@ -2479,6 +2481,7 @@ ${agent.instructions}` : "";
|
|
|
2479
2481
|
return [
|
|
2480
2482
|
`Intercom message for ${agent.name}.`,
|
|
2481
2483
|
`From: ${formatSessionDisplay(from)} (${from.id})`,
|
|
2484
|
+
...message.content.team ? [`[Team: ${message.content.team}]`] : [],
|
|
2482
2485
|
`Message id: ${message.id}`,
|
|
2483
2486
|
"",
|
|
2484
2487
|
message.content.text,
|
|
@@ -2631,7 +2634,8 @@ var VirtualClaudeAgent = class {
|
|
|
2631
2634
|
if (message.expectsReply) {
|
|
2632
2635
|
void this.client.send(from.id, {
|
|
2633
2636
|
text: "This worker is rate limited (too many wakes this minute). Please try again shortly.",
|
|
2634
|
-
replyTo: message.id
|
|
2637
|
+
replyTo: message.id,
|
|
2638
|
+
...message.content.team === void 0 ? {} : { team: message.content.team }
|
|
2635
2639
|
}).catch((error) => {
|
|
2636
2640
|
process.stderr.write(`worker ${this.agent.id}: rate-limit reply failed: ${error instanceof Error ? error.message : String(error)}
|
|
2637
2641
|
`);
|
|
@@ -2693,7 +2697,8 @@ var VirtualClaudeAgent = class {
|
|
|
2693
2697
|
if (message.expectsReply) {
|
|
2694
2698
|
await this.client.send(from.id, {
|
|
2695
2699
|
text: result.result || "(the worker finished without a final message)",
|
|
2696
|
-
replyTo: message.id
|
|
2700
|
+
replyTo: message.id,
|
|
2701
|
+
...message.content.team === void 0 ? {} : { team: message.content.team }
|
|
2697
2702
|
});
|
|
2698
2703
|
process.stderr.write(`worker ${this.agent.id}: replied to ${from.name || from.id} (session ${this.sessionId ?? "none"})
|
|
2699
2704
|
`);
|
|
@@ -2705,7 +2710,8 @@ var VirtualClaudeAgent = class {
|
|
|
2705
2710
|
if (message.expectsReply) {
|
|
2706
2711
|
await this.client.send(from.id, {
|
|
2707
2712
|
text: `Worker error: ${text}`,
|
|
2708
|
-
replyTo: message.id
|
|
2713
|
+
replyTo: message.id,
|
|
2714
|
+
...message.content.team === void 0 ? {} : { team: message.content.team }
|
|
2709
2715
|
}).catch((sendError) => {
|
|
2710
2716
|
process.stderr.write(`worker ${this.agent.id}: error reply failed: ${sendError instanceof Error ? sendError.message : String(sendError)}
|
|
2711
2717
|
`);
|