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 CHANGED
@@ -1,20 +1,20 @@
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, persistent memory, 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, scheduled tasks, and a terminal UI for human oversight.
4
4
 
5
5
  ## Overview
6
6
 
7
- `agent-office` sits between a human operator and one or more AI coding agents running on an OpenCode server. It wraps raw OpenCode sessions with named identities, a messaging system, cron-based scheduling, and per-agent memory -- creating an "office" where AI agents are coworkers that communicate, take direction, and operate autonomously.
7
+ `agent-office` sits between a human operator and one or more AI coding agents running on an OpenCode server. It wraps raw OpenCode sessions with named identities, a messaging system, and cron-based scheduling -- creating an "office" where AI agents are coworkers that communicate, take direction, and operate autonomously.
8
8
 
9
9
  **For humans**, there are three interfaces:
10
10
 
11
- - **`serve`** -- an HTTP server that manages sessions, messages, cron jobs, and memory, backed by PostgreSQL and a running OpenCode server
11
+ - **`serve`** -- an HTTP server that manages sessions, messages, and cron jobs, backed by PostgreSQL and a running OpenCode server
12
12
  - **`manage`** -- a full-screen terminal UI (React Ink) for creating coworkers, sending messages, browsing mail, managing cron jobs, 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, manage cron jobs, and store memories
17
+ - **`worker`** -- subcommands that agents invoke from within their OpenCode sessions to clock in, message coworkers, set status, and manage cron jobs
18
18
 
19
19
  ```
20
20
  +---------------------+
@@ -27,10 +27,10 @@ An office for your AI agents. Manage multiple [OpenCode](https://opencode.ai) co
27
27
  | Ink/React | | :7654 | | |
28
28
  +--------------+ | | +--------------+
29
29
  | CronScheduler |
30
- +--------------+ | MemoryManager | +--------------+
31
- | Communicator |<-->| |<-->| .memory/ |
32
- | Web (HTMX) | | | | SQLite DBs |
33
- | :7655 | +---------+-----------+ +--------------+
30
+ +--------------+ | |
31
+ | Communicator |<-->| |
32
+ | Web (HTMX) | | |
33
+ | :7655 | +---------+-----------+
34
34
  +--------------+ |
35
35
  | /worker/* endpoints
36
36
  +---------+-----------+
@@ -69,7 +69,7 @@ agent-office serve \
69
69
  --password mysecret
70
70
  ```
71
71
 
72
- The server runs migrations automatically on startup, initializes the cron scheduler, and warms up the embedding model for agent memory.
72
+ The server runs migrations automatically on startup and initializes the cron scheduler.
73
73
 
74
74
  ### 2. Open the manager
75
75
 
@@ -84,7 +84,7 @@ From the TUI you can create coworkers, send them messages, browse mail, manage c
84
84
  ### 3. Chat with a coworker in the browser
85
85
 
86
86
  ```bash
87
- agent-office communicator web "Alice" --secret mysecret
87
+ agent-office communicator web "Alice" --password mysecret
88
88
  ```
89
89
 
90
90
  Opens a web-based chat interface at `http://127.0.0.1:7655` for real-time conversation with the named coworker.
@@ -97,11 +97,11 @@ 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, create cron jobs, and store persistent memories.
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.
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
 
104
- 5. **Reset** -- A session can be reverted to its initial state (clearing all conversation history) and re-enrolled. Memories persist across resets since they are stored separately.
104
+ 5. **Reset** - - A session can be reverted to its initial state (clearing all conversation history) and re-enrolled.
105
105
 
106
106
  6. **Delete** -- Deleting a coworker removes both the OpenCode session and the database record.
107
107
 
@@ -139,7 +139,7 @@ cp .env.example .env
139
139
 
140
140
  ### `agent-office serve`
141
141
 
142
- Starts the HTTP server. Connects to PostgreSQL, runs migrations, initializes the cron scheduler, and warms up the embedding model.
142
+ Starts the HTTP server. Connects to PostgreSQL, runs migrations, and initializes the cron scheduler.
143
143
 
144
144
  ```
145
145
  Options:
@@ -147,7 +147,6 @@ Options:
147
147
  --opencode-url <url> OpenCode server URL (default: http://localhost:4096)
148
148
  --host <host> Bind host (default: 127.0.0.1)
149
149
  --port <port> Bind port (default: 7654)
150
- --memory-path <path> Directory for memory storage (default: ./.memory)
151
150
  --password <password> REQUIRED. API password (env: AGENT_OFFICE_PASSWORD)
152
151
  ```
