agent-office 0.0.13 → 0.0.15

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.
@@ -59,11 +59,12 @@ function renderMessage(msg, humanName, spacingClass) {
59
59
  const isMine = msg.from_name === humanName;
60
60
  const bubbleClass = isMine ? "bubble bubble-mine" : "bubble bubble-theirs";
61
61
  const wrapClass = `msg-wrap ${isMine ? "msg-wrap-mine" : "msg-wrap-theirs"} ${spacingClass}`;
62
- const bodyHtml = escapeHtml(msg.body).replace(/\n/g, "<br>");
62
+ // Base64 encode to preserve newlines and special chars reliably
63
+ const bodyEncoded = Buffer.from(msg.body).toString('base64');
63
64
  const unreadDot = !isMine && !msg.read ? `<span class="unread-dot"></span>` : "";
64
65
  return `<div class="${wrapClass}" data-id="${msg.id}">
65
66
  <div class="${bubbleClass}">
66
- <div class="bubble-body">${bodyHtml}</div>
67
+ <div class="bubble-body markdown-body" data-markdown-b64="${bodyEncoded}"></div>
67
68
  <div class="bubble-time">${unreadDot}${formatTime(msg.created_at)}</div>
68
69
  </div>
69
70
  </div>`;
@@ -91,6 +92,9 @@ function renderPage(coworker, msgs, humanName) {
91
92
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
92
93
  <title>${escapeHtml(coworker)} — agent-office</title>
93
94
  <script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script>
95
+ <script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script>
96
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.5.1/github-markdown-light.min.css" media="(prefers-color-scheme: light)">
97
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.5.1/github-markdown-dark.min.css" media="(prefers-color-scheme: dark)">
94
98
  <style>
95
99
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
96
100
 
@@ -264,6 +268,66 @@ function renderPage(coworker, msgs, humanName) {
264
268
  }
265
269
 
266
270
  .bubble-body { font-size: 14.5px; }
271
+ .bubble-body.markdown-body {
272
+ background: transparent;
273
+ color: inherit;
274
+ font-size: 14px;
275
+ line-height: 1.5;
276
+ }
277
+ .bubble-body.markdown-body p { margin: 0 0 8px 0; }
278
+ .bubble-body.markdown-body p:last-child { margin-bottom: 0; }
279
+ .bubble-body.markdown-body pre {
280
+ background: rgba(0,0,0,0.3);
281
+ border-radius: 6px;
282
+ padding: 8px 12px;
283
+ overflow-x: auto;
284
+ margin: 8px 0;
285
+ }
286
+ .bubble-body.markdown-body code {
287
+ background: rgba(0,0,0,0.2);
288
+ padding: 2px 5px;
289
+ border-radius: 3px;
290
+ font-size: 13px;
291
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
292
+ }
293
+ .bubble-body.markdown-body pre code {
294
+ background: transparent;
295
+ padding: 0;
296
+ }
297
+ .bubble-body.markdown-body ul, .bubble-body.markdown-body ol {
298
+ margin: 8px 0;
299
+ padding-left: 20px;
300
+ }
301
+ .bubble-body.markdown-body li { margin: 4px 0; }
302
+ .bubble-body.markdown-body blockquote {
303
+ border-left: 3px solid var(--accent);
304
+ margin: 8px 0;
305
+ padding-left: 12px;
306
+ color: var(--text-dim);
307
+ }
308
+ .bubble-body.markdown-body h1, .bubble-body.markdown-body h2,
309
+ .bubble-body.markdown-body h3, .bubble-body.markdown-body h4 {
310
+ margin: 12px 0 8px 0;
311
+ font-size: 15px;
312
+ font-weight: 600;
313
+ }
314
+ .bubble-body.markdown-body table {
315
+ border-collapse: collapse;
316
+ margin: 8px 0;
317
+ font-size: 13px;
318
+ }
319
+ .bubble-body.markdown-body th, .bubble-body.markdown-body td {
320
+ border: 1px solid var(--border);
321
+ padding: 6px 10px;
322
+ }
323
+ .bubble-body.markdown-body th {
324
+ background: var(--surface2);
325
+ }
326
+ .bubble-body.markdown-body strong,
327
+ .bubble-body.markdown-body b {
328
+ color: #ffffff;
329
+ font-weight: 600;
330
+ }
267
331
  .bubble-time {
268
332
  font-size: 10px;
269
333
  color: var(--text-dim);
@@ -498,6 +562,28 @@ function renderPage(coworker, msgs, humanName) {
498
562
  clearTimeout(el._hideTimer)
499
563
  el._hideTimer = setTimeout(() => el.classList.remove('visible'), 3000)
500
564
  }
565
+
566
+ // Render markdown in chat bubbles
567
+ function renderMarkdown() {
568
+ if (typeof marked === 'undefined') return
569
+ document.querySelectorAll('.markdown-body[data-markdown-b64]').forEach(el => {
570
+ const b64 = el.getAttribute('data-markdown-b64')
571
+ if (b64 && !el.hasAttribute('data-rendered')) {
572
+ // Decode base64 to get original text with preserved newlines
573
+ const text = atob(b64)
574
+ el.innerHTML = marked.parse(text)
575
+ el.setAttribute('data-rendered', 'true')
576
+ }
577
+ })
578
+ }
579
+
580
+ // Initial render
581
+ renderMarkdown()
582
+
583
+ // Re-render after HTMX swaps new content
584
+ document.addEventListener('htmx:afterSwap', () => {
585
+ renderMarkdown()
586
+ })
501
587
  </script>
502
588
  </body>
503
589
  </html>`;
@@ -609,10 +695,19 @@ export async function communicatorWeb(coworker, options) {
609
695
  app.get("/ping", (_req, res) => {
610
696
  res.status(204).end();
611
697
  });
612
- app.listen(port, host, () => {
698
+ const server = app.listen(port, host, () => {
613
699
  console.log(`Communicator running at http://${host}:${port}`);
614
700
  console.log(`Press Ctrl+C to stop.`);
615
701
  });
702
+ server.on('error', (err) => {
703
+ if (err.code === 'EADDRINUSE') {
704
+ console.error(`Error: Port ${port} is already in use. Is another instance running?`);
705
+ }
706
+ else {
707
+ console.error(`Error: ${err.message}`);
708
+ }
709
+ process.exit(1);
710
+ });
616
711
  // Keep process alive
617
712
  await new Promise(() => { });
618
713
  }
