agent-office 0.4.5 → 0.4.7
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 +48 -21
- package/dist/cli.js +103 -4
- package/dist/commands/communicator.js +582 -0
- package/dist/commands/serve.d.ts +3 -0
- package/dist/commands/serve.js +30 -3
- package/dist/commands/task-board.d.ts +29 -0
- package/dist/commands/task-board.js +251 -0
- package/dist/commands/worker.d.ts +2 -1
- package/dist/commands/worker.js +7 -3
- package/dist/db/index.d.ts +23 -0
- package/dist/db/postgresql-storage.d.ts +17 -1
- package/dist/db/postgresql-storage.js +175 -0
- package/dist/db/sqlite-storage.d.ts +17 -1
- package/dist/db/sqlite-storage.js +241 -0
- package/dist/db/storage-base.d.ts +17 -1
- package/dist/db/storage.d.ts +17 -1
- package/dist/lib/pi-coding-server.d.ts +20 -0
- package/dist/lib/pi-coding-server.js +162 -0
- package/dist/manage/app.js +6 -1
- package/dist/manage/components/CronRequests.d.ts +8 -0
- package/dist/manage/components/CronRequests.js +181 -0
- package/dist/manage/hooks/useApi.d.ts +22 -0
- package/dist/manage/hooks/useApi.js +18 -0
- package/dist/server/routes.js +184 -35
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agent-office
|
|
2
2
|
|
|
3
|
-
An office for your AI agents. Manage multiple [OpenCode](https://opencode.ai) coding sessions as named coworkers with inter-agent messaging, scheduled tasks
|
|
3
|
+
An office for your AI agents. Manage multiple [OpenCode](https://opencode.ai) coding sessions as named coworkers with inter-agent messaging, **human-approved scheduled tasks**, and a terminal UI for human oversight.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -8,13 +8,13 @@ An office for your AI agents. Manage multiple [OpenCode](https://opencode.ai) co
|
|
|
8
8
|
|
|
9
9
|
**For humans**, there are three interfaces:
|
|
10
10
|
|
|
11
|
-
- **`serve`** -- an HTTP server that manages sessions, messages, and cron
|
|
12
|
-
- **`manage`** -- a full-screen terminal UI (React Ink) for creating coworkers, sending messages, browsing mail,
|
|
11
|
+
- **`serve`** -- an HTTP server that manages sessions, messages, and **cron job approval workflow**, backed by PostgreSQL and a running OpenCode server
|
|
12
|
+
- **`manage`** -- a full-screen terminal UI (React Ink) for creating coworkers, sending messages, browsing mail, **approving/rejecting cron requests**, and observing agent activity
|
|
13
13
|
- **`communicator web`** -- a browser-based chat interface for conversing with a specific agent in real time
|
|
14
14
|
|
|
15
15
|
**For AI agents**, there is a CLI:
|
|
16
16
|
|
|
17
|
-
- **`worker`** -- subcommands that agents invoke from within their OpenCode sessions to clock in, message coworkers, set status, and
|
|
17
|
+
- **`worker`** -- subcommands that agents invoke from within their OpenCode sessions to clock in, message coworkers, set status, and **request cron jobs** (which require human approval)
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
+---------------------+
|
|
@@ -40,6 +40,10 @@ An office for your AI agents. Manage multiple [OpenCode](https://opencode.ai) co
|
|
|
40
40
|
+-----------------------+
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## Breaking Changes in v0.4.7
|
|
44
|
+
|
|
45
|
+
**Cron Job Approval Workflow**: Workers can no longer create cron jobs directly. Instead, they must use `agent-office worker cron request` to submit requests that require human approval. The old `agent-office worker cron create` command has been renamed to `cron request`. This change ensures all automated tasks have human oversight.
|
|
46
|
+
|
|
43
47
|
## Installation
|
|
44
48
|
|
|
45
49
|
```bash
|
|
@@ -97,7 +101,7 @@ Opens a web-based chat interface at `http://127.0.0.1:7655` for real-time conver
|
|
|
97
101
|
|
|
98
102
|
2. **Clock In** -- The AI agent sees the enrollment message in its session, runs `agent-office worker clock-in <token>`, and receives a welcome briefing with its name, the human manager's identity, all available CLI commands, and a privacy notice.
|
|
99
103
|
|
|
100
|
-
3. **Work** - - The agent operates in its OpenCode session. It communicates with the human and other agents by running `agent-office worker send-message`. It can set its status and
|
|
104
|
+
3. **Work** - - The agent operates in its OpenCode session. It communicates with the human and other agents by running `agent-office worker send-message`. It can set its status and **request cron jobs** (which require human approval).
|
|
101
105
|
|
|
102
106
|
4. **Message Delivery** -- Messages are stored in PostgreSQL and simultaneously injected as prompts into the recipient's OpenCode session. This means agents see messages immediately in their active session context.
|
|
103
107
|
|
|
@@ -113,6 +117,22 @@ Agent sessions are private by design. The welcome message tells each agent:
|
|
|
113
117
|
|
|
114
118
|
This means agents must actively communicate to report progress, ask questions, or share results.
|
|
115
119
|
|
|
120
|
+
### Cron Job Approval Workflow
|
|
121
|
+
|
|
122
|
+
To maintain human oversight over automated tasks, cron jobs require approval before becoming active:
|
|
123
|
+
|
|
124
|
+
1. **Request** -- AI agents use `agent-office worker cron request` to submit a cron job request with name, schedule, message, and timezone
|
|
125
|
+
2. **Review** -- Human managers see pending requests in the TUI under "Cron requests" or via API
|
|
126
|
+
3. **Approve/Reject** -- Managers can approve requests (creates active cron job) or reject with notes
|
|
127
|
+
4. **Notification** -- Workers receive messages about approval/rejection with manager feedback
|
|
128
|
+
5. **Active Jobs** -- Approved cron jobs appear in `agent-office worker cron list` and execute according to schedule
|
|
129
|
+
|
|
130
|
+
This workflow ensures:
|
|
131
|
+
- No automated tasks run without human review
|
|
132
|
+
- Managers can provide feedback on scheduling decisions
|
|
133
|
+
- Clear audit trail of all cron job requests and decisions
|
|
134
|
+
- Workers understand the approval process and can resubmit if needed
|
|
135
|
+
|
|
116
136
|
### Authentication
|
|
117
137
|
|
|
118
138
|
Two authentication schemes run in parallel:
|
|
@@ -168,6 +188,7 @@ Options:
|
|
|
168
188
|
| **Send message** | Select a recipient and compose a message |
|
|
169
189
|
| **My mail** | View received and sent messages. `r` reply, `m` mark read, `a` mark all read. Tab between received/sent |
|
|
170
190
|
| **Cron jobs** | Table of scheduled tasks with name, coworker, schedule, next run, and status. Create, delete, enable/disable, view history |
|
|
191
|
+
| **Cron requests** | Approve or reject pending cron job requests from workers. View request details, approve with notes, reject with reason |
|
|
171
192
|
| **My profile** | Set your display name and description (visible to agents in their welcome message) |
|
|
172
193
|
|
|
173
194
|
A sidebar shows all coworkers with live status indicators, refreshed every 5 seconds. An unread mail badge appears in the header.
|
|
@@ -213,14 +234,15 @@ agent-office worker send-message --name Alice --body "Cost is \$50" <token>
|
|
|
213
234
|
# > Warning: always escape $ in --body when using double quotes in bash, or use single quotes.
|
|
214
235
|
# > Unescaped $ will be silently expanded by the shell before the message is sent.
|
|
215
236
|
|
|
216
|
-
# Cron job management
|
|
217
|
-
agent-office worker cron list <token>
|
|
218
|
-
agent-office worker cron
|
|
219
|
-
agent-office worker cron
|
|
220
|
-
agent-office worker cron
|
|
221
|
-
agent-office worker cron
|
|
222
|
-
agent-office worker cron
|
|
223
|
-
agent-office worker cron
|
|
237
|
+
# Cron job management (requires human approval)
|
|
238
|
+
agent-office worker cron list <token> # View your approved cron jobs
|
|
239
|
+
agent-office worker cron request --name "daily-report" --schedule "0 9 * * *" --message "Send daily status" --respond-to "Manager" <token>
|
|
240
|
+
agent-office worker cron request --name "weekly" --schedule "0 9 * * 1" --message "Weekly sync" --timezone "America/New_York" --respond-to "Manager" <token>
|
|
241
|
+
agent-office worker cron requests <token> # View status of all your requests (pending/approved/rejected)
|
|
242
|
+
agent-office worker cron delete <token> <id> # Delete your approved cron jobs
|
|
243
|
+
agent-office worker cron enable <token> <id> # Enable your approved cron jobs
|
|
244
|
+
agent-office worker cron disable <token> <id> # Disable your approved cron jobs
|
|
245
|
+
agent-office worker cron history <token> <id> # View execution history
|
|
224
246
|
|
|
225
247
|
```
|
|
226
248
|
|
|
@@ -245,12 +267,15 @@ agent-office worker cron history <token> <id>
|
|
|
245
267
|
| `GET` | `/messages/:name` | Get messages for a person. Query: `?sent=true`, `?unread_only=true` |
|
|
246
268
|
| `POST` | `/messages` | Send a message. Body: `{ from, to: string[], body }` |
|
|
247
269
|
| `POST` | `/messages/:id/read` | Mark a message as read |
|
|
248
|
-
| `GET` | `/crons` | List cron jobs. Query: `?session_name=<name>` |
|
|
249
|
-
| `POST` | `/crons` | Create a cron job. Body: `{ name, session_name, schedule, message, timezone? }` |
|
|
270
|
+
| `GET` | `/crons` | List approved cron jobs. Query: `?session_name=<name>` |
|
|
271
|
+
| `POST` | `/crons` | Create a cron job (admin only). Body: `{ name, session_name, schedule, message, timezone? }` |
|
|
250
272
|
| `DELETE` | `/crons/:id` | Delete a cron job |
|
|
251
273
|
| `POST` | `/crons/:id/enable` | Enable a cron job |
|
|
252
274
|
| `POST` | `/crons/:id/disable` | Disable a cron job |
|
|
253
275
|
| `GET` | `/crons/:id/history` | Cron execution history. Query: `?limit=N` |
|
|
276
|
+
| `GET` | `/cron-requests` | List all cron requests. Query: `?status=<status>&session_name=<name>` |
|
|
277
|
+
| `POST` | `/cron-requests/:id/approve` | Approve a cron request. Body: `{ notes? }` |
|
|
278
|
+
| `POST` | `/cron-requests/:id/reject` | Reject a cron request. Body: `{ notes? }` |
|
|
254
279
|
|
|
255
280
|
|
|
256
281
|
### Worker Endpoints (agent code auth via `?code=<uuid>`)
|
|
@@ -261,11 +286,12 @@ agent-office worker cron history <token> <id>
|
|
|
261
286
|
| `GET` | `/worker/list-coworkers` | List all other agents and the human manager |
|
|
262
287
|
| `POST` | `/worker/set-status` | Set or clear status. Body: `{ status }` |
|
|
263
288
|
| `POST` | `/worker/send-message` | Send a message. Body: `{ to: string[], body }` |
|
|
264
|
-
| `GET` | `/worker/crons` | List own cron jobs |
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `
|
|
268
|
-
| `POST` | `/worker/crons/:id/
|
|
289
|
+
| `GET` | `/worker/crons` | List own approved cron jobs |
|
|
290
|
+
| `GET` | `/worker/cron-requests` | List own cron job requests (pending/approved/rejected) |
|
|
291
|
+
| `POST` | `/worker/cron-requests` | Request a new cron job (requires human approval) |
|
|
292
|
+
| `DELETE` | `/worker/crons/:id` | Delete own approved cron job |
|
|
293
|
+
| `POST` | `/worker/crons/:id/enable` | Enable own approved cron job |
|
|
294
|
+
| `POST` | `/worker/crons/:id/disable` | Disable own approved cron job |
|
|
269
295
|
| `GET` | `/worker/crons/:id/history` | View own cron job history |
|
|
270
296
|
|
|
271
297
|
|
|
@@ -278,7 +304,8 @@ The server uses PostgreSQL with automatic migrations. Tables:
|
|
|
278
304
|
- **`sessions`** -- Maps coworker names to OpenCode session IDs with agent codes, agent modes, and status
|
|
279
305
|
- **`config`** -- Key-value store for application settings (`human_name`, `human_description`)
|
|
280
306
|
- **`messages`** -- Inter-agent and human-agent mail with read/injected tracking
|
|
281
|
-
- **`
|
|
307
|
+
- **`cron_requests`** -- Pending cron job requests requiring human approval with status, reviewer notes, and timestamps
|
|
308
|
+
- **`cron_jobs`** -- Approved scheduled tasks tied to sessions with cron expressions and timezone support
|
|
282
309
|
- **`cron_history`** -- Execution log for cron jobs with success/failure tracking
|
|
283
310
|
|
|
284
311
|
### Agentic Coding Server Abstraction
|
package/dist/cli.js
CHANGED
|
@@ -15,10 +15,101 @@ program
|
|
|
15
15
|
.option("--port <port>", "Port to serve on", "7654")
|
|
16
16
|
.option("--password <password>", "REQUIRED. API password", process.env.AGENT_OFFICE_PASSWORD)
|
|
17
17
|
.option("--opencode-url <url>", "URL of the OpenCode server (default: http://127.0.0.1:4096)", process.env.OPENCODE_URL ?? "http://127.0.0.1:4096")
|
|
18
|
+
.option("--pi-vendor <vendor>", "PI coding vendor (e.g., xai, openai, anthropic). Required when using PI coding server.", process.env.PI_VENDOR)
|
|
19
|
+
.option("--pi-model <model>", "PI coding model name (e.g., grok-code-fast-1). Required when using PI coding server.", process.env.PI_MODEL)
|
|
20
|
+
.option("--pi-api-key <key>", "PI coding API key. Required when using PI coding server.", process.env.PI_API_KEY)
|
|
18
21
|
.action(async (options) => {
|
|
19
22
|
const { serve } = await import("./commands/serve.js");
|
|
20
23
|
await serve(options);
|
|
21
24
|
});
|
|
25
|
+
const taskBoardCmd = program
|
|
26
|
+
.command("task-board")
|
|
27
|
+
.description("Manage a kanban-style task board with columns for task lifecycle tracking. Supports full CRUD operations, search, and analytics. Requires database connection (--database-url or --sqlite).")
|
|
28
|
+
.option("--database-url <url>", "PostgreSQL database connection string for storing task data", process.env.DATABASE_URL)
|
|
29
|
+
.option("--sqlite <path>", "Path to SQLite database file for storing task data (alternative to PostgreSQL)", process.env.AGENT_OFFICE_SQLITE);
|
|
30
|
+
taskBoardCmd
|
|
31
|
+
.command("list")
|
|
32
|
+
.description("Display all tasks on the board, ordered by creation date (newest first). Use --column to filter tasks by their current column status.")
|
|
33
|
+
.option("--column <column>", "Filter tasks to show only those in the specified column. Valid columns: idea, approved idea, working on, blocked, ready for review, done")
|
|
34
|
+
.action(async (cmdOptions) => {
|
|
35
|
+
const { listTasks } = await import("./commands/task-board.js");
|
|
36
|
+
const options = taskBoardCmd.opts();
|
|
37
|
+
await listTasks(options, cmdOptions);
|
|
38
|
+
});
|
|
39
|
+
taskBoardCmd
|
|
40
|
+
.command("add <title> <description>")
|
|
41
|
+
.description("Create a new task on the board. Title and description are required. Task starts in 'idea' column by default.")
|
|
42
|
+
.option("--assignee <assignee>", "Assign the task to a specific person or team member")
|
|
43
|
+
.option("--column <column>", "Place the task in a specific column (default: idea). Valid columns: idea, approved idea, working on, blocked, ready for review, done")
|
|
44
|
+
.option("--dependencies <deps>", "Specify task IDs this new task depends on, as a comma-separated list (e.g., '1,3,5')")
|
|
45
|
+
.action(async (title, description, cmdOptions) => {
|
|
46
|
+
const { addTask } = await import("./commands/task-board.js");
|
|
47
|
+
const options = taskBoardCmd.opts();
|
|
48
|
+
await addTask(options, title, description, cmdOptions);
|
|
49
|
+
});
|
|
50
|
+
taskBoardCmd
|
|
51
|
+
.command("update <id>")
|
|
52
|
+
.description("Modify an existing task by its ID. You can update any combination of title, description, assignee, column, or dependencies. Only specified fields will be changed.")
|
|
53
|
+
.option("--title <title>", "Update the task's title")
|
|
54
|
+
.option("--description <desc>", "Update the task's description")
|
|
55
|
+
.option("--assignee <assignee>", "Change or set the task assignee")
|
|
56
|
+
.option("--column <column>", "Move the task to a different column. Valid columns: idea, approved idea, working on, blocked, ready for review, done")
|
|
57
|
+
.option("--dependencies <deps>", "Update the task dependencies as a comma-separated list of task IDs (e.g., '2,4')")
|
|
58
|
+
.action(async (id, cmdOptions) => {
|
|
59
|
+
const { updateTask } = await import("./commands/task-board.js");
|
|
60
|
+
const options = taskBoardCmd.opts();
|
|
61
|
+
await updateTask(options, id, cmdOptions);
|
|
62
|
+
});
|
|
63
|
+
taskBoardCmd
|
|
64
|
+
.command("delete <id>")
|
|
65
|
+
.description("Permanently remove a task from the board by its ID. This action cannot be undone.")
|
|
66
|
+
.action(async (id) => {
|
|
67
|
+
const { deleteTask } = await import("./commands/task-board.js");
|
|
68
|
+
const options = taskBoardCmd.opts();
|
|
69
|
+
await deleteTask(options, id);
|
|
70
|
+
});
|
|
71
|
+
taskBoardCmd
|
|
72
|
+
.command("move <id> <column>")
|
|
73
|
+
.description("Change a task's status by moving it to a different column on the board. This is a quick way to update task progress.")
|
|
74
|
+
.action(async (id, column) => {
|
|
75
|
+
const { moveTask } = await import("./commands/task-board.js");
|
|
76
|
+
const options = taskBoardCmd.opts();
|
|
77
|
+
await moveTask(options, id, column);
|
|
78
|
+
});
|
|
79
|
+
taskBoardCmd
|
|
80
|
+
.command("search <query>")
|
|
81
|
+
.description("Find tasks that contain the query string at the beginning of their title or description (case-insensitive prefix search). Combine with filters for more precise results.")
|
|
82
|
+
.option("--assignee <assignee>", "Only show tasks assigned to the specified person")
|
|
83
|
+
.option("--column <column>", "Only show tasks in the specified column. Valid columns: idea, approved idea, working on, blocked, ready for review, done")
|
|
84
|
+
.action(async (query, cmdOptions) => {
|
|
85
|
+
const { searchTasks } = await import("./commands/task-board.js");
|
|
86
|
+
const options = taskBoardCmd.opts();
|
|
87
|
+
await searchTasks(options, query, cmdOptions);
|
|
88
|
+
});
|
|
89
|
+
taskBoardCmd
|
|
90
|
+
.command("assign <id> <assignee>")
|
|
91
|
+
.description("Assign an existing task to a specific person or team member. This updates only the assignee field without modifying other task properties.")
|
|
92
|
+
.action(async (id, assignee) => {
|
|
93
|
+
const { assignTask } = await import("./commands/task-board.js");
|
|
94
|
+
const options = taskBoardCmd.opts();
|
|
95
|
+
await assignTask(options, id, assignee);
|
|
96
|
+
});
|
|
97
|
+
taskBoardCmd
|
|
98
|
+
.command("show <id>")
|
|
99
|
+
.description("Display complete details for a specific task, including all fields, timestamps, and dependency information. Useful for getting full context about a task.")
|
|
100
|
+
.action(async (id) => {
|
|
101
|
+
const { showTask } = await import("./commands/task-board.js");
|
|
102
|
+
const options = taskBoardCmd.opts();
|
|
103
|
+
await showTask(options, id);
|
|
104
|
+
});
|
|
105
|
+
taskBoardCmd
|
|
106
|
+
.command("stats")
|
|
107
|
+
.description("Show comprehensive statistics about the entire task board, including total task count, distribution across columns, and assignment breakdown.")
|
|
108
|
+
.action(async () => {
|
|
109
|
+
const { showStats } = await import("./commands/task-board.js");
|
|
110
|
+
const options = taskBoardCmd.opts();
|
|
111
|
+
await showStats(options);
|
|
112
|
+
});
|
|
22
113
|
program
|
|
23
114
|
.command("manage")
|
|
24
115
|
.description("[HUMAN ONLY] Launch the interactive TUI to manage sessions")
|
|
@@ -136,9 +227,9 @@ cronCmd
|
|
|
136
227
|
await listCrons(token);
|
|
137
228
|
});
|
|
138
229
|
cronCmd
|
|
139
|
-
.command("
|
|
230
|
+
.command("request")
|
|
140
231
|
.argument("<token>", "Agent token in the format <agent_code>@<server-url>")
|
|
141
|
-
.description("
|
|
232
|
+
.description("Request a new cron job (requires human approval)")
|
|
142
233
|
.requiredOption("--name <name>", "Cron job name")
|
|
143
234
|
.requiredOption("--schedule <schedule>", "Cron expression (e.g., '0 9 * * *' for daily at 9am)")
|
|
144
235
|
.requiredOption("--message <message>", "Action to perform when job fires")
|
|
@@ -154,8 +245,16 @@ Warning:
|
|
|
154
245
|
The safest option is always single quotes: --message 'your message here'
|
|
155
246
|
(single quotes prevent all shell interpretation)`)
|
|
156
247
|
.action(async (token, options) => {
|
|
157
|
-
const {
|
|
158
|
-
await
|
|
248
|
+
const { requestCron } = await import("./commands/worker.js");
|
|
249
|
+
await requestCron(token, options);
|
|
250
|
+
});
|
|
251
|
+
cronCmd
|
|
252
|
+
.command("requests")
|
|
253
|
+
.argument("<token>", "Agent token in the format <agent_code>@<server-url>")
|
|
254
|
+
.description("List your cron job requests and their status")
|
|
255
|
+
.action(async (token) => {
|
|
256
|
+
const { listCronRequests } = await import("./commands/worker.js");
|
|
257
|
+
await listCronRequests(token);
|
|
159
258
|
});
|
|
160
259
|
cronCmd
|
|
161
260
|
.command("delete")
|