agent-office 0.3.1 → 0.4.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/README.md +18 -40
- package/dist/cli.js +23 -64
- package/dist/commands/communicator.d.ts +2 -2
- package/dist/commands/communicator.js +175 -40
- package/dist/commands/notifier.d.ts +11 -0
- package/dist/commands/notifier.js +100 -0
- package/dist/commands/screensaver.d.ts +1 -1
- package/dist/commands/screensaver.js +47 -3
- package/dist/commands/serve.d.ts +0 -2
- package/dist/commands/serve.js +1 -26
- package/dist/commands/worker.d.ts +1 -4
- package/dist/commands/worker.js +2 -64
- package/dist/db/index.d.ts +1 -0
- package/dist/db/postgresql-storage.d.ts +7 -1
- package/dist/db/postgresql-storage.js +37 -14
- package/dist/db/sqlite-storage.d.ts +7 -1
- package/dist/db/sqlite-storage.js +51 -12
- package/dist/db/storage-base.d.ts +7 -1
- package/dist/db/storage-base.js +1 -1
- package/dist/db/storage.d.ts +7 -1
- package/dist/lib/notifier.d.ts +18 -0
- package/dist/lib/notifier.js +15 -0
- package/dist/manage/components/SessionList.js +0 -266
- package/dist/manage/hooks/useApi.d.ts +0 -24
- package/dist/manage/hooks/useApi.js +0 -24
- package/dist/server/index.d.ts +1 -2
- package/dist/server/index.js +3 -3
- package/dist/server/routes.d.ts +2 -3
- package/dist/server/routes.js +72 -252
- package/package.json +4 -4
- package/dist/server/memory.d.ts +0 -87
- package/dist/server/memory.js +0 -348
package/dist/commands/worker.js
CHANGED
|
@@ -96,7 +96,8 @@ export async function listCrons(token) {
|
|
|
96
96
|
console.log(JSON.stringify(crons, null, 2));
|
|
97
97
|
}
|
|
98
98
|
export async function createCron(token, options) {
|
|
99
|
-
const
|
|
99
|
+
const finalMessage = `Action: ${options.message}\n\nWho to respond to when done: ${options.respondTo}`;
|
|
100
|
+
const cron = await postWorker(token, "/worker/crons", { name: options.name, schedule: options.schedule, message: finalMessage, timezone: options.timezone });
|
|
100
101
|
console.log(JSON.stringify(cron, null, 2));
|
|
101
102
|
}
|
|
102
103
|
export async function deleteCron(token, cronId) {
|
|
@@ -138,66 +139,3 @@ export async function cronHistory(token, cronId) {
|
|
|
138
139
|
const history = await fetchWorker(token, `/worker/crons/${cronId}/history`);
|
|
139
140
|
console.log(JSON.stringify(history, null, 2));
|
|
140
141
|
}
|
|
141
|
-
// ── Memory Commands ──────────────────────────────────────────────────────────
|
|
142
|
-
export async function memoryAdd(token, content) {
|
|
143
|
-
const result = await postWorker(token, "/worker/memory/add", { content });
|
|
144
|
-
console.log(JSON.stringify(result, null, 2));
|
|
145
|
-
}
|
|
146
|
-
export async function memorySearch(token, query, limit) {
|
|
147
|
-
const result = await postWorker(token, "/worker/memory/search", { query, limit });
|
|
148
|
-
console.log(JSON.stringify(result, null, 2));
|
|
149
|
-
}
|
|
150
|
-
export async function memoryList(token, limit) {
|
|
151
|
-
const { agentCode, serverUrl } = parseToken(token);
|
|
152
|
-
const url = `${serverUrl}/worker/memory/list?code=${encodeURIComponent(agentCode)}&limit=${limit}`;
|
|
153
|
-
let res;
|
|
154
|
-
try {
|
|
155
|
-
res = await fetch(url);
|
|
156
|
-
}
|
|
157
|
-
catch (err) {
|
|
158
|
-
console.error(`Error: could not reach ${serverUrl}`);
|
|
159
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
let body;
|
|
163
|
-
try {
|
|
164
|
-
body = await res.json();
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
console.error(`Error: invalid response from server`);
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
if (!res.ok) {
|
|
171
|
-
const msg = body.error ?? `HTTP ${res.status}`;
|
|
172
|
-
console.error(`Error: ${msg}`);
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
console.log(JSON.stringify(body, null, 2));
|
|
176
|
-
}
|
|
177
|
-
export async function memoryForget(token, memoryId) {
|
|
178
|
-
const { agentCode, serverUrl } = parseToken(token);
|
|
179
|
-
const url = `${serverUrl}/worker/memory/${encodeURIComponent(memoryId)}?code=${encodeURIComponent(agentCode)}`;
|
|
180
|
-
let res;
|
|
181
|
-
try {
|
|
182
|
-
res = await fetch(url, { method: "DELETE" });
|
|
183
|
-
}
|
|
184
|
-
catch (err) {
|
|
185
|
-
console.error(`Error: could not reach ${serverUrl}`);
|
|
186
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
187
|
-
process.exit(1);
|
|
188
|
-
}
|
|
189
|
-
let body;
|
|
190
|
-
try {
|
|
191
|
-
body = await res.json();
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
console.error(`Error: invalid response from server`);
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
if (!res.ok) {
|
|
198
|
-
const msg = body.error ?? `HTTP ${res.status}`;
|
|
199
|
-
console.error(`Error: ${msg}`);
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
202
|
-
console.log(JSON.stringify(body, null, 2));
|
|
203
|
-
}
|
package/dist/db/index.d.ts
CHANGED
|
@@ -19,11 +19,17 @@ export declare class AgentOfficePostgresqlStorage extends AgentOfficeStorageBase
|
|
|
19
19
|
getAllConfig(): Promise<ConfigRow[]>;
|
|
20
20
|
getConfig(key: string): Promise<string | null>;
|
|
21
21
|
setConfig(key: string, value: string): Promise<void>;
|
|
22
|
-
listMessagesForRecipient(name: string,
|
|
22
|
+
listMessagesForRecipient(name: string, filters?: {
|
|
23
|
+
unread?: boolean;
|
|
24
|
+
olderThanHours?: number;
|
|
25
|
+
notified?: boolean;
|
|
26
|
+
}): Promise<MessageRow[]>;
|
|
23
27
|
listMessagesFromSender(name: string): Promise<MessageRow[]>;
|
|
28
|
+
countUnreadBySender(recipientName: string): Promise<Map<string, number>>;
|
|
24
29
|
createMessageImpl(from: string, to: string, body: string): Promise<MessageRow>;
|
|
25
30
|
markMessageAsRead(id: number): Promise<MessageRow | null>;
|
|
26
31
|
markMessageAsInjected(id: number): Promise<void>;
|
|
32
|
+
markMessagesAsNotified(ids: number[]): Promise<void>;
|
|
27
33
|
listCronJobs(): Promise<CronJobRow[]>;
|
|
28
34
|
listCronJobsForSession(sessionName: string): Promise<CronJobRow[]>;
|
|
29
35
|
getCronJobById(id: number): Promise<CronJobRow | null>;
|
|
@@ -107,21 +107,19 @@ export class AgentOfficePostgresqlStorage extends AgentOfficeStorageBase {
|
|
|
107
107
|
`;
|
|
108
108
|
}
|
|
109
109
|
// Messages
|
|
110
|
-
async listMessagesForRecipient(name,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
async listMessagesForRecipient(name, filters) {
|
|
111
|
+
const whereClauses = ["to_name = $1"];
|
|
112
|
+
const params = [name];
|
|
113
|
+
if (filters?.unread)
|
|
114
|
+
whereClauses.push("read = FALSE");
|
|
115
|
+
if (filters?.notified === false)
|
|
116
|
+
whereClauses.push("notified = FALSE");
|
|
117
|
+
if (filters?.olderThanHours !== undefined) {
|
|
118
|
+
whereClauses.push(`created_at < NOW() - INTERVAL '${filters.olderThanHours} hours'`);
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
WHERE to_name = ${name}
|
|
123
|
-
ORDER BY created_at DESC
|
|
124
|
-
`;
|
|
120
|
+
const whereSQL = whereClauses.join(" AND ");
|
|
121
|
+
const rows = await this.sql.unsafe(`SELECT id, from_name, to_name, body, read, injected, created_at, notified FROM messages WHERE ${whereSQL} ORDER BY created_at DESC`, params);
|
|
122
|
+
return rows;
|
|
125
123
|
}
|
|
126
124
|
async listMessagesFromSender(name) {
|
|
127
125
|
return this.sql `
|
|
@@ -131,6 +129,19 @@ export class AgentOfficePostgresqlStorage extends AgentOfficeStorageBase {
|
|
|
131
129
|
ORDER BY created_at DESC
|
|
132
130
|
`;
|
|
133
131
|
}
|
|
132
|
+
async countUnreadBySender(recipientName) {
|
|
133
|
+
const rows = await this.sql `
|
|
134
|
+
SELECT from_name, COUNT(*) as count
|
|
135
|
+
FROM messages
|
|
136
|
+
WHERE to_name = ${recipientName} AND read = FALSE
|
|
137
|
+
GROUP BY from_name
|
|
138
|
+
`;
|
|
139
|
+
const result = new Map();
|
|
140
|
+
for (const row of rows) {
|
|
141
|
+
result.set(row.from_name, Number(row.count));
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
134
145
|
async createMessageImpl(from, to, body) {
|
|
135
146
|
const [row] = await this.sql `
|
|
136
147
|
INSERT INTO messages (from_name, to_name, body)
|
|
@@ -149,6 +160,11 @@ export class AgentOfficePostgresqlStorage extends AgentOfficeStorageBase {
|
|
|
149
160
|
async markMessageAsInjected(id) {
|
|
150
161
|
await this.sql `UPDATE messages SET injected = TRUE WHERE id = ${id}`;
|
|
151
162
|
}
|
|
163
|
+
async markMessagesAsNotified(ids) {
|
|
164
|
+
if (ids.length === 0)
|
|
165
|
+
return;
|
|
166
|
+
await this.sql `UPDATE messages SET notified = TRUE WHERE id = ANY(${ids})`;
|
|
167
|
+
}
|
|
152
168
|
// Cron Jobs
|
|
153
169
|
async listCronJobs() {
|
|
154
170
|
return this.sql `
|
|
@@ -330,6 +346,13 @@ export class AgentOfficePostgresqlStorage extends AgentOfficeStorageBase {
|
|
|
330
346
|
name: "rename_mode_to_agent",
|
|
331
347
|
sql: `
|
|
332
348
|
ALTER TABLE sessions RENAME COLUMN mode TO agent;
|
|
349
|
+
`,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
version: 9,
|
|
353
|
+
name: "add_notified_to_messages",
|
|
354
|
+
sql: `
|
|
355
|
+
ALTER TABLE messages ADD COLUMN IF NOT EXISTS notified BOOLEAN NOT NULL DEFAULT FALSE;
|
|
333
356
|
`,
|
|
334
357
|
},
|
|
335
358
|
];
|
|
@@ -20,11 +20,17 @@ export declare class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
|
|
|
20
20
|
getAllConfig(): Promise<ConfigRow[]>;
|
|
21
21
|
getConfig(key: string): Promise<string | null>;
|
|
22
22
|
setConfig(key: string, value: string): Promise<void>;
|
|
23
|
-
listMessagesForRecipient(name: string,
|
|
23
|
+
listMessagesForRecipient(name: string, filters?: {
|
|
24
|
+
unread?: boolean;
|
|
25
|
+
olderThanHours?: number;
|
|
26
|
+
notified?: boolean;
|
|
27
|
+
}): Promise<MessageRow[]>;
|
|
24
28
|
listMessagesFromSender(name: string): Promise<MessageRow[]>;
|
|
29
|
+
countUnreadBySender(recipientName: string): Promise<Map<string, number>>;
|
|
25
30
|
createMessageImpl(from: string, to: string, body: string): Promise<MessageRow>;
|
|
26
31
|
markMessageAsRead(id: number): Promise<MessageRow | null>;
|
|
27
32
|
markMessageAsInjected(id: number): Promise<void>;
|
|
33
|
+
markMessagesAsNotified(ids: number[]): Promise<void>;
|
|
28
34
|
listCronJobs(): Promise<CronJobRow[]>;
|
|
29
35
|
listCronJobsForSession(sessionName: string): Promise<CronJobRow[]>;
|
|
30
36
|
getCronJobById(id: number): Promise<CronJobRow | null>;
|
|
@@ -142,21 +142,32 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
|
|
|
142
142
|
stmt.run(key, value);
|
|
143
143
|
}
|
|
144
144
|
// Messages
|
|
145
|
-
async listMessagesForRecipient(name,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
async listMessagesForRecipient(name, filters) {
|
|
146
|
+
let whereClauses = [`to_name = ?`];
|
|
147
|
+
let params = [name];
|
|
148
|
+
if (filters?.unread) {
|
|
149
|
+
whereClauses.push(`read = 0`);
|
|
150
|
+
}
|
|
151
|
+
if (filters?.notified === false) {
|
|
152
|
+
whereClauses.push(`notified != 1`);
|
|
153
|
+
}
|
|
154
|
+
if (filters?.olderThanHours !== undefined) {
|
|
155
|
+
const hours = filters.olderThanHours;
|
|
156
|
+
whereClauses.push(`created_at < datetime('now', '-${hours} hours')`);
|
|
157
|
+
}
|
|
158
|
+
const where = whereClauses.join(' AND ');
|
|
159
|
+
const sql = `SELECT id, from_name, to_name, body, read, injected, created_at, notified
|
|
160
|
+
FROM messages
|
|
161
|
+
WHERE ${where}
|
|
162
|
+
ORDER BY created_at DESC`;
|
|
155
163
|
const stmt = this.db.prepare(sql);
|
|
156
|
-
const rows = stmt.all(
|
|
164
|
+
const rows = stmt.all(...params);
|
|
157
165
|
return rows.map(row => ({
|
|
158
166
|
...row,
|
|
159
|
-
|
|
167
|
+
read: !!row.read,
|
|
168
|
+
injected: !!row.injected,
|
|
169
|
+
notified: !!row.notified,
|
|
170
|
+
created_at: new Date(row.created_at + 'Z'),
|
|
160
171
|
}));
|
|
161
172
|
}
|
|
162
173
|
async listMessagesFromSender(name) {
|
|
@@ -172,6 +183,20 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
|
|
|
172
183
|
created_at: new Date(row.created_at + 'Z'), // Treat SQLite datetime as UTC
|
|
173
184
|
}));
|
|
174
185
|
}
|
|
186
|
+
async countUnreadBySender(recipientName) {
|
|
187
|
+
const stmt = this.db.prepare(`
|
|
188
|
+
SELECT from_name, COUNT(*) as count
|
|
189
|
+
FROM messages
|
|
190
|
+
WHERE to_name = ? AND read = FALSE
|
|
191
|
+
GROUP BY from_name
|
|
192
|
+
`);
|
|
193
|
+
const rows = stmt.all(recipientName);
|
|
194
|
+
const result = new Map();
|
|
195
|
+
for (const row of rows) {
|
|
196
|
+
result.set(row.from_name, row.count);
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
175
200
|
async createMessageImpl(from, to, body) {
|
|
176
201
|
const stmt = this.db.prepare(`
|
|
177
202
|
INSERT INTO messages (from_name, to_name, body)
|
|
@@ -201,6 +226,13 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
|
|
|
201
226
|
const stmt = this.db.prepare(`UPDATE messages SET injected = TRUE WHERE id = ?`);
|
|
202
227
|
stmt.run(id);
|
|
203
228
|
}
|
|
229
|
+
async markMessagesAsNotified(ids) {
|
|
230
|
+
if (ids.length === 0)
|
|
231
|
+
return;
|
|
232
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
233
|
+
const stmt = this.db.prepare(`UPDATE messages SET notified = 1 WHERE id IN (${placeholders})`);
|
|
234
|
+
stmt.run(...ids);
|
|
235
|
+
}
|
|
204
236
|
// Cron Jobs
|
|
205
237
|
async listCronJobs() {
|
|
206
238
|
const stmt = this.db.prepare(`
|
|
@@ -433,6 +465,13 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
|
|
|
433
465
|
ALTER TABLE sessions_new RENAME TO sessions;
|
|
434
466
|
CREATE INDEX IF NOT EXISTS idx_sessions_name ON sessions(name);
|
|
435
467
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_agent_code ON sessions(agent_code);
|
|
468
|
+
`,
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
version: 9,
|
|
472
|
+
name: "add_notified_to_messages",
|
|
473
|
+
sql: `
|
|
474
|
+
ALTER TABLE messages ADD COLUMN notified INTEGER NOT NULL DEFAULT 0;
|
|
436
475
|
`,
|
|
437
476
|
},
|
|
438
477
|
];
|
|
@@ -21,10 +21,16 @@ export declare abstract class AgentOfficeStorageBase implements AgentOfficeStora
|
|
|
21
21
|
abstract getAllConfig(): Promise<ConfigRow[]>;
|
|
22
22
|
abstract getConfig(key: string): Promise<string | null>;
|
|
23
23
|
abstract setConfig(key: string, value: string): Promise<void>;
|
|
24
|
-
abstract listMessagesForRecipient(name: string,
|
|
24
|
+
abstract listMessagesForRecipient(name: string, filters?: {
|
|
25
|
+
unread?: boolean;
|
|
26
|
+
olderThanHours?: number;
|
|
27
|
+
notified?: boolean;
|
|
28
|
+
}): Promise<MessageRow[]>;
|
|
25
29
|
abstract listMessagesFromSender(name: string): Promise<MessageRow[]>;
|
|
30
|
+
abstract countUnreadBySender(recipientName: string): Promise<Map<string, number>>;
|
|
26
31
|
abstract markMessageAsRead(id: number): Promise<MessageRow | null>;
|
|
27
32
|
abstract markMessageAsInjected(id: number): Promise<void>;
|
|
33
|
+
abstract markMessagesAsNotified(ids: number[]): Promise<void>;
|
|
28
34
|
abstract listCronJobs(): Promise<CronJobRow[]>;
|
|
29
35
|
abstract listCronJobsForSession(sessionName: string): Promise<CronJobRow[]>;
|
|
30
36
|
abstract getCronJobById(id: number): Promise<CronJobRow | null>;
|
package/dist/db/storage-base.js
CHANGED
|
@@ -18,7 +18,7 @@ export class AgentOfficeStorageBase {
|
|
|
18
18
|
}
|
|
19
19
|
// For each session, find all messages sent to them and track the latest per sender
|
|
20
20
|
for (const session of sessions) {
|
|
21
|
-
const messages = await this.listMessagesForRecipient(session.name
|
|
21
|
+
const messages = await this.listMessagesForRecipient(session.name);
|
|
22
22
|
const senderMap = this.coworkerMailState.get(session.name);
|
|
23
23
|
for (const message of messages) {
|
|
24
24
|
const existingDate = senderMap.get(message.from_name);
|
package/dist/db/storage.d.ts
CHANGED
|
@@ -30,11 +30,17 @@ export interface AgentOfficeStorage {
|
|
|
30
30
|
getAllConfig(): Promise<ConfigRow[]>;
|
|
31
31
|
getConfig(key: string): Promise<string | null>;
|
|
32
32
|
setConfig(key: string, value: string): Promise<void>;
|
|
33
|
-
listMessagesForRecipient(name: string,
|
|
33
|
+
listMessagesForRecipient(name: string, filters?: {
|
|
34
|
+
unread?: boolean;
|
|
35
|
+
olderThanHours?: number;
|
|
36
|
+
notified?: boolean;
|
|
37
|
+
}): Promise<MessageRow[]>;
|
|
34
38
|
listMessagesFromSender(name: string): Promise<MessageRow[]>;
|
|
39
|
+
countUnreadBySender(recipientName: string): Promise<Map<string, number>>;
|
|
35
40
|
createMessage(from: string, to: string, body: string): Promise<MessageRow>;
|
|
36
41
|
markMessageAsRead(id: number): Promise<MessageRow | null>;
|
|
37
42
|
markMessageAsInjected(id: number): Promise<void>;
|
|
43
|
+
markMessagesAsNotified(ids: number[]): Promise<void>;
|
|
38
44
|
listCronJobs(): Promise<CronJobRow[]>;
|
|
39
45
|
listCronJobsForSession(sessionName: string): Promise<CronJobRow[]>;
|
|
40
46
|
getCronJobById(id: number): Promise<CronJobRow | null>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface NotifyOptions {
|
|
2
|
+
from: string;
|
|
3
|
+
to: string;
|
|
4
|
+
subject: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Abstract notification interface — swap out the implementation
|
|
9
|
+
* (email, SMS, webhook, etc.) without changing the daemon logic.
|
|
10
|
+
*/
|
|
11
|
+
export interface AgentOfficeNotifier {
|
|
12
|
+
send(opts: NotifyOptions): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare class ResendNotifier implements AgentOfficeNotifier {
|
|
15
|
+
private resend;
|
|
16
|
+
constructor(apiKey: string);
|
|
17
|
+
send(opts: NotifyOptions): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Resend } from "resend";
|
|
2
|
+
export class ResendNotifier {
|
|
3
|
+
resend;
|
|
4
|
+
constructor(apiKey) {
|
|
5
|
+
this.resend = new Resend(apiKey);
|
|
6
|
+
}
|
|
7
|
+
async send(opts) {
|
|
8
|
+
await this.resend.emails.send({
|
|
9
|
+
from: opts.from,
|
|
10
|
+
to: [opts.to],
|
|
11
|
+
subject: opts.subject,
|
|
12
|
+
text: opts.text,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|