opencode-titan 0.1.0 → 0.1.1

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
@@ -1,6 +1,9 @@
1
1
  <div align="center">
2
2
  <h3>⚡ opencode-titan ⚡</h3>
3
3
 
4
+ <img src="assets/titan-demo.gif" alt="opencode-titan demo" width="100%">
5
+
6
+
4
7
  <p><i>One mind that never touches the keyboard. A fleet of hands that never stops moving.<br>Plan with the slow, expensive genius, and let the fast ones build in parallel.</i></p>
5
8
 
6
9
  <p><b>OpenCode Orchestration Plugin</b> · Mix any models · Delegate everything · Run in parallel</p>
@@ -32,8 +35,30 @@ The plugin builds a two-tier hierarchy of agents:
32
35
 
33
36
  ## Installation
34
37
 
35
- > [!NOTE]
36
- > This plugin is not published to npm yet, so install it manually from source. npm/Bun package installation is coming soon.
38
+ Add the [`opencode-titan`](https://www.npmjs.com/package/opencode-titan) package to the `plugin` array in your OpenCode config (`opencode.json`). OpenCode installs it automatically from npm on startup:
39
+
40
+ ```json
41
+ {
42
+ "$schema": "https://opencode.ai/config.json",
43
+ "plugin": ["opencode-titan"]
44
+ }
45
+ ```
46
+
47
+ To pin a specific version, append it with `@`:
48
+
49
+ ```json
50
+ {
51
+ "$schema": "https://opencode.ai/config.json",
52
+ "plugin": ["opencode-titan@0.1.0"]
53
+ }
54
+ ```
55
+
56
+ Restart OpenCode and Titan becomes your default agent.
57
+
58
+ <details>
59
+ <summary><b>Alternative: install from source</b></summary>
60
+
61
+ Use this if you want to hack on the plugin or run an unreleased version.
37
62
 
38
63
  **1. Clone and build the plugin:**
39
64
 
@@ -59,6 +84,8 @@ OpenCode treats any plugin entry starting with `.`, `file://`, or an absolute pa
59
84
 
60
85
  Restart OpenCode and Titan becomes your default agent.
61
86
 
87
+ </details>
88
+
62
89
  ## Getting Started
63
90
 
64
91
  1. **Create your plugin config** at `~/.config/opencode/opencode-titan.jsonc`
@@ -120,7 +147,7 @@ Titan is the most intelligent agent in the fleet, and by far the slowest and mos
120
147
 
121
148
  ### ⚙️ Myrmidons — The Fleet
122
149
 
123
- Myrmidons are the hands. Each executes a delegated task and reports back concisely — responses to Titan are enforced to a single paragraph, 500 words max, keeping Titan's context lean. Every Myrmidon declares a `modelType` that shapes how Titan routes work to it:
150
+ Myrmidons are the hands. Each executes a delegated task and reports back concisely — responses to Titan are enforced to a single paragraph, 1000 words max by default (configurable via `maxResponseWords`), keeping Titan's context lean. Every Myrmidon declares a `modelType` that shapes how Titan routes work to it:
124
151
 
125
152
  <table>
126
153
  <tr>
@@ -172,6 +199,7 @@ Myrmidons are the hands. Each executes a delegated task and reports back concise
172
199
  | `myrmidons` | `array` | The Myrmidon fleet (see above) |
173
200
  | `children` | `array` | **Deprecated** alias for `myrmidons`, kept for backwards compatibility. Prefer `myrmidons`; if both are set, `myrmidons` wins |
174
201
  | `disabled_tools` | `string[]` | Tool names to disable for the plugin's agents |
202
+ | `maxResponseWords` | `number` (≥1) | Max word count enforced on each Myrmidon's one-paragraph response to Titan (default: `1000`) |
175
203
  | `backgroundJobs.maxSessionsPerAgent` | `number` (1–10) | Max concurrent sessions per agent (default: `10`) |
176
204
 
177
205
  ### Config Locations
@@ -1,6 +1,6 @@
1
- import type { MyrmidonConfig } from '../config';
1
+ import { type MyrmidonConfig } from '../config';
2
2
  import type { AgentDefinition } from './titan';
3
- export declare function createMyrmidonAgent(index: number, config: MyrmidonConfig): AgentDefinition;
3
+ export declare function createMyrmidonAgent(index: number, config: MyrmidonConfig, maxResponseWords?: number): AgentDefinition;
4
4
  /**
5
5
  * @deprecated Use `createMyrmidonAgent`. Retained as a backwards-compatible
6
6
  * alias for the former "child agent" naming.
@@ -13,5 +13,5 @@ export declare function resolvePrompt(base: string, customPrompt?: string, custo
13
13
  /**
14
14
  * Build the Titan prompt with Myrmidon descriptions dynamically injected.
15
15
  */
16
- export declare function buildTitanPrompt(myrmidons: MyrmidonConfig[]): string;
17
- export declare function createTitanAgent(myrmidons: MyrmidonConfig[], model?: string, customPrompt?: string, customAppendPrompt?: string): AgentDefinition;
16
+ export declare function buildTitanPrompt(myrmidons: MyrmidonConfig[], maxResponseWords?: number): string;
17
+ export declare function createTitanAgent(myrmidons: MyrmidonConfig[], model?: string, customPrompt?: string, customAppendPrompt?: string, maxResponseWords?: number): AgentDefinition;
@@ -1,4 +1,9 @@
1
1
  export declare const TITAN_AGENT_NAME: "titan";
2
+ /**
3
+ * Default maximum word count enforced on a Myrmidon's final response to Titan.
4
+ * Overridable via the `maxResponseWords` plugin config key.
5
+ */
6
+ export declare const DEFAULT_MAX_RESPONSE_WORDS: 1000;
2
7
  export declare const ALL_AGENT_NAMES: readonly ["titan"];
3
8
  export type AgentName = (typeof ALL_AGENT_NAMES)[number];
4
9
  /** Agents that cannot be disabled. */
@@ -85,6 +85,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
85
85
  displayName: z.ZodOptional<z.ZodString>;
86
86
  provider: z.ZodOptional<z.ZodString>;
87
87
  }, z.core.$strict>>>;
