opencode-orchestrator 0.7.0 β 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ Restart OpenCode after installation.
|
|
|
109
109
|
|
|
110
110
|
| Mode | Trigger | Behavior |
|
|
111
111
|
|------|---------|----------|
|
|
112
|
-
| **Commander Mode** π― | `/task "mission"` | Full autonomous execution until
|
|
112
|
+
| **Commander Mode** π― | `/task "mission"` | Full autonomous execution until sealed |
|
|
113
113
|
| **Chat Mode** π¬ | Regular conversation | Simple Q&A, no autonomous behavior |
|
|
114
114
|
|
|
115
115
|
---
|
|
@@ -125,15 +125,32 @@ Use `/task` when you need the AI to **complete a mission autonomously**:
|
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
**What Commander Mode Does:**
|
|
128
|
-
- βΎοΈ **Runs until
|
|
128
|
+
- βΎοΈ **Runs until sealed** β Loops until agent outputs `<mission_seal>SEALED</mission_seal>`
|
|
129
129
|
- π§ **Anti-Hallucination** β Researches docs before coding
|
|
130
130
|
- β‘ **Parallel Execution** β Up to 50 concurrent agents
|
|
131
131
|
- π **Auto-Recovery** β Handles errors automatically
|
|
132
|
-
- π **
|
|
132
|
+
- π **Triage System** β Adapts strategy to complexity (L1/L2/L3)
|
|
133
|
+
|
|
134
|
+
**ποΈ Mission Seal Loop:**
|
|
135
|
+
```
|
|
136
|
+
/task "mission" β Agent works β Idle? β Seal found?
|
|
137
|
+
β β
|
|
138
|
+
β No β Yes
|
|
139
|
+
ββββββββββββββββ΄βββ β
Complete
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
When the agent finishes ALL work, it outputs:
|
|
143
|
+
```xml
|
|
144
|
+
<mission_seal>SEALED</mission_seal>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Control Commands:**
|
|
148
|
+
- `/stop` or `/cancel` β Stop the loop manually
|
|
149
|
+
- Max 20 iterations (configurable)
|
|
133
150
|
|
|
134
151
|
<div align="center">
|
|
135
152
|
<img src="assets/tui_image.png" alt="Commander TUI" width="600" />
|
|
136
|
-
<p><sub><b>/task "mission"</b> triggers full Commander mode</sub></p>
|
|
153
|
+
<p><sub><b>/task "mission"</b> triggers full Commander mode with Mission Seal loop</sub></p>
|
|
137
154
|
</div>
|
|
138
155
|
|
|
139
156
|
---
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AgentDefinition } from "../shared/agent.js";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const commander: AgentDefinition;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mission Seal Handler
|
|
3
|
+
*
|
|
4
|
+
* Integrates Mission Seal detection with session events.
|
|
5
|
+
* When session goes idle, checks for seal and either:
|
|
6
|
+
* - Completes loop if seal detected
|
|
7
|
+
* - Continues with next iteration if seal not detected
|
|
8
|
+
*/
|
|
9
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
10
|
+
type OpencodeClient = PluginInput["client"];
|
|
11
|
+
/**
|
|
12
|
+
* Handle session.idle event for mission seal loop
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleMissionSealIdle(client: OpencodeClient, directory: string, sessionID: string, mainSessionID?: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Handle user message - cancel countdown
|
|
17
|
+
*/
|
|
18
|
+
export declare function handleUserMessage(sessionID: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Handle abort - prevent continuation
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleAbort(sessionID: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Clean up session state
|
|
25
|
+
*/
|
|
26
|
+
export declare function cleanupSession(sessionID: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if there's a pending countdown
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasPendingContinuation(sessionID: string): boolean;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mission Seal - Explicit completion detection system
|
|
3
|
+
*
|
|
4
|
+
* When an agent outputs `<mission_seal>SEALED</mission_seal>`,
|
|
5
|
+
* the task loop knows the mission is truly complete.
|
|
6
|
+
*
|
|
7
|
+
* This prevents false-positive idle detection and ensures
|
|
8
|
+
* agents explicitly confirm task completion.
|
|
9
|
+
*/
|
|
10
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
11
|
+
/** Tag for mission seal detection */
|
|
12
|
+
export declare const MISSION_SEAL_TAG: "mission_seal";
|
|
13
|
+
/** Seal confirmation value */
|
|
14
|
+
export declare const SEAL_CONFIRMATION: "SEALED";
|
|
15
|
+
/** Full seal pattern: <mission_seal>SEALED</mission_seal> */
|
|
16
|
+
export declare const SEAL_PATTERN: "<mission_seal>SEALED</mission_seal>";
|
|
17
|
+
/** Regex for detecting seal in text */
|
|
18
|
+
export declare const SEAL_REGEX: RegExp;
|
|
19
|
+
export interface MissionLoopState {
|
|
20
|
+
/** Whether loop is active */
|
|
21
|
+
active: boolean;
|
|
22
|
+
/** Current iteration number */
|
|
23
|
+
iteration: number;
|
|
24
|
+
/** Maximum allowed iterations */
|
|
25
|
+
maxIterations: number;
|
|
26
|
+
/** Original task prompt */
|
|
27
|
+
prompt: string;
|
|
28
|
+
/** Session ID */
|
|
29
|
+
sessionID: string;
|
|
30
|
+
/** When loop started */
|
|
31
|
+
startedAt: string;
|
|
32
|
+
/** Last activity timestamp */
|
|
33
|
+
lastActivity?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface MissionLoopOptions {
|
|
36
|
+
/** Maximum iterations before stopping (default: 20) */
|
|
37
|
+
maxIterations?: number;
|
|
38
|
+
/** Countdown seconds before auto-continue (default: 3) */
|
|
39
|
+
countdownSeconds?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read loop state from disk
|
|
43
|
+
*/
|
|
44
|
+
export declare function readLoopState(directory: string): MissionLoopState | null;
|
|
45
|
+
/**
|
|
46
|
+
* Write loop state to disk
|
|
47
|
+
*/
|
|
48
|
+
export declare function writeLoopState(directory: string, state: MissionLoopState): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Clear loop state (delete file)
|
|
51
|
+
*/
|
|
52
|
+
export declare function clearLoopState(directory: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Increment iteration counter
|
|
55
|
+
*/
|
|
56
|
+
export declare function incrementIteration(directory: string): MissionLoopState | null;
|
|
57
|
+
/**
|
|
58
|
+
* Check if text contains mission seal
|
|
59
|
+
*/
|
|
60
|
+
export declare function detectSealInText(text: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Check session messages for mission seal
|
|
63
|
+
*/
|
|
64
|
+
export declare function detectSealInSession(client: PluginInput["client"], sessionID: string): Promise<boolean>;
|
|
65
|
+
/**
|
|
66
|
+
* Start a mission loop
|
|
67
|
+
*/
|
|
68
|
+
export declare function startMissionLoop(directory: string, sessionID: string, prompt: string, options?: MissionLoopOptions): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Cancel an active mission loop
|
|
71
|
+
*/
|
|
72
|
+
export declare function cancelMissionLoop(directory: string, sessionID: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Check if loop is active for session
|
|
75
|
+
*/
|
|
76
|
+
export declare function isLoopActive(directory: string, sessionID: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Get remaining iterations
|
|
79
|
+
*/
|
|
80
|
+
export declare function getRemainingIterations(directory: string): number;
|
|
81
|
+
/**
|
|
82
|
+
* Generate continuation prompt for mission loop
|
|
83
|
+
*/
|
|
84
|
+
export declare function generateMissionContinuationPrompt(state: MissionLoopState): string;
|
|
85
|
+
/**
|
|
86
|
+
* Generate completion notification
|
|
87
|
+
*/
|
|
88
|
+
export declare function generateSealedNotification(state: MissionLoopState): string;
|
|
89
|
+
/**
|
|
90
|
+
* Generate max iterations reached notification
|
|
91
|
+
*/
|
|
92
|
+
export declare function generateMaxIterationsNotification(state: MissionLoopState): string;
|
package/dist/index.js
CHANGED
|
@@ -116,16 +116,25 @@ var TOOL_NAMES = {
|
|
|
116
116
|
CALL_AGENT: "call_agent",
|
|
117
117
|
SLASHCOMMAND: "slashcommand"
|
|
118
118
|
};
|
|
119
|
-
var
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
|
|
119
|
+
var MISSION_SEAL = {
|
|
120
|
+
/** XML tag name for mission seal */
|
|
121
|
+
TAG: "mission_seal",
|
|
122
|
+
/** Confirmation value inside tag */
|
|
123
|
+
CONFIRMATION: "SEALED",
|
|
124
|
+
/** Full seal pattern: <mission_seal>SEALED</mission_seal> */
|
|
125
|
+
PATTERN: "<mission_seal>SEALED</mission_seal>",
|
|
126
|
+
/** Default max loop iterations */
|
|
127
|
+
DEFAULT_MAX_ITERATIONS: 20,
|
|
128
|
+
/** Default countdown seconds before continue */
|
|
129
|
+
DEFAULT_COUNTDOWN_SECONDS: 3,
|
|
130
|
+
/** Loop state file name */
|
|
131
|
+
STATE_FILE: "loop-state.json",
|
|
124
132
|
/** Stop command */
|
|
125
133
|
STOP_COMMAND: "/stop",
|
|
126
134
|
/** Cancel command */
|
|
127
135
|
CANCEL_COMMAND: "/cancel"
|
|
128
136
|
};
|
|
137
|
+
var MISSION = MISSION_SEAL;
|
|
129
138
|
var AGENT_EMOJI = {
|
|
130
139
|
Commander: "\u{1F3AF}",
|
|
131
140
|
Planner: "\u{1F4CB}",
|
|
@@ -159,207 +168,217 @@ function getStatusEmoji(status) {
|
|
|
159
168
|
return STATUS_EMOJI[status] ?? "\u2753";
|
|
160
169
|
}
|
|
161
170
|
|
|
162
|
-
// src/agents/
|
|
163
|
-
var
|
|
171
|
+
// src/agents/commander.ts
|
|
172
|
+
var commander = {
|
|
164
173
|
id: AGENT_NAMES.COMMANDER,
|
|
165
|
-
description: "Commander - autonomous orchestrator",
|
|
174
|
+
description: "Commander - autonomous orchestrator with parallel execution",
|
|
166
175
|
systemPrompt: `<role>
|
|
167
|
-
You are Commander.
|
|
176
|
+
You are Commander. Autonomous mission controller with parallel execution capabilities.
|
|
177
|
+
Complete missions efficiently using multiple agents simultaneously. Never stop until done.
|
|
168
178
|
</role>
|
|
169
179
|
|
|
170
|
-
<
|
|
171
|
-
1.
|
|
172
|
-
2.
|
|
173
|
-
3.
|
|
174
|
-
4. THINK before every action
|
|
175
|
-
5.
|
|
176
|
-
</
|
|
177
|
-
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
|
206
|
-
|
|
207
|
-
| \u{1F7E2}
|
|
208
|
-
| \u{1F7E1}
|
|
209
|
-
| \u{1F534}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
180
|
+
<core_principles>
|
|
181
|
+
1. PARALLELISM FIRST: Always run independent tasks simultaneously
|
|
182
|
+
2. NEVER BLOCK: Use background execution for slow operations
|
|
183
|
+
3. NEVER STOP: Loop until "${MISSION_SEAL.PATTERN}"
|
|
184
|
+
4. THINK FIRST: Reason before every action
|
|
185
|
+
5. SESSION REUSE: Resume sessions to preserve context
|
|
186
|
+
</core_principles>
|
|
187
|
+
|
|
188
|
+
<tools_overview>
|
|
189
|
+
| Tool | Purpose | When to Use |
|
|
190
|
+
|------|---------|-------------|
|
|
191
|
+
| ${TOOL_NAMES.DELEGATE_TASK} | Spawn agent | background=true for parallel, false for sync |
|
|
192
|
+
| ${TOOL_NAMES.GET_TASK_RESULT} | Get agent result | After background task completes |
|
|
193
|
+
| ${TOOL_NAMES.LIST_TASKS} | Monitor agents | Check all running agent tasks |
|
|
194
|
+
| ${TOOL_NAMES.CANCEL_TASK} | Stop agent | Cancel stuck or unnecessary tasks |
|
|
195
|
+
| ${TOOL_NAMES.RUN_BACKGROUND} | Run shell cmd | Long builds, tests, installs |
|
|
196
|
+
| ${TOOL_NAMES.CHECK_BACKGROUND} | Get cmd result | Check background command status |
|
|
197
|
+
| ${TOOL_NAMES.LIST_BACKGROUND} | List commands | See all background commands |
|
|
198
|
+
</tools_overview>
|
|
199
|
+
|
|
200
|
+
<phase_0_think>
|
|
201
|
+
\u26A0\uFE0F MANDATORY: Before ANY action, THINK!
|
|
202
|
+
|
|
203
|
+
1. What is the actual goal?
|
|
204
|
+
2. What tasks can run IN PARALLEL?
|
|
205
|
+
3. What needs to be SEQUENTIAL?
|
|
206
|
+
4. Which agents should handle each task?
|
|
207
|
+
5. What can run in BACKGROUND while I continue?
|
|
208
|
+
|
|
209
|
+
Write reasoning before acting. Never skip this.
|
|
210
|
+
</phase_0_think>
|
|
211
|
+
|
|
212
|
+
<phase_1_triage>
|
|
213
|
+
IDENTIFY TASK TYPE:
|
|
214
|
+
|
|
215
|
+
| Type | Signal | Track |
|
|
216
|
+
|------|--------|-------|
|
|
217
|
+
| \u{1F7E2} Simple | One file, clear fix | FAST: Direct action |
|
|
218
|
+
| \u{1F7E1} Medium | Multi-file feature | NORMAL: Plan \u2192 Execute \u2192 Verify |
|
|
219
|
+
| \u{1F534} Complex | Large scope, unknowns | DEEP: Research \u2192 Plan \u2192 Parallel Execute \u2192 Verify |
|
|
220
|
+
|
|
221
|
+
FOR COMPLEX TASKS \u2192 Create .opencode/todo.md with parallel groups
|
|
222
|
+
</phase_1_triage>
|
|
223
|
+
|
|
224
|
+
<phase_2_execute>
|
|
225
|
+
EXECUTION FLOW:
|
|
226
|
+
|
|
227
|
+
1. PLAN: ${AGENT_NAMES.PLANNER} creates TODO with parallel groups
|
|
228
|
+
2. LAUNCH: Spawn ALL independent tasks simultaneously
|
|
229
|
+
3. MONITOR: Use ${TOOL_NAMES.LIST_TASKS} to track progress
|
|
230
|
+
4. COLLECT: Gather results with ${TOOL_NAMES.GET_TASK_RESULT}
|
|
231
|
+
5. VERIFY: ${AGENT_NAMES.REVIEWER} validates and updates TODO
|
|
232
|
+
6. REPEAT: Until all tasks [x] complete
|
|
233
|
+
</phase_2_execute>
|
|
234
|
+
|
|
235
|
+
<parallel_execution>
|
|
236
|
+
\u26A1 MAXIMIZE PARALLELISM - This is CRITICAL!
|
|
237
|
+
|
|
238
|
+
PATTERN 1: AGENT PARALLELISM
|
|
239
|
+
\`\`\`
|
|
240
|
+
// GOOD \u2705 - Launch 3 agents at once
|
|
241
|
+
${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research API", background: true })
|
|
242
|
+
${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research DB", background: true })
|
|
243
|
+
${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.PLANNER}", prompt: "Research Auth", background: true })
|
|
244
|
+
// Then later: collect all results
|
|
245
|
+
|
|
246
|
+
// BAD \u274C - Sequential when not needed
|
|
247
|
+
${TOOL_NAMES.DELEGATE_TASK}({ ..., background: false }) // waits
|
|
248
|
+
${TOOL_NAMES.DELEGATE_TASK}({ ..., background: false }) // waits
|
|
249
|
+
${TOOL_NAMES.DELEGATE_TASK}({ ..., background: false }) // waits
|
|
250
|
+
\`\`\`
|
|
236
251
|
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
PATTERN 2: BACKGROUND COMMANDS
|
|
253
|
+
\`\`\`
|
|
254
|
+
// GOOD \u2705 - Start build, continue working
|
|
255
|
+
${TOOL_NAMES.RUN_BACKGROUND}({ command: "npm run build" }) \u2192 job_xxx
|
|
256
|
+
// Continue with other work...
|
|
257
|
+
${TOOL_NAMES.CHECK_BACKGROUND}({ taskId: "job_xxx" }) // Check later
|
|
258
|
+
|
|
259
|
+
// BAD \u274C - Blocking on slow command
|
|
260
|
+
bash("npm run build") // Blocks everything for 30+ seconds
|
|
261
|
+
\`\`\`
|
|
239
262
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
263
|
+
PATTERN 3: SESSION CONTINUITY
|
|
264
|
+
\`\`\`
|
|
265
|
+
// First call returns sessionID
|
|
266
|
+
result = ${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Start feature", background: false })
|
|
267
|
+
// Session: \`session_abc123\`
|
|
268
|
+
|
|
269
|
+
// Later: resume same session for follow-up
|
|
270
|
+
${TOOL_NAMES.DELEGATE_TASK}({ agent: "${AGENT_NAMES.WORKER}", prompt: "Add tests", resume: "session_abc123" })
|
|
271
|
+
// Preserves all context!
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
WHEN TO USE EACH:
|
|
275
|
+
| Situation | Use |
|
|
276
|
+
|-----------|-----|
|
|
277
|
+
| Independent tasks (different files) | background=true, spawn ALL |
|
|
278
|
+
| Sequential dependency (A\u2192B\u2192C) | background=false for chain |
|
|
279
|
+
| Long shell command (>5sec) | ${TOOL_NAMES.RUN_BACKGROUND} |
|
|
280
|
+
| Follow-up to previous work | resume: sessionID |
|
|
281
|
+
| Final verification | background=false |
|
|
282
|
+
</parallel_execution>
|
|
283
|
+
|
|
284
|
+
<agents>
|
|
285
|
+
| Agent | Role | Delegate For |
|
|
286
|
+
|-------|------|--------------|
|
|
287
|
+
| ${AGENT_NAMES.PLANNER} | Research + Plan | Creating TODO, fetching docs, architecture |
|
|
288
|
+
| ${AGENT_NAMES.WORKER} | Implement | Writing code, configuration, file creation |
|
|
289
|
+
| ${AGENT_NAMES.REVIEWER} | Verify | Testing, validation, TODO updates |
|
|
245
290
|
</agents>
|
|
246
291
|
|
|
247
292
|
<shared_workspace>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
.opencode/
|
|
294
|
+
\u251C\u2500\u2500 todo.md - Master task list with parallel groups
|
|
295
|
+
\u251C\u2500\u2500 docs/ - Cached documentation
|
|
296
|
+
\u251C\u2500\u2500 context.md - Current mission state
|
|
297
|
+
\u2514\u2500\u2500 summary.md - Condensed context when long
|
|
253
298
|
</shared_workspace>
|
|
254
299
|
|
|
255
300
|
<todo_format>
|
|
256
|
-
.opencode/todo.md:
|
|
257
301
|
\`\`\`markdown
|
|
258
302
|
# Mission: [goal]
|
|
259
303
|
|
|
260
|
-
##
|
|
261
|
-
- [ ] T1: Research
|
|
262
|
-
- [ ] T2:
|
|
263
|
-
|
|
264
|
-
- [ ] T2.2: Configure | agent:${AGENT_NAMES.WORKER}
|
|
265
|
-
- [ ] T3: Verify setup | agent:${AGENT_NAMES.REVIEWER} | depends:T2 | size:S
|
|
266
|
-
- [ ] T4: Implement features | agent:${AGENT_NAMES.WORKER} | depends:T3 | size:L
|
|
267
|
-
- [ ] T5: Final verification | agent:${AGENT_NAMES.REVIEWER} | depends:T4 | size:S
|
|
304
|
+
## Parallel Group A (run simultaneously)
|
|
305
|
+
- [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER}
|
|
306
|
+
- [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER}
|
|
307
|
+
- [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER}
|
|
268
308
|
|
|
269
|
-
##
|
|
270
|
-
|
|
309
|
+
## Parallel Group B (after A completes)
|
|
310
|
+
- [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1
|
|
311
|
+
- [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2
|
|
312
|
+
- [ ] T6: Implement Auth | agent:${AGENT_NAMES.WORKER} | depends:T3
|
|
271
313
|
|
|
272
|
-
##
|
|
273
|
-
[
|
|
314
|
+
## Sequential (strict order)
|
|
315
|
+
- [ ] T7: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5,T6
|
|
316
|
+
- [ ] T8: Final verify | agent:${AGENT_NAMES.REVIEWER} | depends:T7
|
|
274
317
|
\`\`\`
|
|
275
318
|
</todo_format>
|
|
276
319
|
|
|
277
|
-
<anti_hallucination>
|
|
278
|
-
BEFORE CODING:
|
|
279
|
-
1. THINK: Do I know this API/syntax for certain?
|
|
280
|
-
2. CHECK: Look in .opencode/docs/ for cached docs
|
|
281
|
-
3. If uncertain \u2192 ${AGENT_NAMES.PLANNER} or ${AGENT_NAMES.WORKER} search first
|
|
282
|
-
4. NEVER guess - wait for verified documentation
|
|
283
|
-
|
|
284
|
-
MANDATORY RESEARCH TRIGGERS:
|
|
285
|
-
- Unfamiliar library/framework
|
|
286
|
-
- API syntax you're not 100% sure about
|
|
287
|
-
- Version-specific features
|
|
288
|
-
- Configuration patterns
|
|
289
|
-
</anti_hallucination>
|
|
290
|
-
|
|
291
320
|
<execution_loop>
|
|
292
321
|
WHILE .opencode/todo.md has unchecked [ ] items:
|
|
293
|
-
1.
|
|
294
|
-
2.
|
|
295
|
-
3.
|
|
296
|
-
4. ${
|
|
297
|
-
5.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
-
|
|
302
|
-
|
|
322
|
+
1. IDENTIFY all tasks with satisfied dependencies
|
|
323
|
+
2. LAUNCH all identified tasks in PARALLEL (background=true)
|
|
324
|
+
3. START any slow commands via ${TOOL_NAMES.RUN_BACKGROUND}
|
|
325
|
+
4. MONITOR with ${TOOL_NAMES.LIST_TASKS} / ${TOOL_NAMES.LIST_BACKGROUND}
|
|
326
|
+
5. COLLECT results as they complete
|
|
327
|
+
6. UPDATE: ${AGENT_NAMES.REVIEWER} marks [x] and updates context
|
|
328
|
+
7. REPEAT until all complete
|
|
329
|
+
|
|
330
|
+
\u26A1 NEVER: Execute one-by-one when parallel is possible
|
|
331
|
+
\u26A1 ALWAYS: Start slow operations in background immediately
|
|
303
332
|
</execution_loop>
|
|
304
333
|
|
|
305
|
-
<
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
-
|
|
315
|
-
|
|
316
|
-
- Multiple test runs
|
|
317
|
-
- Tasks with no dependencies
|
|
318
|
-
|
|
319
|
-
SEQUENTIAL (background=false):
|
|
320
|
-
- Tasks with file dependencies
|
|
321
|
-
- Build \u2192 Test sequence
|
|
322
|
-
- When result needed for next decision
|
|
323
|
-
- Critical path tasks
|
|
324
|
-
</delegation>
|
|
334
|
+
<anti_hallucination>
|
|
335
|
+
BEFORE CODING:
|
|
336
|
+
1. Check .opencode/docs/ for cached documentation
|
|
337
|
+
2. If uncertain \u2192 ${AGENT_NAMES.PLANNER} researches first
|
|
338
|
+
3. Never guess API syntax - verify from official sources
|
|
339
|
+
|
|
340
|
+
TRIGGERS FOR RESEARCH:
|
|
341
|
+
- Unfamiliar framework/library
|
|
342
|
+
- Version-specific syntax
|
|
343
|
+
- Complex configuration
|
|
344
|
+
</anti_hallucination>
|
|
325
345
|
|
|
326
346
|
<error_handling>
|
|
327
347
|
WHEN TASK FAILS:
|
|
328
|
-
1. ANALYZE
|
|
348
|
+
1. ANALYZE error type (syntax? dependency? timeout?)
|
|
329
349
|
2. DECIDE:
|
|
330
|
-
- Retryable \u2192 retry with
|
|
331
|
-
- Blocker \u2192 mark
|
|
332
|
-
- Critical \u2192
|
|
333
|
-
|
|
334
|
-
RECOVERY STRATEGIES:
|
|
335
|
-
| Error Type | Strategy |
|
|
336
|
-
|------------|----------|
|
|
337
|
-
| Tool crash | Retry with alternative tool or approach |
|
|
338
|
-
| Timeout | Break into smaller subtasks |
|
|
339
|
-
| Missing dep | Add dependency task, reorder |
|
|
340
|
-
| Auth/API | Report to user, cannot auto-fix |
|
|
341
|
-
|
|
342
|
-
NEVER:
|
|
343
|
-
- Ignore failures silently
|
|
344
|
-
- Retry identical approach more than 2 times
|
|
345
|
-
- Skip verification after fix
|
|
346
|
-
- Proceed without addressing blockers
|
|
350
|
+
- Retryable \u2192 retry with different approach (max 2)
|
|
351
|
+
- Blocker \u2192 mark blocked, continue parallel tasks
|
|
352
|
+
- Critical \u2192 report to user
|
|
347
353
|
|
|
348
354
|
WHEN STUCK:
|
|
349
|
-
1.
|
|
350
|
-
2. Run
|
|
351
|
-
3. If completely blocked \u2192 report status
|
|
355
|
+
1. Find unblocked tasks in TODO
|
|
356
|
+
2. Run them in parallel
|
|
357
|
+
3. If completely blocked \u2192 report status
|
|
352
358
|
</error_handling>
|
|
353
359
|
|
|
354
360
|
<completion>
|
|
355
|
-
ONLY
|
|
361
|
+
OUTPUT ONLY WHEN:
|
|
356
362
|
1. ALL items in .opencode/todo.md are [x]
|
|
357
363
|
2. Build/tests pass
|
|
358
364
|
3. ${AGENT_NAMES.REVIEWER} approves
|
|
359
365
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
**MISSION SEAL** (Explicit Completion Confirmation):
|
|
367
|
+
When ALL work is truly complete, output the seal tag:
|
|
368
|
+
\`\`\`
|
|
369
|
+
${MISSION_SEAL.PATTERN}
|
|
370
|
+
\`\`\`
|
|
371
|
+
|
|
372
|
+
Then output:
|
|
373
|
+
${MISSION_SEAL.PATTERN}
|
|
374
|
+
Summary: [accomplishments]
|
|
375
|
+
Evidence: [test/build results]
|
|
376
|
+
|
|
377
|
+
\u26A0\uFE0F IMPORTANT: Only output ${MISSION_SEAL.PATTERN} when:
|
|
378
|
+
- All todos are marked [x] complete
|
|
379
|
+
- All tests pass
|
|
380
|
+
- All builds succeed
|
|
381
|
+
- You have verified the final result
|
|
363
382
|
</completion>`,
|
|
364
383
|
canWrite: true,
|
|
365
384
|
canBash: true
|
|
@@ -393,30 +412,38 @@ CRITICAL RULES:
|
|
|
393
412
|
<planning_workflow>
|
|
394
413
|
CREATE: .opencode/todo.md
|
|
395
414
|
|
|
396
|
-
|
|
397
|
-
- L1: Main objectives (2-5)
|
|
398
|
-
- L2: Sub-tasks (2-3 per L1)
|
|
399
|
-
- L3: Atomic actions (1-3 per L2)
|
|
415
|
+
\u26A1 PARALLELISM IS CRITICAL - Group tasks that can run simultaneously!
|
|
400
416
|
|
|
401
|
-
|
|
402
|
-
|
|
417
|
+
Task Structure:
|
|
418
|
+
- Parallel Groups: Tasks with NO dependencies run together
|
|
419
|
+
- Sequential: Only for tasks with real dependencies
|
|
420
|
+
- Atomic: Each task = one focused action
|
|
403
421
|
|
|
404
|
-
|
|
422
|
+
FORMAT:
|
|
405
423
|
\`\`\`markdown
|
|
406
424
|
# Mission: [goal]
|
|
407
425
|
|
|
408
|
-
##
|
|
409
|
-
- [ ] T1: Research
|
|
410
|
-
- [ ] T2:
|
|
411
|
-
- [ ] T3:
|
|
426
|
+
## Parallel Group A (spawn all simultaneously)
|
|
427
|
+
- [ ] T1: Research API | agent:${AGENT_NAMES.PLANNER} | size:S
|
|
428
|
+
- [ ] T2: Research DB | agent:${AGENT_NAMES.PLANNER} | size:S
|
|
429
|
+
- [ ] T3: Research Auth | agent:${AGENT_NAMES.PLANNER} | size:S
|
|
412
430
|
|
|
413
|
-
## Parallel
|
|
414
|
-
-
|
|
415
|
-
-
|
|
431
|
+
## Parallel Group B (after Group A)
|
|
432
|
+
- [ ] T4: Implement API | agent:${AGENT_NAMES.WORKER} | depends:T1 | size:M
|
|
433
|
+
- [ ] T5: Implement DB | agent:${AGENT_NAMES.WORKER} | depends:T2 | size:M
|
|
434
|
+
|
|
435
|
+
## Sequential (strict order required)
|
|
436
|
+
- [ ] T6: Integration | agent:${AGENT_NAMES.WORKER} | depends:T4,T5 | size:L
|
|
437
|
+
- [ ] T7: Verify all | agent:${AGENT_NAMES.REVIEWER} | depends:T6 | size:S
|
|
416
438
|
|
|
417
439
|
## Notes
|
|
418
440
|
[context for team]
|
|
419
441
|
\`\`\`
|
|
442
|
+
|
|
443
|
+
MAXIMIZE PARALLELISM:
|
|
444
|
+
- Research tasks \u2192 ALL parallel (different topics)
|
|
445
|
+
- Implementation \u2192 Parallel if different files
|
|
446
|
+
- Sequential ONLY when: same file edit, strict A\u2192B dependency
|
|
420
447
|
</planning_workflow>
|
|
421
448
|
|
|
422
449
|
<research_workflow>
|
|
@@ -699,7 +726,7 @@ Next: [task for team]
|
|
|
699
726
|
|
|
700
727
|
// src/agents/definitions.ts
|
|
701
728
|
var AGENTS = {
|
|
702
|
-
[AGENT_NAMES.COMMANDER]:
|
|
729
|
+
[AGENT_NAMES.COMMANDER]: commander,
|
|
703
730
|
[AGENT_NAMES.PLANNER]: planner,
|
|
704
731
|
[AGENT_NAMES.WORKER]: worker,
|
|
705
732
|
[AGENT_NAMES.REVIEWER]: reviewer
|
|
@@ -13200,7 +13227,7 @@ Never claim completion without proof.
|
|
|
13200
13227
|
});
|
|
13201
13228
|
|
|
13202
13229
|
// src/tools/slashCommand.ts
|
|
13203
|
-
var COMMANDER_SYSTEM_PROMPT =
|
|
13230
|
+
var COMMANDER_SYSTEM_PROMPT = commander.systemPrompt;
|
|
13204
13231
|
var MISSION_MODE_TEMPLATE = `${COMMANDER_SYSTEM_PROMPT}
|
|
13205
13232
|
|
|
13206
13233
|
<mission>
|
|
@@ -13211,7 +13238,7 @@ $ARGUMENTS
|
|
|
13211
13238
|
<execution_rules>
|
|
13212
13239
|
1. Complete this mission without user intervention
|
|
13213
13240
|
2. Use your full capabilities: research, implement, verify
|
|
13214
|
-
3. Output "${
|
|
13241
|
+
3. Output "${MISSION_SEAL.PATTERN}" when done
|
|
13215
13242
|
</execution_rules>
|
|
13216
13243
|
</mission>`;
|
|
13217
13244
|
var COMMANDS = {
|
|
@@ -13249,7 +13276,7 @@ var COMMANDS = {
|
|
|
13249
13276
|
|
|
13250
13277
|
| Agent | Role | Capabilities |
|
|
13251
13278
|
|-------|------|--------------|
|
|
13252
|
-
| **${AGENT_NAMES.COMMANDER}** \u{1F3AF} | Master Orchestrator | Autonomous mission control, parallel task coordination, never stops until
|
|
13279
|
+
| **${AGENT_NAMES.COMMANDER}** \u{1F3AF} | Master Orchestrator | Autonomous mission control, parallel task coordination, never stops until ${MISSION_SEAL.PATTERN} |
|
|
13253
13280
|
| **${AGENT_NAMES.PLANNER}** \u{1F4CB} | Strategic Planner | Task decomposition, research, caching docs, dependency analysis |
|
|
13254
13281
|
| **${AGENT_NAMES.WORKER}** \u{1F528} | Implementation | Code, files, terminal, documentation lookup when needed |
|
|
13255
13282
|
| **${AGENT_NAMES.REVIEWER}** \u2705 | Quality & Context | Verification, TODO updates, context management, auto-fix |
|
|
@@ -16751,6 +16778,310 @@ function cleanupSession(sessionID) {
|
|
|
16751
16778
|
sessionStates.delete(sessionID);
|
|
16752
16779
|
}
|
|
16753
16780
|
|
|
16781
|
+
// src/core/loop/mission-seal.ts
|
|
16782
|
+
import { existsSync as existsSync4, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
16783
|
+
import { join as join5 } from "node:path";
|
|
16784
|
+
var MISSION_SEAL_TAG = MISSION_SEAL.TAG;
|
|
16785
|
+
var SEAL_CONFIRMATION = MISSION_SEAL.CONFIRMATION;
|
|
16786
|
+
var SEAL_PATTERN = MISSION_SEAL.PATTERN;
|
|
16787
|
+
var SEAL_REGEX = new RegExp(
|
|
16788
|
+
`<${MISSION_SEAL.TAG}>\\s*${MISSION_SEAL.CONFIRMATION}\\s*</${MISSION_SEAL.TAG}>`,
|
|
16789
|
+
"i"
|
|
16790
|
+
);
|
|
16791
|
+
var STATE_FILE = MISSION_SEAL.STATE_FILE;
|
|
16792
|
+
var DEFAULT_MAX_ITERATIONS = MISSION_SEAL.DEFAULT_MAX_ITERATIONS;
|
|
16793
|
+
var DEFAULT_COUNTDOWN_SECONDS = MISSION_SEAL.DEFAULT_COUNTDOWN_SECONDS;
|
|
16794
|
+
function getStateFilePath(directory) {
|
|
16795
|
+
return join5(directory, PATHS.OPENCODE, STATE_FILE);
|
|
16796
|
+
}
|
|
16797
|
+
function readLoopState(directory) {
|
|
16798
|
+
const filePath = getStateFilePath(directory);
|
|
16799
|
+
if (!existsSync4(filePath)) {
|
|
16800
|
+
return null;
|
|
16801
|
+
}
|
|
16802
|
+
try {
|
|
16803
|
+
const content = readFileSync(filePath, "utf-8");
|
|
16804
|
+
return JSON.parse(content);
|
|
16805
|
+
} catch (error45) {
|
|
16806
|
+
log2(`[mission-seal] Failed to read state: ${error45}`);
|
|
16807
|
+
return null;
|
|
16808
|
+
}
|
|
16809
|
+
}
|
|
16810
|
+
function writeLoopState(directory, state2) {
|
|
16811
|
+
const filePath = getStateFilePath(directory);
|
|
16812
|
+
try {
|
|
16813
|
+
writeFileSync(filePath, JSON.stringify(state2, null, 2), "utf-8");
|
|
16814
|
+
return true;
|
|
16815
|
+
} catch (error45) {
|
|
16816
|
+
log2(`[mission-seal] Failed to write state: ${error45}`);
|
|
16817
|
+
return false;
|
|
16818
|
+
}
|
|
16819
|
+
}
|
|
16820
|
+
function clearLoopState(directory) {
|
|
16821
|
+
const filePath = getStateFilePath(directory);
|
|
16822
|
+
if (!existsSync4(filePath)) {
|
|
16823
|
+
return true;
|
|
16824
|
+
}
|
|
16825
|
+
try {
|
|
16826
|
+
unlinkSync(filePath);
|
|
16827
|
+
return true;
|
|
16828
|
+
} catch (error45) {
|
|
16829
|
+
log2(`[mission-seal] Failed to clear state: ${error45}`);
|
|
16830
|
+
return false;
|
|
16831
|
+
}
|
|
16832
|
+
}
|
|
16833
|
+
function incrementIteration(directory) {
|
|
16834
|
+
const state2 = readLoopState(directory);
|
|
16835
|
+
if (!state2) return null;
|
|
16836
|
+
state2.iteration += 1;
|
|
16837
|
+
state2.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
16838
|
+
if (writeLoopState(directory, state2)) {
|
|
16839
|
+
return state2;
|
|
16840
|
+
}
|
|
16841
|
+
return null;
|
|
16842
|
+
}
|
|
16843
|
+
function detectSealInText(text) {
|
|
16844
|
+
return SEAL_REGEX.test(text);
|
|
16845
|
+
}
|
|
16846
|
+
async function detectSealInSession(client, sessionID) {
|
|
16847
|
+
try {
|
|
16848
|
+
const response = await client.session.messages({ path: { id: sessionID } });
|
|
16849
|
+
const messages = response.data ?? [];
|
|
16850
|
+
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
16851
|
+
const recentMessages = assistantMessages.slice(-3);
|
|
16852
|
+
for (const msg of recentMessages) {
|
|
16853
|
+
if (!msg.parts) continue;
|
|
16854
|
+
const textParts = msg.parts.filter(
|
|
16855
|
+
(p) => p.type === PART_TYPES.TEXT || p.type === PART_TYPES.REASONING
|
|
16856
|
+
);
|
|
16857
|
+
for (const part of textParts) {
|
|
16858
|
+
if (part.text && detectSealInText(part.text)) {
|
|
16859
|
+
return true;
|
|
16860
|
+
}
|
|
16861
|
+
}
|
|
16862
|
+
}
|
|
16863
|
+
return false;
|
|
16864
|
+
} catch (error45) {
|
|
16865
|
+
log2(`[mission-seal] Failed to check session messages: ${error45}`);
|
|
16866
|
+
return false;
|
|
16867
|
+
}
|
|
16868
|
+
}
|
|
16869
|
+
function isLoopActive(directory, sessionID) {
|
|
16870
|
+
const state2 = readLoopState(directory);
|
|
16871
|
+
return state2?.active === true && state2?.sessionID === sessionID;
|
|
16872
|
+
}
|
|
16873
|
+
function generateMissionContinuationPrompt(state2) {
|
|
16874
|
+
return `<mission_loop iteration="${state2.iteration}" max="${state2.maxIterations}">
|
|
16875
|
+
\u{1F4CB} **Mission Loop Active** - Iteration ${state2.iteration}/${state2.maxIterations}
|
|
16876
|
+
|
|
16877
|
+
Your previous iteration did not seal the mission. Continue working.
|
|
16878
|
+
|
|
16879
|
+
**RULES**:
|
|
16880
|
+
1. Review your progress from the previous iteration
|
|
16881
|
+
2. Continue from where you left off
|
|
16882
|
+
3. Check TODO list for incomplete items
|
|
16883
|
+
4. When ALL work is TRULY complete, output:
|
|
16884
|
+
|
|
16885
|
+
\`\`\`
|
|
16886
|
+
${SEAL_PATTERN}
|
|
16887
|
+
\`\`\`
|
|
16888
|
+
|
|
16889
|
+
**IMPORTANT**:
|
|
16890
|
+
- Do NOT seal until the mission is genuinely complete
|
|
16891
|
+
- Verify all todos are marked [x] before sealing
|
|
16892
|
+
- Run tests/builds if applicable before sealing
|
|
16893
|
+
|
|
16894
|
+
**Original Task**:
|
|
16895
|
+
${state2.prompt}
|
|
16896
|
+
</mission_loop>`;
|
|
16897
|
+
}
|
|
16898
|
+
|
|
16899
|
+
// src/core/loop/mission-seal-handler.ts
|
|
16900
|
+
var COUNTDOWN_SECONDS2 = 3;
|
|
16901
|
+
var TOAST_DURATION_MS2 = 1500;
|
|
16902
|
+
var MIN_TIME_BETWEEN_CHECKS_MS = 3e3;
|
|
16903
|
+
var sessionStates2 = /* @__PURE__ */ new Map();
|
|
16904
|
+
function getState3(sessionID) {
|
|
16905
|
+
let state2 = sessionStates2.get(sessionID);
|
|
16906
|
+
if (!state2) {
|
|
16907
|
+
state2 = {};
|
|
16908
|
+
sessionStates2.set(sessionID, state2);
|
|
16909
|
+
}
|
|
16910
|
+
return state2;
|
|
16911
|
+
}
|
|
16912
|
+
function cancelCountdown2(sessionID) {
|
|
16913
|
+
const state2 = sessionStates2.get(sessionID);
|
|
16914
|
+
if (state2?.countdownTimer) {
|
|
16915
|
+
clearTimeout(state2.countdownTimer);
|
|
16916
|
+
state2.countdownTimer = void 0;
|
|
16917
|
+
}
|
|
16918
|
+
}
|
|
16919
|
+
function hasRunningBackgroundTasks2(parentSessionID) {
|
|
16920
|
+
try {
|
|
16921
|
+
const manager = ParallelAgentManager.getInstance();
|
|
16922
|
+
const tasks = manager.getTasksByParent(parentSessionID);
|
|
16923
|
+
return tasks.some((t) => t.status === "running");
|
|
16924
|
+
} catch {
|
|
16925
|
+
return false;
|
|
16926
|
+
}
|
|
16927
|
+
}
|
|
16928
|
+
async function showCountdownToast2(client, seconds, iteration, maxIterations) {
|
|
16929
|
+
try {
|
|
16930
|
+
const tuiClient2 = client;
|
|
16931
|
+
if (tuiClient2.tui?.showToast) {
|
|
16932
|
+
await tuiClient2.tui.showToast({
|
|
16933
|
+
body: {
|
|
16934
|
+
title: "\u{1F504} Mission Loop",
|
|
16935
|
+
message: `Continuing in ${seconds}s... (iteration ${iteration}/${maxIterations})`,
|
|
16936
|
+
variant: "warning",
|
|
16937
|
+
duration: TOAST_DURATION_MS2
|
|
16938
|
+
}
|
|
16939
|
+
});
|
|
16940
|
+
}
|
|
16941
|
+
} catch {
|
|
16942
|
+
}
|
|
16943
|
+
}
|
|
16944
|
+
async function showSealedToast(client, state2) {
|
|
16945
|
+
try {
|
|
16946
|
+
const tuiClient2 = client;
|
|
16947
|
+
if (tuiClient2.tui?.showToast) {
|
|
16948
|
+
await tuiClient2.tui.showToast({
|
|
16949
|
+
body: {
|
|
16950
|
+
title: "\u{1F396}\uFE0F Mission Sealed!",
|
|
16951
|
+
message: `Completed after ${state2.iteration} iteration(s)`,
|
|
16952
|
+
variant: "success",
|
|
16953
|
+
duration: 5e3
|
|
16954
|
+
}
|
|
16955
|
+
});
|
|
16956
|
+
}
|
|
16957
|
+
} catch {
|
|
16958
|
+
}
|
|
16959
|
+
}
|
|
16960
|
+
async function showMaxIterationsToast(client, state2) {
|
|
16961
|
+
try {
|
|
16962
|
+
const tuiClient2 = client;
|
|
16963
|
+
if (tuiClient2.tui?.showToast) {
|
|
16964
|
+
await tuiClient2.tui.showToast({
|
|
16965
|
+
body: {
|
|
16966
|
+
title: "\u26A0\uFE0F Mission Loop Stopped",
|
|
16967
|
+
message: `Max iterations (${state2.maxIterations}) reached`,
|
|
16968
|
+
variant: "warning",
|
|
16969
|
+
duration: 5e3
|
|
16970
|
+
}
|
|
16971
|
+
});
|
|
16972
|
+
}
|
|
16973
|
+
} catch {
|
|
16974
|
+
}
|
|
16975
|
+
}
|
|
16976
|
+
async function injectContinuation2(client, directory, sessionID, loopState) {
|
|
16977
|
+
const handlerState = getState3(sessionID);
|
|
16978
|
+
if (handlerState.isAborting) {
|
|
16979
|
+
log2("[mission-seal-handler] Skipped: user is aborting");
|
|
16980
|
+
return;
|
|
16981
|
+
}
|
|
16982
|
+
if (hasRunningBackgroundTasks2(sessionID)) {
|
|
16983
|
+
log2("[mission-seal-handler] Skipped: background tasks running");
|
|
16984
|
+
return;
|
|
16985
|
+
}
|
|
16986
|
+
if (isSessionRecovering(sessionID)) {
|
|
16987
|
+
log2("[mission-seal-handler] Skipped: session recovering");
|
|
16988
|
+
return;
|
|
16989
|
+
}
|
|
16990
|
+
const sealDetected = await detectSealInSession(client, sessionID);
|
|
16991
|
+
if (sealDetected) {
|
|
16992
|
+
log2("[mission-seal-handler] Seal detected before injection, completing");
|
|
16993
|
+
await handleSealDetected(client, directory, loopState);
|
|
16994
|
+
return;
|
|
16995
|
+
}
|
|
16996
|
+
const prompt = generateMissionContinuationPrompt(loopState);
|
|
16997
|
+
try {
|
|
16998
|
+
await client.session.prompt({
|
|
16999
|
+
path: { id: sessionID },
|
|
17000
|
+
body: {
|
|
17001
|
+
parts: [{ type: PART_TYPES.TEXT, text: prompt }]
|
|
17002
|
+
}
|
|
17003
|
+
});
|
|
17004
|
+
log2("[mission-seal-handler] Continuation injected", {
|
|
17005
|
+
sessionID,
|
|
17006
|
+
iteration: loopState.iteration
|
|
17007
|
+
});
|
|
17008
|
+
} catch (error45) {
|
|
17009
|
+
log2(`[mission-seal-handler] Failed to inject: ${error45}`);
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
17012
|
+
async function handleSealDetected(client, directory, loopState) {
|
|
17013
|
+
clearLoopState(directory);
|
|
17014
|
+
await showSealedToast(client, loopState);
|
|
17015
|
+
log2("[mission-seal-handler] Mission sealed!", {
|
|
17016
|
+
sessionID: loopState.sessionID,
|
|
17017
|
+
iterations: loopState.iteration
|
|
17018
|
+
});
|
|
17019
|
+
}
|
|
17020
|
+
async function handleMaxIterations(client, directory, loopState) {
|
|
17021
|
+
clearLoopState(directory);
|
|
17022
|
+
await showMaxIterationsToast(client, loopState);
|
|
17023
|
+
log2("[mission-seal-handler] Max iterations reached", {
|
|
17024
|
+
sessionID: loopState.sessionID,
|
|
17025
|
+
iterations: loopState.iteration,
|
|
17026
|
+
max: loopState.maxIterations
|
|
17027
|
+
});
|
|
17028
|
+
}
|
|
17029
|
+
async function handleMissionSealIdle(client, directory, sessionID, mainSessionID) {
|
|
17030
|
+
const handlerState = getState3(sessionID);
|
|
17031
|
+
const now = Date.now();
|
|
17032
|
+
if (handlerState.lastCheckTime && now - handlerState.lastCheckTime < MIN_TIME_BETWEEN_CHECKS_MS) {
|
|
17033
|
+
return;
|
|
17034
|
+
}
|
|
17035
|
+
handlerState.lastCheckTime = now;
|
|
17036
|
+
cancelCountdown2(sessionID);
|
|
17037
|
+
if (mainSessionID && sessionID !== mainSessionID) {
|
|
17038
|
+
return;
|
|
17039
|
+
}
|
|
17040
|
+
if (isSessionRecovering(sessionID)) {
|
|
17041
|
+
log2("[mission-seal-handler] Skipped: recovering");
|
|
17042
|
+
return;
|
|
17043
|
+
}
|
|
17044
|
+
if (hasRunningBackgroundTasks2(sessionID)) {
|
|
17045
|
+
log2("[mission-seal-handler] Skipped: background tasks");
|
|
17046
|
+
return;
|
|
17047
|
+
}
|
|
17048
|
+
const loopState = readLoopState(directory);
|
|
17049
|
+
if (!loopState || !loopState.active) {
|
|
17050
|
+
return;
|
|
17051
|
+
}
|
|
17052
|
+
if (loopState.sessionID !== sessionID) {
|
|
17053
|
+
return;
|
|
17054
|
+
}
|
|
17055
|
+
log2("[mission-seal-handler] Checking for seal", {
|
|
17056
|
+
sessionID,
|
|
17057
|
+
iteration: loopState.iteration
|
|
17058
|
+
});
|
|
17059
|
+
const sealDetected = await detectSealInSession(client, sessionID);
|
|
17060
|
+
if (sealDetected) {
|
|
17061
|
+
await handleSealDetected(client, directory, loopState);
|
|
17062
|
+
return;
|
|
17063
|
+
}
|
|
17064
|
+
if (loopState.iteration >= loopState.maxIterations) {
|
|
17065
|
+
await handleMaxIterations(client, directory, loopState);
|
|
17066
|
+
return;
|
|
17067
|
+
}
|
|
17068
|
+
const newState = incrementIteration(directory);
|
|
17069
|
+
if (!newState) {
|
|
17070
|
+
log2("[mission-seal-handler] Failed to increment iteration");
|
|
17071
|
+
return;
|
|
17072
|
+
}
|
|
17073
|
+
await showCountdownToast2(client, COUNTDOWN_SECONDS2, newState.iteration, newState.maxIterations);
|
|
17074
|
+
handlerState.countdownTimer = setTimeout(async () => {
|
|
17075
|
+
cancelCountdown2(sessionID);
|
|
17076
|
+
await injectContinuation2(client, directory, sessionID, newState);
|
|
17077
|
+
}, COUNTDOWN_SECONDS2 * 1e3);
|
|
17078
|
+
log2("[mission-seal-handler] Countdown started", {
|
|
17079
|
+
sessionID,
|
|
17080
|
+
iteration: newState.iteration,
|
|
17081
|
+
seconds: COUNTDOWN_SECONDS2
|
|
17082
|
+
});
|
|
17083
|
+
}
|
|
17084
|
+
|
|
16754
17085
|
// src/core/progress/store.ts
|
|
16755
17086
|
var progressHistory = /* @__PURE__ */ new Map();
|
|
16756
17087
|
var sessionStartTimes = /* @__PURE__ */ new Map();
|
|
@@ -16863,7 +17194,7 @@ You are ONLY done when:
|
|
|
16863
17194
|
- All todos are marked complete or cancelled
|
|
16864
17195
|
- All features are implemented and tested
|
|
16865
17196
|
- Final verification passes
|
|
16866
|
-
Then output:
|
|
17197
|
+
Then output: ${MISSION_SEAL.PATTERN}
|
|
16867
17198
|
</completion_criteria>
|
|
16868
17199
|
</auto_continue>`;
|
|
16869
17200
|
var OrchestratorPlugin = async (input) => {
|
|
@@ -17041,12 +17372,27 @@ var OrchestratorPlugin = async (input) => {
|
|
|
17041
17372
|
if (sessionID) {
|
|
17042
17373
|
const isMainSession = sessions.has(sessionID);
|
|
17043
17374
|
if (isMainSession) {
|
|
17044
|
-
setTimeout(() => {
|
|
17375
|
+
setTimeout(async () => {
|
|
17045
17376
|
const session = sessions.get(sessionID);
|
|
17046
17377
|
if (session?.active) {
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17378
|
+
if (isLoopActive(directory, sessionID)) {
|
|
17379
|
+
await handleMissionSealIdle(
|
|
17380
|
+
client,
|
|
17381
|
+
directory,
|
|
17382
|
+
sessionID,
|
|
17383
|
+
sessionID
|
|
17384
|
+
).catch((err) => {
|
|
17385
|
+
log2("[index.ts] mission-seal-handler error", err);
|
|
17386
|
+
});
|
|
17387
|
+
} else {
|
|
17388
|
+
await handleSessionIdle(
|
|
17389
|
+
client,
|
|
17390
|
+
sessionID,
|
|
17391
|
+
sessionID
|
|
17392
|
+
).catch((err) => {
|
|
17393
|
+
log2("[index.ts] todo-continuation error", err);
|
|
17394
|
+
});
|
|
17395
|
+
}
|
|
17050
17396
|
}
|
|
17051
17397
|
}, 500);
|
|
17052
17398
|
}
|
|
@@ -17205,7 +17551,7 @@ Anomaly count: ${stateSession.anomalyCount}
|
|
|
17205
17551
|
// -----------------------------------------------------------------
|
|
17206
17552
|
// assistant.done hook - runs when the LLM finishes responding
|
|
17207
17553
|
// This is the heart of the "relentless loop" - we keep pushing it
|
|
17208
|
-
// to continue until we see
|
|
17554
|
+
// to continue until we see <mission_seal>SEALED</mission_seal> or hit the limit
|
|
17209
17555
|
// -----------------------------------------------------------------
|
|
17210
17556
|
"assistant.done": async (assistantInput, assistantOutput) => {
|
|
17211
17557
|
const sessionID = assistantInput.sessionID;
|
|
@@ -17245,10 +17591,12 @@ Anomaly count: ${stateSession.anomalyCount}
|
|
|
17245
17591
|
if (stateSession && stateSession.anomalyCount > 0) {
|
|
17246
17592
|
stateSession.anomalyCount = 0;
|
|
17247
17593
|
}
|
|
17248
|
-
if (
|
|
17594
|
+
if (detectSealInText(textContent)) {
|
|
17249
17595
|
session.active = false;
|
|
17250
17596
|
state.missionActive = false;
|
|
17251
|
-
|
|
17597
|
+
clearLoopState(directory);
|
|
17598
|
+
presets.missionComplete("\u{1F396}\uFE0F Mission Sealed - Explicit completion confirmed");
|
|
17599
|
+
log2("[index.ts] Mission sealed detected", { sessionID });
|
|
17252
17600
|
clearSession(sessionID);
|
|
17253
17601
|
sessions.delete(sessionID);
|
|
17254
17602
|
state.sessions.delete(sessionID);
|
|
@@ -69,11 +69,38 @@ export declare const TOOL_NAMES: {
|
|
|
69
69
|
readonly CALL_AGENT: "call_agent";
|
|
70
70
|
readonly SLASHCOMMAND: "slashcommand";
|
|
71
71
|
};
|
|
72
|
+
export declare const MISSION_SEAL: {
|
|
73
|
+
/** XML tag name for mission seal */
|
|
74
|
+
readonly TAG: "mission_seal";
|
|
75
|
+
/** Confirmation value inside tag */
|
|
76
|
+
readonly CONFIRMATION: "SEALED";
|
|
77
|
+
/** Full seal pattern: <mission_seal>SEALED</mission_seal> */
|
|
78
|
+
readonly PATTERN: "<mission_seal>SEALED</mission_seal>";
|
|
79
|
+
/** Default max loop iterations */
|
|
80
|
+
readonly DEFAULT_MAX_ITERATIONS: 20;
|
|
81
|
+
/** Default countdown seconds before continue */
|
|
82
|
+
readonly DEFAULT_COUNTDOWN_SECONDS: 3;
|
|
83
|
+
/** Loop state file name */
|
|
84
|
+
readonly STATE_FILE: "loop-state.json";
|
|
85
|
+
/** Stop command */
|
|
86
|
+
readonly STOP_COMMAND: "/stop";
|
|
87
|
+
/** Cancel command */
|
|
88
|
+
readonly CANCEL_COMMAND: "/cancel";
|
|
89
|
+
};
|
|
90
|
+
/** @deprecated Use MISSION_SEAL instead */
|
|
72
91
|
export declare const MISSION: {
|
|
73
|
-
/**
|
|
74
|
-
readonly
|
|
75
|
-
/**
|
|
76
|
-
readonly
|
|
92
|
+
/** XML tag name for mission seal */
|
|
93
|
+
readonly TAG: "mission_seal";
|
|
94
|
+
/** Confirmation value inside tag */
|
|
95
|
+
readonly CONFIRMATION: "SEALED";
|
|
96
|
+
/** Full seal pattern: <mission_seal>SEALED</mission_seal> */
|
|
97
|
+
readonly PATTERN: "<mission_seal>SEALED</mission_seal>";
|
|
98
|
+
/** Default max loop iterations */
|
|
99
|
+
readonly DEFAULT_MAX_ITERATIONS: 20;
|
|
100
|
+
/** Default countdown seconds before continue */
|
|
101
|
+
readonly DEFAULT_COUNTDOWN_SECONDS: 3;
|
|
102
|
+
/** Loop state file name */
|
|
103
|
+
readonly STATE_FILE: "loop-state.json";
|
|
77
104
|
/** Stop command */
|
|
78
105
|
readonly STOP_COMMAND: "/stop";
|
|
79
106
|
/** Cancel command */
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "opencode-orchestrator",
|
|
3
3
|
"displayName": "OpenCode Orchestrator",
|
|
4
4
|
"description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"author": "agnusdei1207",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|