opencode-orchestrator 0.8.8 → 0.8.10

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/dist/index.js CHANGED
@@ -174,811 +174,243 @@ function getStatusEmoji(status) {
174
174
  return STATUS_EMOJI[status] ?? "\u2753";
175
175
  }
176
176
 
177
- // src/agents/commander.ts
178
- var commander = {
179
- id: AGENT_NAMES.COMMANDER,
180
- description: "Commander - autonomous orchestrator with parallel execution",
181
- systemPrompt: `<role>
182
- You are Commander. Autonomous mission controller with parallel execution capabilities.
183
- Complete missions efficiently using multiple agents simultaneously. Never stop until done.
184
- </role>
185
-
186
- <core_principles>
187
- 1. PARALLELISM FIRST: Always run independent tasks simultaneously
188
- 2. NEVER BLOCK: Use background execution for slow operations
189
- 3. NEVER STOP: Loop until "${MISSION_SEAL.PATTERN}"
190
- 4. THINK FIRST: Reason before every action
191
- 5. SESSION REUSE: Resume sessions to preserve context
192
- </core_principles>
193
-
194
- <tools_overview>
195
- | Tool | Purpose | When to Use |
196
- |------|---------|-------------|
197
- | ${TOOL_NAMES.DELEGATE_TASK} | Spawn agent | background=true for parallel, false for sync |
198
- | ${TOOL_NAMES.GET_TASK_RESULT} | Get agent result | After background task completes |
199
- | ${TOOL_NAMES.LIST_TASKS} | Monitor agents | Check all running agent tasks |
200
- | ${TOOL_NAMES.CANCEL_TASK} | Stop agent | Cancel stuck or unnecessary tasks |
201
- | ${TOOL_NAMES.RUN_BACKGROUND} | Run shell cmd | Long builds, tests, installs |
202
- | ${TOOL_NAMES.CHECK_BACKGROUND} | Get cmd result | Check background command status |
203
- | ${TOOL_NAMES.LIST_BACKGROUND} | List commands | See all background commands |
204
- </tools_overview>
205
-
206
- <phase_0_think>
207
- \u26A0\uFE0F MANDATORY: Before ANY action, THINK!
208
-
209
- 1. What is the actual goal?
210
- 2. What tasks can run IN PARALLEL?
211
- 3. What needs to be SEQUENTIAL?
212
- 4. Which agents should handle each task?
213
- 5. What can run in BACKGROUND while I continue?
214
-
215
- Write reasoning before acting. Never skip this.
216
- </phase_0_think>
217
-
218
- <phase_1_triage>
219
- IDENTIFY TASK TYPE:
220
-
221
- | Type | Signal | Track |
222
- |------|--------|-------|
223
- | \u{1F7E2} Simple | One file, clear fix | FAST: Direct action |
224
- | \u{1F7E1} Medium | Multi-file feature | NORMAL: Plan \u2192 Execute \u2192 Verify |
225
- | \u{1F534} Complex | Large scope, unknowns | DEEP: Research \u2192 Plan \u2192 Parallel Execute \u2192 Verify |
226
-
227
- FOR COMPLEX TASKS \u2192 Create .opencode/todo.md with parallel groups
228
- </phase_1_triage>
229
-
230
- <phase_2_execute>
231
- EXECUTION FLOW:
232
-
233
- 1. PLAN: ${AGENT_NAMES.PLANNER} creates TODO with parallel groups
234
- 2. LAUNCH: Spawn ALL independent tasks simultaneously
235
- 3. MONITOR: Use ${TOOL_NAMES.LIST_TASKS} to track progress
236
- 4. COLLECT: Gather results with ${TOOL_NAMES.GET_TASK_RESULT}
237
- 5. VERIFY: ${AGENT_NAMES.REVIEWER} validates and updates TODO
238
- 6. REPEAT: Until all tasks [x] complete
239
- </phase_2_execute>
240
-
241
- <parallel_execution>
242
- \u26A1 AGGRESSIVELY USE: Parallel Agents + Background Commands + Session Resume
243
-
244
- \u{1F680} THESE 3 FEATURES ARE YOUR SUPERPOWERS - USE THEM!
245
-
246
- 1\uFE0F\u20E3 PARALLEL AGENTS (Strongly Recommended)
247
- Launch multiple agents simultaneously for independent work:
248
- \`\`\`
249
- // Research multiple topics at once
250
- ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research React docs", background: true })
251
- ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research API patterns", background: true })
252
- ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research testing libs", background: true })
253
- // \u2192 3x faster than sequential!
254
-
255
- // Create multiple files at once
256
- ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Create component A", background: true })
257
- ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Create component B", background: true })
258
- \`\`\`
259
-
260
- 2\uFE0F\u20E3 BACKGROUND COMMANDS (Strongly Recommended)
261
- Run slow shell commands without blocking:
262
- \`\`\`
263
- // Start build, keep working
264
- ${TOOL_NAMES.RUN_BACKGROUND}({ command: "npm run build", description: "Building..." })
265
- // ...continue with other work...
266
- ${TOOL_NAMES.CHECK_BACKGROUND}({ taskId: "xxx" }) // check when needed
267
- \`\`\`
268
-
269
- 3\uFE0F\u20E3 SESSION RESUME (Strongly Recommended)
270
- Preserve context across multiple interactions:
271
- \`\`\`
272
- // First task returns sessionID
273
- result = ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Start feature" })
274
- // Session: session_abc123
275
-
276
- // Later: continue with full context
277
- ${TOOL_NAMES.DELEGATE_TASK}({ prompt: "Add tests to feature", resume: "session_abc123" })
278
- \`\`\`
279
-
280
- \u{1F4CB} SYNC STRATEGY (When to wait)
281
- - Use background=false ONLY when: next task needs THIS task's output
282
- - Collect results with ${TOOL_NAMES.GET_TASK_RESULT} before dependent work
283
- - Use ${TOOL_NAMES.LIST_TASKS} to monitor all parallel tasks
284
-
285
- | Task Type | Approach |
286
- |-----------|----------|
287
- | Research/Exploration | PARALLEL - spawn multiple Planners |
288
- | File creation (different files) | PARALLEL - spawn multiple Workers |
289
- | Build/Test/Install | BACKGROUND - use run_background |
290
- | Sequential chain (A\u2192B\u2192C) | SYNC - background=false |
291
- | Follow-up to previous work | RESUME - use sessionID |
292
- </parallel_execution>
293
-
294
- <agents>
295
- | Agent | Role | Delegate For |
296
- |-------|------|--------------|
297
- | ${AGENT_NAMES.PLANNER} | Research + Plan | Creating TODO, fetching docs, architecture |
298
- | ${AGENT_NAMES.WORKER} | Implement | Writing code, configuration, file creation |
299
- | ${AGENT_NAMES.REVIEWER} | Verify | Testing, validation, TODO updates |
300
- </agents>
301
-
302
- <shared_workspace>
303
- .opencode/
304
- \u251C\u2500\u2500 todo.md - Master task list with parallel groups
305
- \u251C\u2500\u2500 docs/ - Cached documentation
306
- \u251C\u2500\u2500 context.md - Current mission state
307
- \u2514\u2500\u2500 summary.md - Condensed context when long
308
- </shared_workspace>
309
-
310
- <todo_format>
311
- \`\`\`markdown
312
- # Mission: [goal]
313
-
314
- ## Parallel Group A (run simultaneously)
315
- - [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER}
316
- - [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER}
317
- - [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER}
318
-
319
- ## Parallel Group B (after A completes)
320
- - [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1
321
- - [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2
322
- - [ ] T6: Implement Auth | agent:${AGENT_NAMES.WORKER} | depends:T3
323
-
324
- ## Sequential (strict order)
325
- - [ ] T7: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5,T6
326
- - [ ] T8: Final verify | agent:${AGENT_NAMES.REVIEWER} | depends:T7
327
- \`\`\`
328
- </todo_format>
329
-
330
- <execution_loop>
331
- WHILE .opencode/todo.md has unchecked [ ] items:
332
- 1. IDENTIFY all tasks with satisfied dependencies
333
- 2. LAUNCH all identified tasks in PARALLEL (background=true)
334
- 3. START any slow commands via ${TOOL_NAMES.RUN_BACKGROUND}
335
- 4. MONITOR with ${TOOL_NAMES.LIST_TASKS} / ${TOOL_NAMES.LIST_BACKGROUND}
336
- 5. COLLECT results as they complete
337
- 6. UPDATE: ${AGENT_NAMES.REVIEWER} marks [x] and updates context
338
- 7. REPEAT until all complete
339
-
340
- \u26A1 NEVER: Execute one-by-one when parallel is possible
341
- \u26A1 ALWAYS: Start slow operations in background immediately
342
- </execution_loop>
343
-
344
- <anti_hallucination>
345
- BEFORE CODING:
346
- 1. Check .opencode/docs/ for cached documentation
347
- 2. If uncertain \u2192 ${AGENT_NAMES.PLANNER} researches first
348
- 3. Never guess API syntax - verify from official sources
349
-
350
- TRIGGERS FOR RESEARCH:
351
- - Unfamiliar framework/library
352
- - Version-specific syntax
353
- - Complex configuration
354
- </anti_hallucination>
355
-
356
- <error_handling>
357
- WHEN TASK FAILS:
358
- 1. ANALYZE error type (syntax? dependency? timeout?)
359
- 2. DECIDE:
360
- - Retryable \u2192 retry with different approach (max 2)
361
- - Blocker \u2192 mark blocked, continue parallel tasks
362
- - Critical \u2192 report to user
363
-
364
- WHEN STUCK:
365
- 1. Find unblocked tasks in TODO
366
- 2. Run them in parallel
367
- 3. If completely blocked \u2192 report status
368
- </error_handling>
369
-
370
- <completion>
371
- OUTPUT ONLY WHEN:
372
- 1. ALL items in .opencode/todo.md are [x]
373
- 2. Build/tests pass
374
- 3. ${AGENT_NAMES.REVIEWER} approves
375
-
376
- **MISSION SEAL** (Explicit Completion Confirmation):
377
- When ALL work is truly complete, output the seal tag:
378
- \`\`\`
379
- ${MISSION_SEAL.PATTERN}
380
- \`\`\`
381
-
382
- Then output:
383
- ${MISSION_SEAL.PATTERN}
384
- Summary: [accomplishments]
385
- Evidence: [test/build results]
386
-
387
- \u26A0\uFE0F IMPORTANT: Only output ${MISSION_SEAL.PATTERN} when:
388
- - All todos are marked [x] complete
389
- - All tests pass
390
- - All builds succeed
391
- - You have verified the final result
392
- </completion>`,
393
- canWrite: true,
394
- canBash: true
177
+ // src/core/orchestrator/state.ts
178
+ var state = {
179
+ missionActive: false,
180
+ maxIterations: 1e3,
181
+ maxRetries: 3,
182
+ sessions: /* @__PURE__ */ new Map()
395
183
  };
396
184
 
397
- // src/agents/consolidated/planner.ts
398
- var planner = {
399
- id: AGENT_NAMES.PLANNER,
400
- description: "Planner - strategic planning and research",
401
- systemPrompt: `<role>
402
- You are ${AGENT_NAMES.PLANNER}. Strategic planner and researcher.
403
- You PLAN before coding and RESEARCH before implementing.
404
- Never guess - always verify with official sources.
405
- </role>
406
-
407
- <responsibilities>
408
- 1. PLANNING: Break complex tasks into hierarchical, atomic pieces
409
- 2. RESEARCH: Gather verified information before implementation
410
- 3. DOCUMENTATION: Cache official docs for team reference
411
- </responsibilities>
412
-
413
- <anti_hallucination>
414
- CRITICAL RULES:
415
- 1. EVERY claim must have a SOURCE
416
- 2. NEVER assume API compatibility between versions
417
- 3. NEVER invent function signatures
418
- 4. If not found \u2192 say "I could not find documentation"
419
- 5. Include confidence: HIGH (official) / MEDIUM (github) / LOW (blog)
420
- </anti_hallucination>
421
-
422
- <planning_workflow>
423
- CREATE: .opencode/todo.md
424
-
425
- \u26A1 PARALLELISM IS CRITICAL - Group tasks that can run simultaneously!
426
-
427
- Task Structure:
428
- - Parallel Groups: Tasks with NO dependencies run together
429
- - Sequential: Only for tasks with real dependencies
430
- - Atomic: Each task = one focused action
431
-
432
- FORMAT:
433
- \`\`\`markdown
434
- # Mission: [goal]
435
-
436
- ## Parallel Group A (spawn all simultaneously)
437
- - [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER} | size:S
438
- - [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER} | size:S
439
- - [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER} | size:S
440
-
441
- ## Parallel Group B (after Group A)
442
- - [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1 | size:M
443
- - [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2 | size:M
444
-
445
- ## Sequential (strict order required)
446
- - [ ] T6: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5 | size:L
447
- - [ ] T7: Verify all | agent:${AGENT_NAMES.REVIEWER} | depends:T6 | size:S
448
-
449
- ## Notes
450
- [context for team]
451
- \`\`\`
452
-
453
- MAXIMIZE PARALLELISM:
454
- - Research tasks \u2192 ALL parallel (different topics)
455
- - Implementation \u2192 Parallel if different files
456
- - Sequential ONLY when: same file edit, strict A\u2192B dependency
457
- </planning_workflow>
458
-
459
- <research_workflow>
460
- 1. SEARCH: websearch "[topic] official documentation [version]"
461
- 2. VERIFY: Check for official sources
462
- 3. FETCH: webfetch official docs with cache=true
463
- 4. EXTRACT: Copy EXACT syntax (not paraphrased)
464
- 5. SAVE: Write to .opencode/docs/[topic].md with source URL
465
- </research_workflow>
466
-
467
- <estimation>
468
- TASK SIZING:
469
- | Size | Time | Description |
470
- |------|------|-------------|
471
- | XS | <5min | Config, typo fix |
472
- | S | 5-15min | Small feature |
473
- | M | 15-30min | Multi-file |
474
- | L | 30-60min | Complex |
475
- | XL | >60min | Break down further |
476
- </estimation>
477
-
478
- <fallback_paths>
479
- FOR CRITICAL TASKS:
480
- - Primary: Best approach
481
- - Fallback: If primary fails
482
- - Minimum: Simplest working solution
483
- </fallback_paths>
484
-
485
- <shared_workspace>
486
- ALL WORK IN .opencode/:
487
- - .opencode/todo.md - master TODO (you create)
488
- - .opencode/docs/ - cached documentation (you save)
489
- - .opencode/context.md - current state
490
- </shared_workspace>
491
-
492
- <output>
493
- # Planning Report
494
-
495
- ## Research Done
496
- | Finding | Source | Confidence |
497
- |---------|--------|------------|
498
- | [fact] | [URL] | HIGH/MEDIUM/LOW |
499
-
500
- ## TODO Created
501
- .opencode/todo.md with [N] tasks
502
-
503
- ## Docs Saved
504
- - .opencode/docs/[topic].md
505
-
506
- Ready for ${AGENT_NAMES.WORKER}
507
- </output>`,
508
- canWrite: true,
509
- canBash: true
510
- };
511
-
512
- // src/agents/consolidated/worker.ts
513
- var worker = {
514
- id: AGENT_NAMES.WORKER,
515
- description: "Worker - implementation and documentation",
516
- systemPrompt: `<role>
517
- You are ${AGENT_NAMES.WORKER}. Implementation specialist and documentation handler.
518
- Write code, create files, configure systems.
519
- Fetch and cache official documentation when needed.
520
- Works with ANY language or framework.
521
- </role>
522
-
523
- <responsibilities>
524
- 1. IMPLEMENTATION: Write code, create files, configurations
525
- 2. DOCUMENTATION: Search and cache official docs when needed
526
- 3. VERIFICATION: Verify your own changes work before reporting
527
- </responsibilities>
528
-
529
- <anti_hallucination>
530
- BEFORE CODING:
531
- 1. Check .opencode/docs/ for cached docs
532
- 2. If not found \u2192 websearch for official docs
533
- 3. webfetch official docs with cache=true
534
- 4. NEVER guess API syntax - wait for verified docs
535
-
536
- TRUSTED SOURCES:
537
- - Official docs: docs.[framework].com
538
- - GitHub: github.com/[org]/[repo]
539
- - Package registries: npmjs.com, pypi.org
540
- </anti_hallucination>
541
-
542
- <workflow>
543
- 1. Check .opencode/todo.md for your assigned task
544
- 2. Read .opencode/docs/ for relevant documentation
545
- 3. If docs missing \u2192 search and cache them first
546
- 4. Check existing patterns in codebase
547
- 5. Implement following existing conventions
548
- 6. Verify your changes work (build/test)
549
- 7. Report completion
550
- </workflow>
551
-
552
- <quality_standards>
553
- EVERY IMPLEMENTATION MUST:
554
- 1. Follow existing code patterns
555
- 2. Include error handling (try/catch, validation)
556
- 3. Add JSDoc/comments for public APIs
557
- 4. Type safety (no 'any' unless justified)
558
- 5. No hardcoded values (use constants)
559
-
560
- TEST REQUIREMENTS:
561
- - New feature \u2192 create test file
562
- - Bug fix \u2192 add regression test
563
- - Existing tests must pass
564
- </quality_standards>
565
-
566
- <implementation_checklist>
567
- BEFORE REPORTING COMPLETE:
568
- \u25A1 Code compiles without errors
569
- \u25A1 lsp_diagnostics shows no issues
570
- \u25A1 Existing tests pass
571
- \u25A1 Changes are minimal and focused
572
- \u25A1 No console.log debugging left
573
- \u25A1 Error cases handled
574
- </implementation_checklist>
575
-
576
- <doc_caching>
577
- WHEN DOCS NEEDED:
578
- 1. websearch "[topic] official documentation"
579
- 2. webfetch official docs with cache=true
580
- 3. Save key info to .opencode/docs/[topic].md
581
-
582
- FORMAT:
583
- \`\`\`markdown
584
- # [Topic] Documentation
585
- Source: [official URL]
586
- Version: [version]
587
- Retrieved: [date]
588
-
589
- ## Official API/Syntax
590
- [exact code from docs]
591
- \`\`\`
592
- </doc_caching>
593
-
594
- <shared_workspace>
595
- ALL IN .opencode/:
596
- - .opencode/todo.md - your assigned tasks
597
- - .opencode/docs/ - documentation cache
598
- - .opencode/context.md - current state
599
- </shared_workspace>
600
-
601
- <output>
602
- TASK: T[N] from .opencode/todo.md
603
- CHANGED: [file] [lines]
604
- ACTION: [what]
605
- VERIFY: [build/test result]
606
- DOCS_USED: .opencode/docs/[file]
607
- \u2192 Task complete, ready for ${AGENT_NAMES.REVIEWER}
608
- </output>`,
609
- canWrite: true,
610
- canBash: true
611
- };
612
-
613
- // src/agents/consolidated/reviewer.ts
614
- var reviewer = {
615
- id: AGENT_NAMES.REVIEWER,
616
- description: "Reviewer - verification and context management",
617
- systemPrompt: `<role>
618
- You are ${AGENT_NAMES.REVIEWER}. Verification specialist and context manager.
619
- Verify implementations against docs, track progress, manage .opencode/ context.
620
- Works with ANY language or framework.
621
- </role>
622
-
623
- <responsibilities>
624
- 1. VERIFICATION: Prove implementations work with evidence
625
- 2. TODO TRACKING: Update checkboxes in .opencode/todo.md
626
- 3. CONTEXT MANAGEMENT: Keep .opencode/ lean and relevant
627
- </responsibilities>
628
-
629
- <verification_workflow>
630
- 1. Check .opencode/todo.md for verification tasks
631
- 2. Read .opencode/docs/ for expected patterns
632
- 3. Verify implementation matches docs
633
- 4. Run build/test commands
634
- 5. Update TODO checkboxes
635
- 6. Maintain context.md
636
- </verification_workflow>
637
-
638
- <audit_checklist>
639
- 1. SYNTAX: lsp_diagnostics or language tools
640
- 2. BUILD/TEST: Run project's commands
641
- 3. DOC_COMPLIANCE: Match .opencode/docs/
642
- 4. LOGIC: Manual review if no tests
643
- 5. SECURITY: Check for vulnerabilities
644
- </audit_checklist>
645
-
646
- <auto_fix>
647
- WHEN ISSUES FOUND:
648
- - Trivial (typo, import) \u2192 Fix directly
649
- - Logic issue \u2192 Report to ${AGENT_NAMES.WORKER} with fix suggestion
650
- - Architecture \u2192 Escalate to ${AGENT_NAMES.COMMANDER}
651
-
652
- FIX AUTHORITY:
653
- - \u2705 CAN FIX: Lint errors, formatting, minor typos
654
- - \u26A0\uFE0F SUGGEST: Logic changes, refactoring
655
- - \u274C ESCALATE: Architecture, new dependencies, security
656
- </auto_fix>
657
-
658
- <security_check>
659
- VERIFY:
660
- \u25A1 No hardcoded secrets/passwords
661
- \u25A1 Input validation present
662
- \u25A1 No SQL injection risks
663
- \u25A1 No XSS vulnerabilities
664
- \u25A1 Proper error messages
665
- </security_check>
666
-
667
- <todo_management>
668
- FILE: .opencode/todo.md
669
-
670
- UPDATE FORMAT:
671
- \`\`\`markdown
672
- - [x] T1: [task] | \u2705 DONE
673
- - [ ] T2: [task] | in progress
674
- - [ ] T3: [task] | blocked: [reason]
675
- \`\`\`
676
- </todo_management>
677
-
678
- <context_management>
679
- DYNAMIC DETAIL LEVELS:
680
-
681
- PHASE 1 - EARLY (0-30% done):
682
- - BE DETAILED: Full explanations, decisions
683
- - Include: research, API references
684
-
685
- PHASE 2 - BUILDING (30-70%):
686
- - MODERATE: Key decisions + file references
687
- - Reference: "See src/module.ts"
688
-
689
- PHASE 3 - FINISHING (70-100%):
690
- - BRIEF: Just status, blockers
691
- - Heavy summarization
692
-
693
- ADAPTIVE RULES:
694
- | Condition | Action |
695
- |-----------|--------|
696
- | > 150 lines context.md | Compress to 50 |
697
- | Feature complete | Delete related verbose docs |
698
- | Code exists for feature | Point to code instead |
699
- </context_management>
700
-
701
- <cleanup_triggers>
702
- AFTER EACH UPDATE:
703
- 1. Is this info needed for FUTURE tasks? No \u2192 DELETE
704
- 2. Is this in code now? Yes \u2192 SUMMARIZE to reference
705
- 3. context.md > 150 lines? COMPRESS
706
- 4. Doc > 7 days old + unused? ARCHIVE
707
- </cleanup_triggers>
708
-
709
- <shared_workspace>
710
- .opencode/
711
- \u251C\u2500\u2500 todo.md - Master TODO (update checkboxes)
712
- \u251C\u2500\u2500 context.md - Current state (adaptive size)
713
- \u251C\u2500\u2500 docs/ - Cached documentation
714
- \u2514\u2500\u2500 archive/ - Old context
715
- </shared_workspace>
716
-
717
- <output>
718
- TASK: T[N] from .opencode/todo.md
719
-
720
- \u2705 PASS: [evidence]
721
- Matches: .opencode/docs/[file]
722
-
723
- \u274C FAIL: [issue]
724
- Fix: [suggestion]
725
-
726
- CONTEXT UPDATED:
727
- - todo.md: [X/Y done]
728
- - context.md: [before \u2192 after lines]
729
- - Phase: [EARLY/BUILDING/FINISHING]
730
-
731
- Next: [task for team]
732
- </output>`,
733
- canWrite: true,
734
- canBash: true
735
- };
736
-
737
- // src/agents/definitions.ts
738
- var AGENTS = {
739
- [AGENT_NAMES.COMMANDER]: commander,
740
- [AGENT_NAMES.PLANNER]: planner,
741
- [AGENT_NAMES.WORKER]: worker,
742
- [AGENT_NAMES.REVIEWER]: reviewer
743
- };
744
-
745
- // src/core/orchestrator/state.ts
746
- var state = {
747
- missionActive: false,
748
- maxIterations: 1e3,
749
- maxRetries: 3,
750
- sessions: /* @__PURE__ */ new Map()
751
- };
752
-
753
- // node_modules/zod/v4/classic/external.js
754
- var external_exports = {};
755
- __export(external_exports, {
756
- $brand: () => $brand,
757
- $input: () => $input,
758
- $output: () => $output,
759
- NEVER: () => NEVER,
760
- TimePrecision: () => TimePrecision,
761
- ZodAny: () => ZodAny,
762
- ZodArray: () => ZodArray,
763
- ZodBase64: () => ZodBase64,
764
- ZodBase64URL: () => ZodBase64URL,
765
- ZodBigInt: () => ZodBigInt,
766
- ZodBigIntFormat: () => ZodBigIntFormat,
767
- ZodBoolean: () => ZodBoolean,
768
- ZodCIDRv4: () => ZodCIDRv4,
769
- ZodCIDRv6: () => ZodCIDRv6,
770
- ZodCUID: () => ZodCUID,
771
- ZodCUID2: () => ZodCUID2,
772
- ZodCatch: () => ZodCatch,
773
- ZodCodec: () => ZodCodec,
774
- ZodCustom: () => ZodCustom,
775
- ZodCustomStringFormat: () => ZodCustomStringFormat,
776
- ZodDate: () => ZodDate,
777
- ZodDefault: () => ZodDefault,
778
- ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
779
- ZodE164: () => ZodE164,
780
- ZodEmail: () => ZodEmail,
781
- ZodEmoji: () => ZodEmoji,
782
- ZodEnum: () => ZodEnum,
783
- ZodError: () => ZodError,
784
- ZodFile: () => ZodFile,
785
- ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
786
- ZodFunction: () => ZodFunction,
787
- ZodGUID: () => ZodGUID,
788
- ZodIPv4: () => ZodIPv4,
789
- ZodIPv6: () => ZodIPv6,
790
- ZodISODate: () => ZodISODate,
791
- ZodISODateTime: () => ZodISODateTime,
792
- ZodISODuration: () => ZodISODuration,
793
- ZodISOTime: () => ZodISOTime,
794
- ZodIntersection: () => ZodIntersection,
795
- ZodIssueCode: () => ZodIssueCode,
796
- ZodJWT: () => ZodJWT,
797
- ZodKSUID: () => ZodKSUID,
798
- ZodLazy: () => ZodLazy,
799
- ZodLiteral: () => ZodLiteral,
800
- ZodMap: () => ZodMap,
801
- ZodNaN: () => ZodNaN,
802
- ZodNanoID: () => ZodNanoID,
803
- ZodNever: () => ZodNever,
804
- ZodNonOptional: () => ZodNonOptional,
805
- ZodNull: () => ZodNull,
806
- ZodNullable: () => ZodNullable,
807
- ZodNumber: () => ZodNumber,
808
- ZodNumberFormat: () => ZodNumberFormat,
809
- ZodObject: () => ZodObject,
810
- ZodOptional: () => ZodOptional,
811
- ZodPipe: () => ZodPipe,
812
- ZodPrefault: () => ZodPrefault,
813
- ZodPromise: () => ZodPromise,
814
- ZodReadonly: () => ZodReadonly,
815
- ZodRealError: () => ZodRealError,
816
- ZodRecord: () => ZodRecord,
817
- ZodSet: () => ZodSet,
818
- ZodString: () => ZodString,
819
- ZodStringFormat: () => ZodStringFormat,
820
- ZodSuccess: () => ZodSuccess,
821
- ZodSymbol: () => ZodSymbol,
822
- ZodTemplateLiteral: () => ZodTemplateLiteral,
823
- ZodTransform: () => ZodTransform,
824
- ZodTuple: () => ZodTuple,
825
- ZodType: () => ZodType,
826
- ZodULID: () => ZodULID,
827
- ZodURL: () => ZodURL,
828
- ZodUUID: () => ZodUUID,
829
- ZodUndefined: () => ZodUndefined,
830
- ZodUnion: () => ZodUnion,
831
- ZodUnknown: () => ZodUnknown,
832
- ZodVoid: () => ZodVoid,
833
- ZodXID: () => ZodXID,
834
- _ZodString: () => _ZodString,
835
- _default: () => _default2,
836
- _function: () => _function,
837
- any: () => any,
838
- array: () => array,
839
- base64: () => base642,
840
- base64url: () => base64url2,
841
- bigint: () => bigint2,
842
- boolean: () => boolean2,
843
- catch: () => _catch2,
844
- check: () => check,
845
- cidrv4: () => cidrv42,
846
- cidrv6: () => cidrv62,
847
- clone: () => clone,
848
- codec: () => codec,
849
- coerce: () => coerce_exports,
850
- config: () => config,
851
- core: () => core_exports2,
852
- cuid: () => cuid3,
853
- cuid2: () => cuid22,
854
- custom: () => custom,
855
- date: () => date3,
856
- decode: () => decode2,
857
- decodeAsync: () => decodeAsync2,
858
- discriminatedUnion: () => discriminatedUnion,
859
- e164: () => e1642,
860
- email: () => email2,
861
- emoji: () => emoji2,
862
- encode: () => encode2,
863
- encodeAsync: () => encodeAsync2,
864
- endsWith: () => _endsWith,
865
- enum: () => _enum2,
866
- file: () => file,
867
- flattenError: () => flattenError,
868
- float32: () => float32,
869
- float64: () => float64,
870
- formatError: () => formatError,
871
- function: () => _function,
872
- getErrorMap: () => getErrorMap,
873
- globalRegistry: () => globalRegistry,
874
- gt: () => _gt,
875
- gte: () => _gte,
876
- guid: () => guid2,
877
- hash: () => hash,
878
- hex: () => hex2,
879
- hostname: () => hostname2,
880
- httpUrl: () => httpUrl,
881
- includes: () => _includes,
882
- instanceof: () => _instanceof,
883
- int: () => int,
884
- int32: () => int32,
885
- int64: () => int64,
886
- intersection: () => intersection,
887
- ipv4: () => ipv42,
888
- ipv6: () => ipv62,
889
- iso: () => iso_exports,
890
- json: () => json,
891
- jwt: () => jwt,
892
- keyof: () => keyof,
893
- ksuid: () => ksuid2,
894
- lazy: () => lazy,
895
- length: () => _length,
896
- literal: () => literal,
897
- locales: () => locales_exports,
898
- looseObject: () => looseObject,
899
- lowercase: () => _lowercase,
900
- lt: () => _lt,
901
- lte: () => _lte,
902
- map: () => map,
903
- maxLength: () => _maxLength,
904
- maxSize: () => _maxSize,
905
- mime: () => _mime,
906
- minLength: () => _minLength,
907
- minSize: () => _minSize,
908
- multipleOf: () => _multipleOf,
909
- nan: () => nan,
910
- nanoid: () => nanoid2,
911
- nativeEnum: () => nativeEnum,
912
- negative: () => _negative,
913
- never: () => never,
914
- nonnegative: () => _nonnegative,
915
- nonoptional: () => nonoptional,
916
- nonpositive: () => _nonpositive,
917
- normalize: () => _normalize,
918
- null: () => _null3,
919
- nullable: () => nullable,
920
- nullish: () => nullish2,
921
- number: () => number2,
922
- object: () => object,
923
- optional: () => optional,
924
- overwrite: () => _overwrite,
925
- parse: () => parse2,
926
- parseAsync: () => parseAsync2,
927
- partialRecord: () => partialRecord,
928
- pipe: () => pipe,
929
- positive: () => _positive,
930
- prefault: () => prefault,
931
- preprocess: () => preprocess,
932
- prettifyError: () => prettifyError,
933
- promise: () => promise,
934
- property: () => _property,
935
- readonly: () => readonly,
936
- record: () => record,
937
- refine: () => refine,
938
- regex: () => _regex,
939
- regexes: () => regexes_exports,
940
- registry: () => registry,
941
- safeDecode: () => safeDecode2,
942
- safeDecodeAsync: () => safeDecodeAsync2,
943
- safeEncode: () => safeEncode2,
944
- safeEncodeAsync: () => safeEncodeAsync2,
945
- safeParse: () => safeParse2,
946
- safeParseAsync: () => safeParseAsync2,
947
- set: () => set,
948
- setErrorMap: () => setErrorMap,
949
- size: () => _size,
950
- startsWith: () => _startsWith,
951
- strictObject: () => strictObject,
952
- string: () => string2,
953
- stringFormat: () => stringFormat,
954
- stringbool: () => stringbool,
955
- success: () => success,
956
- superRefine: () => superRefine,
957
- symbol: () => symbol,
958
- templateLiteral: () => templateLiteral,
959
- toJSONSchema: () => toJSONSchema,
960
- toLowerCase: () => _toLowerCase,
961
- toUpperCase: () => _toUpperCase,
962
- transform: () => transform,
963
- treeifyError: () => treeifyError,
964
- trim: () => _trim,
965
- tuple: () => tuple,
966
- uint32: () => uint32,
967
- uint64: () => uint64,
968
- ulid: () => ulid2,
969
- undefined: () => _undefined3,
970
- union: () => union,
971
- unknown: () => unknown,
972
- uppercase: () => _uppercase,
973
- url: () => url,
974
- util: () => util_exports,
975
- uuid: () => uuid2,
976
- uuidv4: () => uuidv4,
977
- uuidv6: () => uuidv6,
978
- uuidv7: () => uuidv7,
979
- void: () => _void2,
980
- xid: () => xid2
981
- });
185
+ // node_modules/zod/v4/classic/external.js
186
+ var external_exports = {};
187
+ __export(external_exports, {
188
+ $brand: () => $brand,
189
+ $input: () => $input,
190
+ $output: () => $output,
191
+ NEVER: () => NEVER,
192
+ TimePrecision: () => TimePrecision,
193
+ ZodAny: () => ZodAny,
194
+ ZodArray: () => ZodArray,
195
+ ZodBase64: () => ZodBase64,
196
+ ZodBase64URL: () => ZodBase64URL,
197
+ ZodBigInt: () => ZodBigInt,
198
+ ZodBigIntFormat: () => ZodBigIntFormat,
199
+ ZodBoolean: () => ZodBoolean,
200
+ ZodCIDRv4: () => ZodCIDRv4,
201
+ ZodCIDRv6: () => ZodCIDRv6,
202
+ ZodCUID: () => ZodCUID,
203
+ ZodCUID2: () => ZodCUID2,
204
+ ZodCatch: () => ZodCatch,
205
+ ZodCodec: () => ZodCodec,
206
+ ZodCustom: () => ZodCustom,
207
+ ZodCustomStringFormat: () => ZodCustomStringFormat,
208
+ ZodDate: () => ZodDate,
209
+ ZodDefault: () => ZodDefault,
210
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
211
+ ZodE164: () => ZodE164,
212
+ ZodEmail: () => ZodEmail,
213
+ ZodEmoji: () => ZodEmoji,
214
+ ZodEnum: () => ZodEnum,
215
+ ZodError: () => ZodError,
216
+ ZodFile: () => ZodFile,
217
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
218
+ ZodFunction: () => ZodFunction,
219
+ ZodGUID: () => ZodGUID,
220
+ ZodIPv4: () => ZodIPv4,
221
+ ZodIPv6: () => ZodIPv6,
222
+ ZodISODate: () => ZodISODate,
223
+ ZodISODateTime: () => ZodISODateTime,
224
+ ZodISODuration: () => ZodISODuration,
225
+ ZodISOTime: () => ZodISOTime,
226
+ ZodIntersection: () => ZodIntersection,
227
+ ZodIssueCode: () => ZodIssueCode,
228
+ ZodJWT: () => ZodJWT,
229
+ ZodKSUID: () => ZodKSUID,
230
+ ZodLazy: () => ZodLazy,
231
+ ZodLiteral: () => ZodLiteral,
232
+ ZodMap: () => ZodMap,
233
+ ZodNaN: () => ZodNaN,
234
+ ZodNanoID: () => ZodNanoID,
235
+ ZodNever: () => ZodNever,
236
+ ZodNonOptional: () => ZodNonOptional,
237
+ ZodNull: () => ZodNull,
238
+ ZodNullable: () => ZodNullable,
239
+ ZodNumber: () => ZodNumber,
240
+ ZodNumberFormat: () => ZodNumberFormat,
241
+ ZodObject: () => ZodObject,
242
+ ZodOptional: () => ZodOptional,
243
+ ZodPipe: () => ZodPipe,
244
+ ZodPrefault: () => ZodPrefault,
245
+ ZodPromise: () => ZodPromise,
246
+ ZodReadonly: () => ZodReadonly,
247
+ ZodRealError: () => ZodRealError,
248
+ ZodRecord: () => ZodRecord,
249
+ ZodSet: () => ZodSet,
250
+ ZodString: () => ZodString,
251
+ ZodStringFormat: () => ZodStringFormat,
252
+ ZodSuccess: () => ZodSuccess,
253
+ ZodSymbol: () => ZodSymbol,
254
+ ZodTemplateLiteral: () => ZodTemplateLiteral,
255
+ ZodTransform: () => ZodTransform,
256
+ ZodTuple: () => ZodTuple,
257
+ ZodType: () => ZodType,
258
+ ZodULID: () => ZodULID,
259
+ ZodURL: () => ZodURL,
260
+ ZodUUID: () => ZodUUID,
261
+ ZodUndefined: () => ZodUndefined,
262
+ ZodUnion: () => ZodUnion,
263
+ ZodUnknown: () => ZodUnknown,
264
+ ZodVoid: () => ZodVoid,
265
+ ZodXID: () => ZodXID,
266
+ _ZodString: () => _ZodString,
267
+ _default: () => _default2,
268
+ _function: () => _function,
269
+ any: () => any,
270
+ array: () => array,
271
+ base64: () => base642,
272
+ base64url: () => base64url2,
273
+ bigint: () => bigint2,
274
+ boolean: () => boolean2,
275
+ catch: () => _catch2,
276
+ check: () => check,
277
+ cidrv4: () => cidrv42,
278
+ cidrv6: () => cidrv62,
279
+ clone: () => clone,
280
+ codec: () => codec,
281
+ coerce: () => coerce_exports,
282
+ config: () => config,
283
+ core: () => core_exports2,
284
+ cuid: () => cuid3,
285
+ cuid2: () => cuid22,
286
+ custom: () => custom,
287
+ date: () => date3,
288
+ decode: () => decode2,
289
+ decodeAsync: () => decodeAsync2,
290
+ discriminatedUnion: () => discriminatedUnion,
291
+ e164: () => e1642,
292
+ email: () => email2,
293
+ emoji: () => emoji2,
294
+ encode: () => encode2,
295
+ encodeAsync: () => encodeAsync2,
296
+ endsWith: () => _endsWith,
297
+ enum: () => _enum2,
298
+ file: () => file,
299
+ flattenError: () => flattenError,
300
+ float32: () => float32,
301
+ float64: () => float64,
302
+ formatError: () => formatError,
303
+ function: () => _function,
304
+ getErrorMap: () => getErrorMap,
305
+ globalRegistry: () => globalRegistry,
306
+ gt: () => _gt,
307
+ gte: () => _gte,
308
+ guid: () => guid2,
309
+ hash: () => hash,
310
+ hex: () => hex2,
311
+ hostname: () => hostname2,
312
+ httpUrl: () => httpUrl,
313
+ includes: () => _includes,
314
+ instanceof: () => _instanceof,
315
+ int: () => int,
316
+ int32: () => int32,
317
+ int64: () => int64,
318
+ intersection: () => intersection,
319
+ ipv4: () => ipv42,
320
+ ipv6: () => ipv62,
321
+ iso: () => iso_exports,
322
+ json: () => json,
323
+ jwt: () => jwt,
324
+ keyof: () => keyof,
325
+ ksuid: () => ksuid2,
326
+ lazy: () => lazy,
327
+ length: () => _length,
328
+ literal: () => literal,
329
+ locales: () => locales_exports,
330
+ looseObject: () => looseObject,
331
+ lowercase: () => _lowercase,
332
+ lt: () => _lt,
333
+ lte: () => _lte,
334
+ map: () => map,
335
+ maxLength: () => _maxLength,
336
+ maxSize: () => _maxSize,
337
+ mime: () => _mime,
338
+ minLength: () => _minLength,
339
+ minSize: () => _minSize,
340
+ multipleOf: () => _multipleOf,
341
+ nan: () => nan,
342
+ nanoid: () => nanoid2,
343
+ nativeEnum: () => nativeEnum,
344
+ negative: () => _negative,
345
+ never: () => never,
346
+ nonnegative: () => _nonnegative,
347
+ nonoptional: () => nonoptional,
348
+ nonpositive: () => _nonpositive,
349
+ normalize: () => _normalize,
350
+ null: () => _null3,
351
+ nullable: () => nullable,
352
+ nullish: () => nullish2,
353
+ number: () => number2,
354
+ object: () => object,
355
+ optional: () => optional,
356
+ overwrite: () => _overwrite,
357
+ parse: () => parse2,
358
+ parseAsync: () => parseAsync2,
359
+ partialRecord: () => partialRecord,
360
+ pipe: () => pipe,
361
+ positive: () => _positive,
362
+ prefault: () => prefault,
363
+ preprocess: () => preprocess,
364
+ prettifyError: () => prettifyError,
365
+ promise: () => promise,
366
+ property: () => _property,
367
+ readonly: () => readonly,
368
+ record: () => record,
369
+ refine: () => refine,
370
+ regex: () => _regex,
371
+ regexes: () => regexes_exports,
372
+ registry: () => registry,
373
+ safeDecode: () => safeDecode2,
374
+ safeDecodeAsync: () => safeDecodeAsync2,
375
+ safeEncode: () => safeEncode2,
376
+ safeEncodeAsync: () => safeEncodeAsync2,
377
+ safeParse: () => safeParse2,
378
+ safeParseAsync: () => safeParseAsync2,
379
+ set: () => set,
380
+ setErrorMap: () => setErrorMap,
381
+ size: () => _size,
382
+ startsWith: () => _startsWith,
383
+ strictObject: () => strictObject,
384
+ string: () => string2,
385
+ stringFormat: () => stringFormat,
386
+ stringbool: () => stringbool,
387
+ success: () => success,
388
+ superRefine: () => superRefine,
389
+ symbol: () => symbol,
390
+ templateLiteral: () => templateLiteral,
391
+ toJSONSchema: () => toJSONSchema,
392
+ toLowerCase: () => _toLowerCase,
393
+ toUpperCase: () => _toUpperCase,
394
+ transform: () => transform,
395
+ treeifyError: () => treeifyError,
396
+ trim: () => _trim,
397
+ tuple: () => tuple,
398
+ uint32: () => uint32,
399
+ uint64: () => uint64,
400
+ ulid: () => ulid2,
401
+ undefined: () => _undefined3,
402
+ union: () => union,
403
+ unknown: () => unknown,
404
+ uppercase: () => _uppercase,
405
+ url: () => url,
406
+ util: () => util_exports,
407
+ uuid: () => uuid2,
408
+ uuidv4: () => uuidv4,
409
+ uuidv6: () => uuidv6,
410
+ uuidv7: () => uuidv7,
411
+ void: () => _void2,
412
+ xid: () => xid2
413
+ });
982
414
 
983
415
  // node_modules/zod/v4/core/index.js
984
416
  var core_exports2 = {};
@@ -13112,65 +12544,633 @@ function preprocess(fn, schema) {
13112
12544
  return pipe(transform(fn), schema);
13113
12545
  }
13114
12546
 
13115
- // node_modules/zod/v4/classic/compat.js
13116
- var ZodIssueCode = {
13117
- invalid_type: "invalid_type",
13118
- too_big: "too_big",
13119
- too_small: "too_small",
13120
- invalid_format: "invalid_format",
13121
- not_multiple_of: "not_multiple_of",
13122
- unrecognized_keys: "unrecognized_keys",
13123
- invalid_union: "invalid_union",
13124
- invalid_key: "invalid_key",
13125
- invalid_element: "invalid_element",
13126
- invalid_value: "invalid_value",
13127
- custom: "custom"
12547
+ // node_modules/zod/v4/classic/compat.js
12548
+ var ZodIssueCode = {
12549
+ invalid_type: "invalid_type",
12550
+ too_big: "too_big",
12551
+ too_small: "too_small",
12552
+ invalid_format: "invalid_format",
12553
+ not_multiple_of: "not_multiple_of",
12554
+ unrecognized_keys: "unrecognized_keys",
12555
+ invalid_union: "invalid_union",
12556
+ invalid_key: "invalid_key",
12557
+ invalid_element: "invalid_element",
12558
+ invalid_value: "invalid_value",
12559
+ custom: "custom"
12560
+ };
12561
+ function setErrorMap(map2) {
12562
+ config({
12563
+ customError: map2
12564
+ });
12565
+ }
12566
+ function getErrorMap() {
12567
+ return config().customError;
12568
+ }
12569
+ var ZodFirstPartyTypeKind;
12570
+ /* @__PURE__ */ (function(ZodFirstPartyTypeKind2) {
12571
+ })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
12572
+
12573
+ // node_modules/zod/v4/classic/coerce.js
12574
+ var coerce_exports = {};
12575
+ __export(coerce_exports, {
12576
+ bigint: () => bigint3,
12577
+ boolean: () => boolean3,
12578
+ date: () => date4,
12579
+ number: () => number3,
12580
+ string: () => string3
12581
+ });
12582
+ function string3(params) {
12583
+ return _coercedString(ZodString, params);
12584
+ }
12585
+ function number3(params) {
12586
+ return _coercedNumber(ZodNumber, params);
12587
+ }
12588
+ function boolean3(params) {
12589
+ return _coercedBoolean(ZodBoolean, params);
12590
+ }
12591
+ function bigint3(params) {
12592
+ return _coercedBigint(ZodBigInt, params);
12593
+ }
12594
+ function date4(params) {
12595
+ return _coercedDate(ZodDate, params);
12596
+ }
12597
+
12598
+ // node_modules/zod/v4/classic/external.js
12599
+ config(en_default());
12600
+
12601
+ // node_modules/@opencode-ai/plugin/dist/tool.js
12602
+ function tool(input) {
12603
+ return input;
12604
+ }
12605
+ tool.schema = external_exports;
12606
+
12607
+ // src/agents/commander.ts
12608
+ var commander = {
12609
+ id: AGENT_NAMES.COMMANDER,
12610
+ description: "Commander - autonomous orchestrator with parallel execution",
12611
+ systemPrompt: `<role>
12612
+ You are Commander. Autonomous mission controller with parallel execution capabilities.
12613
+ Complete missions efficiently using multiple agents simultaneously. Never stop until done.
12614
+ </role>
12615
+
12616
+ <core_principles>
12617
+ 1. PARALLELISM FIRST: Always run independent tasks simultaneously
12618
+ 2. NEVER BLOCK: Use background execution for slow operations
12619
+ 3. NEVER STOP: Loop until "${MISSION_SEAL.PATTERN}"
12620
+ 4. THINK FIRST: Reason before every action
12621
+ 5. SESSION REUSE: Resume sessions to preserve context
12622
+ </core_principles>
12623
+
12624
+ <tools_overview>
12625
+ | Tool | Purpose | When to Use |
12626
+ |------|---------|-------------|
12627
+ | ${TOOL_NAMES.DELEGATE_TASK} | Spawn agent | background=true for parallel, false for sync |
12628
+ | ${TOOL_NAMES.GET_TASK_RESULT} | Get agent result | After background task completes |
12629
+ | ${TOOL_NAMES.LIST_TASKS} | Monitor agents | Check all running agent tasks |
12630
+ | ${TOOL_NAMES.CANCEL_TASK} | Stop agent | Cancel stuck or unnecessary tasks |
12631
+ | ${TOOL_NAMES.RUN_BACKGROUND} | Run shell cmd | Long builds, tests, installs |
12632
+ | ${TOOL_NAMES.CHECK_BACKGROUND} | Get cmd result | Check background command status |
12633
+ | ${TOOL_NAMES.LIST_BACKGROUND} | List commands | See all background commands |
12634
+ </tools_overview>
12635
+
12636
+ <phase_0_think>
12637
+ \u26A0\uFE0F MANDATORY: Before ANY action, THINK!
12638
+
12639
+ 1. What is the actual goal?
12640
+ 2. What tasks can run IN PARALLEL?
12641
+ 3. What needs to be SEQUENTIAL?
12642
+ 4. Which agents should handle each task?
12643
+ 5. What can run in BACKGROUND while I continue?
12644
+
12645
+ Write reasoning before acting. Never skip this.
12646
+ </phase_0_think>
12647
+
12648
+ <phase_1_triage>
12649
+ IDENTIFY TASK TYPE:
12650
+
12651
+ | Type | Signal | Track |
12652
+ |------|--------|-------|
12653
+ | \u{1F7E2} Simple | One file, clear fix | FAST: Direct action |
12654
+ | \u{1F7E1} Medium | Multi-file feature | NORMAL: Plan \u2192 Execute \u2192 Verify |
12655
+ | \u{1F534} Complex | Large scope, unknowns | DEEP: Research \u2192 Plan \u2192 Parallel Execute \u2192 Verify |
12656
+
12657
+ FOR COMPLEX TASKS \u2192 Create .opencode/todo.md with parallel groups
12658
+ </phase_1_triage>
12659
+
12660
+ <phase_2_execute>
12661
+ EXECUTION FLOW:
12662
+
12663
+ 1. PLAN: ${AGENT_NAMES.PLANNER} creates TODO with parallel groups
12664
+ 2. LAUNCH: Spawn ALL independent tasks simultaneously
12665
+ 3. MONITOR: Use ${TOOL_NAMES.LIST_TASKS} to track progress
12666
+ 4. COLLECT: Gather results with ${TOOL_NAMES.GET_TASK_RESULT}
12667
+ 5. VERIFY: ${AGENT_NAMES.REVIEWER} validates and updates TODO
12668
+ 6. REPEAT: Until all tasks [x] complete
12669
+ </phase_2_execute>
12670
+
12671
+ <parallel_execution>
12672
+ \u26A1 AGGRESSIVELY USE: Parallel Agents + Background Commands + Session Resume
12673
+
12674
+ \u{1F680} THESE 3 FEATURES ARE YOUR SUPERPOWERS - USE THEM!
12675
+
12676
+ 1\uFE0F\u20E3 PARALLEL AGENTS (Strongly Recommended)
12677
+ Launch multiple agents simultaneously for independent work:
12678
+ \`\`\`
12679
+ // Research multiple topics at once
12680
+ ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research React docs", background: true })
12681
+ ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research API patterns", background: true })
12682
+ ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research testing libs", background: true })
12683
+ // \u2192 3x faster than sequential!
12684
+
12685
+ // Create multiple files at once
12686
+ ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Create component A", background: true })
12687
+ ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Create component B", background: true })
12688
+ \`\`\`
12689
+
12690
+ 2\uFE0F\u20E3 BACKGROUND COMMANDS (Strongly Recommended)
12691
+ Run slow shell commands without blocking:
12692
+ \`\`\`
12693
+ // Start build, keep working
12694
+ ${TOOL_NAMES.RUN_BACKGROUND}({ command: "npm run build", description: "Building..." })
12695
+ // ...continue with other work...
12696
+ ${TOOL_NAMES.CHECK_BACKGROUND}({ taskId: "xxx" }) // check when needed
12697
+ \`\`\`
12698
+
12699
+ 3\uFE0F\u20E3 SESSION RESUME (Strongly Recommended)
12700
+ Preserve context across multiple interactions:
12701
+ \`\`\`
12702
+ // First task returns sessionID
12703
+ result = ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Start feature" })
12704
+ // Session: session_abc123
12705
+
12706
+ // Later: continue with full context
12707
+ ${TOOL_NAMES.DELEGATE_TASK}({ prompt: "Add tests to feature", resume: "session_abc123" })
12708
+ \`\`\`
12709
+
12710
+ \u{1F4CB} SYNC STRATEGY (When to wait)
12711
+ - Use background=false ONLY when: next task needs THIS task's output
12712
+ - Collect results with ${TOOL_NAMES.GET_TASK_RESULT} before dependent work
12713
+ - Use ${TOOL_NAMES.LIST_TASKS} to monitor all parallel tasks
12714
+
12715
+ | Task Type | Approach |
12716
+ |-----------|----------|
12717
+ | Research/Exploration | PARALLEL - spawn multiple Planners |
12718
+ | File creation (different files) | PARALLEL - spawn multiple Workers |
12719
+ | Build/Test/Install | BACKGROUND - use run_background |
12720
+ | Sequential chain (A\u2192B\u2192C) | SYNC - background=false |
12721
+ | Follow-up to previous work | RESUME - use sessionID |
12722
+ </parallel_execution>
12723
+
12724
+ <agents>
12725
+ | Agent | Role | Delegate For |
12726
+ |-------|------|--------------|
12727
+ | ${AGENT_NAMES.PLANNER} | Research + Plan | Creating TODO, fetching docs, architecture |
12728
+ | ${AGENT_NAMES.WORKER} | Implement | Writing code, configuration, file creation |
12729
+ | ${AGENT_NAMES.REVIEWER} | Verify | Testing, validation, TODO updates |
12730
+ </agents>
12731
+
12732
+ <shared_workspace>
12733
+ .opencode/
12734
+ \u251C\u2500\u2500 todo.md - Master task list with parallel groups
12735
+ \u251C\u2500\u2500 docs/ - Cached documentation
12736
+ \u251C\u2500\u2500 context.md - Current mission state
12737
+ \u2514\u2500\u2500 summary.md - Condensed context when long
12738
+ </shared_workspace>
12739
+
12740
+ <todo_format>
12741
+ \`\`\`markdown
12742
+ # Mission: [goal]
12743
+
12744
+ ## Parallel Group A (run simultaneously)
12745
+ - [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER}
12746
+ - [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER}
12747
+ - [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER}
12748
+
12749
+ ## Parallel Group B (after A completes)
12750
+ - [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1
12751
+ - [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2
12752
+ - [ ] T6: Implement Auth | agent:${AGENT_NAMES.WORKER} | depends:T3
12753
+
12754
+ ## Sequential (strict order)
12755
+ - [ ] T7: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5,T6
12756
+ - [ ] T8: Final verify | agent:${AGENT_NAMES.REVIEWER} | depends:T7
12757
+ \`\`\`
12758
+ </todo_format>
12759
+
12760
+ <execution_loop>
12761
+ WHILE .opencode/todo.md has unchecked [ ] items:
12762
+ 1. IDENTIFY all tasks with satisfied dependencies
12763
+ 2. LAUNCH all identified tasks in PARALLEL (background=true)
12764
+ 3. START any slow commands via ${TOOL_NAMES.RUN_BACKGROUND}
12765
+ 4. MONITOR with ${TOOL_NAMES.LIST_TASKS} / ${TOOL_NAMES.LIST_BACKGROUND}
12766
+ 5. COLLECT results as they complete
12767
+ 6. UPDATE: ${AGENT_NAMES.REVIEWER} marks [x] and updates context
12768
+ 7. REPEAT until all complete
12769
+
12770
+ \u26A1 NEVER: Execute one-by-one when parallel is possible
12771
+ \u26A1 ALWAYS: Start slow operations in background immediately
12772
+ </execution_loop>
12773
+
12774
+ <anti_hallucination>
12775
+ BEFORE CODING:
12776
+ 1. Check .opencode/docs/ for cached documentation
12777
+ 2. If uncertain \u2192 ${AGENT_NAMES.PLANNER} researches first
12778
+ 3. Never guess API syntax - verify from official sources
12779
+
12780
+ TRIGGERS FOR RESEARCH:
12781
+ - Unfamiliar framework/library
12782
+ - Version-specific syntax
12783
+ - Complex configuration
12784
+ </anti_hallucination>
12785
+
12786
+ <error_handling>
12787
+ WHEN TASK FAILS:
12788
+ 1. ANALYZE error type (syntax? dependency? timeout?)
12789
+ 2. DECIDE:
12790
+ - Retryable \u2192 retry with different approach (max 2)
12791
+ - Blocker \u2192 mark blocked, continue parallel tasks
12792
+ - Critical \u2192 report to user
12793
+
12794
+ WHEN STUCK:
12795
+ 1. Find unblocked tasks in TODO
12796
+ 2. Run them in parallel
12797
+ 3. If completely blocked \u2192 report status
12798
+ </error_handling>
12799
+
12800
+ <completion>
12801
+ OUTPUT ONLY WHEN:
12802
+ 1. ALL items in .opencode/todo.md are [x]
12803
+ 2. Build/tests pass
12804
+ 3. ${AGENT_NAMES.REVIEWER} approves
12805
+
12806
+ **MISSION SEAL** (Explicit Completion Confirmation):
12807
+ When ALL work is truly complete, output the seal tag:
12808
+ \`\`\`
12809
+ ${MISSION_SEAL.PATTERN}
12810
+ \`\`\`
12811
+
12812
+ Then output:
12813
+ ${MISSION_SEAL.PATTERN}
12814
+ Summary: [accomplishments]
12815
+ Evidence: [test/build results]
12816
+
12817
+ \u26A0\uFE0F IMPORTANT: Only output ${MISSION_SEAL.PATTERN} when:
12818
+ - All todos are marked [x] complete
12819
+ - All tests pass
12820
+ - All builds succeed
12821
+ - You have verified the final result
12822
+ </completion>`,
12823
+ canWrite: true,
12824
+ canBash: true
12825
+ };
12826
+
12827
+ // src/agents/consolidated/planner.ts
12828
+ var planner = {
12829
+ id: AGENT_NAMES.PLANNER,
12830
+ description: "Planner - strategic planning and research",
12831
+ systemPrompt: `<role>
12832
+ You are ${AGENT_NAMES.PLANNER}. Strategic planner and researcher.
12833
+ You PLAN before coding and RESEARCH before implementing.
12834
+ Never guess - always verify with official sources.
12835
+ </role>
12836
+
12837
+ <responsibilities>
12838
+ 1. PLANNING: Break complex tasks into hierarchical, atomic pieces
12839
+ 2. RESEARCH: Gather verified information before implementation
12840
+ 3. DOCUMENTATION: Cache official docs for team reference
12841
+ </responsibilities>
12842
+
12843
+ <anti_hallucination>
12844
+ CRITICAL RULES:
12845
+ 1. EVERY claim must have a SOURCE
12846
+ 2. NEVER assume API compatibility between versions
12847
+ 3. NEVER invent function signatures
12848
+ 4. If not found \u2192 say "I could not find documentation"
12849
+ 5. Include confidence: HIGH (official) / MEDIUM (github) / LOW (blog)
12850
+ </anti_hallucination>
12851
+
12852
+ <planning_workflow>
12853
+ CREATE: .opencode/todo.md
12854
+
12855
+ \u26A1 PARALLELISM IS CRITICAL - Group tasks that can run simultaneously!
12856
+
12857
+ Task Structure:
12858
+ - Parallel Groups: Tasks with NO dependencies run together
12859
+ - Sequential: Only for tasks with real dependencies
12860
+ - Atomic: Each task = one focused action
12861
+
12862
+ FORMAT:
12863
+ \`\`\`markdown
12864
+ # Mission: [goal]
12865
+
12866
+ ## Parallel Group A (spawn all simultaneously)
12867
+ - [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER} | size:S
12868
+ - [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER} | size:S
12869
+ - [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER} | size:S
12870
+
12871
+ ## Parallel Group B (after Group A)
12872
+ - [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1 | size:M
12873
+ - [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2 | size:M
12874
+
12875
+ ## Sequential (strict order required)
12876
+ - [ ] T6: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5 | size:L
12877
+ - [ ] T7: Verify all | agent:${AGENT_NAMES.REVIEWER} | depends:T6 | size:S
12878
+
12879
+ ## Notes
12880
+ [context for team]
12881
+ \`\`\`
12882
+
12883
+ MAXIMIZE PARALLELISM:
12884
+ - Research tasks \u2192 ALL parallel (different topics)
12885
+ - Implementation \u2192 Parallel if different files
12886
+ - Sequential ONLY when: same file edit, strict A\u2192B dependency
12887
+ </planning_workflow>
12888
+
12889
+ <research_workflow>
12890
+ 1. SEARCH: websearch "[topic] official documentation [version]"
12891
+ 2. VERIFY: Check for official sources
12892
+ 3. FETCH: webfetch official docs with cache=true
12893
+ 4. EXTRACT: Copy EXACT syntax (not paraphrased)
12894
+ 5. SAVE: Write to .opencode/docs/[topic].md with source URL
12895
+ </research_workflow>
12896
+
12897
+ <estimation>
12898
+ TASK SIZING:
12899
+ | Size | Time | Description |
12900
+ |------|------|-------------|
12901
+ | XS | <5min | Config, typo fix |
12902
+ | S | 5-15min | Small feature |
12903
+ | M | 15-30min | Multi-file |
12904
+ | L | 30-60min | Complex |
12905
+ | XL | >60min | Break down further |
12906
+ </estimation>
12907
+
12908
+ <fallback_paths>
12909
+ FOR CRITICAL TASKS:
12910
+ - Primary: Best approach
12911
+ - Fallback: If primary fails
12912
+ - Minimum: Simplest working solution
12913
+ </fallback_paths>
12914
+
12915
+ <shared_workspace>
12916
+ ALL WORK IN .opencode/:
12917
+ - .opencode/todo.md - master TODO (you create)
12918
+ - .opencode/docs/ - cached documentation (you save)
12919
+ - .opencode/context.md - current state
12920
+ </shared_workspace>
12921
+
12922
+ <output>
12923
+ # Planning Report
12924
+
12925
+ ## Research Done
12926
+ | Finding | Source | Confidence |
12927
+ |---------|--------|------------|
12928
+ | [fact] | [URL] | HIGH/MEDIUM/LOW |
12929
+
12930
+ ## TODO Created
12931
+ .opencode/todo.md with [N] tasks
12932
+
12933
+ ## Docs Saved
12934
+ - .opencode/docs/[topic].md
12935
+
12936
+ Ready for ${AGENT_NAMES.WORKER}
12937
+ </output>`,
12938
+ canWrite: true,
12939
+ canBash: true
13128
12940
  };
13129
- function setErrorMap(map2) {
13130
- config({
13131
- customError: map2
13132
- });
13133
- }
13134
- function getErrorMap() {
13135
- return config().customError;
13136
- }
13137
- var ZodFirstPartyTypeKind;
13138
- /* @__PURE__ */ (function(ZodFirstPartyTypeKind2) {
13139
- })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
13140
12941
 
13141
- // node_modules/zod/v4/classic/coerce.js
13142
- var coerce_exports = {};
13143
- __export(coerce_exports, {
13144
- bigint: () => bigint3,
13145
- boolean: () => boolean3,
13146
- date: () => date4,
13147
- number: () => number3,
13148
- string: () => string3
13149
- });
13150
- function string3(params) {
13151
- return _coercedString(ZodString, params);
13152
- }
13153
- function number3(params) {
13154
- return _coercedNumber(ZodNumber, params);
13155
- }
13156
- function boolean3(params) {
13157
- return _coercedBoolean(ZodBoolean, params);
13158
- }
13159
- function bigint3(params) {
13160
- return _coercedBigint(ZodBigInt, params);
13161
- }
13162
- function date4(params) {
13163
- return _coercedDate(ZodDate, params);
13164
- }
12942
+ // src/agents/consolidated/worker.ts
12943
+ var worker = {
12944
+ id: AGENT_NAMES.WORKER,
12945
+ description: "Worker - implementation and documentation",
12946
+ systemPrompt: `<role>
12947
+ You are ${AGENT_NAMES.WORKER}. Implementation specialist and documentation handler.
12948
+ Write code, create files, configure systems.
12949
+ Fetch and cache official documentation when needed.
12950
+ Works with ANY language or framework.
12951
+ </role>
12952
+
12953
+ <responsibilities>
12954
+ 1. IMPLEMENTATION: Write code, create files, configurations
12955
+ 2. DOCUMENTATION: Search and cache official docs when needed
12956
+ 3. VERIFICATION: Verify your own changes work before reporting
12957
+ </responsibilities>
12958
+
12959
+ <anti_hallucination>
12960
+ BEFORE CODING:
12961
+ 1. Check .opencode/docs/ for cached docs
12962
+ 2. If not found \u2192 websearch for official docs
12963
+ 3. webfetch official docs with cache=true
12964
+ 4. NEVER guess API syntax - wait for verified docs
12965
+
12966
+ TRUSTED SOURCES:
12967
+ - Official docs: docs.[framework].com
12968
+ - GitHub: github.com/[org]/[repo]
12969
+ - Package registries: npmjs.com, pypi.org
12970
+ </anti_hallucination>
12971
+
12972
+ <workflow>
12973
+ 1. Check .opencode/todo.md for your assigned task
12974
+ 2. Read .opencode/docs/ for relevant documentation
12975
+ 3. If docs missing \u2192 search and cache them first
12976
+ 4. Check existing patterns in codebase
12977
+ 5. Implement following existing conventions
12978
+ 6. Verify your changes work (build/test)
12979
+ 7. Report completion
12980
+ </workflow>
12981
+
12982
+ <quality_standards>
12983
+ EVERY IMPLEMENTATION MUST:
12984
+ 1. Follow existing code patterns
12985
+ 2. Include error handling (try/catch, validation)
12986
+ 3. Add JSDoc/comments for public APIs
12987
+ 4. Type safety (no 'any' unless justified)
12988
+ 5. No hardcoded values (use constants)
12989
+
12990
+ TEST REQUIREMENTS:
12991
+ - New feature \u2192 create test file
12992
+ - Bug fix \u2192 add regression test
12993
+ - Existing tests must pass
12994
+ </quality_standards>
12995
+
12996
+ <implementation_checklist>
12997
+ BEFORE REPORTING COMPLETE:
12998
+ \u25A1 Code compiles without errors
12999
+ \u25A1 lsp_diagnostics shows no issues
13000
+ \u25A1 Existing tests pass
13001
+ \u25A1 Changes are minimal and focused
13002
+ \u25A1 No console.log debugging left
13003
+ \u25A1 Error cases handled
13004
+ </implementation_checklist>
13005
+
13006
+ <doc_caching>
13007
+ WHEN DOCS NEEDED:
13008
+ 1. websearch "[topic] official documentation"
13009
+ 2. webfetch official docs with cache=true
13010
+ 3. Save key info to .opencode/docs/[topic].md
13011
+
13012
+ FORMAT:
13013
+ \`\`\`markdown
13014
+ # [Topic] Documentation
13015
+ Source: [official URL]
13016
+ Version: [version]
13017
+ Retrieved: [date]
13018
+
13019
+ ## Official API/Syntax
13020
+ [exact code from docs]
13021
+ \`\`\`
13022
+ </doc_caching>
13023
+
13024
+ <shared_workspace>
13025
+ ALL IN .opencode/:
13026
+ - .opencode/todo.md - your assigned tasks
13027
+ - .opencode/docs/ - documentation cache
13028
+ - .opencode/context.md - current state
13029
+ </shared_workspace>
13030
+
13031
+ <output>
13032
+ TASK: T[N] from .opencode/todo.md
13033
+ CHANGED: [file] [lines]
13034
+ ACTION: [what]
13035
+ VERIFY: [build/test result]
13036
+ DOCS_USED: .opencode/docs/[file]
13037
+ \u2192 Task complete, ready for ${AGENT_NAMES.REVIEWER}
13038
+ </output>`,
13039
+ canWrite: true,
13040
+ canBash: true
13041
+ };
13042
+
13043
+ // src/agents/consolidated/reviewer.ts
13044
+ var reviewer = {
13045
+ id: AGENT_NAMES.REVIEWER,
13046
+ description: "Reviewer - verification and context management",
13047
+ systemPrompt: `<role>
13048
+ You are ${AGENT_NAMES.REVIEWER}. Verification specialist and context manager.
13049
+ Verify implementations against docs, track progress, manage .opencode/ context.
13050
+ Works with ANY language or framework.
13051
+ </role>
13052
+
13053
+ <responsibilities>
13054
+ 1. VERIFICATION: Prove implementations work with evidence
13055
+ 2. TODO TRACKING: Update checkboxes in .opencode/todo.md
13056
+ 3. CONTEXT MANAGEMENT: Keep .opencode/ lean and relevant
13057
+ </responsibilities>
13058
+
13059
+ <verification_workflow>
13060
+ 1. Check .opencode/todo.md for verification tasks
13061
+ 2. Read .opencode/docs/ for expected patterns
13062
+ 3. Verify implementation matches docs
13063
+ 4. Run build/test commands
13064
+ 5. Update TODO checkboxes
13065
+ 6. Maintain context.md
13066
+ </verification_workflow>
13067
+
13068
+ <audit_checklist>
13069
+ 1. SYNTAX: lsp_diagnostics or language tools
13070
+ 2. BUILD/TEST: Run project's commands
13071
+ 3. DOC_COMPLIANCE: Match .opencode/docs/
13072
+ 4. LOGIC: Manual review if no tests
13073
+ 5. SECURITY: Check for vulnerabilities
13074
+ </audit_checklist>
13075
+
13076
+ <auto_fix>
13077
+ WHEN ISSUES FOUND:
13078
+ - Trivial (typo, import) \u2192 Fix directly
13079
+ - Logic issue \u2192 Report to ${AGENT_NAMES.WORKER} with fix suggestion
13080
+ - Architecture \u2192 Escalate to ${AGENT_NAMES.COMMANDER}
13081
+
13082
+ FIX AUTHORITY:
13083
+ - \u2705 CAN FIX: Lint errors, formatting, minor typos
13084
+ - \u26A0\uFE0F SUGGEST: Logic changes, refactoring
13085
+ - \u274C ESCALATE: Architecture, new dependencies, security
13086
+ </auto_fix>
13087
+
13088
+ <security_check>
13089
+ VERIFY:
13090
+ \u25A1 No hardcoded secrets/passwords
13091
+ \u25A1 Input validation present
13092
+ \u25A1 No SQL injection risks
13093
+ \u25A1 No XSS vulnerabilities
13094
+ \u25A1 Proper error messages
13095
+ </security_check>
13096
+
13097
+ <todo_management>
13098
+ FILE: .opencode/todo.md
13099
+
13100
+ UPDATE FORMAT:
13101
+ \`\`\`markdown
13102
+ - [x] T1: [task] | \u2705 DONE
13103
+ - [ ] T2: [task] | in progress
13104
+ - [ ] T3: [task] | blocked: [reason]
13105
+ \`\`\`
13106
+ </todo_management>
13107
+
13108
+ <context_management>
13109
+ DYNAMIC DETAIL LEVELS:
13110
+
13111
+ PHASE 1 - EARLY (0-30% done):
13112
+ - BE DETAILED: Full explanations, decisions
13113
+ - Include: research, API references
13114
+
13115
+ PHASE 2 - BUILDING (30-70%):
13116
+ - MODERATE: Key decisions + file references
13117
+ - Reference: "See src/module.ts"
13118
+
13119
+ PHASE 3 - FINISHING (70-100%):
13120
+ - BRIEF: Just status, blockers
13121
+ - Heavy summarization
13122
+
13123
+ ADAPTIVE RULES:
13124
+ | Condition | Action |
13125
+ |-----------|--------|
13126
+ | > 150 lines context.md | Compress to 50 |
13127
+ | Feature complete | Delete related verbose docs |
13128
+ | Code exists for feature | Point to code instead |
13129
+ </context_management>
13130
+
13131
+ <cleanup_triggers>
13132
+ AFTER EACH UPDATE:
13133
+ 1. Is this info needed for FUTURE tasks? No \u2192 DELETE
13134
+ 2. Is this in code now? Yes \u2192 SUMMARIZE to reference
13135
+ 3. context.md > 150 lines? COMPRESS
13136
+ 4. Doc > 7 days old + unused? ARCHIVE
13137
+ </cleanup_triggers>
13138
+
13139
+ <shared_workspace>
13140
+ .opencode/
13141
+ \u251C\u2500\u2500 todo.md - Master TODO (update checkboxes)
13142
+ \u251C\u2500\u2500 context.md - Current state (adaptive size)
13143
+ \u251C\u2500\u2500 docs/ - Cached documentation
13144
+ \u2514\u2500\u2500 archive/ - Old context
13145
+ </shared_workspace>
13146
+
13147
+ <output>
13148
+ TASK: T[N] from .opencode/todo.md
13165
13149
 
13166
- // node_modules/zod/v4/classic/external.js
13167
- config(en_default());
13150
+ \u2705 PASS: [evidence]
13151
+ Matches: .opencode/docs/[file]
13168
13152
 
13169
- // node_modules/@opencode-ai/plugin/dist/tool.js
13170
- function tool(input) {
13171
- return input;
13172
- }
13173
- tool.schema = external_exports;
13153
+ \u274C FAIL: [issue]
13154
+ Fix: [suggestion]
13155
+
13156
+ CONTEXT UPDATED:
13157
+ - todo.md: [X/Y done]
13158
+ - context.md: [before \u2192 after lines]
13159
+ - Phase: [EARLY/BUILDING/FINISHING]
13160
+
13161
+ Next: [task for team]
13162
+ </output>`,
13163
+ canWrite: true,
13164
+ canBash: true
13165
+ };
13166
+
13167
+ // src/agents/definitions.ts
13168
+ var AGENTS = {
13169
+ [AGENT_NAMES.COMMANDER]: commander,
13170
+ [AGENT_NAMES.PLANNER]: planner,
13171
+ [AGENT_NAMES.WORKER]: worker,
13172
+ [AGENT_NAMES.REVIEWER]: reviewer
13173
+ };
13174
13174
 
13175
13175
  // src/tools/callAgent.ts
13176
13176
  var callAgentTool = tool({
@@ -15387,138 +15387,6 @@ function createAsyncAgentTools(manager, client) {
15387
15387
  };
15388
15388
  }
15389
15389
 
15390
- // src/utils/common.ts
15391
- function detectSlashCommand(text) {
15392
- const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
15393
- if (!match) return null;
15394
- return { command: match[1], args: match[2] || "" };
15395
- }
15396
- function formatTimestamp(date5 = /* @__PURE__ */ new Date()) {
15397
- const pad = (n) => n.toString().padStart(2, "0");
15398
- return `${date5.getFullYear()}-${pad(date5.getMonth() + 1)}-${pad(date5.getDate())} ${pad(date5.getHours())}:${pad(date5.getMinutes())}:${pad(date5.getSeconds())}`;
15399
- }
15400
- function formatElapsedTime(startMs, endMs = Date.now()) {
15401
- const elapsed = endMs - startMs;
15402
- if (elapsed < 0) return "0s";
15403
- const seconds = Math.floor(elapsed / 1e3) % 60;
15404
- const minutes = Math.floor(elapsed / (1e3 * 60)) % 60;
15405
- const hours = Math.floor(elapsed / (1e3 * 60 * 60));
15406
- const parts = [];
15407
- if (hours > 0) parts.push(`${hours}h`);
15408
- if (minutes > 0) parts.push(`${minutes}m`);
15409
- if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);
15410
- return parts.join(" ");
15411
- }
15412
-
15413
- // src/utils/sanity.ts
15414
- var SEVERITY = {
15415
- OK: "ok",
15416
- WARNING: "warning",
15417
- CRITICAL: "critical"
15418
- };
15419
- function checkOutputSanity(text) {
15420
- if (!text || text.length < 50) {
15421
- return { isHealthy: true, severity: SEVERITY.OK };
15422
- }
15423
- if (/(.)\1{15,}/.test(text)) {
15424
- return {
15425
- isHealthy: false,
15426
- reason: "Single character repetition detected",
15427
- severity: SEVERITY.CRITICAL
15428
- };
15429
- }
15430
- if (/(.{2,6})\1{8,}/.test(text)) {
15431
- return {
15432
- isHealthy: false,
15433
- reason: "Pattern loop detected",
15434
- severity: SEVERITY.CRITICAL
15435
- };
15436
- }
15437
- if (text.length > 200) {
15438
- const cleanText = text.replace(/\s/g, "");
15439
- if (cleanText.length > 100) {
15440
- const uniqueChars = new Set(cleanText).size;
15441
- const ratio = uniqueChars / cleanText.length;
15442
- if (ratio < 0.02) {
15443
- return {
15444
- isHealthy: false,
15445
- reason: "Low information density",
15446
- severity: SEVERITY.CRITICAL
15447
- };
15448
- }
15449
- }
15450
- }
15451
- const boxChars = (text.match(/[\u2500-\u257f\u2580-\u259f\u2800-\u28ff]/g) || []).length;
15452
- if (boxChars > 100 && boxChars / text.length > 0.3) {
15453
- return {
15454
- isHealthy: false,
15455
- reason: "Visual gibberish detected",
15456
- severity: SEVERITY.CRITICAL
15457
- };
15458
- }
15459
- const lines = text.split("\n").filter((l) => l.trim().length > 10);
15460
- if (lines.length > 10) {
15461
- const lineSet = new Set(lines);
15462
- if (lineSet.size < lines.length * 0.2) {
15463
- return {
15464
- isHealthy: false,
15465
- reason: "Excessive line repetition",
15466
- severity: SEVERITY.WARNING
15467
- };
15468
- }
15469
- }
15470
- const cjkChars = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
15471
- if (cjkChars > 200) {
15472
- const uniqueCjk = new Set(
15473
- text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []
15474
- ).size;
15475
- if (uniqueCjk < 10 && cjkChars / uniqueCjk > 20) {
15476
- return {
15477
- isHealthy: false,
15478
- reason: "CJK character spam detected",
15479
- severity: SEVERITY.CRITICAL
15480
- };
15481
- }
15482
- }
15483
- return { isHealthy: true, severity: SEVERITY.OK };
15484
- }
15485
- var RECOVERY_PROMPT = `<anomaly_recovery>
15486
- \u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).
15487
-
15488
- <recovery_protocol>
15489
- 1. DISCARD the corrupted output completely - do not reference it
15490
- 2. RECALL the original mission objective
15491
- 3. IDENTIFY the last confirmed successful step
15492
- 4. RESTART with a simpler, more focused approach
15493
- </recovery_protocol>
15494
-
15495
- <instructions>
15496
- - If a sub-agent produced bad output: try a different agent or simpler task
15497
- - If stuck in a loop: break down the task into smaller pieces
15498
- - If context seems corrupted: call Reviewer to restore context
15499
- - THINK in English for maximum stability
15500
- </instructions>
15501
-
15502
- What was the original task? Proceed from the last known good state.
15503
- </anomaly_recovery>`;
15504
- var ESCALATION_PROMPT = `<critical_anomaly>
15505
- \u{1F6A8} CRITICAL: Multiple consecutive malformed outputs detected.
15506
-
15507
- <emergency_protocol>
15508
- 1. STOP current execution path immediately
15509
- 2. DO NOT continue with the same approach - it is failing
15510
- 3. CALL Planner for a completely new strategy
15511
- 4. If Planner also fails: report status to user and await guidance
15512
- </emergency_protocol>
15513
-
15514
- <diagnosis>
15515
- The current approach is producing corrupted output.
15516
- This may indicate: context overload, model instability, or task complexity.
15517
- </diagnosis>
15518
-
15519
- Request a fresh plan from Planner with reduced scope.
15520
- </critical_anomaly>`;
15521
-
15522
15390
  // src/core/cache/constants.ts
15523
15391
  var CACHE_DIR = PATHS.DOCS;
15524
15392
  var METADATA_FILE = PATHS.DOC_METADATA;
@@ -16262,135 +16130,118 @@ Try:
16262
16130
  }
16263
16131
  let output = `\u{1F50D} **Code Search Results for: "${query}"**
16264
16132
 
16265
- `;
16266
- output += `Found ${results.length} results${language ? ` (${language})` : ""}
16267
-
16268
- ---
16269
-
16270
- `;
16271
- for (let i = 0; i < results.length; i++) {
16272
- const r = results[i];
16273
- output += `### ${i + 1}. ${r.repo}
16274
- `;
16275
- output += `\u{1F4C4} \`${r.file}\`${r.line ? `:${r.line}` : ""}
16276
- `;
16277
- output += `\u{1F517} [View on GitHub](${r.url})
16278
-
16279
- `;
16280
- if (r.content && r.content !== "(Use webfetch for full content)") {
16281
- output += `\`\`\`
16282
- ${r.content}
16283
- \`\`\`
16284
-
16285
- `;
16286
- }
16287
- }
16288
- output += `---
16289
-
16290
- `;
16291
- output += `\u{1F4A1} **Tip**: Use \`webfetch\` to get the full file content from any of these URLs.`;
16292
- return output;
16293
- }
16294
- });
16295
-
16296
- // src/core/loop/stats.ts
16297
- function getIncompleteCount(todos) {
16298
- return todos.filter(
16299
- (t) => t.status !== TODO_STATUS.COMPLETED && t.status !== TODO_STATUS.CANCELLED
16300
- ).length;
16301
- }
16302
- function hasRemainingWork(todos) {
16303
- return getIncompleteCount(todos) > 0;
16304
- }
16305
- function getNextPending(todos) {
16306
- const pending = todos.filter(
16307
- (t) => t.status === TODO_STATUS.PENDING || t.status === TODO_STATUS.IN_PROGRESS
16308
- );
16309
- const priorityOrder = { high: 0, medium: 1, low: 2 };
16310
- pending.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
16311
- return pending[0];
16312
- }
16313
- function getStats(todos) {
16314
- const stats2 = {
16315
- total: todos.length,
16316
- pending: todos.filter((t) => t.status === TODO_STATUS.PENDING).length,
16317
- inProgress: todos.filter((t) => t.status === TODO_STATUS.IN_PROGRESS).length,
16318
- completed: todos.filter((t) => t.status === TODO_STATUS.COMPLETED).length,
16319
- cancelled: todos.filter((t) => t.status === TODO_STATUS.CANCELLED).length,
16320
- percentComplete: 0
16321
- };
16322
- if (stats2.total > 0) {
16323
- stats2.percentComplete = Math.round(
16324
- (stats2.completed + stats2.cancelled) / stats2.total * 100
16325
- );
16326
- }
16327
- return stats2;
16328
- }
16133
+ `;
16134
+ output += `Found ${results.length} results${language ? ` (${language})` : ""}
16329
16135
 
