@nghyane/arcane 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/package.json +6 -6
  2. package/src/config/settings-schema.ts +0 -10
  3. package/src/discovery/helpers.ts +1 -6
  4. package/src/index.ts +1 -0
  5. package/src/main.ts +3 -0
  6. package/src/memories/index.ts +3 -3
  7. package/src/modes/components/model-selector.ts +1 -1
  8. package/src/modes/components/settings-defs.ts +0 -8
  9. package/src/modes/components/status-line.ts +15 -40
  10. package/src/modes/components/user-message.ts +2 -0
  11. package/src/modes/components/welcome.ts +1 -1
  12. package/src/modes/interactive-mode.ts +2 -0
  13. package/src/modes/theme/dark.json +56 -59
  14. package/src/modes/theme/defaults/dark-catppuccin.json +47 -56
  15. package/src/modes/theme/defaults/dark-dracula.json +24 -32
  16. package/src/modes/theme/defaults/dark-gruvbox.json +53 -74
  17. package/src/modes/theme/defaults/dark-solarized.json +33 -35
  18. package/src/modes/theme/defaults/dark-tokyo-night.json +57 -67
  19. package/src/modes/theme/defaults/index.ts +3 -179
  20. package/src/modes/theme/defaults/light-catppuccin.json +42 -50
  21. package/src/modes/theme/defaults/light-github.json +68 -94
  22. package/src/modes/theme/defaults/light-solarized.json +41 -49
  23. package/src/modes/theme/light.json +14 -12
  24. package/src/modes/theme/theme-schema.json +4 -0
  25. package/src/modes/theme/theme.ts +89 -6
  26. package/src/patch/applicator.ts +12 -4
  27. package/src/prompts/agents/task.md +1 -1
  28. package/src/prompts/system/subagent-system-prompt.md +2 -14
  29. package/src/prompts/system/system-prompt.md +12 -14
  30. package/src/prompts/tools/task.md +49 -178
  31. package/src/prompts/tools/todo-write.md +4 -4
  32. package/src/sdk.ts +15 -16
  33. package/src/session/session-manager.ts +1 -3
  34. package/src/task/executor.ts +2 -2
  35. package/src/task/index.ts +5 -5
  36. package/src/task/types.ts +7 -20
  37. package/src/tools/index.ts +16 -33
  38. package/src/tools/subagent-tool.ts +5 -5
  39. package/src/tools/todo-write.ts +0 -37
  40. package/src/tui/output-block.ts +2 -12
  41. package/src/modes/theme/defaults/alabaster.json +0 -93
  42. package/src/modes/theme/defaults/amethyst.json +0 -96
  43. package/src/modes/theme/defaults/anthracite.json +0 -93
  44. package/src/modes/theme/defaults/basalt.json +0 -91
  45. package/src/modes/theme/defaults/birch.json +0 -95
  46. package/src/modes/theme/defaults/dark-abyss.json +0 -91
  47. package/src/modes/theme/defaults/dark-arctic.json +0 -104
  48. package/src/modes/theme/defaults/dark-aurora.json +0 -95
  49. package/src/modes/theme/defaults/dark-cavern.json +0 -91
  50. package/src/modes/theme/defaults/dark-copper.json +0 -95
  51. package/src/modes/theme/defaults/dark-cosmos.json +0 -90
  52. package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
  53. package/src/modes/theme/defaults/dark-eclipse.json +0 -91
  54. package/src/modes/theme/defaults/dark-ember.json +0 -95
  55. package/src/modes/theme/defaults/dark-equinox.json +0 -90
  56. package/src/modes/theme/defaults/dark-forest.json +0 -96
  57. package/src/modes/theme/defaults/dark-github.json +0 -105
  58. package/src/modes/theme/defaults/dark-lavender.json +0 -95
  59. package/src/modes/theme/defaults/dark-lunar.json +0 -89
  60. package/src/modes/theme/defaults/dark-midnight.json +0 -95
  61. package/src/modes/theme/defaults/dark-monochrome.json +0 -94
  62. package/src/modes/theme/defaults/dark-monokai.json +0 -98
  63. package/src/modes/theme/defaults/dark-nebula.json +0 -90
  64. package/src/modes/theme/defaults/dark-nord.json +0 -97
  65. package/src/modes/theme/defaults/dark-ocean.json +0 -101
  66. package/src/modes/theme/defaults/dark-one.json +0 -100
  67. package/src/modes/theme/defaults/dark-rainforest.json +0 -91
  68. package/src/modes/theme/defaults/dark-reef.json +0 -91
  69. package/src/modes/theme/defaults/dark-retro.json +0 -92
  70. package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
  71. package/src/modes/theme/defaults/dark-sakura.json +0 -95
  72. package/src/modes/theme/defaults/dark-slate.json +0 -95
  73. package/src/modes/theme/defaults/dark-solstice.json +0 -90
  74. package/src/modes/theme/defaults/dark-starfall.json +0 -91
  75. package/src/modes/theme/defaults/dark-sunset.json +0 -99
  76. package/src/modes/theme/defaults/dark-swamp.json +0 -90
  77. package/src/modes/theme/defaults/dark-synthwave.json +0 -103
  78. package/src/modes/theme/defaults/dark-taiga.json +0 -91
  79. package/src/modes/theme/defaults/dark-terminal.json +0 -95
  80. package/src/modes/theme/defaults/dark-tundra.json +0 -91
  81. package/src/modes/theme/defaults/dark-twilight.json +0 -91
  82. package/src/modes/theme/defaults/dark-volcanic.json +0 -91
  83. package/src/modes/theme/defaults/graphite.json +0 -92
  84. package/src/modes/theme/defaults/light-arctic.json +0 -107
  85. package/src/modes/theme/defaults/light-aurora-day.json +0 -91
  86. package/src/modes/theme/defaults/light-canyon.json +0 -91
  87. package/src/modes/theme/defaults/light-cirrus.json +0 -90
  88. package/src/modes/theme/defaults/light-coral.json +0 -95
  89. package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
  90. package/src/modes/theme/defaults/light-dawn.json +0 -90
  91. package/src/modes/theme/defaults/light-dunes.json +0 -91
  92. package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
  93. package/src/modes/theme/defaults/light-forest.json +0 -100
  94. package/src/modes/theme/defaults/light-frost.json +0 -95
  95. package/src/modes/theme/defaults/light-glacier.json +0 -91
  96. package/src/modes/theme/defaults/light-gruvbox.json +0 -108
  97. package/src/modes/theme/defaults/light-haze.json +0 -90
  98. package/src/modes/theme/defaults/light-honeycomb.json +0 -95
  99. package/src/modes/theme/defaults/light-lagoon.json +0 -91
  100. package/src/modes/theme/defaults/light-lavender.json +0 -95
  101. package/src/modes/theme/defaults/light-meadow.json +0 -91
  102. package/src/modes/theme/defaults/light-mint.json +0 -95
  103. package/src/modes/theme/defaults/light-monochrome.json +0 -101
  104. package/src/modes/theme/defaults/light-ocean.json +0 -99
  105. package/src/modes/theme/defaults/light-one.json +0 -99
  106. package/src/modes/theme/defaults/light-opal.json +0 -91
  107. package/src/modes/theme/defaults/light-orchard.json +0 -91
  108. package/src/modes/theme/defaults/light-paper.json +0 -95
  109. package/src/modes/theme/defaults/light-prism.json +0 -90
  110. package/src/modes/theme/defaults/light-retro.json +0 -98
  111. package/src/modes/theme/defaults/light-sand.json +0 -95
  112. package/src/modes/theme/defaults/light-savanna.json +0 -91
  113. package/src/modes/theme/defaults/light-soleil.json +0 -90
  114. package/src/modes/theme/defaults/light-sunset.json +0 -99
  115. package/src/modes/theme/defaults/light-synthwave.json +0 -98
  116. package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
  117. package/src/modes/theme/defaults/light-wetland.json +0 -91
  118. package/src/modes/theme/defaults/light-zenith.json +0 -89
  119. package/src/modes/theme/defaults/limestone.json +0 -94
  120. package/src/modes/theme/defaults/mahogany.json +0 -97
  121. package/src/modes/theme/defaults/marble.json +0 -93
  122. package/src/modes/theme/defaults/obsidian.json +0 -91
  123. package/src/modes/theme/defaults/onyx.json +0 -91
  124. package/src/modes/theme/defaults/pearl.json +0 -93
  125. package/src/modes/theme/defaults/porcelain.json +0 -91
  126. package/src/modes/theme/defaults/quartz.json +0 -96
  127. package/src/modes/theme/defaults/sandstone.json +0 -95
  128. package/src/modes/theme/defaults/titanium.json +0 -90
  129. package/src/prompts/system/subagent-submit-reminder.md +0 -11
  130. package/src/tools/jtd-to-json-schema.ts +0 -247
  131. package/src/tools/submit-result.ts +0 -152