@@ -6,7 +6,7 @@ export interface SessionRow {
6
6
  name: string;
7
7
  session_id: string;
8
8
  agent_code: string;
9
- agent: string | null;
9
+ agent: string;
10
10
  status: string | null;
11
11
  created_at: Date;
12
12
  }
@@ -35,9 +35,9 @@ export interface AgenticCodingServer {
35
35
  * Inject a text message into a session (fire-and-forget).
36
36
  * The server processes the message asynchronously.
37
37
  *
38
- * @param agent optional agent-mode identifier to route the prompt.
38
+ * @param agent agent-mode identifier to route the prompt.
39
39
  */
40
- sendMessage(sessionID: string, text: string, agent?: string): Promise<void>;
40
+ sendMessage(sessionID: string, text: string, agent: string): Promise<void>;
41
41
  /**
42
42
  * Retrieve the message history for a session.
43
43
  *
@@ -4,7 +4,7 @@ export declare class OpenCodeCodingServer implements AgenticCodingServer {
4
4
  constructor(baseURL: string);
5
5
  createSession(): Promise<string>;
6
6
  deleteSession(sessionID: string): Promise<void>;
7
- sendMessage(sessionID: string, text: string, agent?: string): Promise<void>;
7
+ sendMessage(sessionID: string, text: string, agent: string): Promise<void>;
8
8
  getMessages(sessionID: string, limit?: number): Promise<SessionMessage[]>;
9
9
  revertSession(sessionID: string, messageID: string): Promise<void>;
10
10
  getAgentModes(): Promise<AgentMode[]>;
@@ -22,7 +22,7 @@ export class OpenCodeCodingServer {
22
22
  await this.client.session.promptAsync({
23
23
  sessionID,
24
24
  parts: [{ type: "text", text }],
25
- ...(agent ? { agent } : {}),
25
+ agent,
26
26
  });
27
27
  }
28
28
  async getMessages(sessionID, limit) {
@@ -1,7 +1,8 @@
1
1
  interface CreateSessionProps {
2
2
  serverUrl: string;
3
3
  password: string;
4
+ agent: string;
4
5
  onBack: () => void;
5
6
  }
6
- export declare function CreateSession({ serverUrl, password, onBack }: CreateSessionProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function CreateSession({ serverUrl, password, agent, onBack }: CreateSessionProps): import("react/jsx-runtime").JSX.Element;
7
8
  export {};
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { TextInput, Spinner } from "@inkjs/ui";
5
5
  import { useApi, useAsyncState } from "../hooks/useApi.js";
6
- export function CreateSession({ serverUrl, password, onBack }) {
6
+ export function CreateSession({ serverUrl, password, agent, onBack }) {
7
7
  const { createSession } = useApi(serverUrl, password);
8
8
  const { loading, error, run } = useAsyncState();
9
9
  const [created, setCreated] = useState(null);
@@ -20,7 +20,7 @@ export function CreateSession({ serverUrl, password, onBack }) {
20
20
  if (!trimmed)
21
21
  return;
22
22
  setSubmitted(true);
23
- const result = await run(() => createSession(trimmed));
23
+ const result = await run(() => createSession(trimmed, agent));
24
24
  if (result)
25
25
  setCreated(result);
26
26
  };
@@ -474,7 +474,7 @@ function MemoryView({ serverUrl, password, sessionName, contentHeight, onClose }
474
474
  }
475
475
  // ─── Main component ──────────────────────────────────────────────────────────
476
476
  export function SessionList({ serverUrl, password, contentHeight, onSubViewChange }) {
477
- const { listSessions, createSession, deleteSession, regenerateCode, getModes, revertToStart, revertAll } = useApi(serverUrl, password);
477
+ const { listSessions, createSession, deleteSession, updateSessionAgent, regenerateCode, getModes, revertToStart, revertAll } = useApi(serverUrl, password);
478
478
  const { data: sessions, loading, error: loadError, run } = useAsyncState();
479
479
  const [cursor, setCursor] = useState(0);
480
480
  const [revealedRows, setRevealedRows] = useState(new Set());
@@ -554,6 +554,26 @@ export function SessionList({ serverUrl, password, contentHeight, onSubViewChang
554
554
  setActionMsg(null);
555
555
  setMode("confirm-revert-all");
556
556
  }
557
+ if (input === "a" && rows.length > 0) {
558
+ setActionError(null);
559
+ setActionMsg(null);
560
+ setModeCursor(0);
561
+ setMode("changing-agent-loading");
562
+ getModes().then((modes) => {
563
+ const safeMode = Array.isArray(modes) ? modes : [];
564
+ setAvailableModes(safeMode);
565
+ if (safeMode.length > 0) {
566
+ setMode("changing-agent-pick");
567
+ }
568
+ else {
569
+ setActionError("No agent modes available");
570
+ setMode("create-error");
571
+ }
572
+ }).catch(() => {
573
+ setActionError("Failed to fetch agent modes");
574
+ setMode("create-error");
575
+ });
576
+ }
557
577
  if (rows.length > 0) {
558
578
  if (input === "t")
559
579
  setSubView("tail");
@@ -582,6 +602,23 @@ export function SessionList({ serverUrl, password, contentHeight, onSubViewChang
582
602
  }
583
603
  return;
584
604
  }
605
+ if (mode === "changing-agent-pick") {
606
+ const total = availableModes.length;
607
+ if (key.upArrow)
608
+ setModeCursor((c) => (c - 1 + total) % total);
609
+ if (key.downArrow)
610
+ setModeCursor((c) => (c + 1) % total);
611
+ if (key.return) {
612
+ const selected = availableModes[modeCursor]?.name;
613
+ if (selected && rows[cursor]) {
614
+ void handleChangeAgent(rows[cursor].name, selected);
615
+ }
616
+ }
617
+ if (key.escape) {
618
+ setMode("browse");
619
+ }
620
+ return;
621
+ }
585
622
  if (mode === "creating-name" && key.escape) {
586
623
  setMode("browse");
587
624
  return;
@@ -601,9 +638,14 @@ export function SessionList({ serverUrl, password, contentHeight, onSubViewChang
601
638
  const trimmed = name.trim();
602
639
  if (!trimmed)
603
640
  return;
641
+ if (!pendingAgent) {
642
+ setActionError("Agent mode is required");
643
+ setMode("create-error");
644
+ return;
645
+ }
604
646
  setMode("creating-busy");
605
647
  try {
606
- await createSession(trimmed, pendingAgent ?? undefined);
648
+ await createSession(trimmed, pendingAgent);
607
649
  const modeNote = pendingAgent ? ` [${pendingAgent}]` : "";
608
650
  setActionMsg(`Coworker "${trimmed}"${modeNote} created.`);
609
651
  setMode("create-done");
@@ -614,6 +656,19 @@ export function SessionList({ serverUrl, password, contentHeight, onSubViewChang
614
656
  setMode("create-error");
615
657
  }
616
658
  };
659
+ const handleChangeAgent = async (sessionName, agent) => {
660
+ setMode("changing-agent-busy");
661
+ try {
662
+ await updateSessionAgent(sessionName, agent);
663
+ setActionMsg(`Agent for "${sessionName}" changed to [${agent}].`);
664
+ setMode("create-done");
665
+ reload();
666
+ }
667
+ catch (err) {
668
+ setActionError(err instanceof Error ? err.message : String(err));
669
+ setMode("create-error");
670
+ }
671
+ };
617
672
  const handleConfirmDelete = async (confirmed) => {
618
673
  if (!confirmed) {
619
674
  setMode("browse");
@@ -709,6 +764,20 @@ export function SessionList({ serverUrl, password, contentHeight, onSubViewChang
709
764
  setMode("create-error");
710
765
  }
711
766
  };
767
+ // ── Full-screen change-agent flow ──────────────────────────────────────────
768
+ if (mode === "changing-agent-loading") {
769
+ return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading agent configs..." }) }));
770
+ }
771
+ if (mode === "changing-agent-pick") {
772
+ const target = rows[cursor];
773
+ return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Change Agent" }), _jsxs(Text, { dimColor: true, children: ["for \"", target?.name, "\""] }), target?.agent ? _jsxs(Text, { color: "yellow", children: ["[current: ", target.agent, "]"] }) : null] }), _jsx(Box, { flexDirection: "column", children: availableModes.map((m, i) => {
774
+ const sel = i === modeCursor;
775
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: sel ? "▶" : " " }), _jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: m.name }), m.description ? _jsxs(Text, { dimColor: true, children: ["\u2014 ", m.description] }) : null] }, i));
776
+ }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u00B7 Enter select \u00B7 Esc cancel" }) })] }));
777
+ }
778
+ if (mode === "changing-agent-busy") {
779
+ return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Changing agent..." }) }));
780
+ }
712
781
  // ── Full-screen create flow ───────────────────────────────────────────────
713
782
  if (mode === "creating-loading") {
714
783
  return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading agent configs..." }) }));
@@ -64,8 +64,9 @@ export interface MemoryRecord {
64
64
  }
65
65
  export declare function useApi(serverUrl: string, password: string): {
66
66
  listSessions: () => Promise<Session[]>;
67
- createSession: (name: string, agent?: string) => Promise<Session>;
67
+ createSession: (name: string, agent: string) => Promise<Session>;
68
68
  deleteSession: (name: string) => Promise<void>;
69
+ updateSessionAgent: (name: string, agent: string) => Promise<Session>;
69
70
  checkHealth: () => Promise<boolean>;
70
71
  getMessages: (name: string, limit?: number) => Promise<SessionMessage[]>;
71
72
  injectText: (name: string, text: string) => Promise<{
@@ -31,7 +31,7 @@ export function useApi(serverUrl, password) {
31
31
  const createSession = useCallback(async (name, agent) => {
32
32
  return apiFetch(`${base}/sessions`, password, {
33
33
  method: "POST",
34
- body: JSON.stringify({ name, ...(agent ? { agent } : {}) }),
34
+ body: JSON.stringify({ name, agent }),
35
35
  });
36
36
  }, [base, password]);
37
37
  const getModes = useCallback(async () => {
@@ -42,6 +42,12 @@ export function useApi(serverUrl, password) {
42
42
  method: "DELETE",
43
43
  });
44
44
  }, [base, password]);
45
+ const updateSessionAgent = useCallback(async (name, agent) => {
46
+ return apiFetch(`${base}/sessions/${encodeURIComponent(name)}/agent`, password, {
47
+ method: "PATCH",
48
+ body: JSON.stringify({ agent }),
49
+ });
50
+ }, [base, password]);
45
51
  const checkHealth = useCallback(async () => {
46
52
  try {
47
53
  await apiFetch(`${base}/health`, password);
@@ -131,6 +137,7 @@ export function useApi(serverUrl, password) {
131
137
  listSessions,
132
138
  createSession,
133
139
  deleteSession,
140
+ updateSessionAgent,
134
141
  checkHealth,
135
142
  getMessages,
136
143
  injectText,
@@ -65,7 +65,7 @@ export class CronScheduler {
65
65
  throw new Error(`Session "${job.session_name}" not found`);
66
66
  }
67
67
  const injectText = `[Cron Job "${job.name}" — ${executedAt.toISOString()}]\n${job.message}${CRON_INJECTION_BLURB}`;
68
- await this.agenticCodingServer.sendMessage(session.session_id, injectText, session.agent ?? undefined);
68
+ await this.agenticCodingServer.sendMessage(session.session_id, injectText, session.agent);
69
69
  await this.sql `
70
70
  UPDATE cron_jobs SET last_run = ${executedAt} WHERE id = ${job.id}
71
71
  `;
@@ -164,8 +164,12 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
164
164
  res.status(400).json({ error: "name is required" });
165
165
  return;
166
166
  }
167
+ if (!agentArg || typeof agentArg !== "string" || !agentArg.trim()) {
168
+ res.status(400).json({ error: "agent is required" });
169
+ return;
170
+ }
167
171
  const trimmedName = name.trim();
168
- const trimmedAgent = typeof agentArg === "string" && agentArg.trim() ? agentArg.trim() : null;
172
+ const trimmedAgent = agentArg.trim();
169
173
  const existing = await sql `
170
174
  SELECT id FROM sessions WHERE name = ${trimmedName}
171
175
  `;
@@ -203,7 +207,7 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
203
207
  try {
204
208
  const clockInToken = `${row.agent_code}@${serverUrl}`;
205
209
  const enrollmentMessage = generateEnrollmentMessage(clockInToken);
206
- await agenticCodingServer.sendMessage(opencodeSessionId, enrollmentMessage, trimmedAgent ?? undefined);
210
+ await agenticCodingServer.sendMessage(opencodeSessionId, enrollmentMessage, trimmedAgent);
207
211
  }
208
212
  catch (err) {
209
213
  console.warn("Warning: could not send first message to session:", err);
@@ -233,6 +237,35 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
233
237
  res.status(500).json({ error: "Internal server error" });
234
238
  }
235
239
  });
240
+ router.patch("/sessions/:name/agent", async (req, res) => {
241
+ const { name } = req.params;
242
+ const { agent } = req.body;
243
+ if (!agent || typeof agent !== "string" || !agent.trim()) {
244
+ res.status(400).json({ error: "agent is required" });
245
+ return;
246
+ }
247
+ const trimmedAgent = agent.trim();
248
+ const rows = await sql `
249
+ SELECT id FROM sessions WHERE name = ${name}
250
+ `;
251
+ if (rows.length === 0) {
252
+ res.status(404).json({ error: `Session "${name}" not found` });
253
+ return;
254
+ }
255
+ try {
256
+ const [updated] = await sql `
257
+ UPDATE sessions
258
+ SET agent = ${trimmedAgent}
259
+ WHERE name = ${name}
260
+ RETURNING id, name, session_id, agent_code, agent, created_at
261
+ `;
262
+ res.json(updated);
263
+ }
264
+ catch (err) {
265
+ console.error("PATCH /sessions/:name/agent error:", err);
266
+ res.status(500).json({ error: "Internal server error" });
267
+ }
268
+ });
236
269
  router.get("/sessions/:name/messages", async (req, res) => {
237
270
  const { name } = req.params;
238
271
  const limit = Math.min(parseInt(req.query.limit ?? "20", 10), 100);
@@ -286,7 +319,7 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
286
319
  return;
287
320
  }
288
321
  const rows = await sql `
289
- SELECT id, name, session_id, agent_code, created_at FROM sessions WHERE name = ${name}
322
+ SELECT id, name, session_id, agent_code, agent, created_at FROM sessions WHERE name = ${name}
290
323
  `;
291
324
  if (rows.length === 0) {
292
325
  res.status(404).json({ error: `Session "${name}" not found` });
@@ -294,7 +327,7 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
294
327
  }
295
328
  const row = rows[0];
296
329
  try {
297
- await agenticCodingServer.sendMessage(row.session_id, text.trim());
330
+ await agenticCodingServer.sendMessage(row.session_id, text.trim(), row.agent);
298
331
  res.json({ ok: true });
299
332
  }
300
333
  catch (err) {
@@ -482,8 +515,8 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
482
515
  }
483
516
  const trimmedFrom = from.trim();
484
517
  const trimmedBody = body.trim();
485
- const sessions = await sql `SELECT name, session_id FROM sessions`;
486
- const sessionMap = new Map(sessions.map((s) => [s.name, s.session_id]));
518
+ const sessions = await sql `SELECT name, session_id, agent FROM sessions`;
519
+ const sessionMap = new Map(sessions.map((s) => [s.name, { sessionId: s.session_id, agent: s.agent }]));
487
520
  const validRecipients = [];
488
521
  for (const recipient of to) {
489
522
  if (typeof recipient !== "string" || !recipient.trim())
@@ -509,10 +542,10 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
509
542
  const msgId = msgRow.id;
510
543
  let injected = false;
511
544
  if (sessionMap.has(recipient)) {
512
- const sessionId = sessionMap.get(recipient);
545
+ const { sessionId, agent } = sessionMap.get(recipient);
513
546
  const injectText = `[Message from "${trimmedFrom}"]: ${trimmedBody}${MAIL_INJECTION_BLURB}`;
514
547
  try {
515
- await agenticCodingServer.sendMessage(sessionId, injectText);
548
+ await agenticCodingServer.sendMessage(sessionId, injectText, agent ?? undefined);
516
549
  await sql `UPDATE messages SET injected = TRUE WHERE id = ${msgId}`;
517
550
  injected = true;
518
551
  }
@@ -1043,8 +1076,8 @@ export function createWorkerRouter(sql, agenticCodingServer, serverUrl, memoryMa
1043
1076
  return;
1044
1077
  }
1045
1078
  const trimmedBody = body.trim();
1046
- const sessions = await sql `SELECT name, session_id FROM sessions`;
1047
- const sessionMap = new Map(sessions.map((s) => [s.name, s.session_id]));
1079
+ const sessions = await sql `SELECT name, session_id, agent FROM sessions`;
1080
+ const sessionMap = new Map(sessions.map((s) => [s.name, { sessionId: s.session_id, agent: s.agent }]));
1048
1081
  const config = await sql `SELECT value FROM config WHERE key = 'human_name'`;
1049
1082
  const humanName = config[0]?.value ?? "Human";
1050
1083
  const validRecipients = [];
@@ -1070,10 +1103,10 @@ export function createWorkerRouter(sql, agenticCodingServer, serverUrl, memoryMa
1070
1103
  const msgId = msgRow.id;
1071
1104
  let injected = false;
1072
1105
  if (sessionMap.has(recipient)) {
1073
- const recipientSessionId = sessionMap.get(recipient);
1106
+ const { sessionId: recipientSessionId, agent: recipientAgent } = sessionMap.get(recipient);
1074
1107
  const injectText = `[Message from "${session.name}"]: ${trimmedBody}${MAIL_INJECTION_BLURB}`;
1075
1108
  try {
1076
- await agenticCodingServer.sendMessage(recipientSessionId, injectText);
1109
+ await agenticCodingServer.sendMessage(recipientSessionId, injectText, recipientAgent ?? undefined);
1077
1110
  await sql `UPDATE messages SET injected = TRUE WHERE id = ${msgId}`;
1078
1111
  injected = true;
1079
1112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-office",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "An office for your AI agents",
5
5
  "type": "module",
6
6
  "license": "MIT",