16330
- // src/core/loop/formatters.ts
16331
- function formatProgress(todos) {
16332
- const stats2 = getStats(todos);
16333
- const done = stats2.completed + stats2.cancelled;
16334
- return `${done}/${stats2.total} (${stats2.percentComplete}%)`;
16335
- }
16336
- function generateContinuationPrompt(todos) {
16337
- const incomplete = todos.filter(
16338
- (t) => t.status !== TODO_STATUS.COMPLETED && t.status !== TODO_STATUS.CANCELLED
16339
- );
16340
- if (incomplete.length === 0) {
16341
- return "";
16342
- }
16343
- const next = getNextPending(todos);
16344
- const pendingTasks = incomplete.filter((t) => t.status === TODO_STATUS.PENDING);
16345
- const pendingCount = pendingTasks.length;
16346
- let prompt = `<todo_continuation>
16347
- \u{1F4CB} **TODO Progress**: ${formatProgress(todos)}
16136
+ ---
16348
16137
 
16349
- **Incomplete Tasks** (${incomplete.length} remaining):
16350
16138
  `;
16351
- for (const todo of incomplete.slice(0, 5)) {
16352
- const status = todo.status === TODO_STATUS.IN_PROGRESS ? "\u{1F504}" : "\u23F3";
16353
- const priority = todo.priority === "high" ? "\u{1F534}" : todo.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
16354
- prompt += `${status} ${priority} [${todo.id}] ${todo.content}
16139
+ for (let i = 0; i < results.length; i++) {
16140
+ const r = results[i];
16141
+ output += `### ${i + 1}. ${r.repo}
16355
16142
  `;
16356
- }
16357
- if (incomplete.length > 5) {
16358
- prompt += `... and ${incomplete.length - 5} more
16143
+ output += `\u{1F4C4} \`${r.file}\`${r.line ? `:${r.line}` : ""}
16359
16144
  `;
16360
- }
16361
- if (pendingCount >= 2) {
16362
- prompt += `
16363
- \u26A1 **PARALLEL DISPATCH REQUIRED** \u26A1
16364
- You have ${pendingCount} pending tasks. Launch them ALL IN PARALLEL for maximum efficiency:
16145
+ output += `\u{1F517} [View on GitHub](${r.url})
16365
16146
 
16366
- \`\`\`
16367
- // EXECUTE NOW - Launch all ${pendingCount} tasks simultaneously:
16368
16147
  `;
16369
- for (const todo of pendingTasks.slice(0, 6)) {
16370
- prompt += `delegate_task({ agent: "Worker", prompt: "${todo.content}", background: true })
16148
+ if (r.content && r.content !== "(Use webfetch for full content)") {
16149
+ output += `\`\`\`
16150
+ ${r.content}
16151
+ \`\`\`
16152
+
16371
16153
  `;
16154
+ }
16372
16155
  }
16373
- prompt += `\`\`\`
16374
-
16375
- \u26A0\uFE0F Do NOT run these sequentially. Use background=true for ALL.
16376
- After launching, use list_tasks to monitor progress.
16156
+ output += `---
16377
16157
 
16378
16158
  `;
16379
- } else {
16380
- prompt += `
16381
- **Action Required**:
16382
- 1. Continue working on incomplete todos
16383
- 2. Mark each task complete when finished
16384
- 3. Do NOT stop until all todos are completed or cancelled
16159
+ output += `\u{1F4A1} **Tip**: Use \`webfetch\` to get the full file content from any of these URLs.`;
16160
+ return output;
16161
+ }
16162
+ });
16385
16163
 