@@ -1,18 +1,12 @@
1
1
  # Task
2
2
 
3
- Launch subagents to execute parallel, well-scoped tasks.
3
+ Launch a fire-and-forget subagent to execute well-scoped work. Think of it as a productive junior engineer who cannot ask follow-ups once started.
4
+ - **Use for**: Multi-file implementations, cross-layer refactors, mass migrations, boilerplate generation
5
+ - **Don't use for**: Exploratory work, architectural decisions, single-file edits, reading a file
4
6
 
5
- ## What subagents inherit automatically
6
- Subagents receive the **full system prompt**, including AGENTS.md, context files, and skills. Do NOT repeat project rules, coding conventions, or style guidelines in `context` — they already have them.
7
+ ## Subagent capabilities
7
8
 
8
- ## What subagents do NOT have
9
- Subagents have no access to your conversation history. They don't know:
10
- - Decisions you made but didn't write down
11
- - Which approach you chose among alternatives
12
- - What you learned from reading files during this session
13
- - Requirements the user stated only in conversation
14
-
15
- Subagents CAN grep the parent conversation file for supplementary details.
9
+ Subagents receive the **full system prompt**, including AGENTS.md, context files, and skills. They have no access to your conversation history — they don't know decisions you made, approaches you chose, or requirements stated only in conversation. Subagents CAN grep the parent conversation file for supplementary details.
16
10
  ---
17
11
 
18
12
  ## Parameters
@@ -22,35 +16,18 @@ Subagents CAN grep the parent conversation file for supplementary details.
22
16
  Shared background prepended verbatim to every task `assignment`. Use only for session-specific information subagents lack.
23
17
 
24
18
  <critical>
