omnara 0.1.6 → 0.1.9

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.
@@ -0,0 +1,34 @@
1
+ import * as z from 'zod';
2
+ export type OmnaraConfig = z.infer<typeof OmnaraConfig>;
3
+ declare const OmnaraConfig: z.ZodObject<{
4
+ worktrees: z.ZodDefault<z.ZodObject<{
5
+ enabled: z.ZodDefault<z.ZodBoolean>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ enabled: boolean;
8
+ }, {
9
+ enabled?: boolean | undefined;
10
+ }>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ worktrees: {
13
+ enabled: boolean;
14
+ };
15
+ }, {
16
+ worktrees?: {
17
+ enabled?: boolean | undefined;
18
+ } | undefined;
19
+ }>;
20
+ /**
21
+ * Read and parse the omnara.jsonc configuration file
22
+ *
23
+ * @param configPath - Path to the config file (defaults to omnara.jsonc in cwd)
24
+ * @returns Parsed configuration or default config if file doesn't exist
25
+ */
26
+ export declare function readOmnaraConfig(configPath?: string): Promise<OmnaraConfig>;
27
+ /**
28
+ * Check if worktrees are enabled in the configuration
29
+ *
30
+ * @param config - The omnara configuration
31
+ * @returns true if worktrees should be enabled
32
+ */
33
+ export declare function isWorktreesEnabled(config: OmnaraConfig): boolean;
34
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -2,10 +2,12 @@ import { type AgentClient } from './ai-client.js';
2
2
  import { Session } from './session.js';
3
3
  import { WorkerManager } from './worker-manager.js';
4
4
  import type { InboundMessage, OutboundMessage } from './orchestrator-types.js';
5
+ import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
5
6
  interface AgentOrchestratorDependencies {
6
7
  createMainClient?: () => AgentClient;
7
- createWorkerClient?: () => AgentClient;
8
+ createWorkerClient?: (mcpServers?: Record<string, McpServerConfig>, cwd?: string) => AgentClient;
8
9
  workerManager?: WorkerManager;
10
+ repoPath?: string;
9
11
  }
