@formthefog/stratus 2026.2.24 → 2026.3.20

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 (75) hide show
  1. package/.github/sentinel/action.yml +100 -0
  2. package/.github/sentinel/dist/codebase.d.ts +3 -0
  3. package/.github/sentinel/dist/codebase.d.ts.map +1 -0
  4. package/.github/sentinel/dist/context.d.ts +6 -0
  5. package/.github/sentinel/dist/context.d.ts.map +1 -0
  6. package/.github/sentinel/dist/fixer.d.ts +6 -0
  7. package/.github/sentinel/dist/fixer.d.ts.map +1 -0
  8. package/.github/sentinel/dist/index.d.ts +1 -0
  9. package/.github/sentinel/dist/index.d.ts.map +1 -0
  10. package/.github/sentinel/dist/index.js +68808 -0
  11. package/.github/sentinel/dist/index.js.map +1 -0
  12. package/.github/sentinel/dist/licenses.txt +1152 -0
  13. package/.github/sentinel/dist/models/anthropic.d.ts +26 -0
  14. package/.github/sentinel/dist/models/anthropic.d.ts.map +1 -0
  15. package/.github/sentinel/dist/models/openai.d.ts +26 -0
  16. package/.github/sentinel/dist/models/openai.d.ts.map +1 -0
  17. package/.github/sentinel/dist/models/openrouter.d.ts +31 -0
  18. package/.github/sentinel/dist/models/openrouter.d.ts.map +1 -0
  19. package/.github/sentinel/dist/models/types.d.ts +37 -0
  20. package/.github/sentinel/dist/models/types.d.ts.map +1 -0
  21. package/.github/sentinel/dist/orchestrator.d.ts +3 -0
  22. package/.github/sentinel/dist/orchestrator.d.ts.map +1 -0
  23. package/.github/sentinel/dist/policy.d.ts +15 -0
  24. package/.github/sentinel/dist/policy.d.ts.map +1 -0
  25. package/.github/sentinel/dist/reporter.d.ts +8 -0
  26. package/.github/sentinel/dist/reporter.d.ts.map +1 -0
  27. package/.github/sentinel/dist/responder.d.ts +6 -0
  28. package/.github/sentinel/dist/responder.d.ts.map +1 -0
  29. package/.github/sentinel/dist/router.d.ts +2 -0
  30. package/.github/sentinel/dist/router.d.ts.map +1 -0
  31. package/.github/sentinel/dist/schemas/config.d.ts +195 -0
  32. package/.github/sentinel/dist/schemas/config.d.ts.map +1 -0
  33. package/.github/sentinel/dist/schemas/fix.d.ts +130 -0
  34. package/.github/sentinel/dist/schemas/fix.d.ts.map +1 -0
  35. package/.github/sentinel/dist/schemas/review.d.ts +275 -0
  36. package/.github/sentinel/dist/schemas/review.d.ts.map +1 -0
  37. package/.github/sentinel/dist/sourcemap-register.js +1 -0
  38. package/.github/sentinel/dist/subway.d.ts +31 -0
  39. package/.github/sentinel/dist/subway.d.ts.map +1 -0
  40. package/.github/sentinel/dist/types.d.ts +210 -0
  41. package/.github/sentinel/dist/types.d.ts.map +1 -0
  42. package/.github/sentinel/package-lock.json +2389 -0
  43. package/.github/sentinel/package.json +29 -0
  44. package/.github/sentinel/src/codebase.ts +265 -0
  45. package/.github/sentinel/src/context.ts +182 -0
  46. package/.github/sentinel/src/fixer.ts +353 -0
  47. package/.github/sentinel/src/index.ts +263 -0
  48. package/.github/sentinel/src/models/anthropic.ts +244 -0
  49. package/.github/sentinel/src/models/openai.ts +242 -0
  50. package/.github/sentinel/src/models/openrouter.ts +319 -0
  51. package/.github/sentinel/src/models/types.ts +35 -0
  52. package/.github/sentinel/src/orchestrator.ts +287 -0
  53. package/.github/sentinel/src/policy.ts +133 -0
  54. package/.github/sentinel/src/reporter.ts +666 -0
  55. package/.github/sentinel/src/responder.ts +156 -0
  56. package/.github/sentinel/src/router.ts +308 -0
  57. package/.github/sentinel/src/schemas/config.ts +84 -0
  58. package/.github/sentinel/src/schemas/fix.ts +44 -0
  59. package/.github/sentinel/src/schemas/review.ts +73 -0
  60. package/.github/sentinel/src/subway.ts +250 -0
  61. package/.github/sentinel/src/types.ts +234 -0
  62. package/.github/sentinel/tsconfig.json +19 -0
  63. package/.github/sentinel.yml +34 -0
  64. package/.github/workflows/sentinel.yml +55 -0
  65. package/README.md +88 -102
  66. package/SECURITY.md +21 -10
  67. package/TROUBLESHOOTING.md +2 -2
  68. package/index.ts +255 -114
  69. package/openclaw.plugin.json +50 -26
  70. package/package.json +1 -1
  71. package/skills/stratus-info/SKILL.md +70 -10
  72. package/src/client.ts +78 -18
  73. package/src/config.ts +29 -8
  74. package/src/setup.ts +82 -61
  75. package/src/types.ts +11 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: stratus-info
