opencode-orchestrator 0.2.0 → 0.2.3

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.
Files changed (43) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +24 -3
  3. package/dist/agents/coder.d.ts +0 -0
  4. package/dist/agents/definitions.d.ts +0 -0
  5. package/dist/agents/fixer.d.ts +0 -0
  6. package/dist/agents/names.d.ts +0 -0
  7. package/dist/agents/orchestrator.d.ts +0 -0
  8. package/dist/agents/planner.d.ts +0 -0
  9. package/dist/agents/reviewer.d.ts +0 -0
  10. package/dist/agents/searcher.d.ts +0 -0
  11. package/dist/agents/subagents/architect.d.ts +0 -0
  12. package/dist/agents/subagents/builder.d.ts +0 -0
  13. package/dist/agents/subagents/coder.d.ts +0 -0
  14. package/dist/agents/subagents/executor.d.ts +0 -0
  15. package/dist/agents/subagents/fixer.d.ts +0 -0
  16. package/dist/agents/subagents/inspector.d.ts +0 -0
  17. package/dist/agents/subagents/memory.d.ts +0 -0
  18. package/dist/agents/subagents/planner.d.ts +0 -0
  19. package/dist/agents/subagents/publisher.d.ts +0 -0
  20. package/dist/agents/subagents/recorder.d.ts +0 -0
  21. package/dist/agents/subagents/reviewer.d.ts +0 -0
  22. package/dist/agents/subagents/searcher.d.ts +0 -0
  23. package/dist/agents/subagents/strategist.d.ts +0 -0
  24. package/dist/agents/subagents/surgeon.d.ts +0 -0
  25. package/dist/agents/subagents/types.d.ts +0 -0
  26. package/dist/agents/subagents/visualist.d.ts +0 -0
  27. package/dist/agents/types.d.ts +0 -0
  28. package/dist/cli.d.ts +0 -0
  29. package/dist/core/state.d.ts +2 -0
  30. package/dist/core/tasks.d.ts +0 -0
  31. package/dist/index.d.ts +5 -6
  32. package/dist/index.js +340 -103
  33. package/dist/shared/contracts/interfaces.d.ts +0 -0
  34. package/dist/shared/contracts/names.d.ts +0 -0
  35. package/dist/tasks.d.ts +0 -0
  36. package/dist/tools/callAgent.d.ts +0 -0
  37. package/dist/tools/rust.d.ts +0 -0
  38. package/dist/tools/search.d.ts +0 -0
  39. package/dist/tools/slashCommand.d.ts +0 -0
  40. package/dist/utils/binary.d.ts +0 -0
  41. package/dist/utils/common.d.ts +0 -0
  42. package/dist/utils/sanity.d.ts +31 -0
  43. package/package.json +4 -2
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -11,6 +11,17 @@
11
11
 
12
12
  ---
13
13
 
14
+ ## Why I Built This 🤔
15
+
16
+ **I was frustrated coding with DeepSeek and Z.AI.**
17
+
18
+ I wanted to achieve the same quality of work as premium models like **Gemini 3 Pro** and **Claude Opus**, but with affordable models.
19
+ For developers who chose **budget-friendly subscriptions** instead of expensive plans, I built a multi-agent system that can **autonomously complete** complex engineering tasks even with mid-tier LLMs.
20
+
21
+ > *"Intelligence is a resource. Orchestrate it."*
22
+
23
+ ---
24
+
14
25
  ## What is this?
15
26
 
16
27
  A **5-agent autonomous architecture** designed to solve complex engineering tasks with high reliability, even on mid-range LLMs.
@@ -39,8 +50,14 @@ Restart OpenCode after installation.
39
50
 
40
51
  ## Usage
41
52
 
42
- ### Just Select Commander Agent 🎯
43
- Press `tab` → Select **Commander** → Type your mission!
53
+ ### 🚀 Method 1: Select Commander via Tab Key (Recommended)
54
+
55
+ In OpenCode, press `Tab` to open the Agent selection menu. Select **Commander** and type your mission!
56
+
57
+ <div align="center">
58
+ <img src="assets/commander-screenshot.png" alt="Commander Screenshot" width="600" />
59
+ <p><em>Press Tab to select Commander</em></p>
60
+ </div>
44
61
 
45
62
  ```
46
63
  "Fix the login bug in the docker-compose environment"
@@ -53,11 +70,15 @@ The Commander will:
53
70
  4. **Verify**: Run builds/tests to prove the fix works.
54
71
  5. **Complete**: Report results with concrete evidence.
55
72
 
56
- ### Or Use /task Command
73
+ ### 📋 Method 2: Use /task Command
74
+
57
75
  ```bash
58
76
  /task "Implement user authentication with JWT"