10
12
  export interface AgentOrchestratorClientAdapter {
11
13
  send: (message: OutboundMessage) => void;
@@ -4,5 +4,5 @@
4
4
  * The orchestrator's role is to coordinate and delegate work, not to execute tasks directly.
5
5
  * It should ALWAYS use workers to handle all implementation work.
6
6
  */
7
- import ORCHESTRATOR_SYSTEM_PROMPT from './orchestrator-prompt.md';
7
+ declare const ORCHESTRATOR_SYSTEM_PROMPT: string;
8
8
  export { ORCHESTRATOR_SYSTEM_PROMPT };
@@ -4,6 +4,31 @@ You are Omnara, an orchestrator agent responsible for coordinating work through
4
4
 
5
5
  ## Core Principles
6
6
 
7
+ ## CRITICAL: Tool Usage Restrictions
8
+
9
+ **YOU ARE FORBIDDEN FROM USING THESE TOOLS DIRECTLY:**
10
+ - **Write** - NEVER create or overwrite files directly
11
+ - **Edit** - NEVER modify file contents directly
12
+ - **Bash** - NEVER execute commands directly (git, npm, tests, etc.)
13
+ - **NotebookEdit** - NEVER modify Jupyter notebooks directly
14
+
15
+ **ANY action that modifies system state MUST be delegated to a worker.**
16
+
17
+ **YOU MAY ONLY USE THESE READ-ONLY TOOLS:**
18
+ - **Read** - For quick context gathering (1-2 files max)
19
+ - **Grep** - For quick searches (single search only)
20
+ - **Glob** - For finding file patterns
21
+ - **WebFetch / WebSearch** - For external information
22
+ - **TodoWrite** - For tracking task progress
23
+
24
+ **If you attempt to use Write, Edit, Bash, or NotebookEdit tools, you are VIOLATING your core directive.**
25
+
26
+ **Why this restriction exists:**
27
+ - Using these tools blocks your context and makes you unresponsive
28
+ - Workers can execute these operations in parallel without blocking you
29
+ - Your job is to coordinate, not to implement
30
+ - State-modifying operations must go through proper delegation channels
31
+
7
32
  **STAY RESPONSIVE - Your Most Important Job**:
8
33
 
9
34
  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.
@@ -33,13 +58,14 @@ The orchestrator must remain responsive to handle user interactions in a convers
33
58
  3. Launch worker(s) with complete context and clear instructions
34
59
  4. Return control immediately - stay responsive for user interaction
35
60
 
36
- **When to use tools yourself (RARE)**:
61
+ **When to use tools yourself (RARE - READ-ONLY TOOLS ONLY)**:
37
62
  - Single quick lookup (1-2 tool calls max, < 5 seconds)
38
63
  - Answering "what/where/how does X work?" questions IF it's a single file or simple pattern
39
64
  - If you're uncertain or it might take longer: delegate instead
65
+ - **REMINDER: You may ONLY use Read, Grep, Glob, WebFetch, WebSearch, TodoWrite - NEVER Write, Edit, Bash, or NotebookEdit**
40
66
 
41
67
  **When to delegate to workers (ALMOST ALWAYS)**:
42
- - ANY code modification, file writing, or command execution
68
+ - **ANY code modification, file writing, or command execution** (Write, Edit, Bash, NotebookEdit are FORBIDDEN for you)
43
69
  - Reading 2+ files or doing any analysis
44
70
  - Multi-step research or investigation tasks
45
71
  - Running tests, git operations, deployments
@@ -56,13 +82,16 @@ The orchestrator must remain responsive to handle user interactions in a convers
56
82
 
57
83
  ## Using Your Tools Effectively
58
84
 
59
- You have read-only tools available: **Read, Grep, Glob, WebFetch, WebSearch, TodoWrite**
85
+ **You ONLY have access to these READ-ONLY tools:** Read, Grep, Glob, WebFetch, WebSearch, TodoWrite
86
+
87
+ **YOU ARE EXPLICITLY FORBIDDEN from using:** Write, Edit, Bash, NotebookEdit
60
88
 
61
89
  **Key tips**:
62
90
  - **Stay responsive**: Avoid blocking your context with work - delegate instead of using tools directly
63
91
  - **Limit tool use**: If you must use tools, limit to 1-2 calls maximum, then delegate
64
92
  - **Code references**: Use `file_path:line_number` format when explaining locations
65
93
  - **Prefer delegation**: When in doubt, delegate to a worker to keep your context responsive
94
+ - **Never modify state**: All file modifications and command executions MUST go through workers
66
95
 
67
96
  ## Managing Todos with TodoWrite
68
97
 
@@ -309,6 +338,84 @@ Response:
309
338
 
310
339
  **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
340
 
341
+ ## Git Worktree Isolation
342
+
343
+ Workers can optionally operate in ISOLATED git worktrees, providing complete isolation from the main repository and from other workers. You control this via the `worktree_name` parameter when launching a worker.
344
+
345
+ **Controlling Worktree Isolation:**
346
+ - Set `worktree_name` when calling `launch_worker` to specify the worktree context
347
+ - Each worktree provides a completely isolated file system view and git state
348
+ - Multiple workers can share the same worktree context or have separate ones
349
+ - If not specified, the worker operates in the main repository
350
+
351
+ **What Isolated Workers Get:**
352
+ - **Dedicated working directory** - Separate file system view in `.omnara/worktrees/`
353
+ - **Dedicated git branch** - Isolated branch for that worktree context
354
+ - **Independent git state** - Separate staging area, no shared commits, independent checkouts
355
+
356
+ **Key Benefits:**
357
+ 1. **No Conflicts**: Workers in different worktrees can edit the same files simultaneously without interfering
358
+ 2. **Safe Operations**: Workers can stage, commit, and manipulate git state independently
359
+ 3. **Clean Isolation**: Main repository remains untouched until worker results are reviewed and merged
360
+ 4. **Controlled Sharing**: Workers with the same worktree_name share the same isolated environment
361
+
362
+ **Best Practices:**
363
+ - Use worktree isolation when workers need to modify the same files independently
364
+ - Workers in the same worktree_name share the same isolated environment
365
+ - Workers in different worktree contexts are completely isolated from each other
366
+ - Review worker branches before merging changes to main
367
+ - Trust the isolation - workers in different worktrees truly cannot interfere with each other
368
+
369
+ **Worker Launch Information:**
370
+ When you launch a worker with worktree isolation, you'll receive:
371
+ - `worktree_path`: Location of the worker's isolated directory
372
+ - `branch_name`: Name of the worktree's dedicated branch
373
+
374
+ **Examples:**
375
+
376
+ **Example 1: Parallel Edits to Same File**
377
+ ```
378
+ User: "Add error handling to login.ts and also add logging to the same file"
379
+
380
+ Response:
381
+ - Launch worker 1 with worktree_name="feature-1": "Add error handling to login.ts"
382
+ - Launch worker 2 with worktree_name="feature-2": "Add logging to login.ts"
383
+ ✓ Safe - each worker has isolated copy in different worktrees, changes can be merged later
384
+ ```
385
+
386
+ **Example 2: Shared Worktree Context**
387
+ ```
388
+ User: "Refactor the authentication system across multiple files"
389
+
390
+ Response:
391
+ - Launch worker 1 with worktree_name="auth-refactor": "Refactor authentication middleware"
392
+ - Launch worker 2 with worktree_name="auth-refactor": "Update authentication tests"
393
+ ✓ Both workers share the same isolated worktree, can see each other's changes
394
+ ```
395
+
396
+ **Example 3: Mixed Isolation**
397
+ ```
398
+ User: "Fix bug in production code and experiment with a new feature"
399
+
400
+ Response:
401
+ - Launch worker 1 (no worktree_name): "Fix critical bug in production code"
402
+ ✓ Works in main repository
403
+ - Launch worker 2 with worktree_name="new-feature": "Experiment with WebSocket implementation"
404
+ ✓ Isolated in its own worktree, won't affect main code
405
+ ```
406
+
407
+ **Example 4: Complex Sequential Work**
408
+ ```
409
+ User: "Research authentication libraries, then implement the best one"
410
+
411
+ Response:
412
+ - Launch worker 1 (no worktree_name): "Research Node.js authentication libraries and recommend the best option"
413
+ [Wait for completion...]
414
+ - Review worker 1's recommendation
415
+ - Launch worker 2 with worktree_name="auth-impl": "Implement {recommended library} based on research"
416
+ ✓ Implementation isolated in worktree for safe experimentation
417
+ ```
418
+
312
419
  ## Worker Status Tracking
313
420
 
314
421
  **Status in Message History**: When workers complete, a special status update message is added to the conversation history. This message shows:
@@ -383,6 +490,12 @@ You will be automatically triggered when workers complete. Each time this happen
383
490
  - Delegate liberally - it's not just good practice, it's architecturally required
384
491
  - Users need a responsive conversational partner, not a busy worker
385
492
 
493
+ **Tool Usage Restrictions (CRITICAL)**:
494
+ - **NEVER use Write, Edit, Bash, or NotebookEdit** - These are FORBIDDEN for orchestrators
495
+ - **ONLY use Read, Grep, Glob, WebFetch, WebSearch, TodoWrite** - Read-only tools only
496
+ - **ANY state modification MUST be delegated to workers** - No exceptions
497
+ - If you find yourself about to use a forbidden tool, STOP and delegate instead
498
+
386
499
  **Your role:**
387
500
  - You are a COORDINATOR, not an IMPLEMENTER
388
501
  - Workers are your hands - you are the brain
@@ -116,6 +116,8 @@ export type OutboundMessage = {
116
116
  durationMs?: number;
117
117
  costUsd?: number;
118
118
  message?: string;
119
+ worktree_path?: string;
120
+ branch_name?: string;
119
121
  metadata?: Record<string, unknown>;
120
122
  messageId?: string;
121
123
  } | {
@@ -0,0 +1,3 @@
1
+ export { WorktreeManager, WorktreeError, type WorktreeManagerOptions, type WorktreeInfo, } from './worktree-manager.js';
2
+ export { AsyncQueue } from './async-queue.js';
3
+ export type { SessionLogger } from './session-logger.js';
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Configuration options for WorktreeManager
3
+ */
4
+ export interface WorktreeManagerOptions {
5
+ /**
6
+ * The root directory of the git repository
7
+ */
8
+ repoPath: string;
9
+ /**
10
+ * Base directory for worktrees (default: .omnara/worktrees)
11
+ */
12
+ worktreeBasePath?: string;
13
+ /**
14
+ * Whether worktrees are enabled (default: false)
15
+ */
16
+ enabled?: boolean;
17
+ }
18
+ /**
19
+ * Information about a created worktree
20
+ */
21
+ export interface WorktreeInfo {
22
+ /**
23
+ * Unique identifier for the worker
24
+ */
25
+ workerId: string;
26
+ /**
27
+ * Semantic name for the worktree (e.g., "main-feature", "bugfix-auth")
28
+ */
29
+ worktreeName: string;
30
+ /**
31
+ * Absolute path to the worktree directory
32
+ */
33
+ worktreePath: string;
34
+ /**
35
+ * Name of the branch created for this worktree
36
+ */
37
+ branchName: string;
38
+ /**
39
+ * Timestamp when the worktree was created
40
+ */
41
+ createdAt: Date;
42
+ }
43
+ /**
44
+ * Error thrown when worktree operations fail
45
+ */
46
+ export declare class WorktreeError extends Error {
47
+ readonly workerId: string;
48
+ readonly operation: string;
49
+ readonly cause?: Error | undefined;
50
+ constructor(message: string, workerId: string, operation: string, cause?: Error | undefined);
51
+ }
52
+ /**
53
+ * Manages git worktrees for worker isolation
54
+ *
55
+ * This class handles the lifecycle of git worktrees, ensuring each worker
56
+ * operates in an isolated environment. It provides methods for creating,
57
+ * managing, and cleaning up worktrees.
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const manager = new WorktreeManager({ repoPath: '/path/to/repo' });
62
+ * const info = await manager.createWorktree('worker-123', 'main-feature');
63
+ * // Worker operates in info.worktreePath
64
+ * await manager.removeWorktree('worker-123');
65
+ * ```
66
+ */
67
+ export declare class WorktreeManager {
68
+ private readonly git;
69
+ private readonly repoPath;
70
+ private readonly worktreeBasePath;
71
+ private readonly activeWorktrees;
72
+ private readonly enabled;
73
+ constructor(options: WorktreeManagerOptions);
74
+ /**
75
+ * Validate that the repoPath is a git repository
76
+ * Logs an error if worktrees are enabled but not in a git repository
77
+ */
78
+ private validateGitRepository;
79
+ /**
80
+ * Check if worktrees are enabled
81
+ */
82
+ isEnabled(): boolean;
83
+ /**
84
+ * Creates a new worktree for the specified worker
85
+ *
86
+ * @param workerId - Unique identifier for the worker
87
+ * @param worktreeName - Semantic name for the worktree (e.g., "main-feature", "bugfix-auth")
88
+ * @returns Information about the created worktree
89
+ * @throws {WorktreeError} If worktree creation fails
90
+ */
91
+ createWorktree(workerId: string, worktreeName: string): Promise<WorktreeInfo>;
92
+ /**
93
+ * Removes a worktree for the specified worker
94
+ *
95
+ * @param workerId - Unique identifier for the worker
96
+ * @param force - Force removal even if there are uncommitted changes
97
+ * @throws {WorktreeError} If worktree removal fails
98
+ */
99
+ removeWorktree(workerId: string, force?: boolean): Promise<void>;
100
+ /**
101
+ * Gets information about a worker's worktree
102
+ *
103
+ * @param workerId - Unique identifier for the worker
104
+ * @returns Worktree information or undefined if not found
105
+ */
106
+ getWorktreeInfo(workerId: string): WorktreeInfo | undefined;
107
+ /**
108
+ * Lists all active worktrees managed by this instance
109
+ *
110
+ * @returns Array of worktree information
111
+ */
112
+ listActiveWorktrees(): WorktreeInfo[];
113
+ /**
114
+ * Cleans up all active worktrees
115
+ *
116
+ * This method should be called during application shutdown to ensure
117
+ * all worktrees are properly cleaned up.
118
+ *
119
+ * @param force - Force removal even if there are uncommitted changes
120
+ */
121
+ cleanupAll(force?: boolean): Promise<void>;
122
+ /**
123
+ * Parses the output of 'git worktree list --porcelain'
124
+ *
125
+ * @param output - Raw output from git worktree list
126
+ * @returns Array of worktree paths
127
+ */
128
+ private parseWorktreeList;
129
+ /**
130
+ * Checks if the repository is in a clean state for worktree operations
131
+ *
132
+ * @returns True if repository is clean
133
+ */
134
+ isRepositoryClean(): Promise<boolean>;
135
+ }
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
- import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { SDKUserMessage, McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
3
3
  import type { AgentClient } from './ai-client.js';
4
4
  import { AsyncQueue } from './utils/async-queue.js';
5
5
  export type WorkerStatus = 'running' | 'completed' | 'error' | 'canceled';
@@ -21,6 +21,10 @@ export interface WorkerRecord {
21
21
  error?: string;
22
22
  metadata?: Record<string, unknown>;
23
23
  messageQueue: AsyncQueue<SDKUserMessage>;
24
+ worktreePath?: string;
25
+ branchName?: string;
26
+ work_context?: string;
27
+ worktree_name?: string;
24
28
  }
25
29
  export interface WorkerMessageEvent {
26
30
  record: WorkerRecord;
@@ -36,18 +40,27 @@ export interface WorkerMessageEvent {
36
40
  interface LaunchWorkerOptions {
37
41
  task: string;
38
42
  prompt: string;
43
+ work_context: string;
44
+ worktree_name: string;
39
45
  originSessionId?: string;
40
46
  metadata?: Record<string, unknown>;
41
47
  }
42
48
  interface WorkerManagerOptions {
43
- createClient: (mcpServers?: Record<string, any>) => AgentClient;
49
+ createClient: (mcpServers?: Record<string, McpServerConfig>, cwd?: string) => AgentClient;
44
50
  resolveSessionIdBySdk?: (sdkSessionId?: string) => string | undefined;
51
+ repoPath: string;
45
52
  }
46
53
  export declare class WorkerManager extends EventEmitter {
47
54
  private readonly options;
48
55
  private readonly workers;
49
56
  private readonly runningQueries;
57
+ private worktreeManager;
58
+ private configInitialized;
50
59
  constructor(options: WorkerManagerOptions);
60
+ /**
61
+ * Initialize the configuration and update WorktreeManager accordingly
62
+ */
63
+ private initializeConfig;
51
64
  listWorkers(): WorkerRecord[];
52
65
  getWorker(id: string): WorkerRecord | undefined;
53
66
  getWorkerByTask(task: string): WorkerRecord | undefined;
@@ -56,23 +69,26 @@ export declare class WorkerManager extends EventEmitter {
56
69
  * Returns a string describing all active workers and their current state.
57
70
  */
58
71
  getWorkerStatusSummary(): string;
59
- launchWorker({ task, prompt, originSessionId, metadata }: LaunchWorkerOptions): WorkerRecord;
72
+ launchWorker({ task, prompt, work_context, worktree_name, originSessionId, metadata }: LaunchWorkerOptions, toolUseId?: string): WorkerRecord;
73
+ private createWorktreeAndRun;
60
74
  cancelWorker(id: string): boolean;
61
75
  /**
62
- * Send a message to a running worker.
76
+ * Send a message to a worker (running or completed).
63
77
  *
64
78
  * @param task - The task name of the worker to message
65
79
  * @param message - The message content to send
66
80
  * @param interrupt - If true, interrupt current generation; if false, queue for next turn
67
- * @returns true if message was sent, false if worker not found or not running
81
+ * @param toolUseId - Optional tool use ID to track this operation for relay forwarding
82
+ * @returns true if message was sent, false if worker not found or message queue is closed
68
83
  */
69
- messageWorker(task: string, message: string, interrupt: boolean): Promise<boolean>;
84
+ messageWorker(task: string, message: string, interrupt: boolean, toolUseId?: string): Promise<boolean>;
70
85
  private emitUpdate;
71
86
  private emitCompleted;
72
87
  private handleStatusUpdate;
73
88
  /**
74
89
  * Create an async iterable stream of user messages for the worker.
75
90
  * Yields the initial prompt, then streams any additional messages from the queue.
91
+ * If initialPrompt is empty, skips the initial yield and only consumes from the queue.
76
92
  */
77
93
  private createMessageStream;
78
94
  private runWorker;
@@ -81,5 +97,10 @@ export declare class WorkerManager extends EventEmitter {
81
97
  private extractDeltaText;
82
98
  private extractError;
83
99
  private wrapPrompt;
100
+ /**
101
+ * Shutdown the worker manager and cleanup all resources
102
+ * This should be called during application shutdown to ensure all worktrees are cleaned up
103
+ */
104
+ shutdown(): Promise<void>;
84
105
  }
85
106
  export {};