3
3
  description: Knowledge base about Stratus X1-AC world model system
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  author: Stratus Team
6
6
  ---
7
7
 
@@ -25,10 +25,12 @@ This is a **hybrid architecture** with two capabilities:
25
25
  - Predicts compressed representations, not raw outputs
26
26
  - Enables planning by simulating action sequences before executing
27
27
 
28
- ### 2. Policy Head (Action Selector): "What action achieves this goal?"
28
+ ### 2. Policy Head v3 (Action Selector): "What action achieves this goal?"
29
+ - 94.41% masked accuracy — brain-guided action sequencing
29
30
  - Predicts the best action given current state + goal state
31
+ - Returns `brain_confidence` and `brain_goal_proximity` scores
30
32
  - Fast decision-making for known patterns
31
- - Provides confidence scores (low confidence → escalate to LLM)
33
+ - Low confidence → escalate to LLM
32
34
 
33
35
  **Key Innovation:**
34
36
  Instead of just guessing the next action, Stratus can **simulate the outcome of multiple actions** and pick the one most likely to achieve the goal.
@@ -44,6 +46,7 @@ Agents learn from each other in real-time:
44
46
  - When one agent discovers an effective approach, that knowledge flows to all agents
45
47
  - **Continuous learning**: successful patterns get canonicalized and fine-tuned into the model
46
48
  - **Domain-specific fine-tunes** (e-commerce, dev tools, research, etc.) share learnings back to the base model
49
+ - **Hierarchical tool architecture**: Brain handles sub-agent routing (orchestrator → sub-agents with specialized tool sets)
47
50
 
48
51
  It's like a hive mind for AI agents — practical distributed learning at scale.
49
52
 
@@ -78,10 +81,35 @@ see → simulate outcomes → pick best action → act
78
81
 
79
82
  ---
80
83
 
84
+ ## Authentication & Keys
85
+
86
+ ### Zero-Config (Formation Pool)
87
+ No API key needed. Formation holds pooled OpenRouter keys as a universal fallback.
88
+ - 25% markup on usage
89
+ - Instant access to all 2050+ models on day 0
90
+ - No setup required
91
+
92
+ ### BYOK (Bring Your Own Key)
93
+ Set `STRATUS_API_KEY` to bypass the Formation pool (no markup).
94
+ - Keys must start with `stratus_sk_`
95
+ - Get yours at https://stratus.run
96
+
97
+ ### Inline Provider Keys
98
+ Pass provider keys per-request for direct BYOK passthrough:
99
+ - `openai_key` — OpenAI direct
100
+ - `anthropic_key` — Anthropic direct
101
+ - `gemini_key` — Google Gemini direct (also sent as `X-Google-Key` header)
102
+ - Three-tier priority: headers > body > vault
103
+
104
+ Responses include `key_source: "user"` or `key_source: "formation"` so you always know which path was used.
105
+
106
+ ---
107
+
81
108
  ## Available Models
82
109
 
83
- Stratus provides **75 models** in the format:
110
+ Stratus provides **2050+ models** dynamically discovered via OpenRouter at startup.
84
111
 
112
+ Model format:
85
113
  ```
86
114
  stratus-x1ac-{size}-{llm}
87
115
  ```
@@ -89,9 +117,10 @@ stratus-x1ac-{size}-{llm}
89
117
  **Sizes:** `small`, `base`, `large`, `xl`, `huge`
90
118
 
91
119
  **Recommended LLMs:**