25
- Do NOT include project rules, coding conventions, or style guidelines — subagents already have AGENTS.md and context files in their system prompt. Repeating them wastes tokens and inflates context. Restating any rule from AGENTS.md in `context` is a bug — treat it like a lint error.
19
+ Do NOT include project rules, coding conventions, or style guidelines — subagents already have AGENTS.md. Restating any rule from AGENTS.md in `context` is a bug.
26
20
  </critical>
27
- **Before writing each line of context, ask:** "Would this sentence be true for ANY task in this repo, or only for THIS specific batch?" If it applies to any task → it's a project rule → the subagent already has it → delete the line.
28
-
29
- WRONG — restating project rules the subagent already has:
30
- ```
31
- ## Constraints
32
- - Use X import style, not Y (per AGENTS.md)
33
- - Use Z for private fields per AGENTS.md
34
- - Run the formatter after changes
35
- - Follow the logging convention
36
- ```
37
- Every line above restates a project convention. The subagent reads AGENTS.md. Delete them all.
38
-
39
- RIGHT — only session-specific decisions the subagent cannot infer from project files:
40
- ```
41
- ## Constraints
42
- - We decided to use approach A over B (session decision)
43
- - The migration target type is `Foo` from `bar` package (looked up this session)
44
- ```
21
+ **Before writing each line of context, ask:** "Would this sentence be true for ANY task in this repo, or only for THIS specific batch?" If it applies to any task → the subagent already has it → delete the line.
45
22
 
46
23
  Use template; omit non-applicable sections:
47
24
 
48
25
  ````
49
26
  ## Goal
50
- One sentence: batch accomplishes together.
27
+ One sentence: what the batch accomplishes together.
51
28
 
52
29
  ## Non-goals
53
- Explicitly exclude tempting scope — what tasks must not touch/attempt.
30
+ Explicitly exclude tempting scope — what tasks must not touch.
54
31
 
55
32
  ## Constraints
56
33
  - Task-specific MUST / MUST NOT rules not already in AGENTS.md
@@ -58,49 +35,43 @@ Explicitly exclude tempting scope — what tasks must not touch/attempt.
58
35
 
59
36
  ## Reference Files
60
37
  - `path/to/file.ext` — pattern demo
61
- - `path/to/other.ext` — reuse or avoid
62
38
 
63
39
  ## API Contract (if tasks produce/consume shared interface)
64
40
  ```language
65
- // Exact type definitions, function signatures, interface shapes
41
+ // Exact type definitions, function signatures
66
42
  ```
67
43
 
68
44
  ## Acceptance (global)
69
45
  - Definition of "done" for batch
70
- - Note: build/test/lint verification happens AFTER all tasks complete — not inside tasks (see below)
46
+ - For parallel tasks, build/test/lint verification happens AFTER all tasks complete — not inside tasks. Single tasks may self-verify.
71
47
  ````
72
- **Belongs in `context`**: task-specific goal, non-goals, session decisions, reference paths, shared type definitions, API contracts, global acceptance commands — anything 2+ tasks need that isn't already in AGENTS.md.
73
- **Rule of thumb:** if repeat in 2+ tasks, belongs in `context`.
74
- **Does NOT belong in `context`**: project rules already in AGENTS.md/context files, per-task file lists, one-off requirements (go in `assignment`), and strict response contracts that only apply to one task.
48
+ **Belongs in `context`**: session decisions, reference paths, shared type definitions, API contracts, global acceptance — anything 2+ tasks need that isn't in AGENTS.md.
49
+ **Does NOT belong**: project rules from AGENTS.md, per-task file lists, one-off requirements (go in `assignment`).
75
50
 
76
51
  ### `tasks` (required)
77
52
 
78
- Array tasks execute in parallel.
53
+ Array of tasks that execute in parallel.
79
54
 
80
55
  |Field|Required|Purpose|
81
56
  |---|---|---|