59
77
  ```
60
78
 
79
+ > **💡 Tip:** Using the `/task` command makes Commander mode run **2x longer**.
80
+ > Use `/task` for complex tasks that need extended processing!
81
+
61
82
  ---
62
83
 
63
84
  ## Agents (5-Agent Architecture)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/dist/cli.d.ts CHANGED
File without changes
@@ -5,6 +5,8 @@ export interface SessionState {
5
5
  taskRetries: Map<string, number>;
6
6
  currentTask: string;
7
7
  graph?: TaskGraph;
8
+ anomalyCount: number;
9
+ lastHealthyOutput?: string;
8
10
  }
9
11
  export declare const state: {
10
12
  missionActive: boolean;
File without changes
package/dist/index.d.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  /**
2
2
  * OpenCode Orchestrator Plugin
3
3
  *
4
- * 5-Agent Structured Architecture
5
- *
6
- * Optimized for weak models through:
4
+ * This is the main entry point for the 5-Agent structured architecture.
5
+ * We've optimized it for weaker models by using:
7
6
  * - XML-structured prompts with clear boundaries
8
- * - Explicit reasoning patterns (THINK ACT OBSERVE ADJUST)
7
+ * - Explicit reasoning patterns (THINK -> ACT -> OBSERVE -> ADJUST)
9
8
  * - Evidence-based completion requirements
10
- * - Autonomous execution loop
9
+ * - Autonomous execution loop that keeps going until done
11
10
  *
12
- * Agents: Commander, Architect, Builder, Inspector, Recorder
11
+ * The agents are: Commander, Architect, Builder, Inspector, Recorder
13
12
  */
14
13
  import type { PluginInput } from "@opencode-ai/plugin";
15
14
  declare const OrchestratorPlugin: (input: PluginInput) => Promise<{
package/dist/index.js CHANGED
@@ -17,20 +17,20 @@ var AGENT_NAMES = {
17
17
  var orchestrator = {
18
18
  id: AGENT_NAMES.COMMANDER,
19
19
  description: "Commander - autonomous orchestrator",
20
- systemPrompt: `You are Commander. Complete missions autonomously. Never stop until done.
20
+ systemPrompt: `<role>
21
+ You are Commander. Complete missions autonomously. Never stop until done.
22
+ </role>
21
23
 
22
- CORE RULES:
24
+ <core_rules>
23
25
  1. Never stop until "\u2705 MISSION COMPLETE"
24
26
  2. Never wait for user during execution
25
27
  3. Never stop because agent returned nothing
26
28
  4. Always survey environment & codebase BEFORE coding
27
29
  5. Always verify with evidence based on runtime context
28
30
  6. LANGUAGE: THINK and REASON in English for maximum stability. Report final summary in Korean.
31
+ </core_rules>
29
32
 
30
- ---
31
-
32
- PHASE 0: TRIAGE & PROGRESSIVE DISCLOSURE
33
-
33
+ <phase_0 name="TRIAGE">
34
34
  Evaluate the complexity of the request:
35
35
 
36
36
  | Level | Signal | Track |
@@ -38,11 +38,9 @@ Evaluate the complexity of the request:
38
38
  | \u{1F7E2} L1: Simple | One file, clear fix, no dependencies | **FAST TRACK** |
39
39
  | \u{1F7E1} L2: Feature | New functionality, clear patterns | **NORMAL TRACK** |
40
40
  | \u{1F534} L3: Complex | Refactoring, infra change, unknown scope | **DEEP TRACK** |
41
+ </phase_0>
41
42
 
42
- ---
43
-
44
- PHASE 1: CONTEXT GATHERING (Progressive)
45
-
43
+ <phase_1 name="CONTEXT_GATHERING">
46
44
  IF FAST TRACK (L1):
47
45
  - Scan ONLY the target file and its immediate imports.
48
46
  - Skip broad infra/domain/doc scans unless an error occurs.
@@ -55,11 +53,9 @@ IF NORMAL/DEEP TRACK (L2/L3):
55
53
  - 3. Pattern check
56
54
 
57
55
  RECORD findings if on Deep Track.
56
+ </phase_1>
58
57
 
59
- ---
60
-
61
- PHASE 2: TOOL & AGENT SELECTION
62
-
58
+ <phase_2 name="TOOL_AGENT_SELECTION">
63
59
  | Track | Strategy |
64
60
  |-------|----------|
65
61
  | Fast | Use \`builder\` directly. Skip \`architect\`. |
@@ -67,12 +63,10 @@ PHASE 2: TOOL & AGENT SELECTION
67
63
  | Deep | Full \`architect\` DAG + \`recorder\` state tracking. |
68
64
 
69
65
  DEFAULT to Deep Track if unsure to act safely.
66
+ </phase_2>
70
67
 
71
- ---
72
-
73
- PHASE 3: DELEGATION pattern (Context-Aware)
74
-
75
- ---
68
+ <phase_3 name="DELEGATION">
69
+ <delegation_template>
76
70
  AGENT: [name]
77
71
  TASK: [one atomic action]
78
72
  ENVIRONMENT:
@@ -82,55 +76,57 @@ ENVIRONMENT:
82
76
  MUST: [Specific requirements]
83
77
  AVOID: [Restrictions]
84
78
  VERIFY: [Success criteria with evidence]
85
- ---
86
-
87
- ---
88
-
89
- PHASE 4: EXECUTION & FLEXIBLE VERIFICATION
79
+ </delegation_template>
80
+ </phase_3>
90
81
 
82
+ <phase_4 name="EXECUTION_VERIFICATION">
91
83
  During implementation:
92
84
  - Match existing codebase style exactly
93
85
  - Run lsp_diagnostics after each change
94
86
 
95
- FLEXIBLE VERIFICATION (Final Audit):
87
+ <verification_methods>
96
88
  | Infra | Proof Method |
97
89
  |-------|--------------|
98
90
  | OS-Native | npm run build, cargo build, specific test runs |
99
91
  | Container | Docker syntax check + config validation |
100
92
  | Live API | curl /health if reachable, check logs |
101
93
  | Generic | Manual audit by Inspector with logic summary |
94
+ </verification_methods>
95
+ </phase_4>
102
96
 
103
- ---
104
-
105
- FAILURE RECOVERY & EMPTY RESPONSES
106
-
97
+ <failure_recovery>
107
98
  | Failures | Action |
108
99
  |----------|--------|
109
100
  | 1-2 | Adjust approach, retry |
110
101
  | 3+ | STOP. Call architect for new strategy |
111
102
 
103
+ <empty_responses>
112
104
  | Agent Empty (or Gibberish) | Action |
113
105
  |----------------------------|--------|
114
106
  | recorder | Fresh start. Proceed to survey. |
115
107
  | architect | Try simpler plan yourself. |
116
108
  | builder | Call inspector to diagnose. |
117
109
  | inspector | Retry with more context. |
110
+ </empty_responses>
118
111
 
119
- *STRICT RULE: If any agent output contains gibberish, mixed-language hallucinations, or fails the language rule, REJECT it immediately and trigger a "STRICT_CLEAN_START" retry.
112
+ STRICT RULE: If any agent output contains gibberish, mixed-language hallucinations, or fails the language rule, REJECT it immediately and trigger a "STRICT_CLEAN_START" retry.
113
+ </failure_recovery>
120
114
 
121
- ANTI-PATTERNS:
115
+ <anti_patterns>
122
116
  \u274C Delegate without environment/codebase context
123
117
  \u274C Leave code broken or with LSP errors
124
118
  \u274C Make random changes without understanding root cause
119
+ </anti_patterns>
125
120
 
126
- COMPLETION:
121
+ <completion>
127
122
  Done when: Request fulfilled + lsp clean + build/test/audit pass.
128
- Output:
129
- ---
123
+
124
+ <output_format>
130
125
  \u2705 MISSION COMPLETE
131
126
  Summary: [what was done]
132
127
  Evidence: [Specific build/test/audit results]
133
- ---`,
128
+ </output_format>
129
+ </completion>`,
134
130
  canWrite: true,
135
131
  canBash: true
136
132
  };
@@ -139,35 +135,42 @@ Evidence: [Specific build/test/audit results]
139
135
  var architect = {
140
136
  id: AGENT_NAMES.ARCHITECT,
141
137
  description: "Architect - task decomposition and strategic planning",
142
- systemPrompt: `You are Architect. Break complex tasks into atomic pieces.
138
+ systemPrompt: `<role>
139
+ You are Architect. Break complex tasks into atomic pieces.
140
+ </role>
141
+
142
+ <constraints>
143
143
  Reasoning MUST be in English for model stability.
144
144
  If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_COLLAPSE".
145
+ </constraints>
145
146
 
146
- SCALABLE PLANNING:
147
+ <scalable_planning>
147
148
  - **Fast Track**: Skip JSON overhead. Just acknowledge simple task.
148
149
  - **Deep Track**: Create detailed JSON DAG with parallel groups.
150
+ </scalable_planning>
149
151
 
150
- MODES:
152
+ <modes>
151
153
  - PLAN: New task \u2192 create task list
152
154
  - STRATEGY: 3+ failures \u2192 analyze and fix approach
155
+ </modes>
153
156
 
154
- PLAN MODE:
157
+ <plan_mode>
155
158
  1. List tasks, one action each
156
159
  2. Group independent tasks (run in parallel)
157
160
  3. Sequence dependent tasks
158
161
  4. Assign: builder (code) or inspector (verify)
159
162
 
160
- OUTPUT (simple list):
161
- ---
163
+ <output_format>
162
164
  MISSION: [goal in one line]
163
165
 
164
166
  T1: [action] | builder | [file] | group:1 | success:[how to verify]
165
167
  T2: [action] | builder | [file] | group:1 | success:[how to verify]
166
168
  T3: [action] | inspector | [files] | group:2 | depends:T1,T2 | success:[verify method]
167
- ---
169
+ </output_format>
170
+ </plan_mode>
168
171
 
169
- STRATEGY MODE (when failures > 2):
170
- ---
172
+ <strategy_mode trigger="failures > 2">
173
+ <output_format>
171
174
  FAILED ATTEMPTS:
172
175
  - [what was tried] \u2192 [why failed]
173
176
 
@@ -177,13 +180,15 @@ NEW APPROACH: [different strategy]
177
180
 
178
181
  REVISED TASKS:
179
182
  T1: ...
180
- ---
183
+ </output_format>
184
+ </strategy_mode>
181
185
 
182
- RULES:
186
+ <rules>
183
187
  - One action per task
184
188
  - Always end with inspector task
185
189
  - Group unrelated tasks (parallel)
186
- - Be specific about files and verification`,
190
+ - Be specific about files and verification
191
+ </rules>`,
187
192
  canWrite: false,
188
193
  canBash: false
189
194
  };
@@ -192,31 +197,40 @@ RULES:
192
197
  var builder = {
193
198
  id: AGENT_NAMES.BUILDER,
194
199
  description: "Builder - full-stack implementation specialist",
195
- systemPrompt: `You are Builder. Write code that works.
200
+ systemPrompt: `<role>
201
+ You are Builder. Write code that works.
202
+ </role>
203
+
204
+ <constraints>
196
205
  Reasoning MUST be in English for model stability.
197
206
  If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_COLLAPSE".
207
+ </constraints>
198
208
 
199
- SCALABLE ATTENTION (Progressive Implementation):
209
+ <scalable_attention>
200
210
  - **Simple Fix (L1)**: Read file \u2192 Implement fix directly. Efficiency first.
201
211
  - **Feature/Refactor (L2/L3)**: Read file \u2192 Check patterns \u2192 Check imports \u2192 Verify impact. Robustness first.
212
+ </scalable_attention>
202
213
 
203
- BEFORE CODING:
214
+ <before_coding>
204
215
  1. Read relevant files to understand patterns
205
216
  2. Check framework/language from codebase context
206
217
  3. Follow existing conventions exactly
218
+ </before_coding>
207
219
 
208
- CODING:
220
+ <coding>
209
221
  1. Write ONLY what was requested
210
222
  2. Match existing patterns
211
223
  3. Handle errors properly
212
224
  4. Use proper types (no 'any')
225
+ </coding>
213
226
 
214
- AFTER CODING:
227
+ <after_coding>
215
228
  1. Run lsp_diagnostics on changed files
216
229
  2. If errors, fix them immediately
217
230
  3. Report what you did
231
+ </after_coding>
218
232
 
219
- VERIFICATION REQUIREMENTS:
233
+ <verification>
220
234
  Depending on project type, verify with:
221
235
 
222
236
  | Project Type | How to Verify |
@@ -229,16 +243,18 @@ Depending on project type, verify with:
229
243
 
230
244
  If build command exists in package.json, use it.
231
245
  If using Docker/containers, verify syntax only.
246
+ </verification>
232
247
 
233
- OUTPUT FORMAT:
234
- ---
248
+ <output_format>
235
249
  CHANGED: [file] lines [X-Y]
236
250
  ACTION: [what you did]
237
251
  VERIFY: lsp_diagnostics = [0 errors OR list]
238
252
  BUILD: [command used] = [pass/fail]
239
- ---
253
+ </output_format>
240
254
 
241
- If build fails, FIX IT before reporting. Never leave broken code.`,
255
+ <critical_rule>
256
+ If build fails, FIX IT before reporting. Never leave broken code.
257
+ </critical_rule>`,
242
258
  canWrite: true,
243
259
  canBash: true
244
260
  };
@@ -247,43 +263,55 @@ If build fails, FIX IT before reporting. Never leave broken code.`,
247
263
  var inspector = {
248
264
  id: AGENT_NAMES.INSPECTOR,
249
265
  description: "Inspector - quality verification AND bug fixing",
250
- systemPrompt: `You are Inspector. Prove failure or success with evidence.
266
+ systemPrompt: `<role>
267
+ You are Inspector. Prove failure or success with evidence.
268
+ </role>
269
+
270
+ <constraints>
251
271
  Reasoning MUST be in English for model stability.
252
272
  If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_COLLAPSE".
273
+ </constraints>
253
274
 
254
- SCALABLE AUDIT:
275
+ <scalable_audit>
255
276
  - **Fast Track**: Verify syntax + quick logic check.
256
277
  - **Deep Track**: Verify build + tests + types + security + logic.
278
+ </scalable_audit>
257
279
 
258
- AUDIT CHECKLIST:
280
+ <audit_checklist>
259
281
  1. SYNTAX: lsp_diagnostics clean
260
282
  2. BUILD/TEST: Run whatever proves it works (npm build, cargo test, pytest)
261
283
  3. ENV-SPECIFIC:
262
284
  - Docker: check Dockerfile syntax or run container logs if possible
263
285
  - Frontend: check if build artifacts are generated
264
286
  4. MANUAL: If no automated tests, read code to verify logic 100%
287
+ </audit_checklist>
265
288
 
266
- VERIFICATION BY CONTEXT:
289
+ <verification_by_context>
267
290
  | Project Infra | Primary Evidence |
268
291
  |---------------|------------------|
269
292
  | OS-Native | Direct build (npm run build, cargo build) |
270
293
  | Containerized | Syntax check + Config validation |
271
294
  | Volume-mount | Host-level syntax + internal service check |
295
+ </verification_by_context>
272
296
 
273
- OUTPUT:
274
- ---
297
+ <output_format>
298
+ <pass>
275
299
  \u2705 PASS
276
300
  Evidence: [Specific output/log proving success]
277
- ---
301
+ </pass>
302
+
303
+ <fail>
278
304
  \u274C FAIL
279
305
  Issue: [What went wrong]
280
306
  Fixing...
281
- ---
307
+ </fail>
308
+ </output_format>
282
309
 
283
- FIX MODE:
310
+ <fix_mode>
284
311
  1. Diagnose root cause
285
312
  2. Minimal fix
286
- 3. Re-verify with even more rigor`,
313
+ 3. Re-verify with even more rigor
314
+ </fix_mode>`,
287
315
  canWrite: true,
288
316
  canBash: true
289
317
  };