153
152
 
@@ -165,7 +164,7 @@ Options:
165
164
 
166
165
  | Screen | Description |
167
166
  |---|---|
168
- | **Coworkers** | Table of all agents showing name, status, mode, session ID, and masked agent code. Keyboard: `c` create, `d` delete, `r` reveal code, `g` regenerate code, `x` revert session, `X` revert all, `t` tail messages, `i` inject text, `m` coworker mail, `M` memories |
167
+ | **Coworkers** | Table of all agents showing name, status, mode, session ID, and masked agent code. Keyboard: `c` create, `d` delete, `r` reveal code, `g` regenerate code, `x` revert session, `X` revert all, `t` tail messages, `i` inject text, `m` coworker mail |
169
168
  | **Send message** | Select a recipient and compose a message |
170
169
  | **My mail** | View received and sent messages. `r` reply, `m` mark read, `a` mark all read. Tab between received/sent |
171
170
  | **Cron jobs** | Table of scheduled tasks with name, coworker, schedule, next run, and status. Create, delete, enable/disable, view history |
@@ -183,7 +182,7 @@ Arguments:
183
182
 
184
183
  Options:
185
184
  --url <url> Server URL (default: http://127.0.0.1:7654)
186
- --secret <secret> API password (env: AGENT_OFFICE_PASSWORD)
185
+ --password <password> API password (env: AGENT_OFFICE_PASSWORD)
187
186
  --host <host> Communicator bind host (default: 127.0.0.1)
188
187
  --port <port> Communicator bind port (default: 7655)
189
188
  ```
@@ -217,11 +216,6 @@ agent-office worker cron enable <token> <id>
217
216
  agent-office worker cron disable <token> <id>
218
217
  agent-office worker cron history <token> <id>
219
218
 
220
- # Persistent memory (survives session resets)
221
- agent-office worker memory add --content "The auth module uses JWT with RS256" <token>
222
- agent-office worker memory search --query "authentication" <token>
223
- agent-office worker memory list <token>
224
- agent-office worker memory forget <token> <memory-id>
225
219
  ```
226
220
 
227
221
  ## REST API
@@ -251,12 +245,7 @@ agent-office worker memory forget <token> <memory-id>
251
245
  | `POST` | `/crons/:id/enable` | Enable a cron job |
252
246
  | `POST` | `/crons/:id/disable` | Disable a cron job |
253
247
  | `GET` | `/crons/:id/history` | Cron execution history. Query: `?limit=N` |
254
- | `GET` | `/sessions/:name/memories` | List memories. Query: `?limit=N` |
255
- | `POST` | `/sessions/:name/memories` | Add a memory. Body: `{ content, metadata? }` |
256
- | `GET` | `/sessions/:name/memories/:id` | Get a memory |
257
- | `PUT` | `/sessions/:name/memories/:id` | Update a memory. Body: `{ content, metadata? }` |
258
- | `DELETE` | `/sessions/:name/memories/:id` | Delete a memory |
259
- | `POST` | `/sessions/:name/memories/search` | Search memories. Body: `{ query, limit? }` |
248
+
260
249
 
261
250
  ### Worker Endpoints (agent code auth via `?code=<uuid>`)
262
251
 
@@ -272,10 +261,7 @@ agent-office worker memory forget <token> <memory-id>
272
261
  | `POST` | `/worker/crons/:id/enable` | Enable own cron job |
273
262
  | `POST` | `/worker/crons/:id/disable` | Disable own cron job |
274
263
  | `GET` | `/worker/crons/:id/history` | View own cron job history |
275
- | `POST` | `/worker/memory/add` | Add a memory |
276
- | `POST` | `/worker/memory/search` | Search memories |
277
- | `GET` | `/worker/memory/list` | List memories |
278
- | `DELETE` | `/worker/memory/:memoryId` | Delete a memory |
264
+
279
265
 
280
266
  ## Architecture
281
267
 
@@ -289,14 +275,6 @@ The server uses PostgreSQL with automatic migrations. Tables:
289
275
  - **`cron_jobs`** -- Scheduled tasks tied to sessions with cron expressions and timezone support
290
276
  - **`cron_history`** -- Execution log for cron jobs with success/failure tracking
291
277
 
292
- ### Memory System
293
-
294
- Each agent gets a private SQLite database (via [fastmemory](https://github.com/nichochar/fastmemory)) stored in the `--memory-path` directory. Memories support:
295
-
296
- - **Hybrid search** -- BM25 full-text search combined with vector semantic search using Reciprocal Rank Fusion
297
- - **Persistence across resets** -- Memories are stored outside the OpenCode session, so they survive session reverts
298
- - **Quantized embeddings** -- Uses `q4` dtype for efficient storage
299
-
300
278
  ### Agentic Coding Server Abstraction
301
279
 
302
280
  The application does not depend on the OpenCode SDK directly. Instead, all OpenCode interactions go through an `AgenticCodingServer` interface (`src/lib/agentic-coding-server.ts`) with six methods:
@@ -330,7 +308,7 @@ npm run dev:serve -- --password secret
330
308
  npm run dev:manage -- --password secret
331
309
 
332
310
  # Run communicator (in another terminal)
333
- npm run dev:communicator -- "Alice" --secret secret
311
+ npm run dev:communicator -- "Alice" --password secret
334
312
 
335
313
  # Build
336
314
  npm run build
package/dist/cli.js CHANGED
@@ -13,10 +13,8 @@ program
13
13
  .option("--sqlite <path>", "SQLite database file path (alternative to PostgreSQL)", process.env.AGENT_OFFICE_SQLITE)
14
14
  .option("--host <host>", "Host to bind to", "127.0.0.1")
15
15
  .option("--port <port>", "Port to serve on", "7654")
16
- .option("--memory-path <path>", "Directory for memory storage (default: ./.memory)", "./.memory")
17
16
  .option("--password <password>", "REQUIRED. API password", process.env.AGENT_OFFICE_PASSWORD)
18
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")
19
- .option("--simple-memory", "Use lightweight SQLite+BM25 memory instead of the default fastmemory (no embeddings, no model download)")
20
18
  .action(async (options) => {
21
19
  const { serve } = await import("./commands/serve.js");
22
20
  await serve(options);
@@ -34,36 +32,39 @@ const appCmd = program
34
32
  .command("app")
35
33
  .description("[HUMAN ONLY] Interactive visual applications");
36
34
  appCmd
37
- .command("coworker-chat-web")
38
- .description("[HUMAN ONLY] Launch a web chat interface for a single coworker")
39
- .argument("<coworker>", "Name of the coworker to chat with (e.g. 'Howard Roark')")
35
+ .command("chat")
36
+ .description("Web chat interface for human to chat to coworkers")
40
37
  .option("--url <url>", "URL of the agent-office serve endpoint (e.g. http://127.0.0.1:7654)", process.env.AGENT_OFFICE_URL ?? "http://127.0.0.1:7654")
41
- .option("--secret <secret>", "API password for the agent-office server", process.env.AGENT_OFFICE_PASSWORD)
38
+ .option("--password <password>", "API password for the agent-office server", process.env.AGENT_OFFICE_PASSWORD ?? "secret")
42
39
  .option("--host <host>", "Host to bind the web server to", "127.0.0.1")
43
40
  .option("--port <port>", "Port to run the web server on", "7655")
44
- .action(async (coworker, options) => {
45
- if (!options.secret) {
46
- console.error("Error: --secret is required (or set AGENT_OFFICE_PASSWORD)");
47
- process.exit(1);
48
- }
41
+ .action(async (options) => {
49
42
  const { appCoworkerChatWeb } = await import("./commands/communicator.js");
50
- await appCoworkerChatWeb(coworker, options);
43
+ await appCoworkerChatWeb(options);
51
44
  });
52
- appCmd
53
- .command("screensaver")
54
- .description("[HUMAN ONLY] Launch a visualization of recent mail activity (live screensaver)")
45
+ appCmd.command("screensaver")
46
+ .description("3D visualization of recent mail activity")
55
47
  .option("--url <url>", "URL of the agent-office serve endpoint (e.g. http://127.0.0.1:7654)", process.env.AGENT_OFFICE_URL ?? "http://127.0.0.1:7654")
56
- .option("--secret <secret>", "API password for the agent-office server", process.env.AGENT_OFFICE_PASSWORD)
48
+ .option("--password <password>", "API password for the agent-office server", process.env.AGENT_OFFICE_PASSWORD ?? "secret")
57
49
  .option("--host <host>", "Host to bind the screensaver web server to", "127.0.0.1")
58
50
  .option("--port <port>", "Port to run the screensaver web server on", "7656")
59
51
  .action(async (options) => {
60
- if (!options.secret) {
61
- console.error("Error: --secret is required (or set AGENT_OFFICE_PASSWORD)");
62
- process.exit(1);
63
- }
64
52
  const { appScreensaver } = await import("./commands/screensaver.js");
65
53
  await appScreensaver(options);
66
54
  });
55
+ appCmd
56
+ .command('notifier')
57
+ .description('Notify human by email when unread messages have been waiting over certain amount of time')
58
+ .option('--agent-office-url <url>', 'Agent Office server URL', process.env.AGENT_OFFICE_URL ?? 'http://127.0.0.1:7654')
59
+ .option('--password <pw>', 'API password', process.env.AGENT_OFFICE_PASSWORD)
60
+ .option('--to-email <email>', 'Recipient email address', process.env.TO_EMAIL)
61
+ .option('--resend-api-key <key>', 'Resend API key', process.env.RESEND_API_KEY)
62
+ .option('--domain <domain>', 'Sender domain (e.g. coworker.innercontext.com)', process.env.EMAIL_DOMAIN)
63
+ .option('--wait-minutes <minutes>', 'Minutes a message must be unread before notifying', '15')
64
+ .action(async (options) => {
65
+ const { notifier } = await import('./commands/notifier.js');
66
+ await notifier(options);
67
+ });
67
68
  const workerCmd = program
68
69
  .command("worker")
69
70
  .description("Worker agent commands");
@@ -123,7 +124,8 @@ cronCmd
123
124
  .description("Create a new cron job")
124
125
  .requiredOption("--name <name>", "Cron job name")
125
126
  .requiredOption("--schedule <schedule>", "Cron expression (e.g., '0 9 * * *' for daily at 9am)")
126
- .requiredOption("--message <message>", "Message to inject when job fires")
127
+ .requiredOption("--message <message>", "Action to perform when job fires")
128
+ .requiredOption("--respond-to <respondTo>", "Who to respond to when done")
127
129
  .option("--timezone <timezone>", "IANA timezone (e.g., 'America/New_York')")
128
130
  .action(async (token, options) => {
129
131
  const { createCron } = await import("./commands/worker.js");
@@ -186,47 +188,4 @@ cronCmd
186
188
  const { cronHistory } = await import("./commands/worker.js");
187
189
  await cronHistory(token, cronId);
188
190
  });
189
- // ── Worker Memory Commands (nested) ──────────────────────────────────────────
190
- const memoryCmd = workerCmd
191
- .command("memory")
192
- .description("Manage your persistent memories");
193
- memoryCmd
194
- .command("add")
195
- .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
196
- .description("Add a new memory")
197
- .requiredOption("--content <content>", "Memory content to store")
198
- .action(async (token, options) => {
199
- const { memoryAdd } = await import("./commands/worker.js");
200
- await memoryAdd(token, options.content);
201
- });
202
- memoryCmd
203
- .command("search")
204
- .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
205
- .description("Search memories using hybrid search (keyword + semantic)")
206
- .requiredOption("--query <query>", "Search query")
207
- .option("--limit <limit>", "Maximum results (default 10)", "10")
208
- .action(async (token, options) => {
209
- const limit = parseInt(options.limit ?? "10", 10);
210
- const { memorySearch } = await import("./commands/worker.js");
211
- await memorySearch(token, options.query, limit);
212
- });
213
- memoryCmd
214
- .command("list")
215
- .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
216
- .description("List all stored memories")
217
- .option("--limit <limit>", "Maximum memories to list (default 50)", "50")
218
- .action(async (token, options) => {
219
- const limit = parseInt(options.limit ?? "50", 10);
220
- const { memoryList } = await import("./commands/worker.js");
221
- await memoryList(token, limit);
222
- });
223
- memoryCmd
224
- .command("forget")
225
- .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
226
- .argument("<memoryId>", "ID of the memory to forget")
227
- .description("Delete a memory by ID")
228
- .action(async (token, memoryId) => {
229
- const { memoryForget } = await import("./commands/worker.js");
230
- await memoryForget(token, memoryId);
231
- });
232
191
  program.parse();
@@ -1,8 +1,8 @@
1
1
  interface CommunicatorOptions {
2
2
  url: string;
3
- secret: string;
3
+ password: string;
4
4
  host: string;
5
5
  port: string;
6
6
  }
7
- export declare function appCoworkerChatWeb(coworker: string, options: CommunicatorOptions): Promise<void>;
7
+ export declare function appCoworkerChatWeb(options: CommunicatorOptions): Promise<void>;
8
8
  export {};