agent-office 0.4.6 → 0.4.8

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 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, and a terminal UI for human oversight.
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 jobs, backed by PostgreSQL and a running OpenCode server
12
- - **`manage`** -- a full-screen terminal UI (React Ink) for creating coworkers, sending messages, browsing mail, managing cron jobs, and observing agent activity
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 manage cron jobs
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
  +---------------------+
@@ -97,7 +97,7 @@ Opens a web-based chat interface at `http://127.0.0.1:7655` for real-time conver
97
97
 
98
98
  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
99
 
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 create cron jobs.
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 **request cron jobs** (which require human approval).
101
101
 
102
102
  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
103
 
@@ -113,6 +113,22 @@ Agent sessions are private by design. The welcome message tells each agent:
113
113
 
114
114
  This means agents must actively communicate to report progress, ask questions, or share results.
115
115
 
116
+ ### Cron Job Approval Workflow
117
+
118
+ To maintain human oversight over automated tasks, cron jobs require approval before becoming active:
119
+
120
+ 1. **Request** -- AI agents use `agent-office worker cron request` to submit a cron job request with name, schedule, message, and timezone
121
+ 2. **Review** -- Human managers see pending requests in the TUI under "Cron requests" or via API
122
+ 3. **Approve/Reject** -- Managers can approve requests (creates active cron job) or reject with notes
123
+ 4. **Notification** -- Workers receive messages about approval/rejection with manager feedback
124
+ 5. **Active Jobs** -- Approved cron jobs appear in `agent-office worker cron list` and execute according to schedule
125
+
126
+ This workflow ensures:
127
+ - No automated tasks run without human review
128
+ - Managers can provide feedback on scheduling decisions
129
+ - Clear audit trail of all cron job requests and decisions
130
+ - Workers understand the approval process and can resubmit if needed
131
+
116
132
  ### Authentication
117
133
 
118
134
  Two authentication schemes run in parallel:
@@ -168,6 +184,7 @@ Options:
168
184
  | **Send message** | Select a recipient and compose a message |
169
185
  | **My mail** | View received and sent messages. `r` reply, `m` mark read, `a` mark all read. Tab between received/sent |
170
186
  | **Cron jobs** | Table of scheduled tasks with name, coworker, schedule, next run, and status. Create, delete, enable/disable, view history |
187
+ | **Cron requests** | Approve or reject pending cron job requests from workers. View request details, approve with notes, reject with reason |
171
188
  | **My profile** | Set your display name and description (visible to agents in their welcome message) |
172
189
 
173
190
  A sidebar shows all coworkers with live status indicators, refreshed every 5 seconds. An unread mail badge appears in the header.
@@ -185,9 +202,10 @@ Options:
185
202
  --password <password> API password (env: AGENT_OFFICE_PASSWORD)
186
203
  --host <host> Communicator bind host (default: 127.0.0.1)
187
204
  --port <port> Communicator bind port (default: 7655)