92
- - `claude-sonnet-4-5` - Claude 4.5 Sonnet (recommended)
93
- - `gpt-4o` - GPT-4 Optimized
94
- - `claude-haiku-4-5` - Fast, cost-effective
120
+ - `claude-sonnet-4-5` Claude 4.5 Sonnet (recommended)
121
+ - `gpt-4o` GPT-4 Optimized
122
+ - `claude-haiku-4-5` Fast, cost-effective
123
+ - `gemini-2.0-flash` — High context, fast
95
124
 
96
125
  **Example:**
97
126
  ```bash
@@ -99,6 +128,8 @@ openclaw agent "Plan a multi-step research task" \
99
128
  --model stratus/stratus-x1ac-base-claude-sonnet-4-5
100
129
  ```
101
130
 
131
+ Use `/stratus models` to fetch the full live list from the API.
132
+
102
133
  ---
103
134
 
104
135
  ## Available Tools
@@ -121,7 +152,7 @@ Generate 768-dimensional semantic state embeddings.
121
152
  ```
122
153
 
123
154
  ### `stratus_rollout`
124
- Multi-step action sequence planning.
155
+ Multi-step action sequence planning via Policy Head v3.
125
156
 
126
157
  **Use cases:**
127
158
  - Task decomposition
@@ -129,6 +160,12 @@ Multi-step action sequence planning.
129
160
  - Goal-oriented reasoning
130
161
  - Multi-step workflows
131
162
 
163
+ **Response fields:**
164
+ - `steps` — ordered action sequence
165
+ - `metadata.brain_confidence` — how confident the brain is in the plan
166
+ - `metadata.brain_goal_proximity` — estimated closeness to goal state
167
+ - `metadata.key_source` — "user" or "formation"
168
+
132
169
  **Example:**
133
170
  ```typescript
