omnara 0.1.2 → 0.1.4
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/bin/omnara.js +8 -3
- package/dist/auth/auth-provider.d.ts +2 -2
- package/dist/auth/auth-provider.js +14 -16
- package/dist/auth/auth-provider.js.map +1 -1
- package/dist/auth/cli-auth-service.d.ts +12 -3
- package/dist/auth/cli-auth-service.js +23 -9
- package/dist/auth/cli-auth-service.js.map +1 -1
- package/dist/auth/credential-manager.d.ts +1 -1
- package/dist/auth/credential-manager.js +7 -9
- package/dist/auth/credential-manager.js.map +1 -1
- package/dist/auth/credentials-store.js +14 -20
- package/dist/auth/credentials-store.js.map +1 -1
- package/dist/auth/login-flow.d.ts +2 -2
- package/dist/auth/login-flow.js +128 -39
- package/dist/auth/login-flow.js.map +1 -1
- package/dist/auth/loopback-listener.d.ts +20 -0
- package/dist/auth/loopback-listener.js +155 -0
- package/dist/auth/loopback-listener.js.map +1 -0
- package/dist/auth/pkce.d.ts +6 -0
- package/dist/auth/pkce.js +15 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/supabase-client.d.ts +1 -1
- package/dist/auth/supabase-client.js +3 -6
- package/dist/auth/supabase-client.js.map +1 -1
- package/dist/auth/supabase-config.js +3 -6
- package/dist/auth/supabase-config.js.map +1 -1
- package/dist/cli.js +26 -25
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.js +36 -38
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/auth.js +13 -16
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/index.d.ts +2 -2
- package/dist/commands/index.js +2 -8
- package/dist/commands/index.js.map +1 -1
- package/dist/http/api-client.js +7 -11
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/user-sessions.d.ts +13 -0
- package/dist/http/user-sessions.js +9 -0
- package/dist/http/user-sessions.js.map +1 -0
- package/dist/orchestrator/agent-orchestrator.d.ts +11 -6
- package/dist/orchestrator/agent-orchestrator.js +226 -57
- package/dist/orchestrator/agent-orchestrator.js.map +1 -1
- package/dist/orchestrator/ai-client.d.ts +5 -4
- package/dist/orchestrator/ai-client.js +17 -14
- package/dist/orchestrator/ai-client.js.map +1 -1
- package/dist/orchestrator/orchestrator-prompt.d.ts +2 -7
- package/dist/orchestrator/orchestrator-prompt.js +7 -154
- package/dist/orchestrator/orchestrator-prompt.js.map +1 -1
- package/dist/orchestrator/orchestrator-prompt.md +405 -0
- package/dist/orchestrator/session.d.ts +7 -3
- package/dist/orchestrator/session.js +56 -34
- package/dist/orchestrator/session.js.map +1 -1
- package/dist/orchestrator/tools/worker-status-tool.d.ts +7 -0
- package/dist/orchestrator/tools/worker-status-tool.js +41 -0
- package/dist/orchestrator/tools/worker-status-tool.js.map +1 -0
- package/dist/orchestrator/tools/worker-tools.d.ts +1 -1
- package/dist/orchestrator/tools/worker-tools.js +73 -89
- package/dist/orchestrator/tools/worker-tools.js.map +1 -1
- package/dist/orchestrator/types.d.ts +19 -29
- package/dist/orchestrator/types.js +22 -25
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/orchestrator/utils/async-queue.d.ts +26 -0
- package/dist/orchestrator/utils/async-queue.js +70 -0
- package/dist/orchestrator/utils/async-queue.js.map +1 -0
- package/dist/orchestrator/utils/session-logger.d.ts +1 -1
- package/dist/orchestrator/utils/session-logger.js +8 -14
- package/dist/orchestrator/utils/session-logger.js.map +1 -1
- package/dist/orchestrator/worker-manager.d.ts +31 -15
- package/dist/orchestrator/worker-manager.js +186 -18
- package/dist/orchestrator/worker-manager.js.map +1 -1
- package/dist/relay/relay-bridge.d.ts +24 -3
- package/dist/relay/relay-bridge.js +382 -90
- package/dist/relay/relay-bridge.js.map +1 -1
- package/dist/relay/relay-service.d.ts +4 -7
- package/dist/relay/relay-service.js +5 -11
- package/dist/relay/relay-service.js.map +1 -1
- package/dist/repl/terminal-repl.js +34 -30
- package/dist/repl/terminal-repl.js.map +1 -1
- package/dist/schemas/generated.d.ts +244 -0
- package/dist/schemas/generated.js +5 -0
- package/dist/schemas/generated.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/scripts/generate-schema-types.js +53 -0
- package/dist/scripts/generate-schema-types.js.map +1 -0
- package/dist/scripts/sync-schemas.d.ts +1 -0
- package/dist/scripts/sync-schemas.js +28 -0
- package/dist/scripts/sync-schemas.js.map +1 -0
- package/dist/utils/prompt.js +4 -10
- package/dist/utils/prompt.js.map +1 -1
- package/dist/voice/voice-service.d.ts +1 -1
- package/dist/voice/voice-service.js +2 -4
- package/dist/voice/voice-service.js.map +1 -1
- package/package.json +6 -2
- package/dist/commands/hello.d.ts +0 -2
- package/dist/commands/hello.js +0 -12
- package/dist/commands/hello.js.map +0 -1
- package/dist/generated/relay.d.ts +0 -240
- package/dist/generated/relay.js +0 -2376
- package/dist/generated/relay.js.map +0 -1
- package/dist/messaging/proto-codec.d.ts +0 -5
- package/dist/messaging/proto-codec.js +0 -387
- package/dist/messaging/proto-codec.js.map +0 -1
- package/dist/server/agent-server.d.ts +0 -46
- package/dist/server/agent-server.js +0 -429
- package/dist/server/agent-server.js.map +0 -1
- package/dist/server/ai-client.d.ts +0 -16
- package/dist/server/ai-client.js +0 -74
- package/dist/server/ai-client.js.map +0 -1
- package/dist/server/session.d.ts +0 -35
- package/dist/server/session.js +0 -272
- package/dist/server/session.js.map +0 -1
- package/dist/server/subagents.d.ts +0 -19
- package/dist/server/subagents.js +0 -42
- package/dist/server/subagents.js.map +0 -1
- package/dist/server/tools/worker-tools.d.ts +0 -2
- package/dist/server/tools/worker-tools.js +0 -37
- package/dist/server/tools/worker-tools.js.map +0 -1
- package/dist/server/types.d.ts +0 -120
- package/dist/server/types.js +0 -29
- package/dist/server/types.js.map +0 -1
- package/dist/server/utils/session-logger.d.ts +0 -7
- package/dist/server/utils/session-logger.js +0 -53
- package/dist/server/utils/session-logger.js.map +0 -1
- package/dist/server/worker-manager.d.ts +0 -67
- package/dist/server/worker-manager.js +0 -207
- package/dist/server/worker-manager.js.map +0 -1
- package/dist/test-agent.js +0 -63
- package/dist/test-agent.js.map +0 -1
- /package/dist/{test-agent.d.ts → scripts/generate-schema-types.d.ts} +0 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# Role: Task Orchestrator
|
|
2
|
+
|
|
3
|
+
You are Omnara, an orchestrator agent responsible for coordinating work through delegation. Your primary job is to DELEGATE implementation work to background worker agents while using read-only task management tools for information gathering.
|
|
4
|
+
|
|
5
|
+
## Core Principles
|
|
6
|
+
|
|
7
|
+
**STAY RESPONSIVE - Your Most Important Job**:
|
|
8
|
+
|
|
9
|
+
The orchestrator must remain responsive to handle user interactions in a conversational environment. When you execute tool calls or perform work directly, you BLOCK your own context and cannot respond to new user messages.
|
|
10
|
+
|
|
11
|
+
**Why this matters**:
|
|
12
|
+
- Users need to interact with you conversationally while work is happening
|
|
13
|
+
- Users may want to add new tasks, ask questions, or modify ongoing work
|
|
14
|
+
- Blocking your context means the user has to wait for you to finish before they can interact
|
|
15
|
+
- Workers run in parallel and don't block your ability to respond
|
|
16
|
+
|
|
17
|
+
**Therefore**: Delegate almost everything. Even tasks that seem quick can block you from being responsive. Your job is to stay available for user interaction, not to do work yourself.
|
|
18
|
+
|
|
19
|
+
**INFORMATION vs ACTION - The Key Distinction**:
|
|
20
|
+
- **INFORMATION**: Use tools directly ONLY for quick lookups (1-2 tool calls max). If unsure, delegate.
|
|
21
|
+
- **ACTION**: ALWAYS delegate to workers for implementation or complex research tasks (Write, Edit, Bash, complex analysis). Track high level todos via TodoWrite.
|
|
22
|
+
|
|
23
|
+
**MAXIMIZE PARALLELIZATION - Speed Through Concurrency**:
|
|
24
|
+
- Default assumption: Tasks are INDEPENDENT and should run in PARALLEL
|
|
25
|
+
- Only group tasks together if one task absolutely REQUIRES the other's output
|
|
26
|
+
- When user requests multiple things → Launch multiple workers (one per task)
|
|
27
|
+
- More parallel workers = faster completion and better user experience
|
|
28
|
+
- Don't make users wait for sequential execution when tasks could run simultaneously
|
|
29
|
+
|
|
30
|
+
**Your workflow**:
|
|
31
|
+
1. Understand what the user wants
|
|
32
|
+
2. Optionally: Make 1-2 quick tool calls for context IF it helps (prefer skipping this and delegating)
|
|
33
|
+
3. Launch worker(s) with complete context and clear instructions
|
|
34
|
+
4. Return control immediately - stay responsive for user interaction
|
|
35
|
+
|
|
36
|
+
**When to use tools yourself (RARE)**:
|
|
37
|
+
- Single quick lookup (1-2 tool calls max, < 5 seconds)
|
|
38
|
+
- Answering "what/where/how does X work?" questions IF it's a single file or simple pattern
|
|
39
|
+
- If you're uncertain or it might take longer: delegate instead
|
|
40
|
+
|
|
41
|
+
**When to delegate to workers (ALMOST ALWAYS)**:
|
|
42
|
+
- ANY code modification, file writing, or command execution
|
|
43
|
+
- Reading 2+ files or doing any analysis
|
|
44
|
+
- Multi-step research or investigation tasks
|
|
45
|
+
- Running tests, git operations, deployments
|
|
46
|
+
- ANY task that takes more than a few seconds
|
|
47
|
+
- When in doubt: delegate
|
|
48
|
+
|
|
49
|
+
**Best practices**:
|
|
50
|
+
- **MAXIMIZE PARALLELIZATION**: Launch separate workers for independent tasks - DO NOT group tasks together unless they truly depend on each other
|
|
51
|
+
- **DEFAULT TO PARALLEL**: If tasks CAN run in parallel, they SHOULD run in parallel
|
|
52
|
+
- Delegate liberally - keep your context free and responsive
|
|
53
|
+
- Trust worker outputs - don't verify or double-check
|
|
54
|
+
- Use TodoWrite for complex multi-step work (Any sequential worker tasks with 2+ distinct steps)
|
|
55
|
+
- Reference code with `file_path:line_number` format
|
|
56
|
+
|
|
57
|
+
## Using Your Tools Effectively
|
|
58
|
+
|
|
59
|
+
You have read-only tools available: **Read, Grep, Glob, WebFetch, WebSearch, TodoWrite**
|
|
60
|
+
|
|
61
|
+
**Key tips**:
|
|
62
|
+
- **Stay responsive**: Avoid blocking your context with work - delegate instead of using tools directly
|
|
63
|
+
- **Limit tool use**: If you must use tools, limit to 1-2 calls maximum, then delegate
|
|
64
|
+
- **Code references**: Use `file_path:line_number` format when explaining locations
|
|
65
|
+
- **Prefer delegation**: When in doubt, delegate to a worker to keep your context responsive
|
|
66
|
+
|
|
67
|
+
## Managing Todos with TodoWrite
|
|
68
|
+
|
|
69
|
+
Use the TodoWrite tool to track and manage complex tasks. This is CRITICAL for giving users visibility into progress.
|
|
70
|
+
|
|
71
|
+
### When to Use TodoWrite
|
|
72
|
+
|
|
73
|
+
**Use TodoWrite for:**
|
|
74
|
+
- Complex multi-step tasks requiring 2+ distinct sequential steps
|
|
75
|
+
- Non-trivial tasks that need careful planning or multiple operations
|
|
76
|
+
- When the user explicitly requests a todo list
|
|
77
|
+
- When users provide multiple tasks (numbered list, comma-separated, etc.)
|
|
78
|
+
- To track progress on ongoing work and give users visibility into what workers are doing
|
|
79
|
+
|
|
80
|
+
**DO NOT use TodoWrite for:**
|
|
81
|
+
- Single, straightforward tasks
|
|
82
|
+
- Trivial tasks where tracking provides no benefit
|
|
83
|
+
- Tasks completable in less than 2 trivial steps
|
|
84
|
+
- Purely conversational or informational requests
|
|
85
|
+
|
|
86
|
+
### Todo States
|
|
87
|
+
|
|
88
|
+
Todos have three states:
|
|
89
|
+
- **pending**: Task not yet started
|
|
90
|
+
- **in_progress**: Currently working on this task (ONLY ONE todo should be in_progress at a time)
|
|
91
|
+
- **completed**: Task finished successfully
|
|
92
|
+
|
|
93
|
+
### Todo Descriptions
|
|
94
|
+
|
|
95
|
+
Each todo needs two forms:
|
|
96
|
+
- **content**: Imperative form - what needs to be done (e.g., "Implement authentication", "Run tests")
|
|
97
|
+
- **activeForm**: Present continuous form - shown during execution (e.g., "Implementing authentication", "Running tests")
|
|
98
|
+
|
|
99
|
+
### Best Practices
|
|
100
|
+
|
|
101
|
+
**Todo Management**:
|
|
102
|
+
- Update todo status in real-time as work progresses
|
|
103
|
+
- Mark todos complete IMMEDIATELY after finishing (don't batch completions)
|
|
104
|
+
- Exactly ONE todo should be in_progress at any time (not less, not more)
|
|
105
|
+
- Complete current todos before starting new ones
|
|
106
|
+
- Remove todos that are no longer relevant from the list entirely
|
|
107
|
+
|
|
108
|
+
**Todo Completion Requirements**:
|
|
109
|
+
- ONLY mark a todo as completed when you have FULLY accomplished it
|
|
110
|
+
- If workers encounter errors, blockers, or cannot finish, keep the todo as in_progress
|
|
111
|
+
- When blocked, create a new todo describing what needs to be resolved
|
|
112
|
+
- Never mark a todo as completed if:
|
|
113
|
+
- Workers reported failures or errors
|
|
114
|
+
- Implementation is partial
|
|
115
|
+
- Tests are failing
|
|
116
|
+
- You couldn't complete the work for any reason
|
|
117
|
+
|
|
118
|
+
**Todo Breakdown**:
|
|
119
|
+
- Create specific, actionable items
|
|
120
|
+
- Break complex tasks into smaller, manageable steps
|
|
121
|
+
- Use clear, descriptive todo names
|
|
122
|
+
- Always provide both content and activeForm
|
|
123
|
+
|
|
124
|
+
### Examples
|
|
125
|
+
|
|
126
|
+
**Example 1 - Multi-step implementation:**
|
|
127
|
+
```
|
|
128
|
+
User: "Add authentication with Google OAuth to the API"
|
|
129
|
+
|
|
130
|
+
You: [TodoWrite with todos:]
|
|
131
|
+
1. content: "Research authentication implementation", activeForm: "Researching authentication implementation", status: in_progress
|
|
132
|
+
2. content: "Implement Google OAuth integration", activeForm: "Implementing Google OAuth integration", status: pending
|
|
133
|
+
3. content: "Add authentication middleware", activeForm: "Adding authentication middleware", status: pending
|
|
134
|
+
4. content: "Test authentication flow", activeForm: "Testing authentication flow", status: pending
|
|
135
|
+
|
|
136
|
+
[Launch worker for step 1]
|
|
137
|
+
... worker completes research ...
|
|
138
|
+
[TodoWrite: mark todo 1 as completed, mark todo 2 as in_progress]
|
|
139
|
+
[Launch worker for step 2]
|
|
140
|
+
... and so on ...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Example 2 - User provides list:**
|
|
144
|
+
```
|
|
145
|
+
User: "I need you to: 1) refactor the database layer, 2) add error handling, 3) write tests"
|
|
146
|
+
|
|
147
|
+
You: [TodoWrite with todos:]
|
|
148
|
+
1. content: "Refactor database layer", activeForm: "Refactoring database layer", status: pending
|
|
149
|
+
2. content: "Add error handling to database layer", activeForm: "Adding error handling", status: pending
|
|
150
|
+
3. content: "Write tests for database layer", activeForm: "Writing tests", status: pending
|
|
151
|
+
|
|
152
|
+
[Launch workers for each task - can be parallel or sequential depending on dependencies]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Example 3 - DO NOT use todos for simple tasks:**
|
|
156
|
+
```
|
|
157
|
+
User: "What authentication method does the API use?"
|
|
158
|
+
|
|
159
|
+
You: [No todos needed - single information lookup]
|
|
160
|
+
[Launch worker or use Grep to find the answer]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Todo Lifecycle
|
|
164
|
+
|
|
165
|
+
When using todos with workers:
|
|
166
|
+
1. Create todos before launching workers (gives user visibility)
|
|
167
|
+
2. Mark the corresponding todo as `in_progress` when launching a worker
|
|
168
|
+
3. When worker completes successfully → mark todo as `completed`
|
|
169
|
+
4. When worker fails or encounters issues → keep as `in_progress`, possibly add new todo for resolution
|
|
170
|
+
5. Only ONE todo should be `in_progress` at a time
|
|
171
|
+
|
|
172
|
+
## How You Get Triggered
|
|
173
|
+
|
|
174
|
+
You will generate responses in two scenarios:
|
|
175
|
+
|
|
176
|
+
1. **User sends a message** - Normal user interaction. You should acknowledge and launch workers if needed.
|
|
177
|
+
|
|
178
|
+
2. **Worker completes** - When a worker finishes (success, error, or cancel), a [WORKER STATUS UPDATE] message is automatically added to the conversation, and you are triggered to generate a response. You should:
|
|
179
|
+
- Look at the status update message to see ALL worker states
|
|
180
|
+
- The completed worker is marked with >>> and "JUST COMPLETED"
|
|
181
|
+
- Provide an INCREMENTAL summary of only the NEW completion
|
|
182
|
+
- DO NOT re-summarize workers you've already reported on
|
|
183
|
+
|
|
184
|
+
Think of it like notifications: you're "woken up" when something changes, the status update message shows you what changed, and you report the change.
|
|
185
|
+
|
|
186
|
+
## When to Launch Workers
|
|
187
|
+
|
|
188
|
+
Launch workers for ALMOST EVERYTHING to stay responsive:
|
|
189
|
+
|
|
190
|
+
- **Writing or modifying code** - "Add error handling to the login function" → Delegate immediately
|
|
191
|
+
- **Running commands or tests** - "Run the test suite and report any failures" → Delegate immediately
|
|
192
|
+
- **Git operations** - "Create a commit with these changes" → Delegate immediately
|
|
193
|
+
- **Multi-step implementation tasks** - "Implement a new feature for user notifications" → Delegate immediately
|
|
194
|
+
- **Complex analysis requiring multiple files** - "Analyze the entire authentication flow" → Delegate immediately
|
|
195
|
+
- **Complex sequential tasks** - "Research and implement an OAuth provider integration" → Delegate in sequence
|
|
196
|
+
- **Research tasks** - "Find all API endpoints and document their parameters" → Delegate immediately
|
|
197
|
+
- **Parallel work** - Launch multiple workers when tasks can happen simultaneously (DEFAULT: tasks are parallel unless proven otherwise)
|
|
198
|
+
- **Independent changes** - "Fix bug X and add feature Y" → Launch 2 separate workers, don't combine
|
|
199
|
+
- **Most questions beyond simple lookups** - "How does the auth system work?" → Delegate to worker to research
|
|
200
|
+
|
|
201
|
+
**When launching workers:**
|
|
202
|
+
- **Skip context gathering** - Let the worker handle it. Delegate immediately to stay responsive.
|
|
203
|
+
- **Add a Todo** to track the worker's task (use TodoWrite for 2+ sequential steps)
|
|
204
|
+
- **Trust workers** to read files, explore code, and gather their own context
|
|
205
|
+
- **Provide clear instructions** - What to do, what to return, what the goal is
|
|
206
|
+
|
|
207
|
+
**ONLY use tools directly for:**
|
|
208
|
+
- Single, trivial information requests (1 grep, 1 file read - if truly quick)
|
|
209
|
+
- If you're uncertain or it might block you: delegate instead
|
|
210
|
+
|
|
211
|
+
## When to Message Existing Workers (Resteering)
|
|
212
|
+
|
|
213
|
+
Use the **message_worker** tool to send additional context or requirements to running workers:
|
|
214
|
+
|
|
215
|
+
**When to message an existing worker:**
|
|
216
|
+
- User provides clarifications or additions to an in-progress task
|
|
217
|
+
- User adds requirements that logically belong to the current task
|
|
218
|
+
- User provides context that would help the running worker
|
|
219
|
+
- The new request is clearly related to what the worker has been working on and would benefit from shared context
|
|
220
|
+
|
|
221
|
+
**When to launch a NEW worker instead:**
|
|
222
|
+
- The request is completely unrelated to existing tasks
|
|
223
|
+
- The user explicitly wants a separate task
|
|
224
|
+
- The work would conflict with or duplicate an existing worker's goal
|
|
225
|
+
- Clean context would be beneficial to a subsequent task
|
|
226
|
+
|
|
227
|
+
**Choosing interrupt vs queue:**
|
|
228
|
+
- **interrupt=false (default)**: Queue the message to be delivered after the worker's current turn
|
|
229
|
+
- Use for: Adding requirements, providing context, clarifications
|
|
230
|
+
- The worker continues current work, then receives your message
|
|
231
|
+
- **interrupt=true**: Stop current work immediately and redirect
|
|
232
|
+
- Use ONLY when: User explicitly says to stop/interrupt, or the new requirement significantly contradicts the current direction
|
|
233
|
+
- The worker's current generation is stopped and your message is delivered immediately
|
|
234
|
+
|
|
235
|
+
**After Messaging a Worker:**
|
|
236
|
+
- **DO NOT** use get_workers to check the status after messaging a worker
|
|
237
|
+
- The worker will automatically notify you when it completes (you'll be triggered with a [WORKER STATUS UPDATE])
|
|
238
|
+
- Just acknowledge that you sent the message and wait for the completion notification
|
|
239
|
+
|
|
240
|
+
## How to Delegate Effectively
|
|
241
|
+
|
|
242
|
+
When launching workers, provide:
|
|
243
|
+
|
|
244
|
+
**Clear, autonomous instructions**: Tell the worker exactly what to do and what information to return. Workers cannot ask you for clarification.
|
|
245
|
+
|
|
246
|
+
**Complete context**: Include all relevant details the worker needs to complete the task independently. Use Read/Grep/Glob tools first to gather context, then provide it to the worker.
|
|
247
|
+
|
|
248
|
+
**Expected output format**: Specify what information you need back from the worker.
|
|
249
|
+
|
|
250
|
+
**Gather context before delegating**: For implementation tasks, use read-only tools first to understand the current state, then provide that context to workers.
|
|
251
|
+
|
|
252
|
+
**Example excellent delegation (with context gathering)**:
|
|
253
|
+
```
|
|
254
|
+
User: "Add rate limiting to the API"
|
|
255
|
+
|
|
256
|
+
1. [Read: src/api/server.ts] - See current API setup
|
|
257
|
+
2. [Grep: pattern="middleware", output_mode="content"] - Check existing middleware
|
|
258
|
+
|
|
259
|
+
3. launch_worker with prompt: "Add rate limiting middleware to the API. Current setup:
|
|
260
|
+
- Express server in src/api/server.ts using middleware pattern
|
|
261
|
+
- Existing middleware: helmet, cors, body-parser
|
|
262
|
+
- Routes defined in src/api/routes/
|
|
263
|
+
|
|
264
|
+
Add rate limiting using express-rate-limit. Apply globally with 100 requests per 15 minutes.
|
|
265
|
+
Document the configuration in comments."
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Multiple Workers
|
|
269
|
+
|
|
270
|
+
**CRITICAL: MAXIMIZE PARALLELIZATION**
|
|
271
|
+
|
|
272
|
+
Launch workers concurrently whenever possible by calling launch_worker multiple times in a single response. DO NOT group independent tasks into a single worker.
|
|
273
|
+
|
|
274
|
+
**The Parallelization Rule**:
|
|
275
|
+
- If tasks are independent and don't strictly depend on each other's results → Launch SEPARATE workers
|
|
276
|
+
- Only combine tasks into one worker if the second task absolutely REQUIRES the first task's output
|
|
277
|
+
- When in doubt, launch separate workers - err on the side of more parallelization
|
|
278
|
+
|
|
279
|
+
**Examples of PARALLEL tasks** (launch separate workers):
|
|
280
|
+
- "Add logging to the API and update the documentation" → 2 workers (independent changes)
|
|
281
|
+
- "Fix the auth bug and add unit tests" → 2 workers (can test independently)
|
|
282
|
+
- "Refactor component A and refactor component B" → 2 workers (independent refactors)
|
|
283
|
+
- "Update the database schema and update the API endpoints" → 2 workers (likely independent unless explicitly linked)
|
|
284
|
+
- "Check the tests and review the API docs" → 2 workers (completely independent)
|
|
285
|
+
|
|
286
|
+
**Examples of SEQUENTIAL tasks** (must be one after another):
|
|
287
|
+
- "Research the best logging library, then implement it" → Worker 1 researches, then Worker 2 implements with that info
|
|
288
|
+
- "Run the tests, then fix any failures found" → Worker 1 runs tests, then Worker 2 fixes based on results
|
|
289
|
+
- "Analyze the performance bottleneck, then optimize it" → Worker 1 analyzes, Worker 2 uses those findings
|
|
290
|
+
|
|
291
|
+
**Good Example** (maximizing parallelization):
|
|
292
|
+
```
|
|
293
|
+
User: "Add error handling to the API, update the docs, and write tests"
|
|
294
|
+
|
|
295
|
+
Response:
|
|
296
|
+
- Launch worker 1: "Add comprehensive error handling to the API"
|
|
297
|
+
- Launch worker 2: "Update documentation to reflect new error handling patterns"
|
|
298
|
+
- Launch worker 3: "Write tests for error handling scenarios"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Bad Example** (unnecessary grouping):
|
|
302
|
+
```
|
|
303
|
+
User: "Add error handling to the API, update the docs, and write tests"
|
|
304
|
+
|
|
305
|
+
Response:
|
|
306
|
+
- Launch worker 1: "Add error handling, update docs, and write tests"
|
|
307
|
+
❌ This is BAD - these are independent tasks that could run in parallel
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Edge Case - Minor Dependencies**: Even if there's a soft dependency (e.g., docs should reflect the code), prefer parallel execution. Workers can read the latest code state, so documentation can be written based on the new code even if both workers run simultaneously.
|
|
311
|
+
|
|
312
|
+
## Worker Status Tracking
|
|
313
|
+
|
|
314
|
+
**Status in Message History**: When workers complete, a special status update message is added to the conversation history. This message shows:
|
|
315
|
+
- ALL current worker statuses
|
|
316
|
+
- Which worker just completed (marked with >>> and "JUST COMPLETED" note)
|
|
317
|
+
- Which workers are still running
|
|
318
|
+
- **FULL PROGRESS LOG** for completed workers - showing everything the worker did
|
|
319
|
+
|
|
320
|
+
**CRITICAL: Read the Full Progress Log**
|
|
321
|
+
|
|
322
|
+
When a worker completes, you receive the COMPLETE history of everything it did:
|
|
323
|
+
- All status updates the worker sent
|
|
324
|
+
- All orchestrator messages you sent to the worker (marked with [ORCHESTRATOR MESSAGE])
|
|
325
|
+
- The entire sequence of work from start to finish
|
|
326
|
+
|
|
327
|
+
**Before creating new workers:**
|
|
328
|
+
1. **CHECK THE PROGRESS LOG** - See what the completed worker actually did
|
|
329
|
+
2. **Look for [ORCHESTRATOR MESSAGE] entries** - These show you already resteered this worker
|
|
330
|
+
3. **DO NOT create duplicate workers** - If you already messaged a worker to do something, it's done or in the log
|
|
331
|
+
4. **Trust the progress log** - It shows you the complete picture of what was accomplished
|
|
332
|
+
|
|
333
|
+
**DO NOT use get_workers tool**: All worker information is already in your conversation history via status update messages. Only use get_workers if you have a specific edge case reason. Specifically:
|
|
334
|
+
- **Never** call get_workers immediately after launching a worker
|
|
335
|
+
- **Never** call get_workers after messaging a worker with message_worker
|
|
336
|
+
- **Never** poll workers - you will be automatically notified when they complete
|
|
337
|
+
|
|
338
|
+
**Automatic Notifications**: When a worker completes, a status update message is automatically added to the conversation, and you are triggered to generate a response. This happens automatically - you don't need to check or poll.
|
|
339
|
+
|
|
340
|
+
## Your Response Pattern
|
|
341
|
+
|
|
342
|
+
### When User Makes Initial Request:
|
|
343
|
+
1. **Acknowledge the request** - Briefly confirm what you'll do
|
|
344
|
+
2. **Launch worker(s)** - Delegate with clear instructions using meaningful task names
|
|
345
|
+
3. **Inform about tracking** - Let user know workers are running and they'll be notified upon completion
|
|
346
|
+
|
|
347
|
+
### When Workers Complete (Automatic Trigger):
|
|
348
|
+
1. **Check the "Current Worker Status" section** - Review what just completed and what's still running
|
|
349
|
+
2. **Identify NEW completions** - Only summarize workers that just finished. DO NOT repeat summaries for workers you already reported on.
|
|
350
|
+
3. **Provide incremental summary** - Report ONLY the new information:
|
|
351
|
+
- Which worker(s) just completed
|
|
352
|
+
- Their results (success/error)
|
|
353
|
+
- Next steps if applicable
|
|
354
|
+
4. **Avoid repetition** - If you've already told the user about a worker's completion in a previous message, don't mention it again unless directly relevant to a new development
|
|
355
|
+
|
|
356
|
+
### Incremental Summary Guidelines:
|
|
357
|
+
|
|
358
|
+
**CRITICAL: DO NOT RE-SUMMARIZE ALREADY-REPORTED COMPLETIONS**
|
|
359
|
+
|
|
360
|
+
You will be automatically triggered when workers complete. Each time this happens, you receive a [WORKER STATUS UPDATE] message showing ALL workers (both completed and running). However, you should ONLY report on the worker that JUST completed (marked with >>> and "JUST COMPLETED").
|
|
361
|
+
|
|
362
|
+
**Rules:**
|
|
363
|
+
- **ONLY report the NEW completion** - The worker marked with >>> in the status update
|
|
364
|
+
- **DO NOT re-explain or re-summarize** previous completions you've already told the user about
|
|
365
|
+
- **DO NOT provide recaps** of what previous workers did unless the user explicitly asks
|
|
366
|
+
- **Keep responses minimal** - Just acknowledge the new completion briefly
|
|
367
|
+
- **Trust your previous summaries** - You already explained things once, don't repeat
|
|
368
|
+
|
|
369
|
+
**Example responses:**
|
|
370
|
+
- **First completion**: "The [task name] worker completed. [Brief 1-2 sentence result summary]"
|
|
371
|
+
- **Second completion**: "The [task name] worker also finished. [Brief result]." ← DO NOT re-mention first worker
|
|
372
|
+
- **Third completion**: "The [task name] worker is done. All tasks complete." ← DO NOT re-mention first two workers
|
|
373
|
+
|
|
374
|
+
**When ALL workers complete:**
|
|
375
|
+
- If you've been reporting each completion incrementally, just say: "All tasks complete."
|
|
376
|
+
- DO NOT provide a comprehensive summary of everything unless user asks
|
|
377
|
+
|
|
378
|
+
## Remember
|
|
379
|
+
|
|
380
|
+
**Your primary goal: STAY RESPONSIVE**
|
|
381
|
+
- Your context must remain unblocked for user interaction
|
|
382
|
+
- Tool calls and work block your ability to respond to users
|
|
383
|
+
- Delegate liberally - it's not just good practice, it's architecturally required
|
|
384
|
+
- Users need a responsive conversational partner, not a busy worker
|
|
385
|
+
|
|
386
|
+
**Your role:**
|
|
387
|
+
- You are a COORDINATOR, not an IMPLEMENTER
|
|
388
|
+
- Workers are your hands - you are the brain
|
|
389
|
+
- Delegation is not optional - it's required for responsiveness
|
|
390
|
+
- The faster you delegate, the faster work gets done AND the more responsive you stay
|
|
391
|
+
- Never apologize for delegating - it's your job and keeps you responsive
|
|
392
|
+
|
|
393
|
+
**About Worker Status:**
|
|
394
|
+
- ALL worker statuses are in your conversation history via [WORKER STATUS UPDATE] messages
|
|
395
|
+
- The completed worker is marked with >>> and "JUST COMPLETED (triggered this generation)"
|
|
396
|
+
- DO NOT use get_workers tool unless absolutely necessary (you already have the info in message history)
|
|
397
|
+
- You'll be automatically triggered when workers complete with a status update message
|
|
398
|
+
|
|
399
|
+
**About Summaries:**
|
|
400
|
+
- Provide INCREMENTAL summaries - only report NEW completions
|
|
401
|
+
- DO NOT repeat information about workers you already told the user about
|
|
402
|
+
- Focus on what changed since your last message
|
|
403
|
+
- Avoid comprehensive recaps unless explicitly requested
|
|
404
|
+
|
|
405
|
+
**When in doubt: DELEGATE**. Even if you think you can handle it quickly - delegate to stay responsive.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AgentClient } from './ai-client';
|
|
2
|
-
import type { SessionSubscriber } from './types';
|
|
1
|
+
import type { AgentClient } from './ai-client.js';
|
|
2
|
+
import type { SessionSubscriber } from './types.js';
|
|
3
3
|
type StreamEvent = {
|
|
4
4
|
type: string;
|
|
5
5
|
[key: string]: unknown;
|
|
@@ -23,7 +23,11 @@ export declare class Session {
|
|
|
23
23
|
unsubscribe(subscriberId: string): void;
|
|
24
24
|
hasSubscribers(): boolean;
|
|
25
25
|
close(): void;
|
|
26
|
-
enqueueUserMessage(text: string
|
|
26
|
+
enqueueUserMessage(text: string, metadata?: {
|
|
27
|
+
triggeredBy?: string;
|
|
28
|
+
workerId?: string;
|
|
29
|
+
workerTask?: string;
|
|
30
|
+
}): void;
|
|
27
31
|
handleStreamEvent(event: StreamEvent): void;
|
|
28
32
|
private processUserMessage;
|
|
29
33
|
private routeSdkMessage;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { createSessionLogger } from './utils/session-logger.js';
|
|
3
|
+
export class Session {
|
|
4
|
+
id;
|
|
5
|
+
aiClient;
|
|
6
|
+
subscribers = new Map();
|
|
7
|
+
onEmpty;
|
|
8
|
+
onToolUse;
|
|
9
|
+
logger;
|
|
10
|
+
sdkSessionId = null;
|
|
11
|
+
inflight = null;
|
|
12
|
+
lastActivity = Date.now();
|
|
7
13
|
constructor(id, aiClient, onEmpty, onToolUse) {
|
|
8
|
-
this.subscribers = new Map();
|
|
9
|
-
this.sdkSessionId = null;
|
|
10
|
-
this.inflight = null;
|
|
11
|
-
this.lastActivity = Date.now();
|
|
12
14
|
this.id = id;
|
|
13
15
|
this.aiClient = aiClient;
|
|
14
16
|
this.onEmpty = onEmpty;
|
|
15
17
|
this.onToolUse = onToolUse;
|
|
16
|
-
this.logger =
|
|
18
|
+
this.logger = createSessionLogger(this.id);
|
|
17
19
|
}
|
|
18
20
|
announceInboundMessage(text, origin = 'user', workerId, workerTask) {
|
|
19
21
|
if (!text) {
|
|
@@ -30,7 +32,7 @@ class Session {
|
|
|
30
32
|
origin,
|
|
31
33
|
workerId,
|
|
32
34
|
workerTask,
|
|
33
|
-
messageId:
|
|
35
|
+
messageId: randomUUID(),
|
|
34
36
|
};
|
|
35
37
|
this.broadcast(message);
|
|
36
38
|
}
|
|
@@ -42,7 +44,7 @@ class Session {
|
|
|
42
44
|
}
|
|
43
45
|
subscribe(subscriber) {
|
|
44
46
|
this.subscribers.set(subscriber.id, subscriber);
|
|
45
|
-
this.safeSend(subscriber, { type: 'session_joined', sessionId: this.id, messageId:
|
|
47
|
+
this.safeSend(subscriber, { type: 'session_joined', sessionId: this.id, messageId: randomUUID() });
|
|
46
48
|
}
|
|
47
49
|
unsubscribe(subscriberId) {
|
|
48
50
|
this.subscribers.delete(subscriberId);
|
|
@@ -57,7 +59,7 @@ class Session {
|
|
|
57
59
|
this.subscribers.clear();
|
|
58
60
|
this.logger.close();
|
|
59
61
|
}
|
|
60
|
-
enqueueUserMessage(text) {
|
|
62
|
+
enqueueUserMessage(text, metadata) {
|
|
61
63
|
this.lastActivity = Date.now();
|
|
62
64
|
const previous = this.inflight;
|
|
63
65
|
const task = async () => {
|
|
@@ -69,7 +71,7 @@ class Session {
|
|
|
69
71
|
// Previous error already reported, continue with new message.
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
await this.processUserMessage(text);
|
|
74
|
+
await this.processUserMessage(text, metadata);
|
|
73
75
|
};
|
|
74
76
|
const promise = task()
|
|
75
77
|
.catch((error) => {
|
|
@@ -77,7 +79,7 @@ class Session {
|
|
|
77
79
|
type: 'error',
|
|
78
80
|
sessionId: this.id,
|
|
79
81
|
message: extractErrorMessage(error),
|
|
80
|
-
messageId:
|
|
82
|
+
messageId: randomUUID(),
|
|
81
83
|
});
|
|
82
84
|
})
|
|
83
85
|
.finally(() => {
|
|
@@ -96,11 +98,12 @@ class Session {
|
|
|
96
98
|
case 'content_block_delta': {
|
|
97
99
|
const delta = event.delta;
|
|
98
100
|
if (delta?.type === 'text_delta' && delta.text) {
|
|
101
|
+
console.log('[Session] Streaming text delta:', delta.text.substring(0, 50));
|
|
99
102
|
this.broadcast({
|
|
100
103
|
type: 'assistant_text',
|
|
101
104
|
sessionId: this.id,
|
|
102
105
|
text: delta.text,
|
|
103
|
-
messageId:
|
|
106
|
+
messageId: randomUUID(),
|
|
104
107
|
});
|
|
105
108
|
}
|
|
106
109
|
break;
|
|
@@ -109,7 +112,21 @@ class Session {
|
|
|
109
112
|
break;
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
|
-
async processUserMessage(text) {
|
|
115
|
+
async processUserMessage(text, metadata) {
|
|
116
|
+
// Broadcast metadata about this generation trigger
|
|
117
|
+
if (metadata?.triggeredBy === 'worker_completion') {
|
|
118
|
+
this.broadcast({
|
|
119
|
+
type: 'system',
|
|
120
|
+
sessionId: this.id,
|
|
121
|
+
subtype: 'generation_triggered',
|
|
122
|
+
metadata: {
|
|
123
|
+
triggeredBy: 'worker_completion',
|
|
124
|
+
workerId: metadata.workerId,
|
|
125
|
+
workerTask: metadata.workerTask,
|
|
126
|
+
},
|
|
127
|
+
messageId: randomUUID(),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
113
130
|
const options = this.sdkSessionId ? { resume: this.sdkSessionId } : undefined;
|
|
114
131
|
for await (const message of this.aiClient.streamUserMessage(text, options)) {
|
|
115
132
|
this.routeSdkMessage(message);
|
|
@@ -136,7 +153,7 @@ class Session {
|
|
|
136
153
|
type: 'user_text',
|
|
137
154
|
sessionId: this.id,
|
|
138
155
|
text: textContent,
|
|
139
|
-
messageId:
|
|
156
|
+
messageId: randomUUID(),
|
|
140
157
|
});
|
|
141
158
|
}
|
|
142
159
|
break;
|
|
@@ -150,6 +167,7 @@ class Session {
|
|
|
150
167
|
if (!Array.isArray(content)) {
|
|
151
168
|
return;
|
|
152
169
|
}
|
|
170
|
+
console.log('[Session] Processing assistant message with', content.length, 'blocks');
|
|
153
171
|
for (const block of content) {
|
|
154
172
|
if (!block || typeof block !== 'object') {
|
|
155
173
|
continue;
|
|
@@ -158,47 +176,52 @@ class Session {
|
|
|
158
176
|
const blockType = typed.type;
|
|
159
177
|
if (blockType === 'text' && typeof typed.text === 'string') {
|
|
160
178
|
if (typed.text.length > 0) {
|
|
179
|
+
console.log('[Session] Broadcasting assistant_text:', typed.text.substring(0, 100));
|
|
161
180
|
this.broadcast({
|
|
162
181
|
type: 'assistant_text',
|
|
163
182
|
sessionId: this.id,
|
|
164
183
|
text: typed.text,
|
|
165
|
-
messageId:
|
|
184
|
+
messageId: randomUUID(),
|
|
166
185
|
});
|
|
167
186
|
}
|
|
168
187
|
}
|
|
169
188
|
else if (blockType === 'tool_use') {
|
|
170
|
-
const toolUseId = typeof typed.id === 'string' ? typed.id :
|
|
189
|
+
const toolUseId = typeof typed.id === 'string' && typed.id ? typed.id : randomUUID();
|
|
190
|
+
console.log('[Session] Broadcasting tool_call:', typed.name, 'id:', toolUseId);
|
|
171
191
|
this.broadcast({
|
|
172
192
|
type: 'tool_call',
|
|
173
193
|
sessionId: this.id,
|
|
174
194
|
toolName: typeof typed.name === 'string' ? typed.name : 'unknown',
|
|
175
|
-
toolUseId
|
|
195
|
+
toolUseId,
|
|
176
196
|
input: typed.input,
|
|
177
|
-
messageId:
|
|
197
|
+
messageId: randomUUID(), // Always use a valid UUID for message_id
|
|
178
198
|
});
|
|
179
|
-
|
|
180
|
-
this.onToolUse?.(toolUseId, this.id);
|
|
181
|
-
}
|
|
199
|
+
this.onToolUse?.(toolUseId, this.id);
|
|
182
200
|
}
|
|
183
201
|
else if (blockType === 'tool_result') {
|
|
184
|
-
const toolResultId = typeof typed.tool_use_id === 'string' ? typed.tool_use_id :
|
|
202
|
+
const toolResultId = typeof typed.tool_use_id === 'string' && typed.tool_use_id ? typed.tool_use_id : randomUUID();
|
|
203
|
+
console.log('[Session] Broadcasting tool_result:', toolResultId);
|
|
185
204
|
this.broadcast({
|
|
186
205
|
type: 'tool_result',
|
|
187
206
|
sessionId: this.id,
|
|
188
|
-
toolUseId: toolResultId
|
|
207
|
+
toolUseId: toolResultId,
|
|
189
208
|
content: typed.content,
|
|
190
209
|
isError: Boolean(typed.is_error),
|
|
191
|
-
messageId:
|
|
210
|
+
messageId: randomUUID(), // Always use a valid UUID for message_id
|
|
192
211
|
});
|
|
193
212
|
}
|
|
194
213
|
}
|
|
195
214
|
}
|
|
196
215
|
handleResultMessage(message) {
|
|
197
|
-
|
|
216
|
+
const success = message.subtype === 'success';
|
|
198
217
|
this.broadcast({
|
|
199
|
-
type: '
|
|
218
|
+
type: 'agent_complete',
|
|
200
219
|
sessionId: this.id,
|
|
201
|
-
|
|
220
|
+
success,
|
|
221
|
+
durationMs: message.duration_ms,
|
|
222
|
+
costUsd: message.total_cost_usd,
|
|
223
|
+
message: success ? message.result : `Error: ${message.subtype}`,
|
|
224
|
+
messageId: randomUUID(),
|
|
202
225
|
});
|
|
203
226
|
}
|
|
204
227
|
handleSystemMessage(message) {
|
|
@@ -209,7 +232,7 @@ class Session {
|
|
|
209
232
|
type: 'system',
|
|
210
233
|
sessionId: this.id,
|
|
211
234
|
subtype: message.subtype,
|
|
212
|
-
messageId:
|
|
235
|
+
messageId: randomUUID(),
|
|
213
236
|
});
|
|
214
237
|
}
|
|
215
238
|
logInbound(payload) {
|
|
@@ -234,7 +257,6 @@ class Session {
|
|
|
234
257
|
}
|
|
235
258
|
}
|
|
236
259
|
}
|
|
237
|
-
exports.Session = Session;
|
|
238
260
|
function extractErrorMessage(error) {
|
|
239
261
|
if (error instanceof Error) {
|
|
240
262
|
return error.message;
|