agent-office 0.0.14 → 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.
- package/dist/db/index.d.ts +1 -1
- package/dist/lib/agentic-coding-server.d.ts +2 -2
- package/dist/lib/opencode-coding-server.d.ts +1 -1
- package/dist/lib/opencode-coding-server.js +1 -1
- package/dist/manage/components/CreateSession.d.ts +2 -1
- package/dist/manage/components/CreateSession.js +2 -2
- package/dist/manage/components/SessionList.js +71 -2
- package/dist/manage/hooks/useApi.d.ts +2 -1
- package/dist/manage/hooks/useApi.js +8 -1
- package/dist/server/cron.js +1 -1
- package/dist/server/routes.js +45 -12
- package/package.json +1 -1
package/dist/db/index.d.ts
CHANGED
|
@@ -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
|
|
38
|
+
* @param agent agent-mode identifier to route the prompt.
|
|
39
39
|
*/
|
|
40
|
-
sendMessage(sessionID: string, text: string, agent
|
|
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
|
|
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[]>;
|
|
@@ -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
|
|
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
|
|
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,
|
|
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,
|
package/dist/server/cron.js
CHANGED
|
@@ -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
|
|
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
|
`;
|
package/dist/server/routes.js
CHANGED
|
@@ -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 =
|
|
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
|
|
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
|
}
|