82
- |`id`|✓|CamelCase identifier, max 32 chars|
83
- |`description`|✓|Short one-liner for UI display only — not seen by subagent|
84
- |`assignment`|✓|Complete per-task instructions. See [Writing an assignment](#writing-an-assignment).|
85
- |`skills`||Skill names preload. Use only when changes correctness — don’t spam every task.|
86
-
87
- Outputs are free-form text/JSON from each subtask. If you need strict structure, define it clearly inside each `assignment` and validate at the caller.
57
+ |`id`|yes|CamelCase identifier, max 32 chars|
58
+ |`description`|yes|Short one-liner for UI display only — not seen by subagent|
59
+ |`assignment`|yes|Complete per-task instructions (see below)|
60
+ |`skills`||Skill names to preload. Use only when it changes correctness.|
88
61
  ---
89
62
 
90
63
  ## Writing an assignment
91
64
 
92
- <critical>## Task scope
93
-
94
- `assignment` must contain enough info for agent to act **without asking a clarifying question**.
95
- **Minimum bar:** assignment under ~8 lines or missing acceptance criteria = too vague. One-liners guaranteed failure.
65
+ <critical>
66
+ `assignment` must contain enough info for the subagent to act **without asking a clarifying question**. One-liners guarantee failure.
96
67
 
97
- Use structure every assignment:
68
+ Use this structure:
98
69
 
99
70
  ```
100
71
  ## Target
101
72
  - Files: exact path(s)
102
73
  - Symbols/entrypoints: specific functions, types, exports
103
- - Non-goals: what task must NOT touch (prevents scope creep)
74
+ - Non-goals: what task must NOT touch
104
75
 
105
76
  ## Change
106
77
  - Step-by-step: add/remove/rename/restructure
@@ -108,168 +79,68 @@ Use structure every assignment:
108
79
 
109
80
  ## Edge Cases / Don't Break
110
81
  - Tricky case 1: ...
111
- - Tricky case 2: ...
112
- - Existing behavior must survive: ...
82
+ - Existing behavior that must survive: ...
113
83
 
114
84
  ## Acceptance (task-local)
115
85
  - Expected behavior or observable result
116
- - DO NOT include project-wide build/test/lint commands (see below)
86
+ - For parallel tasks: DO NOT include project-wide build/test/lint commands
117
87
  ```
118
88
 
119
- `context` carries shared background. `assignment` carries only delta: file-specific instructions, local edge cases, per-task acceptance checks. Never duplicate shared constraints across assignments.
120
-
121
- ### Anti-patterns (ban these)
122
- **Vague assignments** — agent guesses wrong or stalls:
123
- - "Refactor this to be cleaner."
124
- - "Migrate to N-API."
125
- - "Fix the bug in streaming."
126
- - "Update all constructors in `src/**/*.ts`."
127
- **Vague context** — forces agent invent conventions:
128
- - "Use existing patterns."
129
- - "Follow conventions."
130
- - "No WASM."
131
- **Redundant context** — wastes tokens repeating what subagents already have:
132
- - Restating AGENTS.md rules (coding style, import conventions, formatting commands, logger usage, etc.)
133
- - Repeating project constraints from context files
134
- - Listing tool/framework preferences already documented in the repo
135
-
136
- If a constraint appears in AGENTS.md, it MUST NOT appear in `context`. The subagent has the full system prompt.
137
-
138
- If tempted to write above, expand using templates.
139
- **Test/lint commands in parallel tasks** — edit wars:
140
- Parallel agents share working tree. If two agents run `bun check` or `bun test` concurrently, they see each other's half-finished edits, "fix" phantom errors, loop. **Never tell parallel tasks run project-wide build/test/lint commands.** Each task edits, stops. Caller verifies after all tasks complete.
141
- **If you can't specify scope yet**, create **Discovery task** first: enumerate files, find callsites, list candidates. Then fan out with explicit paths.
89
+ `context` carries shared background. `assignment` carries only delta: file-specific instructions, local edge cases, per-task acceptance.
142
90
 
143
91
  ### Delegate intent, not keystrokes
144
92
 
145
- Your role as tech lead: set direction, define boundaries, call out pitfalls — then get out of way. Dont read every file, decide every edit, dictate line-by-line. That makes you bottleneck; agent typist.
146
- **Be specific about:** constraints, naming conventions, API contracts, "dont break" items, acceptance criteria.
147
- **Delegate:** code reading, approach selection, exact edit locations, implementation details. Agent has tools, can reason about code.
148
-
149
- Micromanaging (you think, agent types):
150
- ```
151
- assignment: "In src/api/handler.ts, line 47, change `throw err` to `throw new ApiError(err.message, 500)`.
152
- On line 63, wrap fetch call try/catch return 502 on failure.
153
- On line 89, add null check before accessing resp.body..."
154
- ```
155
-
156
- Delegating (agent thinks within constraints):
157
- ```
158
- assignment: "## Target\n- Files: src/api/handler.ts\n\n## Change\nImprove error handling: replace raw throws
159
- with typed ApiError instances, add try/catch around external calls, guard against null responses.\n\n
160
- ## Edge Cases / Don't Break\n- Existing error codes in tests must still match\n
161
- - Don't change public function signatures"
162
- ```
163
-
164
- First style wastes your time, brittle if code shifts. Second gives agent room to do work.
93
+ Your role is tech lead: set direction, define boundaries, call out pitfalls — then get out of the way. Don't dictate line-by-line edits.
94
+ **Be specific about:** constraints, naming, API contracts, "don't break" items, acceptance criteria.
95
+ **Delegate:** code reading, approach selection, exact edit locations, implementation details.
165
96
  </critical>
166
97
 
167
- ## Example
168
-
169
- <example type="bad" label="Duplicated context inflates tokens">
170
- <tasks>
171
- <task name="Grep">
172
- <description>Port grep module from WASM to N-API...</description>
173
- <assignment>Port grep module from WASM to N-API... (same blob repeated)</assignment>
174
- </task>
175
- </tasks>
176
- </example>
177
-
178
- <example type="good" label="Shared rules in context, only deltas in assignment">
179
- <context>
180
- ## Goal
181
- Port WASM modules to N-API, matching existing arcane-natives conventions.
182
-
183
- ## Non-goals
184
- Do not touch TS bindings or downstream consumers — separate phase.
185
-
186
- ## Constraints
187
- - MUST use `#[napi]` attribute macro on all exports
188
- - MUST return `napi::Result<T>` for fallible ops; never panic
189
- - MUST use `spawn_blocking` for filesystem I/O or >1ms work
190
- ...
191
-
192
- ## Acceptance (global)
193
- - Caller verifies after all tasks: `cargo test -p arcane-natives` and `cargo build -p arcane-natives` with no warnings
194
- - Individual tasks must NOT run these commands themselves
195
- </context>
196
-
197
- <tasks>
198
- <task name="PortGrep">
199
- <description>Port grep module to N-API</description>
200
- <assignment>
201
- ## Target
202
- - Files: `src/grep.rs`, `src/lib.rs` (registration only)
203
- - Symbols: search, search_multi, compile_pattern
204
-
205
- ## Change
206
- - Implement three N-API exports in grep.rs:
207
- - `search(pattern: JsString, path: JsString, env: Env) -> napi::Result<Vec<Match>>`
208
- ...
209
-
210
- ## Acceptance (task-local)
211
- - Three functions exported with correct signatures (caller verifies build after all tasks)
212
- </assignment>
213
- </task>
214
-
215
- <task name="PortHighlight">
216
- <description>Port highlight module to N-API</description>
217
- <assignment>
218
- ## Target
219
- - Files: `src/highlight.rs`, `src/lib.rs` (registration only)
220
- ...
221
- </assignment>
222
- </task>
223
- </tasks>
224
- </example>
98
+ ### Anti-patterns
99
+ **Vague assignments** — subagent guesses wrong or stalls:
100
+ - "Refactor this to be cleaner."
101
+ - "Fix the bug in streaming."
102
+ - "Update all constructors in `src/**/*.ts`."
103
+ **Test/lint in parallel tasks** edit wars:
104
+ Parallel agents share working tree. If two agents run `bun check` concurrently, they see each other's half-finished edits, "fix" phantom errors, loop. **Never tell parallel tasks to run project-wide build/test/lint.** Each task edits, stops. Caller verifies after all complete. Single-task launches may include verification.
105
+ **If you can't specify scope yet**, create a **Discovery task** first: enumerate files, find callsites, list candidates. Then fan out with explicit paths.
225
106
  ---
226
107
 
227
108
  ## Task scope
228
109
 
229
- Each task small, well-defined scope — **at most 35 files**.
230
- **Signs task too broad:**
231
- - File paths use globs (`src/**/*.ts`) instead of explicit names
232
- - Assignment says "update all" / "migrate everything" / "refactor across"
233
- - Scope covers entire package or directory tree
234
- **Fix:** enumerate files first (grep/glob discovery), then fan out one task per file or small cluster.
110
+ Each task: small, well-defined — **at most 3-5 files**.
111
+ **Signs task is too broad:** file paths use globs, assignment says "update all" / "migrate everything", scope covers entire package.
112
+ **Fix:** enumerate files first (grep/glob), then fan out one task per file or small cluster.
235
113
  ---
236
114
 
237
115
  ## Parallelization
238
116
  **Test:** Can task B produce correct output without seeing task A's result?
239
117
  - **Yes** → parallelize
240
- - **No** → run sequentially (A completes, then launch B with A output in context)
118
+ - **No** → sequential (A completes, then launch B with A's output in context)
241
119
 
242
120
  ### Must be sequential
243
121
 
244
122
  |First|Then|Reason|
245
123
  |---|---|---|
246
124
  |Define types/interfaces|Implement consumers|Consumers need contract|
247
- |Create API exports|Write bindings/callers|Callers need export names/signatures|
248
- |Scaffold structure|Implement bodies|Bodies need shape|
125
+ |Create API exports|Write bindings/callers|Callers need signatures|
249
126
  |Core module|Dependent modules|Dependents import from core|
250
- |Schema/DB migration|Application logic|Logic depends on new schema shape|
251
127
 
252
128
  ### Safe to parallelize
253
129
  - Independent modules, no cross-imports
254
130
  - Tests for already-implemented code
255
131
  - Isolated file-scoped refactors
256
- - Documentation for stable APIs
257
-
258
- ### Phased execution
259
132
 
260
- Layered work with dependencies:
261
- **Phase 1 — Foundation** (do yourself or single task): define interfaces, create scaffolds, establish API shape. Never fan out until contract known.
262
- **Phase 2 — Parallel implementation**: fan out tasks consuming same known interface. Include Phase 1 API contract in `context`.
133
+ ### Multi-phase pattern
134
+ **Phase 1 — Sequential**: define shared contracts (types, interfaces, schemas).
135
+ **Phase 2 — Parallel**: fan out tasks consuming same known interface.
263
136
  **Phase 3 — Integration** (do yourself): wire modules, fix mismatches, verify builds.
264
- **Phase 4 — Dependent layer**: fan out tasks consuming Phase 2 outputs.
265
137
  ---
266
138
 
267
139
  ## Pre-flight checklist
268
140
 
269
141
  Before calling tool, verify:
270
- - [ ] `context` includes only session-specific info not already in AGENTS.md/context files
271
- - [ ] Each `assignment` follows assignment template not one-liner
272
- - [ ] Each `assignment` includes edge cases / "don’t break" items
273
- - [ ] Tasks truly parallel (no hidden dependencies)
274
- - [ ] Scope small, file paths explicit (no globs)
275
- - [ ] No task runs project-wide build/test/lint — you do after all tasks complete
142
+ - [ ] `context` includes only session-specific info not already in AGENTS.md
143
+ - [ ] Each assignment has Target, Change, Edge Cases, Acceptance sections
144
+ - [ ] Assignments reference exact file paths (no globs)
145
+ - [ ] Scope small, file paths explicit
146
+ - [ ] Parallel tasks don't run project-wide build/test/lint — you do after all tasks complete (single tasks may self-verify)
@@ -18,11 +18,11 @@ Use proactively:
18
18
  - in_progress: working
19
19
  - completed: finished
20
20
  2. **Task Management**:
21
- - Update status in real time
22
- - Mark complete IMMEDIATELY after finishing (no batching)
23
- - Keep exactly ONE task in_progress at a time
21
+ - Update status each turn
22
+ - Mark completed after each code execution finishes — do not defer to a later turn
23
+ - Multiple tasks may be in_progress simultaneously when working in parallel
24
24
  - Remove tasks no longer relevant
25
- - Complete tasks in list order (do not mark later tasks completed while earlier tasks remain incomplete)
25
+ - Mark tasks completed as they finish, regardless of list order
26
26
  3. **Task Completion Requirements**:
27
27
  - ONLY mark completed when FULLY accomplished
28
28
  - On errors/blockers/inability to finish, keep in_progress
package/src/sdk.ts CHANGED
@@ -72,6 +72,7 @@ import {
72
72
  loadSshTool,
73
73
  PythonTool,
74
74
  ReadTool,
75
+ type SubagentContext,
75
76
  setPreferredImageProvider,
76
77
  setPreferredSearchProvider,
77
78
  type Tool,
@@ -157,12 +158,8 @@ export interface CreateAgentSessionOptions {
157
158
  /** Tool names explicitly requested (enables disabled-by-default tools) */
158
159
  toolNames?: string[];
159
160
 
160
- /** Output schema for structured completion (subagents) */
161
- outputSchema?: unknown;
162
- /** Whether to include the submit_result tool by default */
163
- requireSubmitResultTool?: boolean;
164
- /** Task recursion depth (for subagent sessions). Default: 0 */
165
- taskDepth?: number;
161
+ /** Whether this is a subagent session. Default: false */
162
+ isSubagent?: boolean;
166
163
  /** Parent task ID prefix for nested artifact naming (e.g., "6-Extensions") */
167
164
  parentTaskPrefix?: string;
168
165
 
@@ -223,6 +220,7 @@ export {
223
220
  PythonTool,
224
221
  ReadTool,
225
222
  WriteTool,
223
+ type SubagentContext,
226
224
  type ToolSession,
227
225
  };
228
226
 
@@ -629,8 +627,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
629
627
 
630
628
  // For subagent sessions using GitHub Copilot, add X-Initiator header
631
629
  // to ensure proper billing (agent-initiated vs user-initiated)
632
- const taskDepth = options.taskDepth ?? 0;
633
- const forceCopilotAgentInitiator = taskDepth > 0;
630
+ const isSubagent = options.isSubagent ?? false;
631
+ const forceCopilotAgentInitiator = isSubagent;
634
632
  if (forceCopilotAgentInitiator && model?.provider === "github-copilot") {
635
633
  model = {
636
634
  ...model,
@@ -722,9 +720,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
722
720
  contextFiles,
723
721
  skills,
724
722
  eventBus,
725
- outputSchema: options.outputSchema,
726
- requireSubmitResultTool: options.requireSubmitResultTool,
727
- taskDepth: options.taskDepth ?? 0,
723
+ isSubagent: options.isSubagent ?? false,
728
724
  getSessionFile: () => sessionManager.getSessionFile() ?? null,
729
725
  getSessionId: () => sessionManager.getSessionId?.() ?? null,
730
726
  getSessionSpawns: () => options.spawns ?? "*",
@@ -736,10 +732,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
736
732
  if (model) return formatModelString(model);
737
733
  return undefined;
738
734
  },
739
- getCompactContext: () => session.formatCompactContext(),
735
+ subagentContext: {
736
+ getCompactContext: () => session.formatCompactContext(),
737
+ authStorage,
738
+ modelRegistry,
739
+ },
740
740
  settings,
741
- authStorage,
742
- modelRegistry,
743
741
  };
744
742
 
745
743
  // Initialize internal URL router for internal protocols (agent://, artifact://, memory://, skill://, rule://)
@@ -798,7 +796,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
798
796
  });
799
797
  time("discoverAndLoadMCPTools");
800
798
  mcpManager = mcpResult.manager;
801
- toolSession.mcpManager = mcpManager;
799
+ if (!toolSession.subagentContext) toolSession.subagentContext = {};
800
+ toolSession.subagentContext.mcpManager = mcpManager;
802
801
 
803
802
  // If we extracted Exa API keys from MCP configs and EXA_AARCANE_KEY isn't set, use the first one
804
803
  if (mcpResult.exaApiKeys.length > 0 && !$env.EXA_AARCANE_KEY) {
@@ -1273,7 +1272,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1273
1272
  settings,
1274
1273
  modelRegistry,
1275
1274
  agentDir,
1276
- taskDepth,
1275
+ isSubagent,
1277
1276
  });
1278
1277
 
1279
1278
  return {
@@ -124,8 +124,6 @@ export interface SessionInitEntry extends SessionEntryBase {
124
124
  task: string;
125
125
  /** Tools available to the agent */
126
126
  tools: string[];
127
- /** Output schema if structured output was requested */
128
- outputSchema?: unknown;
129
127
  }
130
128
 
131
129
  /** Mode change entry - tracks agent mode transitions. */
@@ -1685,7 +1683,7 @@ export class SessionManager {
1685
1683
  }
1686
1684
 
1687
1685
  /** Append session init metadata (for subagent debugging/replay). Returns entry id. */
1688
- appendSessionInit(init: { systemPrompt: string; task: string; tools: string[]; outputSchema?: unknown }): string {
1686
+ appendSessionInit(init: { systemPrompt: string; task: string; tools: string[] }): string {
1689
1687
  const entry: SessionInitEntry = {
1690
1688
  type: "session_init",
1691
1689
  id: generateId(this.#byId),
@@ -107,7 +107,7 @@ export interface ExecutorOptions {
107
107
  id: string;
108
108
  modelOverride?: string | string[];
109
109
  thinkingLevel?: ThinkingLevel;
110
- taskDepth?: number;
110
+ isSubagent?: boolean;
111
111
  enableLsp?: boolean;
112
112
  signal?: AbortSignal;
113
113
  onProgress?: (progress: AgentProgress) => void;
@@ -682,7 +682,7 @@ export async function runAgent(options: ExecutorOptions): Promise<SingleResult>
682
682
  sessionManager,
683
683
  hasUI: false,
684
684
  spawns: "",
685
- taskDepth: (options.taskDepth ?? 0) + 1,
685
+ isSubagent: true,
686
686
  parentTaskPrefix: id,
687
687
  enableLsp: lspEnabled,
688
688
  skipPythonPreflight,
package/src/task/index.ts CHANGED
@@ -93,7 +93,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
93
93
 
94
94
  try {
95
95
  await fs.mkdir(effectiveArtifactsDir, { recursive: true });
96
- const compactContext = this.session.getCompactContext?.();
96
+ const compactContext = this.session.subagentContext?.getCompactContext?.();
97
97
  let contextFilePath: string | undefined;
98
98
  if (compactContext) {
99
99
  contextFilePath = path.join(effectiveArtifactsDir, "context.md");
@@ -164,7 +164,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
164
164
  description: rendered.description,
165
165
  index: 0,
166
166
  id: uniqueId,
167
- taskDepth: this.session.taskDepth ?? 0,
167
+ isSubagent: true,
168
168
  modelOverride,
169
169
  sessionFile,
170
170
  persistArtifacts: !!artifactsDir,
@@ -177,10 +177,10 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
177
177
  progressMap.set(0, { ...structuredClone(progress) });
178
178
  emitProgress();
179
179
  },
180
- authStorage: this.session.authStorage,
181
- modelRegistry: this.session.modelRegistry,
180
+ authStorage: this.session.subagentContext?.authStorage,
181
+ modelRegistry: this.session.subagentContext?.modelRegistry,
182
182
  settings: this.session.settings,
183
- mcpManager: this.session.mcpManager,
183
+ mcpManager: this.session.subagentContext?.mcpManager,
184
184
  contextFiles,
185
185
  skills: resolvedSkills,
186
186
  preloadedSkills,
package/src/task/types.ts CHANGED
@@ -24,26 +24,13 @@ export const TASK_SUBAGENT_EVENT_CHANNEL = "task:subagent:event";
24
24
  /** EventBus channel for aggregated subagent progress */
25
25
  export const TASK_SUBAGENT_PROGRESS_CHANNEL = "task:subagent:progress";
26
26
 
27
- /** Single task item for parallel execution */
28
- export const taskItemSchema = Type.Object({
29
- id: Type.String({
30
- description: "CamelCase identifier, max 32 chars",
31
- maxLength: 32,
32
- }),
33
- description: Type.String({
34
- description: "Short one-liner for UI display only — not seen by the subagent",
35
- }),
36
- assignment: Type.String({
37
- description:
38
- "Complete per-task instructions the subagent executes. Must follow the Target/Change/Edge Cases/Acceptance structure. Only include per-task deltas — shared background belongs in `context`.",
39
- }),
40
- skills: Type.Optional(
41
- Type.Array(Type.String(), {
42
- description: "Skill names to preload into the subagent. Use only where it changes correctness.",
43
- }),
44
- ),
45
- });
46
- export type TaskItem = Static<typeof taskItemSchema>;
27
+ /** Single task item for execution */
28
+ export interface TaskItem {
29
+ id: string;
30
+ description: string;
31
+ assignment: string;
32
+ skills?: string[];
33
+ }
47
34
 
48
35
  /** Task schema — single task with optional context */
49
36
  export const taskSchema = Type.Object({
@@ -31,7 +31,6 @@ import { PythonTool } from "./python";
31
31
  import { ReadTool } from "./read";
32
32
  import { ReviewerTool } from "./reviewer-tool";
33
33
  import { loadSshTool } from "./ssh";
34
- import { SubmitResultTool } from "./submit-result";
35
34
  import { TodoWriteTool } from "./todo-write";
36
35
  import { UndoEditTool } from "./undo-edit";
37
36
  import { WriteTool } from "./write";
@@ -94,7 +93,6 @@ export { OracleTool } from "./oracle";
94
93
  export { PythonTool, type PythonToolDetails, type PythonToolOptions } from "./python";
95
94
  export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
96
95
  export { loadSshTool, type SSHToolDetails, SshTool } from "./ssh";
97
- export { SubmitResultTool } from "./submit-result";
98
96
  export { type TodoItem, TodoWriteTool, type TodoWriteToolDetails } from "./todo-write";
99
97
  export { UndoEditTool, type UndoEditToolDetails } from "./undo-edit";
100
98
  export { WriteTool, type WriteToolDetails, type WriteToolInput } from "./write";
@@ -108,6 +106,14 @@ export type ContextFileEntry = {
108
106
  depth?: number;
109
107
  };
110
108
 
109
+ /** Forwarded context for spawning subagent processes */
110
+ export interface SubagentContext {
111
+ authStorage?: import("../session/auth-storage").AuthStorage;
112
+ modelRegistry?: import("../config/model-registry").ModelRegistry;
113
+ mcpManager?: import("../mcp/manager").MCPManager;
114
+ getCompactContext?: () => string;
115
+ }
116
+
111
117
  /** Session context for tool factories */
112
118
  export interface ToolSession {
113
119
  /** Current working directory */
@@ -128,12 +134,8 @@ export interface ToolSession {
128
134
  hasEditTool?: boolean;
129
135
  /** Event bus for tool/extension communication */
130
136
  eventBus?: EventBus;
131
- /** Output schema for structured completion (subagents) */
132
- outputSchema?: unknown;
133
- /** Whether to include the submit_result tool by default */
134
- requireSubmitResultTool?: boolean;
135
- /** Task recursion depth (0 = top-level, 1 = first child, etc.) */
136
- taskDepth?: number;
137
+ /** Whether this session is a subagent (spawned by task tool) */
138
+ isSubagent?: boolean;
137
139
  /** Get session file */
138
140
  getSessionFile: () => string | null;
139
141
  /** Get session ID */
@@ -148,20 +150,14 @@ export interface ToolSession {
148
150
  getModelString?: () => string | undefined;
149
151
  /** Get the current session model string, regardless of how it was chosen */
150
152
  getActiveModelString?: () => string | undefined;
151
- /** Auth storage for passing to subagents (avoids re-discovery) */
152
- authStorage?: import("../session/auth-storage").AuthStorage;
153
- /** Model registry for passing to subagents (avoids re-discovery) */
154
- modelRegistry?: import("../config/model-registry").ModelRegistry;
155
- /** MCP manager for proxying MCP calls through parent */
156
- mcpManager?: import("../mcp/manager").MCPManager;
153
+ /** Context for spawning subagent processes (only used by task/subagent tools) */
154
+ subagentContext?: SubagentContext;
157
155
  /** Internal URL router for agent:// and skill:// URLs */
158
156
  internalRouter?: InternalUrlRouter;
159
157
  /** Agent output manager for unique agent:// IDs across task invocations */
160
158
  agentOutputManager?: AgentOutputManager;
161
159
  /** Settings instance for passing to subagents */
162
160
  settings: Settings;
163
- /** Get compact conversation context for subagents (excludes tool results, system prompts) */
164
- getCompactContext?: () => string;
165
161
  }
166
162
 
167
163
  type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
@@ -191,10 +187,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
191
187
  write: s => new WriteTool(s),
192
188
  };
193
189
 
194
- export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
195
- submit_result: s => new SubmitResultTool(s),
196
- };
197
-
198
190
  export type ToolName = keyof typeof BUILTIN_TOOLS;
199
191
 
200
192
  export type PythonToolMode = "ipy-only" | "bash-only" | "both";
@@ -232,7 +224,6 @@ function getPythonModeFromEnv(): PythonToolMode | null {
232
224
  */
233
225
  export async function createTools(session: ToolSession, toolNames?: string[]): Promise<Tool[]> {
234
226
  time("createTools:start");
235
- const includeSubmitResult = session.requireSubmitResultTool === true;
236
227
  const enableLsp = session.enableLsp ?? true;
237
228
  const requestedTools = toolNames && toolNames.length > 0 ? [...new Set(toolNames)] : undefined;
238
229
  const pythonMode = getPythonModeFromEnv() ?? session.settings.get("python.toolMode");
@@ -278,12 +269,12 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
278
269
  ) {
279
270
  requestedTools.push("bash");
280
271
  }
281
- const allTools: Record<string, ToolFactory> = { ...BUILTIN_TOOLS, ...HIDDEN_TOOLS };
272
+ const allTools: Record<string, ToolFactory> = { ...BUILTIN_TOOLS };
282
273
  const isToolAllowed = (name: string) => {
283
274
  if (name === "lsp") return enableLsp;
284
275
  if (name === "bash") return allowBash;
285
276
  if (name === "python") return allowPython;
286
- if (name === "todo_write") return !includeSubmitResult && session.settings.get("todo.enabled");
277
+ if (name === "todo_write") return session.settings.get("todo.enabled");
287
278
  if (name === "find") return session.settings.get("find.enabled");
288
279
  if (name === "grep") return session.settings.get("grep.enabled");
289
280
  if (name === "notebook") return session.settings.get("notebook.enabled");
@@ -295,25 +286,17 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
295
286
  if (name === "librarian") return session.settings.get("librarian.enabled");
296
287
  if (name === "oracle") return session.settings.get("oracle.enabled");
297
288
  if (name === "task") {
298
- const maxDepth = session.settings.get("task.maxRecursionDepth") ?? 2;
299
- const currentDepth = session.taskDepth ?? 0;
300
- return maxDepth < 0 || currentDepth < maxDepth;
289
+ return !session.isSubagent;
301
290
  }
302
291
  return true;
303
292
  };
304
- if (includeSubmitResult && requestedTools && !requestedTools.includes("submit_result")) {
305
- requestedTools.push("submit_result");
306
- }
307
293
 
308
294
  const filteredRequestedTools = requestedTools?.filter(name => name in allTools && isToolAllowed(name));
309
295
 
310
296
  const entries =
311
297
  filteredRequestedTools !== undefined
312
298
  ? filteredRequestedTools.map(name => [name, allTools[name]] as const)
313
- : [
314
- ...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name)),
315
- ...(includeSubmitResult ? ([["submit_result", HIDDEN_TOOLS.submit_result]] as const) : []),
316
- ];
299
+ : [...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name))];
317
300
 
318
301
  const results = await Promise.all(
319
302
  entries.map(async ([name, factory]) => {