205
+ --xai-key <key> xAI API key for voice chat (enables voice button)
188
206
  ```
189
207
 
190
- Features: dark theme, iMessage-style chat bubbles, auto-scroll, Enter to send (Shift+Enter for newline), live message polling (5s), unread indicators, status display, and a reset button to revert the agent's session.
208
+ Features: dark theme, iMessage-style chat bubbles, auto-scroll, Enter to send (Shift+Enter for newline), live message polling (5s), unread indicators, status display, and a reset button to revert the agent's session. **Voice mode**: When an xAI API key is provided, a microphone button appears for voice conversations with full tool access (read/write/edit/bash).
191
209
 
192
210
  ### `agent-office worker` (for AI agents)
193
211
 
@@ -213,14 +231,15 @@ agent-office worker send-message --name Alice --body "Cost is \$50" <token>
213
231
  # > Warning: always escape $ in --body when using double quotes in bash, or use single quotes.
214
232
  # > Unescaped $ will be silently expanded by the shell before the message is sent.
215
233
 
216
- # Cron job management
217
- agent-office worker cron list <token>
218
- agent-office worker cron create --name "daily-report" --schedule "0 9 * * *" --message "Send daily status" <token>
219
- agent-office worker cron create --name "weekly" --schedule "0 9 * * 1" --message "Weekly sync" --timezone "America/New_York" <token>
220
- agent-office worker cron delete <token> <id>
221
- agent-office worker cron enable <token> <id>
222
- agent-office worker cron disable <token> <id>
223
- agent-office worker cron history <token> <id>
234
+ # Cron job management (requires human approval)
235
+ agent-office worker cron list <token> # View your approved cron jobs
236
+ agent-office worker cron request --name "daily-report" --schedule "0 9 * * *" --message "Send daily status" --respond-to "Manager" <token>
237
+ agent-office worker cron request --name "weekly" --schedule "0 9 * * 1" --message "Weekly sync" --timezone "America/New_York" --respond-to "Manager" <token>
238
+ agent-office worker cron requests <token> # View status of all your requests (pending/approved/rejected)
239
+ agent-office worker cron delete <token> <id> # Delete your approved cron jobs
240
+ agent-office worker cron enable <token> <id> # Enable your approved cron jobs
241
+ agent-office worker cron disable <token> <id> # Disable your approved cron jobs
242
+ agent-office worker cron history <token> <id> # View execution history
224
243
 
225
244
  ```
226
245
 
@@ -245,12 +264,15 @@ agent-office worker cron history <token> <id>
245
264
  | `GET` | `/messages/:name` | Get messages for a person. Query: `?sent=true`, `?unread_only=true` |
246
265
  | `POST` | `/messages` | Send a message. Body: `{ from, to: string[], body }` |
247
266
  | `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? }` |
267
+ | `GET` | `/crons` | List approved cron jobs. Query: `?session_name=<name>` |
268
+ | `POST` | `/crons` | Create a cron job (admin only). Body: `{ name, session_name, schedule, message, timezone? }` |
250
269
  | `DELETE` | `/crons/:id` | Delete a cron job |
251
270
  | `POST` | `/crons/:id/enable` | Enable a cron job |
252
271
  | `POST` | `/crons/:id/disable` | Disable a cron job |
253
272
  | `GET` | `/crons/:id/history` | Cron execution history. Query: `?limit=N` |
273
+ | `GET` | `/cron-requests` | List all cron requests. Query: `?status=<status>&session_name=<name>` |
274
+ | `POST` | `/cron-requests/:id/approve` | Approve a cron request. Body: `{ notes? }` |
275
+ | `POST` | `/cron-requests/:id/reject` | Reject a cron request. Body: `{ notes? }` |
254
276
 
255
277
 
256
278
  ### Worker Endpoints (agent code auth via `?code=<uuid>`)
@@ -261,11 +283,12 @@ agent-office worker cron history <token> <id>
261
283
  | `GET` | `/worker/list-coworkers` | List all other agents and the human manager |
262
284
  | `POST` | `/worker/set-status` | Set or clear status. Body: `{ status }` |
263
285
  | `POST` | `/worker/send-message` | Send a message. Body: `{ to: string[], body }` |
264
- | `GET` | `/worker/crons` | List own cron jobs |
265
- | `POST` | `/worker/crons` | Create a cron job |
266
- | `DELETE` | `/worker/crons/:id` | Delete own cron job |
267
- | `POST` | `/worker/crons/:id/enable` | Enable own cron job |
268
- | `POST` | `/worker/crons/:id/disable` | Disable own cron job |
286
+ | `GET` | `/worker/crons` | List own approved cron jobs |
287
+ | `GET` | `/worker/cron-requests` | List own cron job requests (pending/approved/rejected) |
288
+ | `POST` | `/worker/cron-requests` | Request a new cron job (requires human approval) |
289
+ | `DELETE` | `/worker/crons/:id` | Delete own approved cron job |
290
+ | `POST` | `/worker/crons/:id/enable` | Enable own approved cron job |
291
+ | `POST` | `/worker/crons/:id/disable` | Disable own approved cron job |
269
292
  | `GET` | `/worker/crons/:id/history` | View own cron job history |