16386
- `;
16164
+ // src/core/progress/store.ts
16165
+ var progressHistory = /* @__PURE__ */ new Map();
16166
+ var sessionStartTimes = /* @__PURE__ */ new Map();
16167
+ var MAX_HISTORY2 = 100;
16168
+ function startSession(sessionId) {
16169
+ sessionStartTimes.set(sessionId, /* @__PURE__ */ new Date());
16170
+ progressHistory.set(sessionId, []);
16171
+ }
16172
+ function recordSnapshot(sessionId, data) {
16173
+ const startedAt = sessionStartTimes.get(sessionId) || /* @__PURE__ */ new Date();
16174
+ const now = /* @__PURE__ */ new Date();
16175
+ const snapshot = {
16176
+ sessionId,
16177
+ timestamp: now,
16178
+ todos: {
16179
+ total: data.todoTotal || 0,
16180
+ completed: data.todoCompleted || 0,
16181
+ pending: (data.todoTotal || 0) - (data.todoCompleted || 0),
16182
+ percentage: data.todoTotal ? Math.round((data.todoCompleted || 0) / data.todoTotal * 100) : 0
16183
+ },
16184
+ tasks: {
16185
+ total: data.taskTotal || 0,
16186
+ running: data.taskRunning || 0,
16187
+ completed: data.taskCompleted || 0,
16188
+ failed: data.taskFailed || 0,
16189
+ percentage: data.taskTotal ? Math.round(((data.taskCompleted || 0) + (data.taskFailed || 0)) / data.taskTotal * 100) : 0
16190
+ },
16191
+ steps: {
16192
+ current: data.currentStep || 0,
16193
+ max: data.maxSteps || Infinity
16194
+ },
16195
+ startedAt,
16196
+ elapsedMs: now.getTime() - startedAt.getTime()
16197
+ };
16198
+ const history = progressHistory.get(sessionId) || [];
16199
+ history.push(snapshot);
16200
+ if (history.length > MAX_HISTORY2) {
16201
+ history.shift();
16387
16202
  }
16388
- if (next) {
16389
- prompt += `**Next Task**: [${next.id}] ${next.content}
16390
- `;
16203
+ progressHistory.set(sessionId, history);
16204
+ return snapshot;
16205
+ }
16206
+ function getLatest(sessionId) {
16207
+ const history = progressHistory.get(sessionId);
16208
+ return history?.[history.length - 1];
16209
+ }
16210
+ function clearSession(sessionId) {
16211
+ progressHistory.delete(sessionId);
16212
+ sessionStartTimes.delete(sessionId);
16213
+ }
16214
+
16215
+ // src/core/progress/formatters.ts
16216
+ function formatElapsed(ms) {
16217
+ const seconds = Math.floor(ms / 1e3);
16218
+ const minutes = Math.floor(seconds / 60);
16219
+ const hours = Math.floor(minutes / 60);
16220
+ if (hours > 0) {
16221
+ return `${hours}h ${minutes % 60}m`;
16222
+ } else if (minutes > 0) {
16223
+ return `${minutes}m ${seconds % 60}s`;
16224
+ } else {
16225
+ return `${seconds}s`;
16391
16226
  }
16392
- prompt += `</todo_continuation>`;
16393
- return prompt;
16227
+ }
16228
+ function formatCompact(snapshot) {
16229
+ const parts = [];
16230
+ if (snapshot.todos.total > 0) {
16231
+ parts.push(`\u2705${snapshot.todos.completed}/${snapshot.todos.total}`);
16232
+ }
16233
+ if (snapshot.tasks.running > 0) {
16234
+ parts.push(`\u26A1${snapshot.tasks.running}`);
16235
+ }
16236
+ parts.push(`\u23F1${formatElapsed(snapshot.elapsedMs)}`);
16237
+ return parts.join(" | ");
16238
+ }
16239
+
16240
+ // src/core/progress/tracker.ts
16241
+ function formatCompact2(sessionId) {
16242
+ const snapshot = getLatest(sessionId);
16243
+ if (!snapshot) return "...";
16244
+ return formatCompact(snapshot);
16394
16245
  }
16395
16246
 
16396
16247
  // src/shared/error-patterns.ts
@@ -16417,7 +16268,7 @@ function detectErrorType(error45) {
16417
16268
  // src/core/recovery/constants.ts
16418
16269
  var MAX_RETRIES = 3;
16419
16270
  var BASE_DELAY = 1e3;
16420
- var MAX_HISTORY2 = 100;
16271
+ var MAX_HISTORY3 = 100;
16421
16272
 
16422
16273
  // src/core/recovery/patterns.ts
16423
16274
  var errorPatterns = [
@@ -16502,7 +16353,7 @@ function handleError(context) {
16502
16353
  action,
16503
16354
  timestamp: /* @__PURE__ */ new Date()
16504
16355
  });
16505
- if (recoveryHistory.length > MAX_HISTORY2) {
16356
+ if (recoveryHistory.length > MAX_HISTORY3) {
16506
16357
  recoveryHistory.shift();
16507
16358
  }
16508
16359
  return action;
@@ -16632,19 +16483,119 @@ async function handleSessionError(client, sessionID, error45, properties) {
16632
16483
  state2.isRecovering = false;
16633
16484
  return false;
16634
16485
  }
16635
- }
16636
- function markRecoveryComplete(sessionID) {
16637
- const state2 = recoveryState.get(sessionID);
16638
- if (state2) {
16639
- state2.isRecovering = false;
16640
- state2.errorCount = 0;
16486
+ }
16487
+ function markRecoveryComplete(sessionID) {
16488
+ const state2 = recoveryState.get(sessionID);
16489
+ if (state2) {
16490
+ state2.isRecovering = false;
16491
+ state2.errorCount = 0;
16492
+ }
16493
+ }
16494
+ function cleanupSessionRecovery(sessionID) {
16495
+ recoveryState.delete(sessionID);
16496
+ }
16497
+ function isSessionRecovering(sessionID) {
16498
+ return recoveryState.get(sessionID)?.isRecovering ?? false;
16499
+ }
16500
+
16501
+ // src/core/loop/stats.ts
16502
+ function getIncompleteCount(todos) {
16503
+ return todos.filter(
16504
+ (t) => t.status !== TODO_STATUS.COMPLETED && t.status !== TODO_STATUS.CANCELLED
16505
+ ).length;
16506
+ }
16507
+ function hasRemainingWork(todos) {
16508
+ return getIncompleteCount(todos) > 0;
16509
+ }
16510
+ function getNextPending(todos) {
16511
+ const pending = todos.filter(
16512
+ (t) => t.status === TODO_STATUS.PENDING || t.status === TODO_STATUS.IN_PROGRESS
16513
+ );
16514
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
16515
+ pending.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
16516
+ return pending[0];
16517
+ }
16518
+ function getStats(todos) {
16519
+ const stats2 = {
16520
+ total: todos.length,
16521
+ pending: todos.filter((t) => t.status === TODO_STATUS.PENDING).length,
16522
+ inProgress: todos.filter((t) => t.status === TODO_STATUS.IN_PROGRESS).length,
16523
+ completed: todos.filter((t) => t.status === TODO_STATUS.COMPLETED).length,
16524
+ cancelled: todos.filter((t) => t.status === TODO_STATUS.CANCELLED).length,
16525
+ percentComplete: 0
16526
+ };
16527
+ if (stats2.total > 0) {
16528
+ stats2.percentComplete = Math.round(
16529
+ (stats2.completed + stats2.cancelled) / stats2.total * 100
16530
+ );
16531
+ }
16532
+ return stats2;
16533
+ }
16534
+
16535
+ // src/core/loop/formatters.ts
16536
+ function formatProgress(todos) {
16537
+ const stats2 = getStats(todos);
16538
+ const done = stats2.completed + stats2.cancelled;
16539
+ return `${done}/${stats2.total} (${stats2.percentComplete}%)`;
16540
+ }
16541
+ function generateContinuationPrompt(todos) {
16542
+ const incomplete = todos.filter(
16543
+ (t) => t.status !== TODO_STATUS.COMPLETED && t.status !== TODO_STATUS.CANCELLED
16544
+ );
16545
+ if (incomplete.length === 0) {
16546
+ return "";
16547
+ }
16548
+ const next = getNextPending(todos);
16549
+ const pendingTasks = incomplete.filter((t) => t.status === TODO_STATUS.PENDING);
16550
+ const pendingCount = pendingTasks.length;
16551
+ let prompt = `<todo_continuation>
16552
+ \u{1F4CB} **TODO Progress**: ${formatProgress(todos)}
16553
+
16554
+ **Incomplete Tasks** (${incomplete.length} remaining):
16555
+ `;
16556
+ for (const todo of incomplete.slice(0, 5)) {
16557
+ const status = todo.status === TODO_STATUS.IN_PROGRESS ? "\u{1F504}" : "\u23F3";
16558
+ const priority = todo.priority === "high" ? "\u{1F534}" : todo.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
16559
+ prompt += `${status} ${priority} [${todo.id}] ${todo.content}
16560
+ `;
16561
+ }
16562
+ if (incomplete.length > 5) {
16563
+ prompt += `... and ${incomplete.length - 5} more
16564
+ `;
16565
+ }
16566
+ if (pendingCount >= 2) {
16567
+ prompt += `
16568
+ \u26A1 **PARALLEL DISPATCH REQUIRED** \u26A1
16569
+ You have ${pendingCount} pending tasks. Launch them ALL IN PARALLEL for maximum efficiency:
16570
+
16571
+ \`\`\`
16572
+ // EXECUTE NOW - Launch all ${pendingCount} tasks simultaneously:
16573
+ `;
16574
+ for (const todo of pendingTasks.slice(0, 6)) {
16575
+ prompt += `delegate_task({ agent: "Worker", prompt: "${todo.content}", background: true })
16576
+ `;
16577
+ }
16578
+ prompt += `\`\`\`
16579
+
16580
+ \u26A0\uFE0F Do NOT run these sequentially. Use background=true for ALL.
16581
+ After launching, use list_tasks to monitor progress.
16582
+
16583
+ `;
16584
+ } else {
16585
+ prompt += `
16586
+ **Action Required**:
16587
+ 1. Continue working on incomplete todos
16588
+ 2. Mark each task complete when finished
16589
+ 3. Do NOT stop until all todos are completed or cancelled
16590
+
16591
+ `;
16592
+ }
16593
+ if (next) {
16594
+ prompt += `**Next Task**: [${next.id}] ${next.content}
16595
+ `;
16641
16596
  }