88
+ maxResponseWords: z.ZodOptional<z.ZodNumber>;
88
89
  disabled_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
89
90
  backgroundJobs: z.ZodOptional<z.ZodObject<{
90
91
  maxSessionsPerAgent: z.ZodDefault<z.ZodNumber>;
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/config/constants.ts
2
2
  var TITAN_AGENT_NAME = "titan";
3
+ var DEFAULT_MAX_RESPONSE_WORDS = 1e3;
3
4
  var DELEGATION_REMINDER = `<internal_reminder>DELEGATE EVERYTHING POSSIBLE TO MYRMIDONS. You are the slowest agent by far - your only job is planning, routing, and synthesizing results. Never do work a Myrmidon can handle. Parallelize aggressively: tool calls made in the SAME response turn run concurrently \u2014 always batch all ready task() dispatches into ONE response, never one per turn. SAY WHAT YOU DO: if you announce launching N Myrmidons, emit exactly N task() calls in that same response \u2014 never announce multiple then dispatch only one. Before ending a dispatch turn, count your task() calls and confirm they match the number of Myrmidons you named. The task() tool takes only subagent_type, description, and prompt \u2014 never pass any other parameters. </internal_reminder>`;
4
5
  var DELEGATION_REMINDER_SENTINEL = "DELEGATE EVERYTHING POSSIBLE TO MYRMIDONS";
5
6
  var PER_MESSAGE_DELEGATION_REMINDER = `<delegation_directive>
@@ -51,6 +52,9 @@ var PluginConfigSchema = z.object({
51
52
  // Deprecated alias for `myrmidons`, retained for backwards compatibility.
52
53
  // If both are present, `myrmidons` takes precedence.
53
54
  children: z.array(MyrmidonConfigSchema).optional(),
55
+ // Maximum word count enforced on each Myrmidon's final response to Titan.
56
+ // Keeps Titan's context lean. Defaults to DEFAULT_MAX_RESPONSE_WORDS (1000).
57
+ maxResponseWords: z.number().int().min(1).optional(),
54
58
  disabled_tools: z.array(z.string()).optional(),
55
59
  backgroundJobs: z.object({
56
60
  maxSessionsPerAgent: z.number().int().min(1).max(10).default(10)
@@ -300,7 +304,7 @@ You are operating with a hard context budget. Exceeding it causes failure. Follo
300
304
  </ContextBudget>
301
305
 
302
306
  <OutputConstraints>
303
- - **CRITICAL: Your final response to Titan MUST be ONE PARAGRAPH, no more than 500 words.**
307
+ - **CRITICAL: Your final response to Titan MUST be ONE PARAGRAPH, no more than {{MAX_WORDS}} words.**
304
308
  - No preamble, no summary of what you did \u2014 just the results.
305
309
  - Report success/failure clearly in your first sentence.
306
310
  - Include only actionable details: file paths, line numbers, errors, generated code snippets.
@@ -317,10 +321,14 @@ You are operating with a hard context budget. Exceeding it causes failure. Follo
317
321
  {{TASK_PROMPT}}
318
322
  </Task>
319
323
  `;
320
- function createMyrmidonAgent(index, config) {
324
+ function createMyrmidonAgent(index, config, maxResponseWords = DEFAULT_MAX_RESPONSE_WORDS) {
321
325
  const name = `myrmidon-${index}`;
322
326
  const modelTypeHint = config.modelType === "dense" ? "You are running a dense model \u2014 you excel at logic, reasoning, and complex problem-solving. Use this strength for tasks requiring careful analysis." : "You are running a sparse model \u2014 you excel at fast information gathering, broad search, and rapid lookups. Use this strength for research and exploration tasks.";
323
- const prompt = `${MYRMIDON_PROMPT_TEMPLATE.replace("{{TASK_PROMPT}}", "")}
327
+ const template = MYRMIDON_PROMPT_TEMPLATE.replace(
328
+ "{{MAX_WORDS}}",
329
+ String(maxResponseWords)
330
+ );
331
+ const prompt = `${template.replace("{{TASK_PROMPT}}", "")}
324
332
 
325
333
  ${modelTypeHint}`;
326
334
  return {
@@ -348,7 +356,7 @@ function resolvePrompt(base, customPrompt, customAppendPrompt) {
348
356
 
349
357
  ${customAppendPrompt}` : effectiveBase;
350
358
  }
351
- function buildTitanPrompt(myrmidons) {
359
+ function buildTitanPrompt(myrmidons, maxResponseWords = DEFAULT_MAX_RESPONSE_WORDS) {
352
360
  const resolveProvider = resolveMyrmidonProvider;
353
361
  const myrmidonDescriptions = myrmidons.map((myrmidon, idx) => {
354
362
  const name = `myrmidon-${idx}`;
@@ -539,14 +547,14 @@ When delegating to a Myrmidon:
539
547
  - Be specific about the task and expected output format
540
548
  - Reference file paths/lines, don't paste full contents
541
549
  - Set clear boundaries: what to do and what not to do
542
- - Tell the Myrmidon to report back concisely (one paragraph, under 500 words max)
550
+ - Tell the Myrmidon to report back concisely (one paragraph, under ${maxResponseWords} words max)
543
551
  </CommunicationWithMyrmidons>
544
552
 
545
553
  ${DELEGATION_REMINDER}
546
554
  `;
547
555
  }
548
- function createTitanAgent(myrmidons, model, customPrompt, customAppendPrompt) {
549
- const basePrompt = buildTitanPrompt(myrmidons);
556
+ function createTitanAgent(myrmidons, model, customPrompt, customAppendPrompt, maxResponseWords = DEFAULT_MAX_RESPONSE_WORDS) {
557
+ const basePrompt = buildTitanPrompt(myrmidons, maxResponseWords);
550
558
  const prompt = resolvePrompt(basePrompt, customPrompt, customAppendPrompt);
551
559
  const definition = {
552
560
  name: "titan",
@@ -565,6 +573,7 @@ function createTitanAgent(myrmidons, model, customPrompt, customAppendPrompt) {
565
573
  // src/agents/index.ts
566
574
  function createAgents(config, options) {
567
575
  const myrmidons = getMyrmidonConfigs(config);
576
+ const maxResponseWords = config?.maxResponseWords ?? DEFAULT_MAX_RESPONSE_WORDS;
568
577
  const titanModel = config?.titan?.model;
569
578
  const titanPrompts = loadAgentPrompt("titan", {
570
579
  projectDirectory: options?.projectDirectory
@@ -573,7 +582,8 @@ function createAgents(config, options) {
573
582
  myrmidons,
574
583
  typeof titanModel === "string" ? titanModel : void 0,
575
584
  config?.titan?.prompt,
576
- void 0
585
+ void 0,
586
+ maxResponseWords
577
587
  );
578
588
  titan.config.prompt = resolvePrompt(
579
589
  titan.config.prompt ?? "",
@@ -589,7 +599,7 @@ function createAgents(config, options) {
589
599
  }
590
600
  }
591
601
  const myrmidonAgents = myrmidons.map(
592
- (myrmidonConfig, idx) => createMyrmidonAgent(idx, myrmidonConfig)
602
+ (myrmidonConfig, idx) => createMyrmidonAgent(idx, myrmidonConfig, maxResponseWords)
593
603
  );
594
604
  const aliasAgents = myrmidonAgents.map((agent, idx) => ({
595
605
  ...agent,
@@ -41,6 +41,12 @@
41
41
  "$ref": "#/$defs/myrmidon"
42
42
  }
43
43
  },
44
+ "maxResponseWords": {
45
+ "type": "integer",
46
+ "minimum": 1,
47
+ "default": 1000,
48
+ "description": "Maximum word count enforced on each Myrmidon's final (one-paragraph) response to Titan. Keeps Titan's context lean. Defaults to 1000."
49
+ },
44
50
  "disabled_tools": {
45
51
  "type": "array",
46
52
  "description": "Tool names to disable completely. Disabled tools are not registered with OpenCode.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-titan",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Distributed delegation plugin for OpenCode — Titan orchestrator with parallel Myrmidons",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",