agent-office 0.0.14 → 0.0.16
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/cli.js +0 -8
- package/dist/commands/serve.js +1 -1
- package/dist/commands/worker.d.ts +0 -1
- package/dist/commands/worker.js +0 -29
- package/dist/db/index.d.ts +1 -1
- package/dist/lib/agentic-coding-server.d.ts +5 -2
- package/dist/lib/opencode-coding-server.d.ts +1 -1
- package/dist/lib/opencode-coding-server.js +3 -2
- 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.d.ts +2 -1
- package/dist/server/cron.js +12 -3
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.js +83 -59
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50,14 +50,6 @@ communicatorCmd
|
|
|
50
50
|
const workerCmd = program
|
|
51
51
|
.command("worker")
|
|
52
52
|
.description("Worker agent commands");
|
|
53
|
-
workerCmd
|
|
54
|
-
.command("clock-in")
|
|
55
|
-
.description("Clock in as a worker agent")
|
|
56
|
-
.argument("<token>", "Agent token in the format <agent_code>@<server-url>")
|
|
57
|
-
.action(async (token) => {
|
|
58
|
-
const { clockIn } = await import("./commands/worker.js");
|
|
59
|
-
await clockIn(token);
|
|
60
|
-
});
|
|
61
53
|
workerCmd
|
|
62
54
|
.command("list-coworkers")
|
|
63
55
|
.description("List all other workers (coworkers)")
|
package/dist/commands/serve.js
CHANGED
|
@@ -57,7 +57,7 @@ export async function serve(options) {
|
|
|
57
57
|
// Create Express app
|
|
58
58
|
const app = createApp(sql, agenticCodingServer, password, serverUrl, cronScheduler, memoryManager);
|
|
59
59
|
// Start cron scheduler
|
|
60
|
-
await cronScheduler.start(sql, agenticCodingServer);
|
|
60
|
+
await cronScheduler.start(sql, agenticCodingServer, serverUrl);
|
|
61
61
|
// Start server
|
|
62
62
|
const server = app.listen(port, options.host, () => {
|
|
63
63
|
console.log(`agent-office server listening on ${serverUrl}`);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function clockIn(token: string): Promise<void>;
|
|
2
1
|
export declare function listCoworkers(token: string): Promise<void>;
|
|
3
2
|
export declare function setStatus(token: string, status: string | null): Promise<void>;
|
|
4
3
|
export declare function sendMessage(token: string, recipients: string[], body: string): Promise<void>;
|
package/dist/commands/worker.js
CHANGED
|
@@ -79,35 +79,6 @@ async function postWorker(token, endpoint, payload) {
|
|
|
79
79
|
}
|
|
80
80
|
return body;
|
|
81
81
|
}
|
|
82
|
-
export async function clockIn(token) {
|
|
83
|
-
const { agentCode, serverUrl } = parseToken(token);
|
|
84
|
-
const parsedUrl = new URL(serverUrl);
|
|
85
|
-
const clockInUrl = `${parsedUrl.origin}/worker/clock-in?code=${encodeURIComponent(agentCode)}`;
|
|
86
|
-
let res;
|
|
87
|
-
try {
|
|
88
|
-
res = await fetch(clockInUrl);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
console.error(`Error: could not reach ${parsedUrl.origin}`);
|
|
92
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
let body;
|
|
96
|
-
try {
|
|
97
|
-
body = await res.json();
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
console.error(`Error: invalid response from server`);
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
if (!res.ok) {
|
|
104
|
-
const msg = body.error ?? `HTTP ${res.status}`;
|
|
105
|
-
console.error(`Error: ${msg}`);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
const { message } = body;
|
|
109
|
-
console.log(message);
|
|
110
|
-
}
|
|
111
82
|
export async function listCoworkers(token) {
|
|
112
83
|
const workers = await fetchWorker(token, "/worker/list-coworkers");
|
|
113
84
|
console.log(JSON.stringify(workers, null, 2));
|
package/dist/db/index.d.ts
CHANGED
|
@@ -35,9 +35,12 @@ 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
|
+
* @param system system prompt appended after the agent's built-in system
|
|
40
|
+
* prompt. Always pass the worker briefing here so the agent
|
|
41
|
+
* has persistent identity/context on every message turn.
|
|
39
42
|
*/
|
|
40
|
-
sendMessage(sessionID: string, text: string, agent
|
|
43
|
+
sendMessage(sessionID: string, text: string, agent: string, system: string): Promise<void>;
|
|
41
44
|
/**
|
|
42
45
|
* Retrieve the message history for a session.
|
|
43
46
|
*
|
|
@@ -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, system: 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[]>;
|
|
@@ -18,11 +18,12 @@ export class OpenCodeCodingServer {
|
|
|
18
18
|
async deleteSession(sessionID) {
|
|
19
19
|
await this.client.session.delete({ sessionID });
|
|
20
20
|
}
|
|
21
|
-
async sendMessage(sessionID, text, agent) {
|
|
21
|
+
async sendMessage(sessionID, text, agent, system) {
|
|
22
22
|
await this.client.session.promptAsync({
|
|
23
23
|
sessionID,
|
|
24
24
|
parts: [{ type: "text", text }],
|
|
25
|
-
|
|
25
|
+
agent,
|
|
26
|
+
system,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
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
|
|
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.d.ts
CHANGED
|
@@ -9,9 +9,10 @@ export declare class CronScheduler {
|
|
|
9
9
|
private activeJobs;
|
|
10
10
|
private sql;
|
|
11
11
|
private agenticCodingServer;
|
|
12
|
+
private serverUrl;
|
|
12
13
|
private started;
|
|
13
14
|
constructor(options?: CronSchedulerOptions);
|
|
14
|
-
start(sql: Sql, agenticCodingServer: AgenticCodingServer): Promise<void>;
|
|
15
|
+
start(sql: Sql, agenticCodingServer: AgenticCodingServer, serverUrl: string): Promise<void>;
|
|
15
16
|
stop(): void;
|
|
16
17
|
private addJob;
|
|
17
18
|
private executeJob;
|
package/dist/server/cron.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Cron } from "croner";
|
|
2
|
+
import { generateSystemPrompt } from "./routes.js";
|
|
2
3
|
const CRON_INJECTION_BLURB = [
|
|
3
4
|
``,
|
|
4
5
|
`---`,
|
|
@@ -10,15 +11,17 @@ export class CronScheduler {
|
|
|
10
11
|
activeJobs = new Map();
|
|
11
12
|
sql = null;
|
|
12
13
|
agenticCodingServer = null;
|
|
14
|
+
serverUrl = "";
|
|
13
15
|
started = false;
|
|
14
16
|
constructor(options = {}) {
|
|
15
17
|
this.options = options;
|
|
16
18
|
}
|
|
17
|
-
async start(sql, agenticCodingServer) {
|
|
19
|
+
async start(sql, agenticCodingServer, serverUrl) {
|
|
18
20
|
if (this.started)
|
|
19
21
|
return;
|
|
20
22
|
this.sql = sql;
|
|
21
23
|
this.agenticCodingServer = agenticCodingServer;
|
|
24
|
+
this.serverUrl = serverUrl;
|
|
22
25
|
const rows = await sql `
|
|
23
26
|
SELECT id, name, schedule, timezone, message, session_name
|
|
24
27
|
FROM cron_jobs
|
|
@@ -59,13 +62,19 @@ export class CronScheduler {
|
|
|
59
62
|
const executedAt = new Date();
|
|
60
63
|
try {
|
|
61
64
|
const [session] = await this.sql `
|
|
62
|
-
SELECT session_id, agent FROM sessions WHERE name = ${job.session_name}
|
|
65
|
+
SELECT session_id, agent, agent_code, status FROM sessions WHERE name = ${job.session_name}
|
|
63
66
|
`;
|
|
64
67
|
if (!session) {
|
|
65
68
|
throw new Error(`Session "${job.session_name}" not found`);
|
|
66
69
|
}
|
|
70
|
+
const nameRows = await this.sql `SELECT value FROM config WHERE key = 'human_name'`;
|
|
71
|
+
const descRows = await this.sql `SELECT value FROM config WHERE key = 'human_description'`;
|
|
72
|
+
const humanName = nameRows[0]?.value ?? "your human manager";
|
|
73
|
+
const humanDescription = descRows[0]?.value ?? "";
|
|
67
74
|
const injectText = `[Cron Job "${job.name}" — ${executedAt.toISOString()}]\n${job.message}${CRON_INJECTION_BLURB}`;
|
|
68
|
-
|
|
75
|
+
const token = `${session.agent_code}@${this.serverUrl}`;
|
|
76
|
+
const system = generateSystemPrompt(job.session_name, session.status, humanName, humanDescription, token);
|
|
77
|
+
await this.agenticCodingServer.sendMessage(session.session_id, injectText, session.agent, system);
|
|
69
78
|
await this.sql `
|
|
70
79
|
UPDATE cron_jobs SET last_run = ${executedAt} WHERE id = ${job.id}
|
|
71
80
|
`;
|
package/dist/server/routes.d.ts
CHANGED
|
@@ -3,5 +3,12 @@ import type { Sql } from "../db/index.js";
|
|
|
3
3
|
import type { AgenticCodingServer } from "../lib/agentic-coding-server.js";
|
|
4
4
|
import { CronScheduler } from "./cron.js";
|
|
5
5
|
import type { MemoryManager } from "./memory.js";
|
|
6
|
+
/**
|
|
7
|
+
* Build the persistent system-prompt briefing for a worker session.
|
|
8
|
+
* This is injected as the `system` field on every `promptAsync` call so the
|
|
9
|
+
* agent always has its identity, token, and command reference in context —
|
|
10
|
+
* without consuming a user-message turn.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateSystemPrompt(name: string, status: string | null, humanName: string, humanDescription: string, token: string): string;
|
|
6
13
|
export declare function createRouter(sql: Sql, agenticCodingServer: AgenticCodingServer, serverUrl: string, scheduler: CronScheduler, memoryManager: MemoryManager): Router;
|
|
7
14
|
export declare function createWorkerRouter(sql: Sql, agenticCodingServer: AgenticCodingServer, serverUrl: string, memoryManager: MemoryManager): Router;
|
package/dist/server/routes.js
CHANGED
|
@@ -16,16 +16,19 @@ const MAIL_INJECTION_BLURB = [
|
|
|
16
16
|
`Tip: For currency or prices, use code blocks. Example: put numbers in single or`,
|
|
17
17
|
`double quotes to preserve formatting characters like dollar signs.`,
|
|
18
18
|
].join("\n");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Build the persistent system-prompt briefing for a worker session.
|
|
21
|
+
* This is injected as the `system` field on every `promptAsync` call so the
|
|
22
|
+
* agent always has its identity, token, and command reference in context —
|
|
23
|
+
* without consuming a user-message turn.
|
|
24
|
+
*/
|
|
25
|
+
export function generateSystemPrompt(name, status, humanName, humanDescription, token) {
|
|
23
26
|
return [
|
|
24
27
|
`╔══════════════════════════════════════════════════════╗`,
|
|
25
28
|
`║ WELCOME TO THE AGENT OFFICE ║`,
|
|
26
29
|
`╚══════════════════════════════════════════════════════╝`,
|
|
27
30
|
``,
|
|
28
|
-
`You are
|
|
31
|
+
`You are an AI worker agent enrolled in the agent office.`,
|
|
29
32
|
` Name: ${name}`,
|
|
30
33
|
...(status ? [` Status: ${status}`] : []),
|
|
31
34
|
` Human manager: ${humanName} — the human who created your`,
|
|
@@ -94,7 +97,7 @@ function generateWelcomeMessage(name, agent, status, humanName, humanDescription
|
|
|
94
97
|
` ${token} <memory-id>`,
|
|
95
98
|
``,
|
|
96
99
|
`════════════════════════════════════════════════════════`,
|
|
97
|
-
`
|
|
100
|
+
` IMPORTANT: YOUR SESSIONS ARE PRIVATE`,
|
|
98
101
|
`════════════════════════════════════════════════════════`,
|
|
99
102
|
``,
|
|
100
103
|
` Nobody — not ${humanName}, not your coworkers — can see`,
|
|
@@ -129,6 +132,15 @@ function generateWelcomeMessage(name, agent, status, humanName, humanDescription
|
|
|
129
132
|
``,
|
|
130
133
|
].join("\n");
|
|
131
134
|
}
|
|
135
|
+
/** Load human_name and human_description from the config table. */
|
|
136
|
+
async function loadHumanConfig(sql) {
|
|
137
|
+
const nameRows = await sql `SELECT value FROM config WHERE key = 'human_name'`;
|
|
138
|
+
const descRows = await sql `SELECT value FROM config WHERE key = 'human_description'`;
|
|
139
|
+
return {
|
|
140
|
+
humanName: nameRows[0]?.value ?? "your human manager",
|
|
141
|
+
humanDescription: descRows[0]?.value ?? "",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
132
144
|
export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, memoryManager) {
|
|
133
145
|
const router = Router();
|
|
134
146
|
router.get("/health", (_req, res) => {
|
|
@@ -164,8 +176,12 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
164
176
|
res.status(400).json({ error: "name is required" });
|
|
165
177
|
return;
|
|
166
178
|
}
|
|
179
|
+
if (!agentArg || typeof agentArg !== "string" || !agentArg.trim()) {
|
|
180
|
+
res.status(400).json({ error: "agent is required" });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
167
183
|
const trimmedName = name.trim();
|
|
168
|
-
const trimmedAgent =
|
|
184
|
+
const trimmedAgent = agentArg.trim();
|
|
169
185
|
const existing = await sql `
|
|
170
186
|
SELECT id FROM sessions WHERE name = ${trimmedName}
|
|
171
187
|
`;
|
|
@@ -201,9 +217,10 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
201
217
|
return;
|
|
202
218
|
}
|
|
203
219
|
try {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
220
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
221
|
+
const token = `${row.agent_code}@${serverUrl}`;
|
|
222
|
+
const system = generateSystemPrompt(trimmedName, null, humanName, humanDescription, token);
|
|
223
|
+
await agenticCodingServer.sendMessage(opencodeSessionId, "You are now active. Begin your work.", trimmedAgent, system);
|
|
207
224
|
}
|
|
208
225
|
catch (err) {
|
|
209
226
|
console.warn("Warning: could not send first message to session:", err);
|
|
@@ -233,6 +250,35 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
233
250
|
res.status(500).json({ error: "Internal server error" });
|
|
234
251
|
}
|
|
235
252
|
});
|
|
253
|
+
router.patch("/sessions/:name/agent", async (req, res) => {
|
|
254
|
+
const { name } = req.params;
|
|
255
|
+
const { agent } = req.body;
|
|
256
|
+
if (!agent || typeof agent !== "string" || !agent.trim()) {
|
|
257
|
+
res.status(400).json({ error: "agent is required" });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const trimmedAgent = agent.trim();
|
|
261
|
+
const rows = await sql `
|
|
262
|
+
SELECT id FROM sessions WHERE name = ${name}
|
|
263
|
+
`;
|
|
264
|
+
if (rows.length === 0) {
|
|
265
|
+
res.status(404).json({ error: `Session "${name}" not found` });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
const [updated] = await sql `
|
|
270
|
+
UPDATE sessions
|
|
271
|
+
SET agent = ${trimmedAgent}
|
|
272
|
+
WHERE name = ${name}
|
|
273
|
+
RETURNING id, name, session_id, agent_code, agent, created_at
|
|
274
|
+
`;
|
|
275
|
+
res.json(updated);
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
console.error("PATCH /sessions/:name/agent error:", err);
|
|
279
|
+
res.status(500).json({ error: "Internal server error" });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
236
282
|
router.get("/sessions/:name/messages", async (req, res) => {
|
|
237
283
|
const { name } = req.params;
|
|
238
284
|
const limit = Math.min(parseInt(req.query.limit ?? "20", 10), 100);
|
|
@@ -286,7 +332,7 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
286
332
|
return;
|
|
287
333
|
}
|
|
288
334
|
const rows = await sql `
|
|
289
|
-
SELECT id, name, session_id, agent_code, created_at FROM sessions WHERE name = ${name}
|
|
335
|
+
SELECT id, name, session_id, agent_code, agent, status, created_at FROM sessions WHERE name = ${name}
|
|
290
336
|
`;
|
|
291
337
|
if (rows.length === 0) {
|
|
292
338
|
res.status(404).json({ error: `Session "${name}" not found` });
|
|
@@ -294,7 +340,10 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
294
340
|
}
|
|
295
341
|
const row = rows[0];
|
|
296
342
|
try {
|
|
297
|
-
|
|
343
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
344
|
+
const token = `${row.agent_code}@${serverUrl}`;
|
|
345
|
+
const system = generateSystemPrompt(row.name, row.status ?? null, humanName, humanDescription, token);
|
|
346
|
+
await agenticCodingServer.sendMessage(row.session_id, text.trim(), row.agent, system);
|
|
298
347
|
res.json({ ok: true });
|
|
299
348
|
}
|
|
300
349
|
catch (err) {
|
|
@@ -324,9 +373,10 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
324
373
|
return;
|
|
325
374
|
}
|
|
326
375
|
await agenticCodingServer.revertSession(session.session_id, firstMessage.id);
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
|
|
376
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
377
|
+
const token = `${session.agent_code}@${serverUrl}`;
|
|
378
|
+
const system = generateSystemPrompt(session.name, session.status ?? null, humanName, humanDescription, token);
|
|
379
|
+
await agenticCodingServer.sendMessage(session.session_id, "You are now active. Begin your work.", session.agent ?? undefined, system);
|
|
330
380
|
res.json({ ok: true, messageID: firstMessage.id });
|
|
331
381
|
}
|
|
332
382
|
catch (err) {
|
|
@@ -339,6 +389,7 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
339
389
|
SELECT id, name, session_id, agent_code, agent, status, created_at FROM sessions
|
|
340
390
|
`;
|
|
341
391
|
const results = [];
|
|
392
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
342
393
|
for (const session of allSessions) {
|
|
343
394
|
try {
|
|
344
395
|
const messages = await agenticCodingServer.getMessages(session.session_id);
|
|
@@ -352,9 +403,9 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
352
403
|
continue;
|
|
353
404
|
}
|
|
354
405
|
await agenticCodingServer.revertSession(session.session_id, firstMessage.id);
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
await agenticCodingServer.sendMessage(session.session_id,
|
|
406
|
+
const token = `${session.agent_code}@${serverUrl}`;
|
|
407
|
+
const system = generateSystemPrompt(session.name, session.status ?? null, humanName, humanDescription, token);
|
|
408
|
+
await agenticCodingServer.sendMessage(session.session_id, "You are now active. Begin your work.", session.agent ?? undefined, system);
|
|
358
409
|
results.push({ name: session.name, ok: true });
|
|
359
410
|
}
|
|
360
411
|
catch (err) {
|
|
@@ -482,15 +533,14 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
482
533
|
}
|
|
483
534
|
const trimmedFrom = from.trim();
|
|
484
535
|
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]));
|
|
536
|
+
const sessions = await sql `SELECT name, session_id, agent, agent_code, status FROM sessions`;
|
|
537
|
+
const sessionMap = new Map(sessions.map((s) => [s.name, { sessionId: s.session_id, agent: s.agent, agentCode: s.agent_code, status: s.status }]));
|
|
538
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
487
539
|
const validRecipients = [];
|
|
488
540
|
for (const recipient of to) {
|
|
489
541
|
if (typeof recipient !== "string" || !recipient.trim())
|
|
490
542
|
continue;
|
|
491
543
|
const r = recipient.trim();
|
|
492
|
-
const config = await sql `SELECT value FROM config WHERE key = 'human_name'`;
|
|
493
|
-
const humanName = config[0]?.value ?? "Human";
|
|
494
544
|
if (sessionMap.has(r) || r === humanName) {
|
|
495
545
|
validRecipients.push(r);
|
|
496
546
|
}
|
|
@@ -509,10 +559,12 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
509
559
|
const msgId = msgRow.id;
|
|
510
560
|
let injected = false;
|
|
511
561
|
if (sessionMap.has(recipient)) {
|
|
512
|
-
const sessionId = sessionMap.get(recipient);
|
|
562
|
+
const { sessionId, agent, agentCode, status } = sessionMap.get(recipient);
|
|
513
563
|
const injectText = `[Message from "${trimmedFrom}"]: ${trimmedBody}${MAIL_INJECTION_BLURB}`;
|
|
564
|
+
const token = `${agentCode}@${serverUrl}`;
|
|
565
|
+
const system = generateSystemPrompt(recipient, status ?? null, humanName, humanDescription, token);
|
|
514
566
|
try {
|
|
515
|
-
await agenticCodingServer.sendMessage(sessionId, injectText);
|
|
567
|
+
await agenticCodingServer.sendMessage(sessionId, injectText, agent ?? undefined, system);
|
|
516
568
|
await sql `UPDATE messages SET injected = TRUE WHERE id = ${msgId}`;
|
|
517
569
|
injected = true;
|
|
518
570
|
}
|
|
@@ -923,35 +975,6 @@ export function createRouter(sql, agenticCodingServer, serverUrl, scheduler, mem
|
|
|
923
975
|
}
|
|
924
976
|
export function createWorkerRouter(sql, agenticCodingServer, serverUrl, memoryManager) {
|
|
925
977
|
const router = Router();
|
|
926
|
-
router.get("/worker/clock-in", async (req, res) => {
|
|
927
|
-
const { code } = req.query;
|
|
928
|
-
if (!code || typeof code !== "string") {
|
|
929
|
-
res.status(400).json({ error: "code query parameter is required" });
|
|
930
|
-
return;
|
|
931
|
-
}
|
|
932
|
-
const rows = await sql `
|
|
933
|
-
SELECT id, name, session_id, agent_code, created_at
|
|
934
|
-
FROM sessions
|
|
935
|
-
WHERE agent_code = ${code}
|
|
936
|
-
`;
|
|
937
|
-
if (rows.length === 0) {
|
|
938
|
-
res.status(401).json({ error: "Invalid agent code" });
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
const session = rows[0];
|
|
942
|
-
const token = `${session.agent_code}@<server-url>`;
|
|
943
|
-
const humanConfig = await sql `SELECT value FROM config WHERE key = 'human_name'`;
|
|
944
|
-
const humanName = humanConfig[0]?.value ?? "your human manager";
|
|
945
|
-
const humanDescConfig = await sql `SELECT value FROM config WHERE key = 'human_description'`;
|
|
946
|
-
const humanDescription = humanDescConfig[0]?.value ?? "";
|
|
947
|
-
const message = generateWelcomeMessage(session.name, session.agent, session.status ?? null, humanName, humanDescription, token);
|
|
948
|
-
res.json({
|
|
949
|
-
ok: true,
|
|
950
|
-
name: session.name,
|
|
951
|
-
session_id: session.session_id,
|
|
952
|
-
message,
|
|
953
|
-
});
|
|
954
|
-
});
|
|
955
978
|
router.get("/worker/list-coworkers", async (req, res) => {
|
|
956
979
|
const { code } = req.query;
|
|
957
980
|
if (!code || typeof code !== "string") {
|
|
@@ -1043,10 +1066,9 @@ export function createWorkerRouter(sql, agenticCodingServer, serverUrl, memoryMa
|
|
|
1043
1066
|
return;
|
|
1044
1067
|
}
|
|
1045
1068
|
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]));
|
|
1048
|
-
const
|
|
1049
|
-
const humanName = config[0]?.value ?? "Human";
|
|
1069
|
+
const sessions = await sql `SELECT name, session_id, agent, agent_code, status FROM sessions`;
|
|
1070
|
+
const sessionMap = new Map(sessions.map((s) => [s.name, { sessionId: s.session_id, agent: s.agent, agentCode: s.agent_code, status: s.status }]));
|
|
1071
|
+
const { humanName, humanDescription } = await loadHumanConfig(sql);
|
|
1050
1072
|
const validRecipients = [];
|
|
1051
1073
|
for (const recipient of to) {
|
|
1052
1074
|
if (typeof recipient !== "string" || !recipient.trim())
|
|
@@ -1070,10 +1092,12 @@ export function createWorkerRouter(sql, agenticCodingServer, serverUrl, memoryMa
|
|
|
1070
1092
|
const msgId = msgRow.id;
|
|
1071
1093
|
let injected = false;
|
|
1072
1094
|
if (sessionMap.has(recipient)) {
|
|
1073
|
-
const recipientSessionId = sessionMap.get(recipient);
|
|
1095
|
+
const { sessionId: recipientSessionId, agent: recipientAgent, agentCode, status } = sessionMap.get(recipient);
|
|
1074
1096
|
const injectText = `[Message from "${session.name}"]: ${trimmedBody}${MAIL_INJECTION_BLURB}`;
|
|
1097
|
+
const token = `${agentCode}@${serverUrl}`;
|
|
1098
|
+
const system = generateSystemPrompt(recipient, status ?? null, humanName, humanDescription, token);
|
|
1075
1099
|
try {
|
|
1076
|
-
await agenticCodingServer.sendMessage(recipientSessionId, injectText);
|
|
1100
|
+
await agenticCodingServer.sendMessage(recipientSessionId, injectText, recipientAgent ?? undefined, system);
|
|
1077
1101
|
await sql `UPDATE messages SET injected = TRUE WHERE id = ${msgId}`;
|
|
1078
1102
|
injected = true;
|
|
1079
1103
|
}
|