134
171
  {
@@ -162,6 +199,27 @@ const response = await openai.chat.completions.create({
162
199
 
163
200
  Stratus handles planning, then routes execution to the specified LLM (GPT-4o, Claude, etc.).
164
201
 
202
+ Non-Stratus models (e.g. raw `gpt-4o`) bypass the world model pipeline entirely (LLM passthrough).
203
+
204
+ Tool limit: **1024 tools per request** (raised from 100).
205
+
206
+ ---
207
+
208
+ ## SDKs
209
+
210
+ - **TypeScript**: This OpenClaw plugin (`@formthefog/stratus`)
211
+ - **Python**: `pip install stratus-sdk-py` (v0.0.6, full TS SDK parity, 76 tests)
212
+ - **Pi Agent**: `pi-formation` package (7 curated X1-AC models for Pi coding agent)
213
+
214
+ ---
215
+
216
+ ## Billing
217
+
218
+ - **Signup credits**: 200 credits (20K units) for new users
219
+ - **Saved cards**: Stripe-backed auto top-up when balance is low
220
+ - **Subscriptions**: Monthly credit bundles (starter/pro/enterprise) — 25% more credits vs one-time
221
+ - **Formation pool**: 25% markup on pooled key usage; BYOK users pay no markup
222
+
165
223
  ---
166
224
 
167
225
  ## Common Misconceptions
@@ -171,6 +229,7 @@ Stratus handles planning, then routes execution to the specified LLM (GPT-4o, Cl
171
229
  | "Stratus is just Claude with extended reasoning" | Stratus is a world model that predicts action consequences + collective learning network |
172
230
  | "It's a language model" | It's a hybrid architecture (world model + policy head) that routes to LLMs for execution |
173
231
  | "It's an LLM wrapper" | It's a JEPA-based predictive system with its own training and inference pipeline |
232
+ | "You need an API key" | Formation pool provides zero-config access; BYOK is optional |
174
233
 
175
234
  ---
176
235
 
@@ -187,10 +246,10 @@ For full technical details, see:
187
246
 
188
247
  1. **Install plugin:**
189
248
  ```bash
190
- openclaw plugins install @hathbanger/stratus
249
+ openclaw plugins install @formthefog/stratus
191
250
  ```
192
251
 
193
- 2. **Set API key:**
252
+ 2. **(Optional) Set API key for BYOK:**
194
253
  ```bash
195
254
  export STRATUS_API_KEY=stratus_sk_your_key_here
196
255
  ```
@@ -215,6 +274,7 @@ For full technical details, see:
215
274
  - Research workflows
216
275
  - Complex decision-making with uncertain outcomes
217
276
  - Tasks requiring "what-if" simulation
277
+ - Large tool sets (up to 1024 tools)
218
278
 
219
279
  **Overkill for:**
220
280
  - Simple Q&A
package/src/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  StratusEmbeddingsRequest,
3
3
  StratusEmbeddingsResponse,
4
+ StratusInlineKeys,
4
5
  StratusPluginConfig,
5
6
  StratusRolloutRequest,
6
7
  StratusRolloutResponse,
@@ -10,44 +11,74 @@ import type {
10
11
  * SECURITY MANIFEST
11
12
  *
12
13
  * Environment Variables Accessed:
13
- * - STRATUS_API_KEY: User's Stratus API key (optional, can use config instead)
14
+ * - STRATUS_API_KEY: User's Stratus API key (optional Formation pool used as fallback)
15
+ * - OPENAI_API_KEY: Optional inline key for BYOK passthrough
16
+ * - ANTHROPIC_API_KEY: Optional inline key for BYOK passthrough
17
+ * - GOOGLE_API_KEY: Optional inline key for BYOK passthrough (also sent as X-Google-Key header)
14
18
  *
15
19
  * Network Endpoints:
16
20
  * - https://api.stratus.run/v1/embeddings (POST)
17
21
  * - https://api.stratus.run/v1/rollout (POST)
22
+ * - https://api.stratus.run/v1/models (GET)
18
23
  *
19
24
  * Data Transmitted:
20
- * - Authorization header: Bearer token (Stratus API key)
21
- * - Request body: User-provided text, goals, and parameters
25
+ * - Authorization header: Bearer token (Stratus API key, when provided)
26
+ * - X-Google-Key header: Google API key (when provided)
27
+ * - Request body: User-provided text, goals, parameters, and optional inline LLM keys
22
28
  *
23
29
  * Data Retention:
24
30
  * - All data sent to Stratus API per their privacy policy: https://stratus.run/privacy
25
31
  * - No local storage of credentials beyond process memory
26
32
  *
27
33
  * Security:
28
- * - API key validated (must start with 'stratus_sk_')
34
+ * - API key validated when present (must start with 'stratus_sk_')
35
+ * - Keyless operation uses Formation pooled keys (25% markup)
29
36
  * - HTTPS-only connections
30
37
  * - Keys never logged or persisted to disk by this plugin
31
38
  */
32
39
 
33
40
  export interface StratusClientConfig {
34
- apiKey: string;
41
+ apiKey?: string;
35
42
  baseUrl: string;
43
+ inlineKeys?: StratusInlineKeys;
36
44
  }
37
45
 
38
46
  export class StratusClient {
39
47
  constructor(private config: StratusClientConfig) {}
40
48
 
49
+ private buildHeaders(): Record<string, string> {
50
+ const headers: Record<string, string> = {
51
+ "Content-Type": "application/json",
52
+ };
53
+
54
+ if (this.config.apiKey) {
55
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
56
+ }
57
+
58
+ if (this.config.inlineKeys?.gemini_key) {
59
+ headers["X-Google-Key"] = this.config.inlineKeys.gemini_key;
60
+ }
61
+
62
+ return headers;
63
+ }
64
+
65
+ private buildInlineKeyBody(): Record<string, string> {
66
+ const body: Record<string, string> = {};
67
+ if (this.config.inlineKeys?.openai_key) body.openai_key = this.config.inlineKeys.openai_key;
68
+ if (this.config.inlineKeys?.anthropic_key) body.anthropic_key = this.config.inlineKeys.anthropic_key;
69
+ if (this.config.inlineKeys?.gemini_key) body.gemini_key = this.config.inlineKeys.gemini_key;
70
+ return body;
71
+ }
72
+
41
73
  private async request<T>(endpoint: string, body: unknown): Promise<T> {
42
74
  const url = `${this.config.baseUrl}${endpoint}`;
75
+ const inlineKeys = this.buildInlineKeyBody();
76
+ const mergedBody = { ...(body as Record<string, unknown>), ...inlineKeys };
43
77
 
44
78
  const response = await fetch(url, {
45
79
  method: "POST",
46
- headers: {
47
- Authorization: `Bearer ${this.config.apiKey}`,
48
- "Content-Type": "application/json",
49
- },
50
- body: JSON.stringify(body),
80
+ headers: this.buildHeaders(),
81
+ body: JSON.stringify(mergedBody),
51
82
  });
52
83
 
53
84
  if (!response.ok) {
@@ -76,23 +107,52 @@ export class StratusClient {
76
107
  return_intermediate: params.return_intermediate ?? true,
77
108
  });
78
109
  }
110
+
111
+ async listModels(): Promise<{ object: string; data: Array<{ id: string; object: string; created: number; owned_by: string }> }> {
112
+ const url = `${this.config.baseUrl}/v1/models`;
113
+ const headers: Record<string, string> = {};
114
+ if (this.config.apiKey) {
115
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
116
+ }
117
+
118
+ const response = await fetch(url, {
119
+ method: "GET",
120
+ headers,
121
+ });
122
+
123
+ if (!response.ok) {
124
+ const errorText = await response.text();
125
+ throw new Error(
126
+ `Stratus API error (${response.status}): ${errorText || response.statusText}`,
127
+ );
128
+ }
129
+
130
+ return response.json();
131
+ }
79
132
  }
80
133
 
81
134
  export function createStratusClient(config: StratusPluginConfig | undefined): StratusClient {
82
135
  const apiKey = config?.apiKey || process.env.STRATUS_API_KEY;
83
136
  const baseUrl = config?.baseUrl || "https://api.stratus.run";
84
137
 
85
- if (!apiKey) {
86
- throw new Error(
87
- "Stratus API key not configured. Set STRATUS_API_KEY env var or plugins.stratus.apiKey in config.",
88
- );
89
- }
90
-
91
- if (!apiKey.startsWith("stratus_sk_")) {
138
+ if (apiKey && !apiKey.startsWith("stratus_sk_")) {
92
139
  throw new Error(
93
140
  `Invalid Stratus API key format. Expected key starting with 'stratus_sk_', got '${apiKey.substring(0, 10)}...'`,
94
141
  );
95
142
  }
96
143
 
97
- return new StratusClient({ apiKey, baseUrl });
144
+ const inlineKeys: StratusInlineKeys = {
145
+ ...config?.inlineKeys,
146
+ openai_key: config?.inlineKeys?.openai_key || process.env.OPENAI_API_KEY,
147
+ anthropic_key: config?.inlineKeys?.anthropic_key || process.env.ANTHROPIC_API_KEY,
148
+ gemini_key: config?.inlineKeys?.gemini_key || process.env.GOOGLE_API_KEY,
149
+ };
150
+
151
+ const hasAnyInlineKey = inlineKeys.openai_key || inlineKeys.anthropic_key || inlineKeys.gemini_key;
152
+
153
+ return new StratusClient({
154
+ apiKey: apiKey || undefined,
155
+ baseUrl,
156
+ inlineKeys: hasAnyInlineKey ? inlineKeys : undefined,
157
+ });
98
158
  }
package/src/config.ts CHANGED
@@ -3,12 +3,18 @@ import type { StratusPluginConfig } from "./types.js";
3
3
 
4
4
  export const StratusConfigSchema: OpenClawPluginConfigSchema = {
5
5
  parse(value: unknown): StratusPluginConfig {
6
- const raw =
6
+ const entry =
7
7
  value && typeof value === "object" && !Array.isArray(value)
8
8
  ? (value as Record<string, unknown>)
9
9
  : {};
10
10
 
11
- const enabled = typeof raw.enabled === "boolean" ? raw.enabled : true;
11
+ const enabled = typeof entry.enabled === "boolean" ? entry.enabled : true;
12
+
13
+ const raw =
14
+ entry.config && typeof entry.config === "object" && !Array.isArray(entry.config)
15
+ ? (entry.config as Record<string, unknown>)
16
+ : {};
17
+
12
18
  const apiKey = typeof raw.apiKey === "string" ? raw.apiKey : process.env.STRATUS_API_KEY;
13
19
  const baseUrl = typeof raw.baseUrl === "string" ? raw.baseUrl : "https://api.stratus.run";
14
20
 
@@ -37,10 +43,22 @@ export const StratusConfigSchema: OpenClawPluginConfigSchema = {
37
43
  ? (tools.rollout as Record<string, unknown>)
38
44
  : {};
39
45
 
46
+ const inlineKeysRaw =
47
+ raw.inlineKeys && typeof raw.inlineKeys === "object" && !Array.isArray(raw.inlineKeys)
48
+ ? (raw.inlineKeys as Record<string, unknown>)
49
+ : {};
50
+
51
+ const inlineKeys = {
52
+ openai_key: typeof inlineKeysRaw.openai_key === "string" ? inlineKeysRaw.openai_key : undefined,
53
+ anthropic_key: typeof inlineKeysRaw.anthropic_key === "string" ? inlineKeysRaw.anthropic_key : undefined,
54
+ gemini_key: typeof inlineKeysRaw.gemini_key === "string" ? inlineKeysRaw.gemini_key : undefined,
55
+ };
56
+
40
57
  return {
41
58
  enabled,
42
59
  apiKey,
43
60
  baseUrl,
61
+ inlineKeys,
44
62
  provider: {
45
63
  enabled: providerEnabled,
46
64
  defaultModel,
@@ -57,14 +75,17 @@ export const StratusConfigSchema: OpenClawPluginConfigSchema = {
57
75
  },
58
76
  uiHints: {
59
77
  enabled: { label: "Enabled" },
60
- apiKey: { label: "API Key", sensitive: true },
61
- baseUrl: { label: "Base URL", placeholder: "https://api.stratus.run" },
62
- "provider.enabled": { label: "Provider Enabled" },
63
- "provider.defaultModel": {
78
+ "config.apiKey": { label: "API Key", sensitive: true },
79
+ "config.baseUrl": { label: "Base URL", placeholder: "https://api.stratus.run" },
80
+ "config.inlineKeys.openai_key": { label: "OpenAI Key (BYOK)", sensitive: true },
81
+ "config.inlineKeys.anthropic_key": { label: "Anthropic Key (BYOK)", sensitive: true },
82
+ "config.inlineKeys.gemini_key": { label: "Google Gemini Key (BYOK)", sensitive: true },
83
+ "config.provider.enabled": { label: "Provider Enabled" },
84
+ "config.provider.defaultModel": {
64
85
  label: "Default Model",
65
86
  placeholder: "stratus-x1ac-base-claude-sonnet-4-5",
66
87
  },
67
- "tools.embeddings.enabled": { label: "Embeddings Tool Enabled" },
68
- "tools.rollout.enabled": { label: "Rollout Tool Enabled" },
88
+ "config.tools.embeddings.enabled": { label: "Embeddings Tool Enabled" },
89
+ "config.tools.rollout.enabled": { label: "Rollout Tool Enabled" },
69
90
  },
70
91
  };
package/src/setup.ts CHANGED
@@ -14,35 +14,43 @@ interface SetupResult {
14
14
  details?: string[];
15
15
  }
16
16
 
17
- export async function setupStratus(prompter?: any): Promise<SetupResult> {
17
+ interface SetupOptions {
18
+ apiKey?: string;
19
+ silent?: boolean;
20
+ }
21
+
22
+ export async function setupStratus(prompterOrOptions?: any, options?: SetupOptions): Promise<SetupResult> {
23
+ const opts: SetupOptions = options ?? (
24
+ prompterOrOptions && typeof prompterOrOptions === "object" && ("apiKey" in prompterOrOptions || "silent" in prompterOrOptions)
25
+ ? prompterOrOptions as SetupOptions
26
+ : {}
27
+ );
28
+
18
29
  const details: string[] = [];
19
30
 
20
31
  try {
21
- // Step 1: Check for existing API key
22
- const apiKey = process.env.STRATUS_API_KEY;
23
-
24
- if (!apiKey) {
25
- return {
26
- success: false,
27
- message: "STRATUS_API_KEY not found",
28
- details: [
29
- "Please set your Stratus API key as an environment variable:",
30
- "",
31
- " export STRATUS_API_KEY=stratus_sk_your_key_here",
32
- "",
33
- "Or add to your shell config (~/.zshrc or ~/.bashrc):",
34
- "",
35
- " echo 'export STRATUS_API_KEY=stratus_sk_your_key_here' >> ~/.zshrc",
36
- " source ~/.zshrc",
37
- "",
38
- "Get your API key at: https://stratus.run",
39
- ],
40
- };
32
+ const apiKey = opts.apiKey || process.env.STRATUS_API_KEY;
33
+ const usingFormationPool = !apiKey;
34
+
35
+ if (apiKey) {
36
+ if (!apiKey.startsWith("stratus_sk_")) {
37
+ return {
38
+ success: false,
39
+ message: "Invalid API key format",
40
+ details: [
41
+ "API key must start with 'stratus_sk_'",
42
+ "",
43
+ "Get your API key at: https://stratus.run",
44
+ ],
45
+ };
46
+ }
47
+ details.push(`✓ Using Stratus API key (BYOK — no markup)${opts.apiKey ? " [provided via arg]" : " [from environment]"}`);
48
+ } else {
49
+ details.push(" No API key found — using Formation pooled keys (zero-config)");
50
+ details.push(" ℹ Formation pool applies a 25% markup on usage");
51
+ details.push(" 💡 Set STRATUS_API_KEY for BYOK (no markup)");
41
52
  }
42
53
 
43
- details.push("✓ Using STRATUS_API_KEY from environment");
44
-
45
- // Paths
46
54
  const homeDir = os.homedir();
47
55
  const openclawConfig = path.join(homeDir, ".openclaw", "openclaw.json");
48
56
  const authProfiles = path.join(
@@ -54,28 +62,22 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
54
62
  "auth-profiles.json"
55
63
  );
56
64
 
57
- // Step 2: Update OpenClaw config
58
65
  details.push("🔧 Updating OpenClaw configuration...");
59
66
 
60
67
  if (fs.existsSync(openclawConfig)) {
61
- // Backup
62
68
  const backupPath = `${openclawConfig}.backup-${Date.now()}`;
63
69
  fs.copyFileSync(openclawConfig, backupPath);
64
70
  details.push(` 📦 Created backup: ${path.basename(backupPath)}`);
65
71
 
66
- // Read and update config
67
72
  const config = JSON.parse(fs.readFileSync(openclawConfig, "utf-8"));
68
73
 
69
- // Add models.providers.stratus if not present
70
- if (!config.models) {
71
- config.models = {};
72
- }
73
- if (!config.models.providers) {
74
- config.models.providers = {};
75
- }
74
+ if (!config.models) config.models = {};
75
+ if (!config.models.providers) config.models.providers = {};
76
+
76
77
  if (!config.models.providers.stratus) {
77
78
  config.models.providers.stratus = {
78
79
  baseUrl: "https://api.stratus.run/v1",
80
+ ...(apiKey ? { apiKey } : {}),
79
81
  api: "openai-completions",
80
82
  models: [
81
83
  {
@@ -91,27 +93,31 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
91
93
  };
92
94
  details.push(" ✓ Added Stratus provider configuration");
93
95
  } else {
96
+ if (apiKey && config.models.providers.stratus.apiKey !== apiKey) {
97
+ config.models.providers.stratus.apiKey = apiKey;
98
+ details.push(" ✓ Updated API key in provider config");
99
+ }
94
100
  details.push(" ✓ Stratus provider already configured");
95
101
  }
96
102
 
97
- // Add model alias
98
- if (!config.agents?.defaults?.models) {
99
- if (!config.agents) config.agents = {};
100
- if (!config.agents.defaults) config.agents.defaults = {};
101
- if (!config.agents.defaults.models) config.agents.defaults.models = {};
103
+ if (!config.plugins) config.plugins = {};
104
+ if (!config.plugins.entries) config.plugins.entries = {};
105
+ if (!config.plugins.entries.stratus) {
106
+ config.plugins.entries.stratus = { enabled: true, config: {} };
102
107
  }
103
-
104
- if (!config.agents.defaults.models["stratus/stratus-x1ac-base-claude-sonnet-4-5"]) {
105
- config.agents.defaults.models["stratus/stratus-x1ac-base-claude-sonnet-4-5"] = {
106
- alias: "stratus",
107
- params: {},
108
+ if (apiKey) {
109
+ config.plugins.entries.stratus.config = {
110
+ ...config.plugins.entries.stratus.config,
111
+ apiKey,
108
112
  };
109
- details.push(" ✓ Added model alias 'stratus'");
110
- } else {
111
- details.push(" ✓ Model alias already configured");
113
+ details.push(" ✓ Persisted API key in plugin config");
114
+ }
115
+
116
+ if (config.agents?.defaults?.models?.["stratus/stratus-x1ac-base-claude-sonnet-4-5"]) {
117
+ delete config.agents.defaults.models["stratus/stratus-x1ac-base-claude-sonnet-4-5"];
118
+ details.push(" ✓ Removed restrictive model alias (allows all Stratus models)");
112
119
  }
113
120
 
114
- // Write updated config
115
121
  fs.writeFileSync(openclawConfig, JSON.stringify(config, null, 2));
116
122
  } else {
117
123
  return {
@@ -125,7 +131,6 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
125
131
  };
126
132
  }
127
133
 
128
- // Step 3: Update auth profiles
129
134
  details.push("🔑 Configuring authentication...");
130
135
 
131
136
  const authDir = path.dirname(authProfiles);
@@ -135,14 +140,10 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
135
140
 
136
141
  let authConfig: any;
137
142
  if (fs.existsSync(authProfiles)) {
138
- // Backup
139
143
  const backupPath = `${authProfiles}.backup-${Date.now()}`;
140
144
  fs.copyFileSync(authProfiles, backupPath);
141
-
142
- // Update existing
143
145
  authConfig = JSON.parse(fs.readFileSync(authProfiles, "utf-8"));
144
146
  } else {
145
- // Create new
146
147
  authConfig = {
147
148
  version: 1,
148
149
  profiles: {},
@@ -151,16 +152,26 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
151
152
  };
152
153
  }
153
154
 
154
- // Add Stratus profile
155
- authConfig.profiles["stratus:default"] = {
156
- type: "api_key",
157
- provider: "stratus",
158
- key: apiKey,
159
- };
160
- authConfig.lastGood.stratus = "stratus:default";
155
+ if (apiKey) {
156
+ authConfig.profiles["stratus:default"] = {
157
+ type: "api_key",
158
+ provider: "stratus",
159
+ key: apiKey,
160
+ };
161
+ authConfig.lastGood.stratus = "stratus:default";
162
+ details.push(" ✓ Updated auth profile (BYOK)");
163
+ } else {
164
+ authConfig.profiles["stratus:formation-pool"] = {
165
+ type: "formation_pool",
166
+ provider: "stratus",
167
+ };
168
+ authConfig.lastGood.stratus = "stratus:formation-pool";
169
+ details.push(" ✓ Configured Formation pool auth (zero-config)");
170
+ }
161
171
 
162
172
  fs.writeFileSync(authProfiles, JSON.stringify(authConfig, null, 2));
163
- details.push(" ✓ Updated auth profile");
173
+
174
+ const modeLabel = usingFormationPool ? "Formation pool (zero-config)" : "BYOK (no markup)";
164
175
 
165
176
  return {
166
177
  success: true,
@@ -168,6 +179,8 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
168
179
  details: [
169
180
  ...details,
170
181
  "",
182
+ `🔒 Auth mode: ${modeLabel}`,
183
+ "",
171
184
  "🎯 Next steps:",
172
185
  " 1. Restart gateway: openclaw gateway stop && openclaw gateway install",
173
186
  " 2. Verify: openclaw models list | grep stratus",
@@ -176,6 +189,14 @@ export async function setupStratus(prompter?: any): Promise<SetupResult> {
176
189
  "📚 Available tools:",
177
190
  " • stratus_embeddings - Generate semantic embeddings",
178
191
  " • stratus_rollout - Multi-step task planning",
192
+ ...(usingFormationPool
193
+ ? [
194
+ "",
195
+ "💡 To remove the 25% Formation markup, set your own key:",
196
+ " export STRATUS_API_KEY=stratus_sk_your_key_here",
197
+ " Then re-run /stratus setup",
198
+ ]
199
+ : []),
179
200
  ],
180
201
  };
181
202
  } catch (error) {
package/src/types.ts CHANGED
@@ -38,13 +38,24 @@ export interface StratusRolloutResponse {
38
38
  metadata?: {
39
39
  model: string;
40
40
  duration_ms?: number;
41
+ brain_confidence?: number;
42
+ brain_goal_proximity?: number;
43
+ key_source?: "user" | "formation";
44
+ formation_markup_applied?: number;
41
45
  };
42
46
  }
43
47
 
48
+ export interface StratusInlineKeys {
49
+ openai_key?: string;
50
+ anthropic_key?: string;
51
+ gemini_key?: string;
52
+ }
53
+
44
54
  export interface StratusPluginConfig {
45
55
  enabled?: boolean;
46
56
  apiKey?: string;
47
57
  baseUrl?: string;
58
+ inlineKeys?: StratusInlineKeys;
48
59
  provider?: {
49
60
  enabled?: boolean;
50
61
  defaultModel?: string;