16642
- }
16643
- function cleanupSessionRecovery(sessionID) {
16644
- recoveryState.delete(sessionID);
16645
- }
16646
- function isSessionRecovering(sessionID) {
16647
- return recoveryState.get(sessionID)?.isRecovering ?? false;
16597
+ prompt += `</todo_continuation>`;
16598
+ return prompt;
16648
16599
  }
16649
16600
 
16650
16601
  // src/core/loop/todo-continuation.ts
@@ -16652,6 +16603,8 @@ var sessionStates = /* @__PURE__ */ new Map();
16652
16603
  var COUNTDOWN_SECONDS = 2;
16653
16604
  var TOAST_DURATION_MS = 1500;
16654
16605
  var MIN_TIME_BETWEEN_CONTINUATIONS_MS = 3e3;
16606
+ var COUNTDOWN_GRACE_PERIOD_MS = 500;
16607
+ var ABORT_WINDOW_MS = 3e3;
16655
16608
  function getState2(sessionID) {
16656
16609
  let state2 = sessionStates.get(sessionID);
16657
16610
  if (!state2) {
@@ -16757,6 +16710,15 @@ async function handleSessionIdle(client, sessionID, mainSessionID) {
16757
16710
  log2("[todo-continuation] Skipped: in recovery mode", { sessionID });
16758
16711
  return;
16759
16712
  }
16713
+ if (state2.abortDetectedAt) {
16714
+ const timeSinceAbort = Date.now() - state2.abortDetectedAt;
16715
+ if (timeSinceAbort < ABORT_WINDOW_MS) {
16716
+ log2("[todo-continuation] Skipped: abort detected recently", { sessionID, timeSinceAbort });
16717
+ state2.abortDetectedAt = void 0;
16718
+ return;
16719
+ }
16720
+ state2.abortDetectedAt = void 0;
16721
+ }
16760
16722
  if (hasRunningBackgroundTasks(sessionID)) {
16761
16723
  log2("[todo-continuation] Skipped: background tasks running", { sessionID });
16762
16724
  return;
@@ -16799,11 +16761,28 @@ async function handleSessionIdle(client, sessionID, mainSessionID) {
16799
16761
  }
16800
16762
  function handleUserMessage(sessionID) {
16801
16763
  const state2 = getState2(sessionID);
16764
+ if (state2.countdownStartedAt) {
16765
+ const elapsed = Date.now() - state2.countdownStartedAt;
16766
+ if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
16767
+ log2("[todo-continuation] Ignoring message in grace period", { sessionID, elapsed });
16768
+ return;
16769
+ }
16770
+ }
16802
16771
  if (state2.countdownTimer) {
16803
16772
  log2("[todo-continuation] Cancelled: user interaction", { sessionID });
16804
16773
  cancelCountdown(sessionID);
16805
16774
  }
16806
16775
  state2.isAborting = false;
16776
+ state2.abortDetectedAt = void 0;
16777
+ }
16778
+ function handleSessionError2(sessionID, error45) {
16779
+ const state2 = getState2(sessionID);
16780
+ const errorObj = error45;
16781
+ if (errorObj?.name === "MessageAbortedError" || errorObj?.name === "AbortError") {
16782
+ state2.abortDetectedAt = Date.now();
16783
+ log2("[todo-continuation] Abort detected", { sessionID, errorName: errorObj.name });
16784
+ }
16785
+ cancelCountdown(sessionID);
16807
16786
  }
16808
16787
  function cleanupSession(sessionID) {
16809
16788
  cancelCountdown(sessionID);
@@ -16898,33 +16877,61 @@ async function detectSealInSession(client, sessionID) {
16898
16877
  return false;
16899
16878
  }
16900
16879
  }
16880
+ function startMissionLoop(directory, sessionID, prompt, options = {}) {
16881
+ const state2 = {
16882
+ active: true,
16883
+ iteration: 1,
16884
+ maxIterations: options.maxIterations ?? DEFAULT_MAX_ITERATIONS,
16885
+ prompt,
16886
+ sessionID,
16887
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
16888
+ };
16889
+ const success2 = writeLoopState(directory, state2);
16890
+ if (success2) {
16891
+ log2(`[mission-seal] Loop started`, {
16892
+ sessionID,
16893
+ maxIterations: state2.maxIterations
16894
+ });
16895
+ }
16896
+ return success2;
16897
+ }
16901
16898
  function isLoopActive(directory, sessionID) {
16902
16899
  const state2 = readLoopState(directory);
16903
16900
  return state2?.active === true && state2?.sessionID === sessionID;
16904
16901
  }
16905
16902
  function generateMissionContinuationPrompt(state2) {
16906
16903
  return `<mission_loop iteration="${state2.iteration}" max="${state2.maxIterations}">
16907
- \u{1F4CB} **Mission Loop Active** - Iteration ${state2.iteration}/${state2.maxIterations}
16904
+ \u26A0\uFE0F **MISSION NOT COMPLETE** - Iteration ${state2.iteration}/${state2.maxIterations}
16905
+
16906
+ The mission is INCOMPLETE. You MUST continue working NOW.
16907
+
16908
+ **FORBIDDEN**:
16909
+ \u274C Do NOT stop or ask for permission
16910
+ \u274C Do NOT say "I've completed..." without SEAL
16911
+ \u274C Do NOT output partial results
16912
+ \u274C Do NOT wait for user input
16908
16913
 
16909
- Your previous iteration did not seal the mission. Continue working.
16914
+ **REQUIRED SEQUENCE**:
16915
+ 1\uFE0F\u20E3 Check .opencode/todo.md for [ ] incomplete items
16916
+ 2\uFE0F\u20E3 Execute the NEXT pending task
16917
+ 3\uFE0F\u20E3 Use delegate_task with background=true for parallel work
16918
+ 4\uFE0F\u20E3 Mark completed items as [x]
16919
+ 5\uFE0F\u20E3 Repeat until ALL items are [x]
16910
16920
 
16911
- **RULES**:
16912
- 1. Review your progress from the previous iteration
16913
- 2. Continue from where you left off
16914
- 3. Check TODO list for incomplete items
16915
- 4. When ALL work is TRULY complete, output:
16921
+ **ONLY WHEN TRULY DONE**:
16922
+ - All todos marked [x]
16923
+ - All tests pass
16924
+ - All builds succeed
16925
+ Then and ONLY then output:
16916
16926
 
16917
16927
  \`\`\`
16918
16928
  ${SEAL_PATTERN}
16919
16929
  \`\`\`
16920
16930
 
16921
- **IMPORTANT**:
16922
- - Do NOT seal until the mission is genuinely complete
16923
- - Verify all todos are marked [x] before sealing
16924
- - Run tests/builds if applicable before sealing
16925
-
16926
- **Original Task**:
16931
+ **Your Original Task**:
16927
16932
  ${state2.prompt}
16933
+
16934
+ **NOW**: Continue executing until ${SEAL_PATTERN} is output!
16928
16935
  </mission_loop>`;
16929
16936
  }
16930
16937
 
@@ -17113,95 +17120,464 @@ async function handleMissionSealIdle(client, directory, sessionID, mainSessionID
17113
17120
  seconds: COUNTDOWN_SECONDS2
17114
17121
  });
17115
17122
  }