270
293
 
271
294
 
@@ -278,7 +301,8 @@ The server uses PostgreSQL with automatic migrations. Tables:
278
301
  - **`sessions`** -- Maps coworker names to OpenCode session IDs with agent codes, agent modes, and status
279
302
  - **`config`** -- Key-value store for application settings (`human_name`, `human_description`)
280
303
  - **`messages`** -- Inter-agent and human-agent mail with read/injected tracking
281
- - **`cron_jobs`** -- Scheduled tasks tied to sessions with cron expressions and timezone support
304
+ - **`cron_requests`** -- Pending cron job requests requiring human approval with status, reviewer notes, and timestamps
305
+ - **`cron_jobs`** -- Approved scheduled tasks tied to sessions with cron expressions and timezone support
282
306
  - **`cron_history`** -- Execution log for cron jobs with success/failure tracking
283
307
 
284
308
  ### Agentic Coding Server Abstraction
package/dist/cli.js CHANGED
@@ -22,6 +22,94 @@ program
22
22
  const { serve } = await import("./commands/serve.js");
23
23
  await serve(options);
24
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
+ });
25
113
  program
26
114
  .command("manage")
27
115
  .description("[HUMAN ONLY] Launch the interactive TUI to manage sessions")
@@ -41,6 +129,7 @@ appCmd
41
129
  .option("--password <password>", "API password for the agent-office server", process.env.AGENT_OFFICE_PASSWORD ?? "secret")
42
130
  .option("--host <host>", "Host to bind the web server to", "127.0.0.1")
43
131
  .option("--port <port>", "Port to run the web server on", "7655")
132
+ .option("--xai-key <key>", "xAI API key for voice chat (enables voice button)", process.env.XAI_API_KEY)
44
133
  .action(async (options) => {
45
134
  const { appCoworkerChatWeb } = await import("./commands/communicator.js");
46
135
  await appCoworkerChatWeb(options);
@@ -139,9 +228,9 @@ cronCmd
139
228
  await listCrons(token);
140
229
  });
141
230
  cronCmd
142
- .command("create")
231
+ .command("request")
143
232
  .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
144
- .description("Create a new cron job")
233
+ .description("Request a new cron job (requires human approval)")
145
234
  .requiredOption("--name <name>", "Cron job name")
146
235
  .requiredOption("--schedule <schedule>", "Cron expression (e.g., '0 9 * * *' for daily at 9am)")
147
236
  .requiredOption("--message <message>", "Action to perform when job fires")
@@ -157,8 +246,16 @@ Warning:
157
246
  The safest option is always single quotes: --message 'your message here'
158
247
  (single quotes prevent all shell interpretation)`)
159
248
  .action(async (token, options) => {
160
- const { createCron } = await import("./commands/worker.js");
161
- await createCron(token, options);
249
+ const { requestCron } = await import("./commands/worker.js");
250
+ await requestCron(token, options);
251
+ });
252
+ cronCmd
253
+ .command("requests")
254
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
255
+ .description("List your cron job requests and their status")
256
+ .action(async (token) => {
257
+ const { listCronRequests } = await import("./commands/worker.js");
258
+ await listCronRequests(token);
162
259
  });
163
260
  cronCmd
164
261
  .command("delete")
@@ -3,6 +3,7 @@ interface CommunicatorOptions {
3
3
  password: string;
4
4
  host: string;
5
5
  port: string;
6
+ xaiKey?: string;
6
7
  }
7
8
  export declare function appCoworkerChatWeb(options: CommunicatorOptions): Promise<void>;
8
9
  export {};