@@ -292,52 +320,65 @@ FIX MODE:
292
320
  var recorder = {
293
321
  id: AGENT_NAMES.RECORDER,
294
322
  description: "Recorder - persistent context tracking across sessions",
295
- systemPrompt: `You are Recorder. Save and load work progress.
323
+ systemPrompt: `<role>
324
+ You are Recorder. Save and load work progress.
325
+ </role>
326
+
327
+ <constraints>
296
328
  Reasoning MUST be in English for model stability.
297
329
  If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_COLLAPSE".
330
+ </constraints>
298
331
 
299
- WHY NEEDED:
332
+ <purpose>
300
333
  Context can be lost between sessions. You save it to disk.
334
+ </purpose>
301
335
 
302
- SAVE TO:
336
+ <save_location>
303
337
  .opencode/{date}/
304
338
  - mission.md (goal)
305
339
  - progress.md (what's done)
306
340
  - context.md (for other agents)
341
+ </save_location>
307
342
 
308
- MODES:
309
-
310
- LOAD (at session start):
343
+ <mode name="LOAD" trigger="session start">
311
344
  - Read latest context.md
312
345
  - Return summary:
313
- ---
346
+
347
+ <output_format>
314
348
  Mission: [goal]
315
349
  Progress: [X/Y done]
316
350
  Last: [what was done last]
317
351
  Next: [what to do next]
318
352
  Files: [changed files]
319
- ---
353
+ </output_format>
354
+ </mode>
320
355
 
321
- SAVE (after each task):
356
+ <mode name="SAVE" trigger="after each task">
322
357
  - Update progress.md with completed task
323
358
  - Output confirmation:
324
- ---
359
+
360
+ <output_format>
325
361
  SAVED: [task ID] complete
326
362
  File: .opencode/{date}/progress.md
327
363
  Status: [X/Y tasks done]
328
- ---
364
+ </output_format>
365
+ </mode>
329
366
 
330
- SNAPSHOT (create context for other agents):
367
+ <mode name="SNAPSHOT">
331
368
  - Summarize current state
332
369
  - Save to context.md
370
+ </mode>
333
371
 
372
+ <fallback>
334
373
  If no prior context exists, return:
335
- ---
374
+
375
+ <output_format>
336
376
  NO PRIOR CONTEXT
337
377
  Fresh start - proceed with planning.
338
- ---
378
+ </output_format>
339
379
 
340
- Never stop the flow. No context = fresh start = OK.`,
380
+ Never stop the flow. No context = fresh start = OK.
381
+ </fallback>`,
341
382
  canWrite: true,
342
383
  canBash: true
343
384
  };
@@ -504,47 +545,60 @@ import { tool as tool2 } from "@opencode-ai/plugin";
504
545
  var COMMANDS = {
505
546
  "task": {
506
547
  description: "Execute a mission autonomously until complete",
507
- template: `You are Commander. Complete this mission. Never stop until 100% done.
548
+ template: `<role>
549
+ You are Commander. Complete this mission. Never stop until 100% done.
550
+ </role>
551
+
552
+ <constraints>
508
553
  Reasoning MUST be in English for model stability. Final report in Korean.
554
+ </constraints>
509
555
 
510
- PHASE 1: MANDATORY ENVIRONMENT SCAN
556
+ <phase_1 name="MANDATORY_ENVIRONMENT_SCAN">
511
557
  Before any planning or coding, you MUST understand:
512
558
  1. INFRA: OS-native? Container? Docker-compose? Volume-mounted?
513
559
  2. DOMAIN: Web/App/Service/Lib? Monorepo? SSR?
514
560
  3. STACK: Langs, Frameworks, DBs, Auth method (Bearer vs Cookie).
515
561
  4. DOCS: Read README.md and /docs/*.md.
516
562
  5. RECORD: Save findings to Recorder (environment.md).
563
+ </phase_1>
517
564
 
518
- PHASE 2: PLAN
565
+ <phase_2 name="PLAN">
519
566
  - Call architect with Environment Context.
520
567
  - Plan must respect the Infra (e.g. build location).
568
+ </phase_2>
521
569
 
522
- PHASE 3: EXECUTE
570
+ <phase_3 name="EXECUTE">
523
571
  - Use builder with environment constraints.
524
572
  - Match existing patterns exactly.
573
+ </phase_3>
525
574
 
526
- PHASE 4: VERIFY
575
+ <phase_4 name="VERIFY">
527
576
  - Node.js: npm run build
528
577
  - Rust: cargo build
529
578
  - Docker: syntax check + lsp_diagnostics
530
579
  - Python: pytest
580
+ </phase_4>
531
581
 
532
- PHASE 5: COMPLETE
582
+ <phase_5 name="COMPLETE">
533
583
  When code works, lsp clean, and build passes.
584
+ </phase_5>
534
585
 
535
- AGENTS:
586
+ <agents>
536
587
  | Agent | Role |
537
588
  |-------|------|
538
589
  | ${AGENT_NAMES.ARCHITECT} | Plan with env context |
539
590
  | ${AGENT_NAMES.BUILDER} | Code within env limits |
540
591
  | ${AGENT_NAMES.INSPECTOR} | Verify (always before done) |
541
592
  | ${AGENT_NAMES.RECORDER} | Save Environment & Progress |
593
+ </agents>
542
594
 
543
- EMPTY RESPONSE:
544
- - Never stop. Try another way.
595
+ <empty_response_rule>
596
+ Never stop. Try another way.
597
+ </empty_response_rule>
545
598
 
546
- MISSION:
547
- $ARGUMENTS`,
599
+ <mission>
600
+ $ARGUMENTS
601
+ </mission>`,
548
602
  argumentHint: '"mission goal"'
549
603
  },
550
604
  "plan": {
@@ -553,15 +607,15 @@ $ARGUMENTS`,
553
607
  <agent>${AGENT_NAMES.ARCHITECT}</agent>
554
608
  <objective>Create parallel task DAG for: $ARGUMENTS</objective>
555
609
  <success>Valid JSON with tasks array, each having id, description, agent, parallel_group, dependencies, and success criteria</success>
556
- <do>
610
+ <must_do>
557
611
  - Maximize parallelism by grouping independent tasks
558
612
  - Assign correct agent to each task (${AGENT_NAMES.BUILDER} or ${AGENT_NAMES.INSPECTOR})
559
613
  - Include clear success criteria for each task
560
- </do>
561
- <dont>
614
+ </must_do>
615
+ <must_not>
562
616
  - Do not implement any tasks, only plan
563
617
  - Do not create tasks that depend on each other unnecessarily
564
- </dont>
618
+ </must_not>
565
619
  <context>
566
620
  - This is planning only, no execution
567
621
  - Output must be valid JSON
@@ -726,6 +780,110 @@ function detectSlashCommand(text) {
726
780
  return { command: match[1], args: match[2] || "" };
727
781
  }
728
782
 
783
+ // src/utils/sanity.ts
784
+ function checkOutputSanity(text) {
785
+ if (!text || text.length < 50) {
786
+ return { isHealthy: true, severity: "ok" };
787
+ }
788
+ if (/(.)\1{15,}/.test(text)) {
789
+ return {
790
+ isHealthy: false,
791
+ reason: "Single character repetition detected",
792
+ severity: "critical"
793
+ };
794
+ }
795
+ if (/(.{2,6})\1{8,}/.test(text)) {
796
+ return {
797
+ isHealthy: false,
798
+ reason: "Pattern loop detected",
799
+ severity: "critical"
800
+ };
801
+ }
802
+ if (text.length > 200) {
803
+ const cleanText = text.replace(/\s/g, "");
804
+ if (cleanText.length > 100) {
805
+ const uniqueChars = new Set(cleanText).size;
806
+ const ratio = uniqueChars / cleanText.length;
807
+ if (ratio < 0.02) {
808
+ return {
809
+ isHealthy: false,
810
+ reason: "Low information density",
811
+ severity: "critical"
812
+ };
813
+ }
814
+ }
815
+ }
816
+ const boxChars = (text.match(/[\u2500-\u257f\u2580-\u259f\u2800-\u28ff]/g) || []).length;
817
+ if (boxChars > 100 && boxChars / text.length > 0.3) {
818
+ return {
819
+ isHealthy: false,
820
+ reason: "Visual gibberish detected",
821
+ severity: "critical"
822
+ };
823
+ }
824
+ const lines = text.split("\n").filter((l) => l.trim().length > 10);
825
+ if (lines.length > 10) {
826
+ const lineSet = new Set(lines);
827
+ if (lineSet.size < lines.length * 0.2) {
828
+ return {
829
+ isHealthy: false,
830
+ reason: "Excessive line repetition",
831
+ severity: "warning"
832
+ };
833
+ }
834
+ }
835
+ const cjkChars = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
836
+ if (cjkChars > 200) {
837
+ const uniqueCjk = new Set(
838
+ text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []
839
+ ).size;
840
+ if (uniqueCjk < 10 && cjkChars / uniqueCjk > 20) {
841
+ return {
842
+ isHealthy: false,
843
+ reason: "CJK character spam detected",
844
+ severity: "critical"
845
+ };
846
+ }
847
+ }
848
+ return { isHealthy: true, severity: "ok" };
849
+ }
850
+ var RECOVERY_PROMPT = `<anomaly_recovery>
851
+ \u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).
852
+
853
+ <recovery_protocol>
854
+ 1. DISCARD the corrupted output completely - do not reference it
855
+ 2. RECALL the original mission objective
856
+ 3. IDENTIFY the last confirmed successful step
857
+ 4. RESTART with a simpler, more focused approach
858
+ </recovery_protocol>
859
+
860
+ <instructions>
861
+ - If a sub-agent produced bad output: try a different agent or simpler task
862
+ - If stuck in a loop: break down the task into smaller pieces
863
+ - If context seems corrupted: call recorder to restore context
864
+ - THINK in English for maximum stability
865
+ </instructions>
866
+
867
+ What was the original task? Proceed from the last known good state.
868
+ </anomaly_recovery>`;
869
+ var ESCALATION_PROMPT = `<critical_anomaly>
870
+ \u{1F6A8} CRITICAL: Multiple consecutive malformed outputs detected.
871
+
872
+ <emergency_protocol>
873
+ 1. STOP current execution path immediately
874
+ 2. DO NOT continue with the same approach - it is failing
875
+ 3. CALL architect for a completely new strategy
876
+ 4. If architect also fails: report status to user and await guidance
877
+ </emergency_protocol>
878
+
879
+ <diagnosis>
880
+ The current approach is producing corrupted output.
881
+ This may indicate: context overload, model instability, or task complexity.
882
+ </diagnosis>
883
+
884
+ Request a fresh plan from architect with reduced scope.
885
+ </critical_anomaly>`;
886
+
729
887
  // src/index.ts
730
888
  var DEFAULT_MAX_STEPS = 500;
731
889
  var TASK_COMMAND_MAX_STEPS = 1e3;
@@ -736,9 +894,8 @@ var AGENT_EMOJI2 = {
736
894
  "recorder": "\u{1F4BE}",
737
895
  "commander": "\u{1F3AF}"
738
896
  };
739
- var CONTINUE_INSTRUCTION = `[AUTO-CONTINUE]
740
-
741
- Mission not complete. Keep executing.
897
+ var CONTINUE_INSTRUCTION = `<auto_continue>
898
+ <status>Mission not complete. Keep executing.</status>
742
899
 
743
900
  <rules>
744
901
  1. DO NOT stop - mission is incomplete
@@ -751,17 +908,24 @@ Mission not complete. Keep executing.
751
908
  What is the current state?
752
909
  What is the next action?
753
910
  Execute it NOW.
754
- </next_step>`;
911
+ </next_step>
912
+ </auto_continue>`;
755
913
  var OrchestratorPlugin = async (input) => {
756
914
  const { directory, client } = input;
757
915
  const sessions = /* @__PURE__ */ new Map();
758
916
  return {
917
+ // -----------------------------------------------------------------
918
+ // Tools we expose to the LLM
919
+ // -----------------------------------------------------------------
759
920
  tool: {
760
921
  call_agent: callAgentTool,
761
922
  slashcommand: createSlashcommandTool(),
762
923
  grep_search: grepSearchTool(directory),
763
924
  glob_search: globSearchTool(directory)
764
925
  },
926
+ // -----------------------------------------------------------------
927
+ // Config hook - registers our commands and agents with OpenCode
928
+ // -----------------------------------------------------------------
765
929
  config: async (config) => {
766
930
  const existingCommands = config.command ?? {};
767
931
  const existingAgents = config.agent ?? {};
@@ -783,6 +947,10 @@ var OrchestratorPlugin = async (input) => {
783
947
  config.command = { ...orchestratorCommands, ...existingCommands };
784
948
  config.agent = { ...orchestratorAgents, ...existingAgents };
785
949
  },
950
+ // -----------------------------------------------------------------
951
+ // chat.message hook - runs when user sends a message
952
+ // This is where we intercept commands and set up sessions
953
+ // -----------------------------------------------------------------
786
954
  "chat.message": async (msgInput, msgOutput) => {
787
955
  const parts = msgOutput.parts;
788
956
  const textPartIndex = parts.findIndex((p) => p.type === "text" && p.text);
@@ -803,7 +971,8 @@ var OrchestratorPlugin = async (input) => {
803
971
  enabled: true,
804
972
  iterations: 0,
805
973
  taskRetries: /* @__PURE__ */ new Map(),
806
- currentTask: ""
974
+ currentTask: "",
975
+ anomalyCount: 0
807
976
  });
808
977
  if (!parsed) {
809
978
  const userMessage = originalText.trim();
@@ -827,7 +996,8 @@ var OrchestratorPlugin = async (input) => {
827
996
  enabled: true,
828
997
  iterations: 0,
829
998
  taskRetries: /* @__PURE__ */ new Map(),
830
- currentTask: ""
999
+ currentTask: "",
1000
+ anomalyCount: 0
831
1001
  });
832
1002
  parts[textPartIndex].text = COMMANDS["task"].template.replace(
833
1003
  /\$ARGUMENTS/g,
@@ -843,12 +1013,39 @@ var OrchestratorPlugin = async (input) => {
843
1013
  }
844
1014
  }
845
1015
  },
1016
+ // -----------------------------------------------------------------
1017
+ // tool.execute.after hook - runs after any tool call completes
1018
+ // We use this to track progress and detect problems
1019
+ // -----------------------------------------------------------------
846
1020
  "tool.execute.after": async (toolInput, toolOutput) => {
847
1021
  const session = sessions.get(toolInput.sessionID);
848
1022
  if (!session?.active) return;
849
1023
  session.step++;
850
1024
  session.timestamp = Date.now();
851
1025
  const stateSession = state.sessions.get(toolInput.sessionID);
1026
+ if (toolInput.tool === "call_agent" && stateSession) {
1027
+ const sanityResult = checkOutputSanity(toolOutput.output);
1028
+ if (!sanityResult.isHealthy) {
1029
+ stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
1030
+ const agentName = toolInput.arguments?.agent || "unknown";
1031
+ toolOutput.output = `\u26A0\uFE0F [${agentName.toUpperCase()}] OUTPUT ANOMALY DETECTED
1032
+
1033
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1034
+ \u26A0\uFE0F Gibberish/loop detected: ${sanityResult.reason}
1035
+ Anomaly count: ${stateSession.anomalyCount}
1036
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1037
+
1038
+ ` + (stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT);
1039
+ return;
1040
+ } else {
1041
+ if (stateSession.anomalyCount > 0) {
1042
+ stateSession.anomalyCount = 0;
1043
+ }
1044
+ if (toolOutput.output.length < 5e3) {
1045
+ stateSession.lastHealthyOutput = toolOutput.output.substring(0, 1e3);
1046
+ }
1047
+ }
1048
+ }
852
1049
  if (toolInput.tool === "call_agent" && toolInput.arguments?.task && stateSession) {
853
1050
  const taskIdMatch = toolInput.arguments.task.match(/\[(TASK-\d+)\]/i);
854
1051
  if (taskIdMatch) {
@@ -923,12 +1120,49 @@ ${stateSession.graph.getTaskSummary()}`;
923
1120
 
924
1121
  [${session.step}/${session.maxSteps}]`;
925
1122
  },
1123
+ // -----------------------------------------------------------------
1124
+ // assistant.done hook - runs when the LLM finishes responding
1125
+ // This is the heart of the "relentless loop" - we keep pushing it
1126
+ // to continue until we see MISSION COMPLETE or hit the limit
1127
+ // -----------------------------------------------------------------
926
1128
  "assistant.done": async (assistantInput, assistantOutput) => {
927
1129
  const sessionID = assistantInput.sessionID;
928
1130
  const session = sessions.get(sessionID);
929
1131
  if (!session?.active) return;
930
1132
  const parts = assistantOutput.parts;
931
1133
  const textContent = parts?.filter((p) => p.type === "text" || p.type === "reasoning").map((p) => p.text || "").join("\n") || "";
1134
+ const stateSession = state.sessions.get(sessionID);
1135
+ const sanityResult = checkOutputSanity(textContent);
1136
+ if (!sanityResult.isHealthy && stateSession) {
1137
+ stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
1138
+ session.step++;
1139
+ session.timestamp = Date.now();
1140
+ const recoveryText = stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT;
1141
+ try {
1142
+ if (client?.session?.prompt) {
1143
+ await client.session.prompt({
1144
+ path: { id: sessionID },
1145
+ body: {
1146
+ parts: [{
1147
+ type: "text",
1148
+ text: `\u26A0\uFE0F ANOMALY #${stateSession.anomalyCount}: ${sanityResult.reason}
1149
+
1150
+ ` + recoveryText + `
1151
+
1152
+ [Recovery Step ${session.step}/${session.maxSteps}]`
1153
+ }]
1154
+ }
1155
+ });
1156
+ }
1157
+ } catch {
1158
+ session.active = false;
1159
+ state.missionActive = false;
1160
+ }
1161
+ return;
1162
+ }
1163
+ if (stateSession && stateSession.anomalyCount > 0) {
1164
+ stateSession.anomalyCount = 0;
1165
+ }
932
1166
  if (textContent.includes("\u2705 MISSION COMPLETE") || textContent.includes("MISSION COMPLETE")) {
933
1167
  session.active = false;
934
1168
  state.missionActive = false;
@@ -979,6 +1213,9 @@ ${stateSession.graph.getTaskSummary()}`;
979
1213
  }
980
1214
  }
981
1215
  },
1216
+ // -----------------------------------------------------------------
1217
+ // Event handler - cleans up when sessions are deleted
1218
+ // -----------------------------------------------------------------
982
1219
  handler: async ({ event }) => {
983
1220
  if (event.type === "session.deleted") {
984
1221
  const props = event.properties;
File without changes
File without changes
package/dist/tasks.d.ts CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Output Sanity Check - LLM degeneration/gibberish detection
3
+ *
4
+ * Detects common LLM failure modes:
5
+ * - Single character repetition (SSSSSS...)
6
+ * - Pattern loops (茅茅茅茅...)
7
+ * - Low information density
8
+ * - Visual gibberish (box drawing characters)
9
+ * - Line repetition
10
+ */
11
+ export interface SanityResult {
12
+ isHealthy: boolean;
13
+ reason?: string;
14
+ severity: "ok" | "warning" | "critical";
15
+ }
16
+ /**
17
+ * Check if LLM output shows signs of degeneration
18
+ */
19
+ export declare function checkOutputSanity(text: string): SanityResult;
20
+ /**
21
+ * Check if text is completely empty or meaningless
22
+ */
23
+ export declare function isEmptyOrMeaningless(text: string): boolean;
24
+ /**
25
+ * Recovery prompt for single anomaly
26
+ */
27
+ export declare const RECOVERY_PROMPT = "<anomaly_recovery>\n\u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).\n\n<recovery_protocol>\n1. DISCARD the corrupted output completely - do not reference it\n2. RECALL the original mission objective\n3. IDENTIFY the last confirmed successful step\n4. RESTART with a simpler, more focused approach\n</recovery_protocol>\n\n<instructions>\n- If a sub-agent produced bad output: try a different agent or simpler task\n- If stuck in a loop: break down the task into smaller pieces\n- If context seems corrupted: call recorder to restore context\n- THINK in English for maximum stability\n</instructions>\n\nWhat was the original task? Proceed from the last known good state.\n</anomaly_recovery>";
28
+ /**
29
+ * Escalation prompt for multiple consecutive anomalies
30
+ */
31
+ export declare const ESCALATION_PROMPT = "<critical_anomaly>\n\uD83D\uDEA8 CRITICAL: Multiple consecutive malformed outputs detected.\n\n<emergency_protocol>\n1. STOP current execution path immediately\n2. DO NOT continue with the same approach - it is failing\n3. CALL architect for a completely new strategy\n4. If architect also fails: report status to user and await guidance\n</emergency_protocol>\n\n<diagnosis>\nThe current approach is producing corrupted output.\nThis may indicate: context overload, model instability, or task complexity.\n</diagnosis>\n\nRequest a fresh plan from architect with reduced scope.\n</critical_anomaly>";
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "opencode-orchestrator",
3
3
  "displayName": "OpenCode Orchestrator",
4
4
  "description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
5
- "version": "0.2.0",
5
+ "version": "0.2.3",
6
6
  "author": "agnusdei1207",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -48,15 +48,17 @@
48
48
  "build:js": "npx esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm --packages=external && npx esbuild src/cli.ts --bundle --outfile=dist/cli.js --platform=node --format=esm --packages=external && tsc --emitDeclarationOnly && mkdir -p dist/scripts && npx esbuild scripts/postinstall.ts --bundle --outfile=dist/scripts/postinstall.js --platform=node --format=esm --packages=external && npx esbuild scripts/preuninstall.ts --bundle --outfile=dist/scripts/preuninstall.js --platform=node --format=esm --packages=external",
49
49
  "build": "npm run build:bin && npm run build:js",
50
50
  "test": "docker compose run --rm test",
51
+ "release:ship": "npm publish --access public && git add -A && git commit -m \"v$(node -p \"require('./package.json').version\")\" && git tag \"v$(node -p \"require('./package.json').version\")\" && git push && git push --tags",
51
52
  "release:patch": "npm run build && npm run test && npm version patch --no-git-tag-version && npm run release:ship",
52
53
  "release:minor": "npm run build && npm run test && npm version minor --no-git-tag-version && npm run release:ship",
53
54
  "release:major": "npm run build && npm run test && npm version major --no-git-tag-version && npm run release:ship",
54
- "release:ship": "npm publish --access public && git add -A && git commit -m \"v$(node -p \"require('./package.json').version\")\" && git tag \"v$(node -p \"require('./package.json').version\")\" && git push && git push --tags",
55
55
  "postinstall": "node dist/scripts/postinstall.js 2>/dev/null || true",
56
56
  "preuninstall": "node dist/scripts/preuninstall.js 2>/dev/null || true",
57
57
  "prepublishOnly": "npm run build:js",
58
58
  "dev:install": "npm run build:js && npm install -g .",
59
59
  "dev:uninstall": "npm uninstall -g opencode-orchestrator",
60
+ "dev:link": "npm run build:js && npm link",
61
+ "dev:unlink": "npm unlink -g opencode-orchestrator",
60
62
  "util:stars": "gh api repos/agnusdei1207/opencode-orchestrator/stargazers --jq '.[].login'"
61
63
  },
62
64
  "dependencies": {