17123
+ function handleAbort(sessionID) {
17124
+ const state2 = getState3(sessionID);
17125
+ state2.isAborting = true;
17126
+ cancelCountdown2(sessionID);
17127
+ log2("[mission-seal-handler] Marked as aborting");
17128
+ }
17116
17129
 
17117
- // src/core/progress/store.ts
17118
- var progressHistory = /* @__PURE__ */ new Map();
17119
- var sessionStartTimes = /* @__PURE__ */ new Map();
17120
- var MAX_HISTORY3 = 100;
17121
- function startSession(sessionId) {
17122
- sessionStartTimes.set(sessionId, /* @__PURE__ */ new Date());
17123
- progressHistory.set(sessionId, []);
17130
+ // src/plugin-handlers/event-handler.ts
17131
+ function createEventHandler(ctx) {
17132
+ const { client, directory, sessions, state: state2 } = ctx;
17133
+ return async (input) => {
17134
+ const { event } = input;
17135
+ try {
17136
+ const manager = ParallelAgentManager.getInstance();
17137
+ manager.handleEvent(event);
17138
+ } catch {
17139
+ }
17140
+ if (event.type === "session.created") {
17141
+ const sessionID = event.properties?.id || "";
17142
+ log2("[event-handler] session.created", { sessionID });
17143
+ presets.missionStarted(`Session ${sessionID.slice(0, 12)}...`);
17144
+ }
17145
+ if (event.type === "session.deleted") {
17146
+ const sessionID = event.properties?.id || event.properties?.info?.id || "";
17147
+ const session = sessions.get(sessionID);
17148
+ if (session) {
17149
+ const totalTime = Date.now() - session.startTime;
17150
+ const duration3 = totalTime < 6e4 ? `${Math.round(totalTime / 1e3)}s` : `${Math.round(totalTime / 6e4)}m`;
17151
+ log2("[event-handler] session.deleted", {
17152
+ sessionID,
17153
+ steps: session.step,
17154
+ duration: duration3
17155
+ });
17156
+ sessions.delete(sessionID);
17157
+ state2.sessions.delete(sessionID);
17158
+ clearSession(sessionID);
17159
+ cleanupSessionRecovery(sessionID);
17160
+ cleanupSession(sessionID);
17161
+ presets.sessionCompleted(sessionID, duration3);
17162
+ }
17163
+ }
17164
+ if (event.type === "session.error") {
17165
+ const sessionID = event.properties?.sessionId || event.properties?.sessionID || "";
17166
+ const error45 = event.properties?.error;
17167
+ log2("[event-handler] session.error", { sessionID, error: error45 });
17168
+ if (sessionID) {
17169
+ handleSessionError2(sessionID, error45);
17170
+ handleAbort(sessionID);
17171
+ }
17172
+ if (sessionID && error45) {
17173
+ const recovered = await handleSessionError(
17174
+ client,
17175
+ sessionID,
17176
+ error45,
17177
+ event.properties
17178
+ );
17179
+ if (recovered) {
17180
+ log2("[event-handler] auto-recovery initiated", { sessionID });
17181
+ return;
17182
+ }
17183
+ }
17184
+ presets.taskFailed("session", String(error45).slice(0, 50));
17185
+ }
17186
+ if (event.type === "message.updated") {
17187
+ const messageInfo = event.properties?.info;
17188
+ const sessionID = messageInfo?.sessionID;
17189
+ const role = messageInfo?.role;
17190
+ if (sessionID && role === "assistant") {
17191
+ markRecoveryComplete(sessionID);
17192
+ }
17193
+ }
17194
+ if (event.type === "session.idle") {
17195
+ const sessionID = event.properties?.sessionID || "";
17196
+ if (sessionID) {
17197
+ const isMainSession = sessions.has(sessionID);
17198
+ if (isMainSession) {
17199
+ setTimeout(async () => {
17200
+ const session = sessions.get(sessionID);
17201
+ if (session?.active) {
17202
+ if (isLoopActive(directory, sessionID)) {
17203
+ await handleMissionSealIdle(
17204
+ client,
17205
+ directory,
17206
+ sessionID,
17207
+ sessionID
17208
+ ).catch((err) => {
17209
+ log2("[event-handler] mission-seal-handler error", err);
17210
+ });
17211
+ } else {
17212
+ await handleSessionIdle(
17213
+ client,
17214
+ sessionID,
17215
+ sessionID
17216
+ ).catch((err) => {
17217
+ log2("[event-handler] todo-continuation error", err);
17218
+ });
17219
+ }
17220
+ }
17221
+ }, 500);
17222
+ }
17223
+ }
17224
+ }
17225
+ };
17124
17226
  }
17125
- function recordSnapshot(sessionId, data) {
17126
- const startedAt = sessionStartTimes.get(sessionId) || /* @__PURE__ */ new Date();
17127
- const now = /* @__PURE__ */ new Date();
17128
- const snapshot = {
17129
- sessionId,
17130
- timestamp: now,
17131
- todos: {
17132
- total: data.todoTotal || 0,
17133
- completed: data.todoCompleted || 0,
17134
- pending: (data.todoTotal || 0) - (data.todoCompleted || 0),
17135
- percentage: data.todoTotal ? Math.round((data.todoCompleted || 0) / data.todoTotal * 100) : 0
17136
- },
17137
- tasks: {
17138
- total: data.taskTotal || 0,
17139
- running: data.taskRunning || 0,
17140
- completed: data.taskCompleted || 0,
17141
- failed: data.taskFailed || 0,
17142
- percentage: data.taskTotal ? Math.round(((data.taskCompleted || 0) + (data.taskFailed || 0)) / data.taskTotal * 100) : 0
17143
- },
17144
- steps: {
17145
- current: data.currentStep || 0,
17146
- max: data.maxSteps || Infinity
17147
- },
17148
- startedAt,
17149
- elapsedMs: now.getTime() - startedAt.getTime()
17227
+
17228
+ // src/plugin-handlers/config-handler.ts
17229
+ function createConfigHandler() {
17230
+ const commanderPrompt = AGENTS[AGENT_NAMES.COMMANDER]?.systemPrompt || "";
17231
+ return async (config2) => {
17232
+ const existingCommands = config2.command ?? {};
17233
+ const existingAgents = config2.agent ?? {};
17234
+ const orchestratorCommands = {};
17235
+ for (const [name, cmd] of Object.entries(COMMANDS)) {
17236
+ orchestratorCommands[name] = {
17237
+ description: cmd.description,
17238
+ template: cmd.template,
17239
+ argumentHint: cmd.argumentHint
17240
+ };
17241
+ }
17242
+ const orchestratorAgents = {
17243
+ // Primary agent - the main orchestrator
17244
+ [AGENT_NAMES.COMMANDER]: {
17245
+ description: "Autonomous orchestrator - executes until mission complete",
17246
+ mode: "primary",
17247
+ prompt: commanderPrompt,
17248
+ maxTokens: 64e3,
17249
+ thinking: { type: "enabled", budgetTokens: 32e3 },
17250
+ color: "#FF6B6B"
17251
+ },
17252
+ // Consolidated subagents (4 agents instead of 6)
17253
+ [AGENT_NAMES.PLANNER]: {
17254
+ description: "Strategic planning and research specialist",
17255
+ mode: "subagent",
17256
+ hidden: true,
17257
+ prompt: AGENTS[AGENT_NAMES.PLANNER]?.systemPrompt || "",
17258
+ maxTokens: 32e3,
17259
+ color: "#9B59B6"
17260
+ },
17261
+ [AGENT_NAMES.WORKER]: {
17262
+ description: "Implementation and documentation specialist",
17263
+ mode: "subagent",
17264
+ hidden: true,
17265
+ prompt: AGENTS[AGENT_NAMES.WORKER]?.systemPrompt || "",
17266
+ maxTokens: 32e3,
17267
+ color: "#E67E22"
17268
+ },
17269
+ [AGENT_NAMES.REVIEWER]: {
17270
+ description: "Verification and context management specialist",
17271
+ mode: "subagent",
17272
+ hidden: true,
17273
+ prompt: AGENTS[AGENT_NAMES.REVIEWER]?.systemPrompt || "",
17274
+ maxTokens: 32e3,
17275
+ color: "#27AE60"
17276
+ }
17277
+ };
17278
+ const processedExistingAgents = { ...existingAgents };
17279
+ if (processedExistingAgents.build) {
17280
+ processedExistingAgents.build = {
17281
+ ...processedExistingAgents.build,
17282
+ mode: "subagent",
17283
+ hidden: true
17284
+ };
17285
+ }
17286
+ if (processedExistingAgents.plan) {
17287
+ processedExistingAgents.plan = {
17288
+ ...processedExistingAgents.plan,
17289
+ mode: "subagent"
17290
+ };
17291
+ }
17292
+ config2.command = { ...existingCommands, ...orchestratorCommands };
17293
+ config2.agent = { ...processedExistingAgents, ...orchestratorAgents };
17294
+ config2.default_agent = AGENT_NAMES.COMMANDER;
17295
+ console.log(`[orchestrator] Registered agents: ${Object.keys(orchestratorAgents).join(", ")}`);
17296
+ console.log(`[orchestrator] Default agent: ${AGENT_NAMES.COMMANDER}`);
17150
17297
  };
17151
- const history = progressHistory.get(sessionId) || [];
17152
- history.push(snapshot);
17153
- if (history.length > MAX_HISTORY3) {
17154
- history.shift();
17155
- }
17156
- progressHistory.set(sessionId, history);
17157
- return snapshot;
17158
17298
  }
17159
- function getLatest(sessionId) {
17160
- const history = progressHistory.get(sessionId);
17161
- return history?.[history.length - 1];
17299
+
17300
+ // src/utils/common.ts
17301
+ function detectSlashCommand(text) {
17302
+ const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
17303
+ if (!match) return null;
17304
+ return { command: match[1], args: match[2] || "" };
17162
17305
  }
17163
- function clearSession(sessionId) {
17164
- progressHistory.delete(sessionId);
17165
- sessionStartTimes.delete(sessionId);
17306
+ function formatTimestamp(date5 = /* @__PURE__ */ new Date()) {
17307
+ const pad = (n) => n.toString().padStart(2, "0");
17308
+ return `${date5.getFullYear()}-${pad(date5.getMonth() + 1)}-${pad(date5.getDate())} ${pad(date5.getHours())}:${pad(date5.getMinutes())}:${pad(date5.getSeconds())}`;
17309
+ }
17310
+ function formatElapsedTime(startMs, endMs = Date.now()) {
17311
+ const elapsed = endMs - startMs;
17312
+ if (elapsed < 0) return "0s";
17313
+ const seconds = Math.floor(elapsed / 1e3) % 60;
17314
+ const minutes = Math.floor(elapsed / (1e3 * 60)) % 60;
17315
+ const hours = Math.floor(elapsed / (1e3 * 60 * 60));
17316
+ const parts = [];
17317
+ if (hours > 0) parts.push(`${hours}h`);
17318
+ if (minutes > 0) parts.push(`${minutes}m`);
17319
+ if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);
17320
+ return parts.join(" ");
17321
+ }
17322
+
17323
+ // src/plugin-handlers/chat-message-handler.ts
17324
+ function createChatMessageHandler(ctx) {
17325
+ const { directory, sessions } = ctx;
17326
+ return async (msgInput, msgOutput) => {
17327
+ const parts = msgOutput.parts;
17328
+ const textPartIndex = parts.findIndex((p) => p.type === PART_TYPES.TEXT && p.text);
17329
+ if (textPartIndex === -1) return;
17330
+ const originalText = parts[textPartIndex].text || "";
17331
+ const parsed = detectSlashCommand(originalText);
17332
+ const sessionID = msgInput.sessionID;
17333
+ const agentName = (msgInput.agent || "").toLowerCase();
17334
+ log2("[chat-message-handler] hook triggered", { sessionID, agent: agentName, textLength: originalText.length });
17335
+ if (sessionID) {
17336
+ handleUserMessage(sessionID);
17337
+ }
17338
+ if (agentName === AGENT_NAMES.COMMANDER) {
17339
+ if (!sessions.has(sessionID)) {
17340
+ const now = Date.now();
17341
+ sessions.set(sessionID, {
17342
+ active: true,
17343
+ step: 0,
17344
+ timestamp: now,
17345
+ startTime: now,
17346
+ lastStepTime: now
17347
+ });
17348
+ state.missionActive = true;
17349
+ state.sessions.set(sessionID, {
17350
+ enabled: true,
17351
+ iterations: 0,
17352
+ taskRetries: /* @__PURE__ */ new Map(),
17353
+ currentTask: "",
17354
+ anomalyCount: 0
17355
+ });
17356
+ startSession(sessionID);
17357
+ presets.taskStarted(sessionID, AGENT_NAMES.COMMANDER);
17358
+ }
17359
+ if (!parsed || parsed.command !== "task") {
17360
+ const taskTemplate = COMMANDS["task"].template;
17361
+ const userMessage = parsed?.args || originalText;
17362
+ parts[textPartIndex].text = taskTemplate.replace(
17363
+ /\$ARGUMENTS/g,
17364
+ userMessage || PROMPTS.CONTINUE
17365
+ );
17366
+ startMissionLoop(directory, sessionID, userMessage || originalText);
17367
+ log2("[chat-message-handler] Auto-applied mission mode + started loop", { originalLength: originalText.length });
17368
+ }
17369
+ }
17370
+ if (parsed) {
17371
+ const command = COMMANDS[parsed.command];
17372
+ if (command && agentName !== AGENT_NAMES.COMMANDER) {
17373
+ parts[textPartIndex].text = command.template.replace(
17374
+ /\$ARGUMENTS/g,
17375
+ parsed.args || PROMPTS.CONTINUE
17376
+ );
17377
+ } else if (command && parsed.command === "task") {
17378
+ parts[textPartIndex].text = command.template.replace(
17379
+ /\$ARGUMENTS/g,
17380
+ parsed.args || PROMPTS.CONTINUE
17381
+ );
17382
+ startMissionLoop(directory, sessionID, parsed.args || "continue from where we left off");
17383
+ log2("[chat-message-handler] /task command: started mission loop", { sessionID, args: parsed.args?.slice(0, 50) });
17384
+ }
17385
+ }
17386
+ };
17387
+ }
17388
+
17389
+ // src/utils/sanity.ts
17390
+ var SEVERITY = {
17391
+ OK: "ok",
17392
+ WARNING: "warning",
17393
+ CRITICAL: "critical"
17394
+ };
17395
+ function checkOutputSanity(text) {
17396
+ if (!text || text.length < 50) {
17397
+ return { isHealthy: true, severity: SEVERITY.OK };
17398
+ }
17399
+ if (/(.)\1{15,}/.test(text)) {
17400
+ return {
17401
+ isHealthy: false,
17402
+ reason: "Single character repetition detected",
17403
+ severity: SEVERITY.CRITICAL
17404
+ };
17405
+ }
17406
+ if (/(.{2,6})\1{8,}/.test(text)) {
17407
+ return {
17408
+ isHealthy: false,
17409
+ reason: "Pattern loop detected",
17410
+ severity: SEVERITY.CRITICAL
17411
+ };
17412
+ }
17413
+ if (text.length > 200) {
17414
+ const cleanText = text.replace(/\s/g, "");
17415
+ if (cleanText.length > 100) {
17416
+ const uniqueChars = new Set(cleanText).size;
17417
+ const ratio = uniqueChars / cleanText.length;
17418
+ if (ratio < 0.02) {
17419
+ return {
17420
+ isHealthy: false,
17421
+ reason: "Low information density",
17422
+ severity: SEVERITY.CRITICAL
17423
+ };
17424
+ }
17425
+ }
17426
+ }
17427
+ const boxChars = (text.match(/[\u2500-\u257f\u2580-\u259f\u2800-\u28ff]/g) || []).length;
17428
+ if (boxChars > 100 && boxChars / text.length > 0.3) {
17429
+ return {
17430
+ isHealthy: false,
17431
+ reason: "Visual gibberish detected",
17432
+ severity: SEVERITY.CRITICAL
17433
+ };
17434
+ }
17435
+ const lines = text.split("\n").filter((l) => l.trim().length > 10);
17436
+ if (lines.length > 10) {
17437
+ const lineSet = new Set(lines);
17438
+ if (lineSet.size < lines.length * 0.2) {
17439
+ return {
17440
+ isHealthy: false,
17441
+ reason: "Excessive line repetition",
17442
+ severity: SEVERITY.WARNING
17443
+ };
17444
+ }
17445
+ }
17446
+ const cjkChars = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
17447
+ if (cjkChars > 200) {
17448
+ const uniqueCjk = new Set(
17449
+ text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []
17450
+ ).size;
17451
+ if (uniqueCjk < 10 && cjkChars / uniqueCjk > 20) {
17452
+ return {
17453
+ isHealthy: false,
17454
+ reason: "CJK character spam detected",
17455
+ severity: SEVERITY.CRITICAL
17456
+ };
17457
+ }
17458
+ }
17459
+ return { isHealthy: true, severity: SEVERITY.OK };
17166
17460
  }
17461
+ var RECOVERY_PROMPT = `<anomaly_recovery>
17462
+ \u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).
17463
+
17464
+ <recovery_protocol>
17465
+ 1. DISCARD the corrupted output completely - do not reference it
17466
+ 2. RECALL the original mission objective
17467
+ 3. IDENTIFY the last confirmed successful step
17468
+ 4. RESTART with a simpler, more focused approach
17469
+ </recovery_protocol>
17470
+
17471
+ <instructions>
17472
+ - If a sub-agent produced bad output: try a different agent or simpler task
17473
+ - If stuck in a loop: break down the task into smaller pieces
17474
+ - If context seems corrupted: call Reviewer to restore context
17475
+ - THINK in English for maximum stability
17476
+ </instructions>
17477
+
17478
+ What was the original task? Proceed from the last known good state.
17479
+ </anomaly_recovery>`;
17480
+ var ESCALATION_PROMPT = `<critical_anomaly>
17481
+ \u{1F6A8} CRITICAL: Multiple consecutive malformed outputs detected.
17482
+
17483
+ <emergency_protocol>
17484
+ 1. STOP current execution path immediately
17485
+ 2. DO NOT continue with the same approach - it is failing
17486
+ 3. CALL Planner for a completely new strategy
17487
+ 4. If Planner also fails: report status to user and await guidance
17488
+ </emergency_protocol>
17167
17489
 
17168
- // src/core/progress/formatters.ts
17169
- function formatElapsed(ms) {
17170
- const seconds = Math.floor(ms / 1e3);
17171
- const minutes = Math.floor(seconds / 60);
17172
- const hours = Math.floor(minutes / 60);
17173
- if (hours > 0) {
17174
- return `${hours}h ${minutes % 60}m`;
17175
- } else if (minutes > 0) {
17176
- return `${minutes}m ${seconds % 60}s`;
17177
- } else {
17178
- return `${seconds}s`;
17179
- }
17180
- }
17181
- function formatCompact(snapshot) {
17182
- const parts = [];
17183
- if (snapshot.todos.total > 0) {
17184
- parts.push(`\u2705${snapshot.todos.completed}/${snapshot.todos.total}`);
17185
- }
17186
- if (snapshot.tasks.running > 0) {
17187
- parts.push(`\u26A1${snapshot.tasks.running}`);
17188
- }
17189
- parts.push(`\u23F1${formatElapsed(snapshot.elapsedMs)}`);
17190
- return parts.join(" | ");
17191
- }
17490
+ <diagnosis>
17491
+ The current approach is producing corrupted output.
17492
+ This may indicate: context overload, model instability, or task complexity.
17493
+ </diagnosis>
17192
17494
 
17193
- // src/core/progress/tracker.ts
17194
- function formatCompact2(sessionId) {
17195
- const snapshot = getLatest(sessionId);
17196
- if (!snapshot) return "...";
17197
- return formatCompact(snapshot);
17495
+ Request a fresh plan from Planner with reduced scope.
17496
+ </critical_anomaly>`;
17497
+
17498
+ // src/plugin-handlers/tool-execute-handler.ts
17499
+ function createToolExecuteAfterHandler(ctx) {
17500
+ const { sessions } = ctx;
17501
+ return async (toolInput, toolOutput) => {
17502
+ const session = sessions.get(toolInput.sessionID);
17503
+ if (!session?.active) return;
17504
+ const now = Date.now();
17505
+ const stepDuration = formatElapsedTime(session.lastStepTime, now);
17506
+ const totalElapsed = formatElapsedTime(session.startTime, now);
17507
+ session.step++;
17508
+ session.timestamp = now;
17509
+ session.lastStepTime = now;
17510
+ const stateSession = state.sessions.get(toolInput.sessionID);
17511
+ if (toolInput.tool === TOOL_NAMES.CALL_AGENT && stateSession) {
17512
+ const sanityResult = checkOutputSanity(toolOutput.output);
17513
+ if (!sanityResult.isHealthy) {
17514
+ stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
17515
+ const agentName = toolInput.arguments?.agent || "unknown";
17516
+ toolOutput.output = `\u26A0\uFE0F [${agentName.toUpperCase()}] OUTPUT ANOMALY DETECTED
17517
+
17518
+ \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
17519
+ \u26A0\uFE0F Gibberish/loop detected: ${sanityResult.reason}
17520
+ Anomaly count: ${stateSession.anomalyCount}
17521
+ \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
17522
+
17523
+ ` + (stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT);
17524
+ return;
17525
+ } else {
17526
+ if (stateSession.anomalyCount > 0) {
17527
+ stateSession.anomalyCount = 0;
17528
+ }
17529
+ if (toolOutput.output.length < 5e3) {
17530
+ stateSession.lastHealthyOutput = toolOutput.output.substring(0, 1e3);
17531
+ }
17532
+ }
17533
+ }
17534
+ if (toolInput.tool === TOOL_NAMES.CALL_AGENT && toolInput.arguments?.task && stateSession) {
17535
+ const taskIdMatch = toolInput.arguments.task.match(/\[(TASK-\d+)\]/i);
17536
+ if (taskIdMatch) {
17537
+ stateSession.currentTask = taskIdMatch[1].toUpperCase();
17538
+ }
17539
+ const agentName = toolInput.arguments.agent;
17540
+ const emoji3 = AGENT_EMOJI[agentName] || "\u{1F916}";
17541
+ toolOutput.output = `${emoji3} [${agentName.toUpperCase()}] Working...
17542
+
17543
+ ` + toolOutput.output;
17544
+ }
17545
+ if (stateSession) {
17546
+ const taskId = stateSession.currentTask;
17547
+ if (toolOutput.output.includes("\u2705 PASS") || toolOutput.output.includes("AUDIT RESULT: PASS")) {
17548
+ if (taskId) {
17549
+ stateSession.taskRetries.clear();
17550
+ toolOutput.output += `
17551
+
17552
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17553
+ \u2705 ${taskId} VERIFIED`;
17554
+ }
17555
+ } else if (toolOutput.output.includes("\u274C FAIL") || toolOutput.output.includes("AUDIT RESULT: FAIL")) {
17556
+ if (taskId) {
17557
+ const retries = (stateSession.taskRetries.get(taskId) || 0) + 1;
17558
+ stateSession.taskRetries.set(taskId, retries);
17559
+ if (retries >= state.maxRetries) {
17560
+ toolOutput.output += `
17561
+
17562
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17563
+ \u26A0\uFE0F ${taskId} FAILED (${retries}x)`;
17564
+ } else {
17565
+ toolOutput.output += `
17566
+
17567
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17568
+ \u{1F504} RETRY ${retries}/${state.maxRetries}`;
17569
+ }
17570
+ }
17571
+ }
17572
+ }
17573
+ const currentTime = formatTimestamp();
17574
+ toolOutput.output += `
17575
+
17576
+ \u23F1\uFE0F [${currentTime}] Step ${session.step} | This step: ${stepDuration} | Total: ${totalElapsed}`;
17577
+ };
17198
17578
  }
17199
17579
 
17200
- // src/index.ts
17201
- var require2 = createRequire(import.meta.url);
17202
- var { version: PLUGIN_VERSION } = require2("../package.json");
17203
- var UNLIMITED_MODE = true;
17204
- var DEFAULT_MAX_STEPS = UNLIMITED_MODE ? Infinity : 500;
17580
+ // src/plugin-handlers/assistant-done-handler.ts
17205
17581
  var CONTINUE_INSTRUCTION = `<auto_continue>
17206
17582
  <status>Mission not complete. Keep executing.</status>
17207
17583
 
@@ -17229,6 +17605,120 @@ You are ONLY done when:
17229
17605
  Then output: ${MISSION_SEAL.PATTERN}
17230
17606
  </completion_criteria>
17231
17607
  </auto_continue>`;
17608
+ function createAssistantDoneHandler(ctx) {
17609
+ const { client, directory, sessions } = ctx;
17610
+ return async (assistantInput, assistantOutput) => {
17611
+ const sessionID = assistantInput.sessionID;
17612
+ const session = sessions.get(sessionID);
17613
+ if (!session?.active) return;
17614
+ const parts = assistantOutput.parts;
17615
+ const textContent = parts?.filter((p) => p.type === PART_TYPES.TEXT || p.type === PART_TYPES.REASONING).map((p) => p.text || "").join("\n") || "";
17616
+ const stateSession = state.sessions.get(sessionID);
17617
+ const sanityResult = checkOutputSanity(textContent);
17618
+ if (!sanityResult.isHealthy && stateSession) {
17619
+ stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
17620
+ session.step++;
17621
+ session.timestamp = Date.now();
17622
+ const recoveryText = stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT;
17623
+ try {
17624
+ if (client?.session?.prompt) {
17625
+ await client.session.prompt({
17626
+ path: { id: sessionID },
17627
+ body: {
17628
+ parts: [{
17629
+ type: PART_TYPES.TEXT,
17630
+ text: `\u26A0\uFE0F ANOMALY #${stateSession.anomalyCount}: ${sanityResult.reason}
17631
+
17632
+ ` + recoveryText + `
17633
+
17634
+ [Recovery Step ${session.step}]`
17635
+ }]
17636
+ }
17637
+ });
17638
+ }
17639
+ } catch {
17640
+ session.active = false;
17641
+ state.missionActive = false;
17642
+ }
17643
+ return;
17644
+ }
17645
+ if (stateSession && stateSession.anomalyCount > 0) {
17646
+ stateSession.anomalyCount = 0;
17647
+ }
17648
+ if (isLoopActive(directory, sessionID) && detectSealInText(textContent)) {
17649
+ session.active = false;
17650
+ state.missionActive = false;
17651
+ clearLoopState(directory);
17652
+ presets.missionComplete("\u{1F396}\uFE0F Mission Sealed - Explicit completion confirmed");
17653
+ log2("[assistant-done-handler] Mission sealed detected", { sessionID });
17654
+ clearSession(sessionID);
17655
+ sessions.delete(sessionID);
17656
+ state.sessions.delete(sessionID);
17657
+ return;
17658
+ }
17659
+ if (textContent.includes(MISSION.STOP_COMMAND) || textContent.includes(MISSION.CANCEL_COMMAND)) {
17660
+ session.active = false;
17661
+ state.missionActive = false;
17662
+ presets.taskFailed(sessionID, "Cancelled by user");
17663
+ clearSession(sessionID);
17664
+ sessions.delete(sessionID);
17665
+ state.sessions.delete(sessionID);
17666
+ return;
17667
+ }
17668
+ const now = Date.now();
17669
+ const stepDuration = formatElapsedTime(session.lastStepTime, now);
17670
+ const totalElapsed = formatElapsedTime(session.startTime, now);
17671
+ session.step++;
17672
+ session.timestamp = now;
17673
+ session.lastStepTime = now;
17674
+ const currentTime = formatTimestamp();
17675
+ recordSnapshot(sessionID, {
17676
+ currentStep: session.step
17677
+ });
17678
+ const progressInfo = formatCompact2(sessionID);
17679
+ try {
17680
+ if (client?.session?.prompt) {
17681
+ await client.session.prompt({
17682
+ path: { id: sessionID },
17683
+ body: {
17684
+ parts: [{
17685
+ type: PART_TYPES.TEXT,
17686
+ text: CONTINUE_INSTRUCTION + `
17687
+
17688
+ \u23F1\uFE0F [${currentTime}] Step ${session.step} | ${progressInfo} | This step: ${stepDuration} | Total: ${totalElapsed}`
17689
+ }]
17690
+ }
17691
+ });
17692
+ }
17693
+ } catch (error45) {
17694
+ log2("[assistant-done-handler] Continuation injection failed, retrying...", { sessionID, error: error45 });
17695
+ try {
17696
+ await new Promise((r) => setTimeout(r, 500));
17697
+ if (client?.session?.prompt) {
17698
+ await client.session.prompt({
17699
+ path: { id: sessionID },
17700
+ body: { parts: [{ type: PART_TYPES.TEXT, text: PROMPTS.CONTINUE }] }
17701
+ });
17702
+ }
17703
+ } catch (retryError) {
17704
+ log2("[assistant-done-handler] Both continuation attempts failed, waiting for idle handler", {
17705
+ sessionID,
17706
+ error: retryError,
17707
+ loopActive: isLoopActive(directory, sessionID)
17708
+ });
17709
+ if (!isLoopActive(directory, sessionID)) {
17710
+ log2("[assistant-done-handler] No active loop, stopping session", { sessionID });
17711
+ session.active = false;
17712
+ state.missionActive = false;
17713
+ }
17714
+ }
17715
+ }
17716
+ };
17717
+ }
17718
+
17719
+ // src/index.ts
17720
+ var require2 = createRequire(import.meta.url);
17721
+ var { version: PLUGIN_VERSION } = require2("../package.json");
17232
17722
  var OrchestratorPlugin = async (input) => {
17233
17723
  const { directory, client } = input;
17234
17724
  console.log(`[orchestrator] v${PLUGIN_VERSION} loaded`);
@@ -17242,6 +17732,12 @@ var OrchestratorPlugin = async (input) => {
17242
17732
  const asyncAgentTools = createAsyncAgentTools(parallelAgentManager2, client);
17243
17733
  taskToastManager.setConcurrencyController(parallelAgentManager2.getConcurrency());
17244
17734
  log2("[index.ts] ParallelAgentManager initialized with TaskToastManager integration");
17735
+ const handlerContext = {
17736
+ client,
17737
+ directory,
17738
+ sessions,
17739
+ state
17740
+ };
17245
17741
  return {
17246
17742
  // -----------------------------------------------------------------
17247
17743
  // Tools we expose to the LLM
@@ -17252,443 +17748,39 @@ var OrchestratorPlugin = async (input) => {
17252
17748
  [TOOL_NAMES.GREP_SEARCH]: grepSearchTool(directory),
17253
17749
  [TOOL_NAMES.GLOB_SEARCH]: globSearchTool(directory),
17254
17750
  [TOOL_NAMES.MGREP]: mgrepTool(directory),
17255
- // Multi-pattern grep (parallel, Rust-powered)
17256
- // Background task tools - run shell commands asynchronously
17751
+ // Background task tools
17257
17752
  [TOOL_NAMES.RUN_BACKGROUND]: runBackgroundTool,
17258
17753
  [TOOL_NAMES.CHECK_BACKGROUND]: checkBackgroundTool,
17259
17754
  [TOOL_NAMES.LIST_BACKGROUND]: listBackgroundTool,
17260
17755
  [TOOL_NAMES.KILL_BACKGROUND]: killBackgroundTool,
17261
- // Web tools - documentation research and caching
17756
+ // Web tools
17262
17757
  [TOOL_NAMES.WEBFETCH]: webfetchTool,
17263
17758
  [TOOL_NAMES.WEBSEARCH]: websearchTool,
17264
17759
  [TOOL_NAMES.CACHE_DOCS]: cacheDocsTool,
17265
17760
  [TOOL_NAMES.CODESEARCH]: codesearchTool,
17266
- // Async agent tools - spawn agents in parallel sessions
17761
+ // Async agent tools
17267
17762
  ...asyncAgentTools
17268
17763
  },
17269
17764
  // -----------------------------------------------------------------
17270
17765
  // Config hook - registers our commands and agents with OpenCode
17271
17766
  // -----------------------------------------------------------------
17272
- config: async (config2) => {
17273
- const existingCommands = config2.command ?? {};
17274
- const existingAgents = config2.agent ?? {};
17275
- const orchestratorCommands = {};
17276
- for (const [name, cmd] of Object.entries(COMMANDS)) {
17277
- orchestratorCommands[name] = {
17278
- description: cmd.description,
17279
- template: cmd.template,
17280
- argumentHint: cmd.argumentHint
17281
- };
17282
- }
17283
- const commanderPrompt = AGENTS[AGENT_NAMES.COMMANDER]?.systemPrompt || "";
17284
- console.log(`[orchestrator] Commander prompt length: ${commanderPrompt.length} chars`);
17285
- const orchestratorAgents = {
17286
- // Primary agent - the main orchestrator
17287
- [AGENT_NAMES.COMMANDER]: {
17288
- description: "Autonomous orchestrator - executes until mission complete",
17289
- mode: "primary",
17290
- prompt: commanderPrompt,
17291
- maxTokens: 64e3,
17292
- thinking: { type: "enabled", budgetTokens: 32e3 },
17293
- color: "#FF6B6B"
17294
- },
17295
- // Consolidated subagents (4 agents instead of 6)
17296
- [AGENT_NAMES.PLANNER]: {
17297
- description: "Strategic planning and research specialist",
17298
- mode: "subagent",
17299
- hidden: true,
17300
- prompt: AGENTS[AGENT_NAMES.PLANNER]?.systemPrompt || "",
17301
- maxTokens: 32e3,
17302
- color: "#9B59B6"
17303
- },
17304
- [AGENT_NAMES.WORKER]: {
17305
- description: "Implementation and documentation specialist",
17306
- mode: "subagent",
17307
- hidden: true,
17308
- prompt: AGENTS[AGENT_NAMES.WORKER]?.systemPrompt || "",
17309
- maxTokens: 32e3,
17310
- color: "#E67E22"
17311
- },
17312
- [AGENT_NAMES.REVIEWER]: {
17313
- description: "Verification and context management specialist",
17314
- mode: "subagent",
17315
- hidden: true,
17316
- prompt: AGENTS[AGENT_NAMES.REVIEWER]?.systemPrompt || "",
17317
- maxTokens: 32e3,
17318
- color: "#27AE60"
17319
- }
17320
- };
17321
- const processedExistingAgents = { ...existingAgents };
17322
- if (processedExistingAgents.build) {
17323
- processedExistingAgents.build = {
17324
- ...processedExistingAgents.build,
17325
- mode: "subagent",
17326
- hidden: true
17327
- };
17328
- }
17329
- if (processedExistingAgents.plan) {
17330
- processedExistingAgents.plan = {
17331
- ...processedExistingAgents.plan,
17332
- mode: "subagent"
17333
- };
17334
- }
17335
- config2.command = { ...existingCommands, ...orchestratorCommands };
17336
- config2.agent = { ...processedExistingAgents, ...orchestratorAgents };
17337
- config2.default_agent = AGENT_NAMES.COMMANDER;
17338
- console.log(`[orchestrator] Registered agents: ${Object.keys(orchestratorAgents).join(", ")}`);
17339
- console.log(`[orchestrator] Default agent: ${AGENT_NAMES.COMMANDER}`);
17340
- },
17767
+ config: createConfigHandler(),
17341
17768
  // -----------------------------------------------------------------
17342
- // Event hook - handles OpenCode events (SDK official)
17343
- // Replaces non-standard session.start/session.end hooks
17769
+ // Event hook - handles OpenCode events
17344
17770
  // -----------------------------------------------------------------
17345
- event: async (input2) => {
17346
- const { event } = input2;
17347
- try {
17348
- const manager = ParallelAgentManager.getInstance();
17349
- manager.handleEvent(event);
17350
- } catch {
17351
- }
17352
- if (event.type === "session.created") {
17353
- const sessionID = event.properties?.id || "";
17354
- log2("[index.ts] event: session.created", { sessionID });
17355
- presets.missionStarted(`Session ${sessionID.slice(0, 12)}...`);
17356
- }
17357
- if (event.type === "session.deleted") {
17358
- const sessionID = event.properties?.id || event.properties?.info?.id || "";
17359
- const session = sessions.get(sessionID);
17360
- if (session) {
17361
- const totalTime = Date.now() - session.startTime;
17362
- const duration3 = totalTime < 6e4 ? `${Math.round(totalTime / 1e3)}s` : `${Math.round(totalTime / 6e4)}m`;
17363
- log2("[index.ts] event: session.deleted", {
17364
- sessionID,
17365
- steps: session.step,
17366
- duration: duration3
17367
- });
17368
- sessions.delete(sessionID);
17369
- state.sessions.delete(sessionID);
17370
- clearSession(sessionID);
17371
- cleanupSessionRecovery(sessionID);
17372
- cleanupSession(sessionID);
17373
- presets.sessionCompleted(sessionID, duration3);
17374
- }
17375
- }
17376
- if (event.type === "session.error") {
17377
- const sessionID = event.properties?.sessionId || event.properties?.sessionID || "";
17378
- const error45 = event.properties?.error;
17379
- log2("[index.ts] event: session.error", { sessionID, error: error45 });
17380
- if (sessionID && error45) {
17381
- const recovered = await handleSessionError(
17382
- client,
17383
- sessionID,
17384
- error45,
17385
- event.properties
17386
- );
17387
- if (recovered) {
17388
- log2("[index.ts] session.error: auto-recovery initiated", { sessionID });
17389
- return;
17390
- }
17391
- }
17392
- presets.taskFailed("session", String(error45).slice(0, 50));
17393
- }
17394
- if (event.type === "message.updated") {
17395
- const messageInfo = event.properties?.info;
17396
- const sessionID = messageInfo?.sessionID;
17397
- const role = messageInfo?.role;
17398
- if (sessionID && role === "assistant") {
17399
- markRecoveryComplete(sessionID);
17400
- }
17401
- }
17402
- if (event.type === "session.idle") {
17403
- const sessionID = event.properties?.sessionID || "";
17404
- if (sessionID) {
17405
- const isMainSession = sessions.has(sessionID);
17406
- if (isMainSession) {
17407
- setTimeout(async () => {
17408
- const session = sessions.get(sessionID);
17409
- if (session?.active) {
17410
- if (isLoopActive(directory, sessionID)) {
17411
- await handleMissionSealIdle(
17412
- client,
17413
- directory,
17414
- sessionID,
17415
- sessionID
17416
- ).catch((err) => {
17417
- log2("[index.ts] mission-seal-handler error", err);
17418
- });
17419
- } else {
17420
- await handleSessionIdle(
17421
- client,
17422
- sessionID,
17423
- sessionID
17424
- ).catch((err) => {
17425
- log2("[index.ts] todo-continuation error", err);
17426
- });
17427
- }
17428
- }
17429
- }, 500);
17430
- }
17431
- }
17432
- }
17433
- },
17771
+ event: createEventHandler(handlerContext),
17434
17772
  // -----------------------------------------------------------------
17435
- // chat.message hook - runs when user sends a message
17436
- // This is where we intercept commands and set up sessions
17773
+ // chat.message hook - intercepts commands and sets up sessions
17437
17774
  // -----------------------------------------------------------------
17438
- "chat.message": async (msgInput, msgOutput) => {
17439
- const parts = msgOutput.parts;
17440
- const textPartIndex = parts.findIndex((p) => p.type === PART_TYPES.TEXT && p.text);
17441
- if (textPartIndex === -1) return;
17442
- const originalText = parts[textPartIndex].text || "";
17443
- const parsed = detectSlashCommand(originalText);
17444
- const sessionID = msgInput.sessionID;
17445
- const agentName = (msgInput.agent || "").toLowerCase();
17446
- log2("[index.ts] chat.message hook", { sessionID, agent: agentName, textLength: originalText.length });
17447
- if (sessionID) {
17448
- handleUserMessage(sessionID);
17449
- }
17450
- if (agentName === AGENT_NAMES.COMMANDER) {
17451
- if (!sessions.has(sessionID)) {
17452
- const now = Date.now();
17453
- sessions.set(sessionID, {
17454
- active: true,
17455
- step: 0,
17456
- maxSteps: DEFAULT_MAX_STEPS,
17457
- timestamp: now,
17458
- startTime: now,
17459
- lastStepTime: now
17460
- });
17461
- state.missionActive = true;
17462
- state.sessions.set(sessionID, {
17463
- enabled: true,
17464
- iterations: 0,
17465
- taskRetries: /* @__PURE__ */ new Map(),
17466
- currentTask: "",
17467
- anomalyCount: 0
17468
- });
17469
- startSession(sessionID);
17470
- presets.taskStarted(sessionID, AGENT_NAMES.COMMANDER);
17471
- }
17472
- if (!parsed || parsed.command !== "task") {
17473
- const taskTemplate = COMMANDS["task"].template;
17474
- const userMessage = parsed?.args || originalText;
17475
- parts[textPartIndex].text = taskTemplate.replace(
17476
- /\$ARGUMENTS/g,
17477
- userMessage || PROMPTS.CONTINUE
17478
- );
17479
- log2("[index.ts] Auto-applied mission mode", { originalLength: originalText.length });
17480
- }
17481
- }
17482
- if (parsed) {
17483
- const command = COMMANDS[parsed.command];
17484
- if (command && agentName !== AGENT_NAMES.COMMANDER) {
17485
- parts[textPartIndex].text = command.template.replace(
17486
- /\$ARGUMENTS/g,
17487
- parsed.args || PROMPTS.CONTINUE
17488
- );
17489
- } else if (command && parsed.command === "task") {
17490
- parts[textPartIndex].text = command.template.replace(
17491
- /\$ARGUMENTS/g,
17492
- parsed.args || PROMPTS.CONTINUE
17493
- );
17494
- }
17495
- }
17496
- },
17775
+ "chat.message": createChatMessageHandler(handlerContext),
17497
17776
  // -----------------------------------------------------------------
17498
17777
  // tool.execute.after hook - runs after any tool call completes
17499
- // We use this to track progress and detect problems
17500
17778
  // -----------------------------------------------------------------
17501
- "tool.execute.after": async (toolInput, toolOutput) => {
17502
- const session = sessions.get(toolInput.sessionID);
17503
- if (!session?.active) return;
17504
- const now = Date.now();
17505
- const stepDuration = formatElapsedTime(session.lastStepTime, now);
17506
- const totalElapsed = formatElapsedTime(session.startTime, now);
17507
- session.step++;
17508
- session.timestamp = now;
17509
- session.lastStepTime = now;
17510
- const stateSession = state.sessions.get(toolInput.sessionID);
17511
- if (toolInput.tool === TOOL_NAMES.CALL_AGENT && stateSession) {
17512
- const sanityResult = checkOutputSanity(toolOutput.output);
17513
- if (!sanityResult.isHealthy) {
17514
- stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
17515
- const agentName = toolInput.arguments?.agent || "unknown";
17516
- toolOutput.output = `\u26A0\uFE0F [${agentName.toUpperCase()}] OUTPUT ANOMALY DETECTED
17517
-
17518
- \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
17519
- \u26A0\uFE0F Gibberish/loop detected: ${sanityResult.reason}
17520
- Anomaly count: ${stateSession.anomalyCount}
17521
- \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
17522
-
17523
- ` + (stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT);
17524
- return;
17525
- } else {
17526
- if (stateSession.anomalyCount > 0) {
17527
- stateSession.anomalyCount = 0;
17528
- }
17529
- if (toolOutput.output.length < 5e3) {
17530
- stateSession.lastHealthyOutput = toolOutput.output.substring(0, 1e3);
17531
- }
17532
- }
17533
- }
17534
- if (toolInput.tool === TOOL_NAMES.CALL_AGENT && toolInput.arguments?.task && stateSession) {
17535
- const taskIdMatch = toolInput.arguments.task.match(/\[(TASK-\d+)\]/i);
17536
- if (taskIdMatch) {
17537
- stateSession.currentTask = taskIdMatch[1].toUpperCase();
17538
- }
17539
- const agentName = toolInput.arguments.agent;
17540
- const emoji3 = AGENT_EMOJI[agentName] || "\u{1F916}";
17541
- toolOutput.output = `${emoji3} [${agentName.toUpperCase()}] Working...
17542
-
17543
- ` + toolOutput.output;
17544
- }
17545
- if (session.step >= session.maxSteps) {
17546
- session.active = false;
17547
- state.missionActive = false;
17548
- return;
17549
- }
17550
- if (stateSession) {
17551
- const taskId = stateSession.currentTask;
17552
- if (toolOutput.output.includes("\u2705 PASS") || toolOutput.output.includes("AUDIT RESULT: PASS")) {
17553
- if (taskId) {
17554
- stateSession.taskRetries.clear();
17555
- toolOutput.output += `
17556
-
17557
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17558
- \u2705 ${taskId} VERIFIED`;
17559
- }
17560
- } else if (toolOutput.output.includes("\u274C FAIL") || toolOutput.output.includes("AUDIT RESULT: FAIL")) {
17561
- if (taskId) {
17562
- const retries = (stateSession.taskRetries.get(taskId) || 0) + 1;
17563
- stateSession.taskRetries.set(taskId, retries);
17564
- if (retries >= state.maxRetries) {
17565
- toolOutput.output += `
17566
-
17567
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17568
- \u26A0\uFE0F ${taskId} FAILED (${retries}x)`;
17569
- } else {
17570
- toolOutput.output += `
17571
-
17572
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
17573
- \u{1F504} RETRY ${retries}/${state.maxRetries}`;
17574
- }
17575
- }
17576
- }
17577
- }
17578
- const currentTime = formatTimestamp();
17579
- toolOutput.output += `
17580
-
17581
- \u23F1\uFE0F [${currentTime}] Step ${session.step}/${session.maxSteps} | This step: ${stepDuration} | Total: ${totalElapsed}`;
17582
- },
17779
+ "tool.execute.after": createToolExecuteAfterHandler(handlerContext),
17583
17780
  // -----------------------------------------------------------------
17584
17781
  // assistant.done hook - runs when the LLM finishes responding
17585
- // This is the heart of the "relentless loop" - we keep pushing it
17586
- // to continue until we see <mission_seal>SEALED</mission_seal> or hit the limit
17587
17782
  // -----------------------------------------------------------------
17588
- "assistant.done": async (assistantInput, assistantOutput) => {
17589
- const sessionID = assistantInput.sessionID;
17590
- const session = sessions.get(sessionID);
17591
- if (!session?.active) return;
17592
- const parts = assistantOutput.parts;
17593
- const textContent = parts?.filter((p) => p.type === PART_TYPES.TEXT || p.type === PART_TYPES.REASONING).map((p) => p.text || "").join("\n") || "";
17594
- const stateSession = state.sessions.get(sessionID);
17595
- const sanityResult = checkOutputSanity(textContent);
17596
- if (!sanityResult.isHealthy && stateSession) {
17597
- stateSession.anomalyCount = (stateSession.anomalyCount || 0) + 1;
17598
- session.step++;
17599
- session.timestamp = Date.now();
17600
- const recoveryText = stateSession.anomalyCount >= 2 ? ESCALATION_PROMPT : RECOVERY_PROMPT;
17601
- try {
17602
- if (client?.session?.prompt) {
17603
- await client.session.prompt({
17604
- path: { id: sessionID },
17605
- body: {
17606
- parts: [{
17607
- type: PART_TYPES.TEXT,
17608
- text: `\u26A0\uFE0F ANOMALY #${stateSession.anomalyCount}: ${sanityResult.reason}
17609
-
17610
- ` + recoveryText + `
17611
-
17612
- [Recovery Step ${session.step}/${session.maxSteps}]`
17613
- }]
17614
- }
17615
- });
17616
- }
17617
- } catch {
17618
- session.active = false;
17619
- state.missionActive = false;
17620
- }
17621
- return;
17622
- }
17623
- if (stateSession && stateSession.anomalyCount > 0) {
17624
- stateSession.anomalyCount = 0;
17625
- }
17626
- if (detectSealInText(textContent)) {
17627
- session.active = false;
17628
- state.missionActive = false;
17629
- clearLoopState(directory);
17630
- presets.missionComplete("\u{1F396}\uFE0F Mission Sealed - Explicit completion confirmed");
17631
- log2("[index.ts] Mission sealed detected", { sessionID });
17632
- clearSession(sessionID);
17633
- sessions.delete(sessionID);
17634
- state.sessions.delete(sessionID);
17635
- return;
17636
- }
17637
- if (textContent.includes(MISSION.STOP_COMMAND) || textContent.includes(MISSION.CANCEL_COMMAND)) {
17638
- session.active = false;
17639
- state.missionActive = false;
17640
- presets.taskFailed(sessionID, "Cancelled by user");
17641
- clearSession(sessionID);
17642
- sessions.delete(sessionID);
17643
- state.sessions.delete(sessionID);
17644
- return;
17645
- }
17646
- const now = Date.now();
17647
- const stepDuration = formatElapsedTime(session.lastStepTime, now);
17648
- const totalElapsed = formatElapsedTime(session.startTime, now);
17649
- session.step++;
17650
- session.timestamp = now;
17651
- session.lastStepTime = now;
17652
- const currentTime = formatTimestamp();
17653
- if (session.step >= session.maxSteps) {
17654
- session.active = false;
17655
- state.missionActive = false;
17656
- return;
17657
- }
17658
- recordSnapshot(sessionID, {
17659
- currentStep: session.step,
17660
- maxSteps: session.maxSteps
17661
- });
17662
- const progressInfo = formatCompact2(sessionID);
17663
- try {
17664
- if (client?.session?.prompt) {
17665
- await client.session.prompt({
17666
- path: { id: sessionID },
17667
- body: {
17668
- parts: [{
17669
- type: PART_TYPES.TEXT,
17670
- text: CONTINUE_INSTRUCTION + `
17671
-
17672
- \u23F1\uFE0F [${currentTime}] Step ${session.step}/${session.maxSteps} | ${progressInfo} | This step: ${stepDuration} | Total: ${totalElapsed}`
17673
- }]
17674
- }
17675
- });
17676
- }
17677
- } catch {
17678
- try {
17679
- await new Promise((r) => setTimeout(r, 500));
17680
- if (client?.session?.prompt) {
17681
- await client.session.prompt({
17682
- path: { id: sessionID },
17683
- body: { parts: [{ type: PART_TYPES.TEXT, text: PROMPTS.CONTINUE }] }
17684
- });
17685
- }
17686
- } catch {
17687
- session.active = false;
17688
- state.missionActive = false;
17689
- }
17690
- }
17691
- }
17783
+ "assistant.done": createAssistantDoneHandler(handlerContext)
17692
17784
  };
17693
17785
  };
17694
17786
  var index_default = OrchestratorPlugin;