@formthefog/stratus 2026.2.24 → 2026.3.19

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 +219 -109
  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 +53 -61
  75. package/src/types.ts +11 -0
package/index.ts CHANGED
@@ -4,6 +4,9 @@ import type { StratusPluginConfig } from "./src/types.js";
4
4
  import { createStratusClient } from "./src/client.js";
5
5
  import { StratusConfigSchema } from "./src/config.js";
6
6
  import { setupStratus } from "./src/setup.js";
7
+ import { readFileSync, writeFileSync, existsSync } from "fs";
8
+ import { join } from "path";
9
+ import { homedir } from "os";
7
10
 
8
11
  const PROVIDER_ID = "stratus";
9
12
  const PROVIDER_LABEL = "Stratus";
@@ -26,59 +29,108 @@ function buildModelDefinition(params: {
26
29
  };
27
30
  }
28
31
 
29
- const STRATUS_SIZES = ["small", "base", "large", "xl", "huge"] as const;
30
-
31
- const OPENAI_MODELS = [
32
- { id: "gpt-4o", name: "GPT-4o", context: 128000, tokens: 8192 },
33
- { id: "gpt-4o-mini", name: "GPT-4o Mini", context: 128000, tokens: 8192 },
34
- { id: "gpt-4-turbo", name: "GPT-4 Turbo", context: 128000, tokens: 4096 },
35
- { id: "gpt-4", name: "GPT-4", context: 8192, tokens: 4096 },
36
- { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo", context: 16385, tokens: 4096 },
37
- ] as const;
38
-
39
- const ANTHROPIC_MODELS = [
40
- { id: "claude-sonnet-4-5", name: "Claude 4.5 Sonnet", context: 200000, tokens: 8192 },
41
- { id: "claude-opus-4-5", name: "Claude 4.5 Opus", context: 200000, tokens: 8192 },
42
- { id: "claude-sonnet-4", name: "Claude 4 Sonnet", context: 200000, tokens: 8192 },
43
- { id: "claude-opus-4", name: "Claude 4 Opus", context: 200000, tokens: 8192 },
44
- { id: "claude-haiku-4-5", name: "Claude 4.5 Haiku", context: 200000, tokens: 8192 },
45
- { id: "claude-3-7-sonnet", name: "Claude 3.7 Sonnet", context: 200000, tokens: 8192 },
46
- { id: "claude-3-5-sonnet", name: "Claude 3.5 Sonnet", context: 200000, tokens: 8192 },
47
- { id: "claude-3-opus", name: "Claude 3 Opus", context: 200000, tokens: 4096 },
48
- { id: "claude-3-sonnet", name: "Claude 3 Sonnet", context: 200000, tokens: 4096 },
49
- { id: "claude-3-haiku", name: "Claude 3 Haiku", context: 200000, tokens: 4096 },
50
- ] as const;
51
-
52
- function generateAllModels() {
53
- const models = [];
54
-
55
- for (const size of STRATUS_SIZES) {
56
- const sizeLabel = size.charAt(0).toUpperCase() + size.slice(1);
57
-
58
- for (const llm of OPENAI_MODELS) {
59
- models.push(
60
- buildModelDefinition({
61
- id: `stratus-x1ac-${size}-${llm.id}`,
62
- name: `Stratus X1AC ${sizeLabel} (${llm.name})`,
63
- contextWindow: llm.context,
64
- maxTokens: llm.tokens,
65
- }),
66
- );
67
- }
32
+ // Model metadata for naming/context/tokens (fallback if API doesn't provide details)
33
+ const MODEL_METADATA: Record<string, { name: string; context: number; tokens: number }> = {
34
+ "gpt-4o": { name: "GPT-4o", context: 128000, tokens: 8192 },
35
+ "gpt-4o-mini": { name: "GPT-4o Mini", context: 128000, tokens: 8192 },
36
+ "gpt-4-turbo": { name: "GPT-4 Turbo", context: 128000, tokens: 4096 },
37
+ "gpt-4": { name: "GPT-4", context: 8192, tokens: 4096 },
38
+ "gpt-3.5-turbo": { name: "GPT-3.5 Turbo", context: 16385, tokens: 4096 },
39
+ "claude-sonnet-4-6": { name: "Claude 4.6 Sonnet", context: 200000, tokens: 8192 },
40
+ "claude-opus-4-6": { name: "Claude 4.6 Opus", context: 200000, tokens: 8192 },
41
+ "claude-sonnet-4-5": { name: "Claude 4.5 Sonnet", context: 200000, tokens: 8192 },
42
+ "claude-opus-4-5": { name: "Claude 4.5 Opus", context: 200000, tokens: 8192 },
43
+ "claude-haiku-4-5": { name: "Claude 4.5 Haiku", context: 200000, tokens: 8192 },
44
+ "claude-sonnet-4": { name: "Claude 4 Sonnet", context: 200000, tokens: 8192 },
45
+ "claude-opus-4-1": { name: "Claude 4.1 Opus", context: 200000, tokens: 8192 },
46
+ "claude-opus-4": { name: "Claude 4 Opus", context: 200000, tokens: 8192 },
47
+ "claude-3-7-sonnet": { name: "Claude 3.7 Sonnet", context: 200000, tokens: 8192 },
48
+ "claude-3-5-sonnet": { name: "Claude 3.5 Sonnet", context: 200000, tokens: 8192 },
49
+ "claude-3-opus": { name: "Claude 3 Opus", context: 200000, tokens: 4096 },
50
+ "claude-3-sonnet": { name: "Claude 3 Sonnet", context: 200000, tokens: 4096 },
51
+ "claude-3-haiku": { name: "Claude 3 Haiku", context: 200000, tokens: 4096 },
52
+ "gemini-2.0-flash": { name: "Gemini 2.0 Flash", context: 1048576, tokens: 8192 },
53
+ "gemini-1.5-pro": { name: "Gemini 1.5 Pro", context: 2097152, tokens: 8192 },
54
+ "gemini-1.5-flash": { name: "Gemini 1.5 Flash", context: 1048576, tokens: 8192 },
55
+ "gemini-pro": { name: "Gemini Pro", context: 32768, tokens: 8192 },
56
+ };
68
57
 
69
- for (const llm of ANTHROPIC_MODELS) {
70
- models.push(
71
- buildModelDefinition({
72
- id: `stratus-x1ac-${size}-${llm.id}`,
73
- name: `Stratus X1AC ${sizeLabel} (${llm.name})`,
74
- contextWindow: llm.context,
75
- maxTokens: llm.tokens,
76
- }),
77
- );
78
- }
58
+ function generateModelName(modelId: string): string {
59
+ // Parse model ID like "stratus-x1ac-base-claude-sonnet-4-5"
60
+ const parts = modelId.split("-");
61
+
62
+ // Extract size (small/base/large/xl/huge)
63
+ let size = "";
64
+ let baseModelId = "";
65
+
66
+ if (parts.length >= 4 && parts[0] === "stratus" && parts[1] === "x1ac") {
67
+ size = parts[2];
68
+ baseModelId = parts.slice(3).join("-");
69
+ } else {
70
+ // Fallback for non-standard format
71
+ return modelId;
72
+ }
73
+
74
+ const sizeLabel = size.charAt(0).toUpperCase() + size.slice(1);
75
+ const metadata = MODEL_METADATA[baseModelId];
76
+
77
+ if (metadata) {
78
+ return `Stratus X1AC ${sizeLabel} (${metadata.name})`;
79
79
  }
80
+
81
+ // Fallback: capitalize base model
82
+ return `Stratus X1AC ${sizeLabel} (${baseModelId})`;
83
+ }
84
+
85
+ async function generateAllModels(apiKey?: string, baseUrl?: string) {
86
+ const root = (baseUrl || DEFAULT_BASE_URL).replace(/\/v\d+\/?$/, "");
87
+ try {
88
+ const url = `${root}/v1/models`;
89
+ const headers: Record<string, string> = {};
90
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
91
+
92
+ const response = await fetch(url, { headers, signal: AbortSignal.timeout(5000) });
93
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
94
+ const data = await response.json() as { data: Array<{ id: string }> };
95
+
96
+ return data.data.map((model) => {
97
+ const modelId = model.id;
98
+ const baseModelId = modelId.replace(/^stratus-x1ac-(small|base|large|xl|huge)-/, "");
99
+ const metadata = MODEL_METADATA[baseModelId];
100
+ return buildModelDefinition({
101
+ id: modelId,
102
+ name: generateModelName(modelId),
103
+ contextWindow: metadata?.context || 128000,
104
+ maxTokens: metadata?.tokens || 8192,
105
+ });
106
+ });
107
+ } catch (error) {
108
+ console.error("[stratus] Failed to fetch models from API:", error);
109
+ return [
110
+ buildModelDefinition({
111
+ id: "stratus-x1ac-base-claude-sonnet-4-5",
112
+ name: "Stratus X1AC Base (Claude 4.5 Sonnet)",
113
+ contextWindow: 200000,
114
+ maxTokens: 8192,
115
+ }),
116
+ ];
117
+ }
118
+ }
80
119
 
81
- return models;
120
+ async function updateStoredModels(models: ReturnType<typeof buildModelDefinition>[]) {
121
+ const configPath = join(homedir(), ".openclaw", "openclaw.json");
122
+ if (!existsSync(configPath)) return;
123
+
124
+ try {
125
+ const raw = readFileSync(configPath, "utf-8");
126
+ const config = JSON.parse(raw);
127
+ if (!config?.models?.providers?.stratus) return;
128
+
129
+ config.models.providers.stratus.models = models;
130
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
131
+ } catch {
132
+ // Silent fail — stale models are better than a crash
133
+ }
82
134
  }
83
135
 
84
136
  const stratusPlugin = {
@@ -98,15 +150,13 @@ const stratusPlugin = {
98
150
  auth: [
99
151
  {
100
152
  id: "api-key",
101
- label: "API Key",
153
+ label: "API Key (optional — Formation pool available)",
102
154
  kind: "api_key",
103
155
  run: async (ctx) => {
104
156
  const apiKey = await ctx.prompter.text({
105
- message: "Enter your Stratus API key:",
157
+ message: "Enter your Stratus API key (leave blank for Formation pool — zero-config):",
106
158
  validate: (val: string) => {
107
- if (!val.trim()) {
108
- return "API key is required";
109
- }
159
+ if (!val.trim()) return undefined;
110
160
  if (!val.startsWith("stratus_sk_")) {
111
161
  return "API key must start with 'stratus_sk_'";
112
162
  }
@@ -114,36 +164,30 @@ const stratusPlugin = {
114
164
  },
115
165
  });
116
166
 
117
- const profileId = `${PROVIDER_ID}:default`;
167
+ const usingPool = !apiKey?.trim();
168
+ const resolvedKey = usingPool ? undefined : apiKey;
169
+ const profileId = `${PROVIDER_ID}:${usingPool ? "formation-pool" : "default"}`;
170
+
171
+ const models = await generateAllModels(resolvedKey, DEFAULT_BASE_URL);
172
+
173
+ const credential = usingPool
174
+ ? { type: "formation_pool" as const, provider: PROVIDER_ID }
175
+ : { type: "api_key" as const, provider: PROVIDER_ID, key: resolvedKey! };
176
+
177
+ const authMode = usingPool
178
+ ? "Formation pool (zero-config, 25% markup)"
179
+ : "BYOK (no markup)";
118
180
 
119
181
  return {
120
- profiles: [
121
- {
122
- profileId,
123
- credential: {
124
- type: "api_key",
125
- provider: PROVIDER_ID,
126
- key: apiKey,
127
- },
128
- },
129
- ],
182
+ profiles: [{ profileId, credential }],
130
183
  configPatch: {
131
184
  models: {
132
185
  providers: {
133
186
  [PROVIDER_ID]: {
134
187
  baseUrl: DEFAULT_BASE_URL,
135
- apiKey: apiKey,
188
+ ...(resolvedKey ? { apiKey: resolvedKey } : {}),
136
189
  api: "openai-completions",
137
- models: generateAllModels(),
138
- },
139
- },
140
- },
141
- agents: {
142
- defaults: {
143
- models: {
144
- [`${PROVIDER_ID}/stratus-x1ac-base-claude-sonnet-4-5`]: {
145
- alias: "stratus",
146
- },
190
+ models: models,
147
191
  },
148
192
  },
149
193
  },
@@ -152,20 +196,26 @@ const stratusPlugin = {
152
196
  notes: [
153
197
  "🌊 Stratus X1-AC: World Model + Collective Intelligence System",
154
198
  "",
199
+ `🔒 Auth: ${authMode}`,
200
+ "",
155
201
  "NOT just an LLM wrapper — this is predictive planning:",
156
202
  " Traditional agents: see → think → act → see again",
157
203
  " Stratus agents: see → simulate outcomes → pick best → act",
158
204
  "",
159
205
  "Architecture:",
160
206
  " • World Model (X1): Predicts future states from actions (JEPA-based)",
161
- " • Policy Head (X1-AC): Selects optimal actions toward goals",
207
+ " • Policy Head v3 (X1-AC): 94.4% accuracy, brain-guided action sequencing",
162
208
  " • Collective Network: Agents learn from each other in real-time",
163
209
  "",
210
+ `Loaded ${models.length} models dynamically from Stratus API`,
211
+ " 2050+ models via OpenRouter — OpenAI, Anthropic, Google, and more",
212
+ " BYOK support: pass your own provider keys to bypass Formation pool",
213
+ "",
164
214
  "Available Tools:",
165
215
  " • stratus_embeddings() - 768-dim semantic state embeddings",
166
- " • stratus_rollout() - Multi-step action sequence planning",
216
+ " • stratus_rollout() - Multi-step action sequence planning (Policy Head v3)",
167
217
  "",
168
- "Learn more: /stratus-info (bundled skill)",
218
+ "Commands: /stratus setup | /stratus verify | /stratus models",
169
219
  "Technical docs: https://stratus.run/docs/technical-overview",
170
220
  ],
171
221
  };
@@ -174,6 +224,16 @@ const stratusPlugin = {
174
224
  ],
175
225
  });
176
226
 
227
+ api.on("gateway_start", async () => {
228
+ const apiKey = pluginConfig.apiKey || process.env.STRATUS_API_KEY;
229
+ try {
230
+ const models = await generateAllModels(apiKey, pluginConfig.baseUrl);
231
+ await updateStoredModels(models);
232
+ } catch {
233
+ // Silent fail — don't disrupt gateway startup
234
+ }
235
+ });
236
+
177
237
  if (!pluginConfig.tools?.embeddings?.enabled && !pluginConfig.tools?.rollout?.enabled) {
178
238
  return;
179
239
  }
@@ -285,11 +345,24 @@ const stratusPlugin = {
285
345
  ? `Successfully planned ${steps.length} steps to reach goal`
286
346
  : `Planning incomplete (${steps.length} steps generated)`;
287
347
 
348
+ const meta = response.metadata;
349
+ const metaLines: string[] = [];
350
+ if (meta?.brain_confidence != null) {
351
+ metaLines.push(`Brain confidence: ${(meta.brain_confidence * 100).toFixed(1)}%`);
352
+ }
353
+ if (meta?.brain_goal_proximity != null) {
354
+ metaLines.push(`Goal proximity: ${(meta.brain_goal_proximity * 100).toFixed(1)}%`);
355
+ }
356
+ if (meta?.key_source) {
357
+ metaLines.push(`Key source: ${meta.key_source}${meta.formation_markup_applied ? ` (${meta.formation_markup_applied * 100}% markup)` : ""}`);
358
+ }
359
+ const metaText = metaLines.length > 0 ? `\n\n${metaLines.join("\n")}` : "";
360
+
288
361
  return {
289
362
  content: [
290
363
  {
291
364
  type: "text",
292
- text: `${summary}\nGoal: ${response.goal}\n\nSteps:\n${stepText}`,
365
+ text: `${summary}\nGoal: ${response.goal}\n\nSteps:\n${stepText}${metaText}`,
293
366
  },
294
367
  ],
295
368
  details: response,
@@ -328,43 +401,79 @@ const stratusPlugin = {
328
401
  process.exit(1);
329
402
  }
330
403
  return { text: "" }; // Return empty to avoid double output
404
+ } else if (subcommand === "models") {
405
+ const apiKey = pluginConfig.apiKey || process.env.STRATUS_API_KEY;
406
+ const models = await generateAllModels(apiKey, pluginConfig.baseUrl);
407
+
408
+ await updateStoredModels(models);
409
+
410
+ const sizes = ["small", "base", "large", "xl", "huge"];
411
+ const grouped = sizes
412
+ .map((size) => {
413
+ const sizeModels = models.filter(
414
+ (m) => m.id.includes(`-x1ac-${size}-`) || m.id.endsWith(`-x1ac-${size}`)
415
+ );
416
+ if (sizeModels.length === 0) return null;
417
+ return ` ${size.toUpperCase()} (${sizeModels.length})\n` +
418
+ sizeModels.map((m) => ` • ${m.id}`).join("\n");
419
+ })
420
+ .filter(Boolean)
421
+ .join("\n\n");
422
+
423
+ return {
424
+ text:
425
+ `Stratus Models (${models.length} total)\n` +
426
+ `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n` +
427
+ grouped +
428
+ `\n\nConfig synced. Run /models to see the full list.`,
429
+ };
331
430
  } else if (subcommand === "verify") {
332
- // Run verify
333
431
  console.log("🔍 Verifying Stratus configuration...\n");
334
432
 
335
- let errors = 0;
433
+ const hasKey = !!(pluginConfig.apiKey || process.env.STRATUS_API_KEY);
336
434
 
337
- // Check 1: Environment variable
338
- console.log("1️⃣ Checking STRATUS_API_KEY...");
339
- if (process.env.STRATUS_API_KEY) {
340
- console.log(" ✓ STRATUS_API_KEY is set");
435
+ console.log("1️⃣ Auth mode:");
436
+ if (hasKey) {
437
+ console.log(" ✓ BYOK — using STRATUS_API_KEY (no markup)");
341
438
  } else {
342
- console.log(" STRATUS_API_KEY not found");
343
- errors++;
439
+ console.log(" Formation pool — zero-config (25% markup)");
440
+ console.log(" 💡 Set STRATUS_API_KEY to remove markup");
344
441
  }
345
442
 
346
- // Check 2: Config exists
347
- console.log("\n2️⃣ Checking plugin configuration...");
348
- if (pluginConfig.apiKey || process.env.STRATUS_API_KEY) {
349
- console.log(" ✓ API key configured");
350
- } else {
351
- console.log(" ❌ API key not configured");
352
- errors++;
443
+ console.log("\n2️⃣ API connectivity...");
444
+ try {
445
+ const models = await generateAllModels(
446
+ hasKey ? (pluginConfig.apiKey || process.env.STRATUS_API_KEY) : undefined,
447
+ pluginConfig.baseUrl,
448
+ );
449
+ console.log(` ✓ Connected — ${models.length} models available`);
450
+ } catch {
451
+ console.log(" ⚠ Could not reach API (may still work at runtime)");
353
452
  }
354
453
 
355
- console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
356
- if (errors === 0) {
357
- console.log(" All checks passed!");
358
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
359
- console.log("🎯 Try it out:");
360
- console.log(" openclaw agent 'Hello Stratus!' --model stratus\n");
361
- } else {
362
- console.log(`❌ ${errors} issue(s) found`);
363
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
364
- console.log("💡 Run: openclaw stratus setup\n");
365
- process.exit(1);
454
+ console.log("\n3️⃣ Inline BYOK keys:");
455
+ const envKeys = [
456
+ ["OPENAI_API_KEY", process.env.OPENAI_API_KEY],
457
+ ["ANTHROPIC_API_KEY", process.env.ANTHROPIC_API_KEY],
458
+ ["GOOGLE_API_KEY", process.env.GOOGLE_API_KEY],
459
+ ] as const;
460
+ let anyInline = false;
461
+ for (const [name, val] of envKeys) {
462
+ if (val) {
463
+ console.log(` ✓ ${name} detected (BYOK passthrough)`);
464
+ anyInline = true;
465
+ }
366
466
  }
367
- return { text: "" }; // Return empty to avoid double output
467
+ if (!anyInline) {
468
+ console.log(" – None set (optional — Formation pool covers all providers)");
469
+ }
470
+
471
+ console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
472
+ console.log("✅ Stratus is ready!");
473
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
474
+ console.log("🎯 Try it out:");
475
+ console.log(" openclaw agent 'Hello Stratus!' --model stratus\n");
476
+ return { text: "" };
368
477
  } else {
369
478
  // Show help
370
479
  return {
@@ -372,7 +481,8 @@ const stratusPlugin = {
372
481
  "🌊 Stratus X1-AC Plugin\n\n" +
373
482
  "Commands:\n" +
374
483
  " /stratus setup - Interactive setup wizard\n" +
375
- " /stratus verify - Verify configuration\n\n" +
484
+ " /stratus verify - Verify configuration\n" +
485
+ " /stratus models - List all available models (fetches live from API)\n\n" +
376
486
  "Get your API key: https://stratus.run\n" +
377
487
  "Docs: https://stratus.run/docs\n" +
378
488
  "Issues: https://github.com/formthefog/openclaw-stratus-x1-plugin/issues",
@@ -12,46 +12,70 @@
12
12
  "default": true,
13
13
  "description": "Enable Stratus plugin"
14
14
  },
15
- "apiKey": {
16
- "type": "string",
17
- "description": "Stratus API key (or use STRATUS_API_KEY env var)"
18
- },
19
- "baseUrl": {
20
- "type": "string",
21
- "default": "https://api.stratus.run",
22
- "description": "Stratus API base URL"
23
- },
24
- "provider": {
15
+ "config": {
25
16
  "type": "object",
17
+ "description": "Stratus plugin configuration",
26
18
  "properties": {
27
- "enabled": {
28
- "type": "boolean",
29
- "default": true
19
+ "apiKey": {
20
+ "type": "string",
21
+ "description": "Stratus API key (optional — Formation pool used as fallback)"
30
22
  },
31
- "defaultModel": {
23
+ "baseUrl": {
32
24
  "type": "string",
33
- "default": "stratus-x1ac-base-claude-sonnet-4-5"
34
- }
35
- }
36
- },
37
- "tools": {
38
- "type": "object",
39
- "properties": {
40
- "embeddings": {
25
+ "default": "https://api.stratus.run",
26
+ "description": "Stratus API base URL"
27
+ },
28
+ "inlineKeys": {
41
29
  "type": "object",
30
+ "description": "Optional BYOK keys passed per-request to bypass Formation pool",
42
31
  "properties": {
43
- "enabled": {
44
- "type": "boolean",
45
- "default": true
32
+ "openai_key": {
33
+ "type": "string",
34
+ "description": "OpenAI API key for BYOK passthrough"
35
+ },
36
+ "anthropic_key": {
37
+ "type": "string",
38
+ "description": "Anthropic API key for BYOK passthrough"
39
+ },
40
+ "gemini_key": {
41
+ "type": "string",
42
+ "description": "Google Gemini API key for BYOK passthrough (also sent as X-Google-Key header)"
46
43
  }
47
44
  }
48
45
  },
49
- "rollout": {
46
+ "provider": {
50
47
  "type": "object",
51
48
  "properties": {
52
49
  "enabled": {
53
50
  "type": "boolean",
54
51
  "default": true
52
+ },
53
+ "defaultModel": {
54
+ "type": "string",
55
+ "default": "stratus-x1ac-base-claude-sonnet-4-5"
56
+ }
57
+ }
58
+ },
59
+ "tools": {
60
+ "type": "object",
61
+ "properties": {
62
+ "embeddings": {
63
+ "type": "object",
64
+ "properties": {
65
+ "enabled": {
66
+ "type": "boolean",
67
+ "default": true
68
+ }
69
+ }
70
+ },
71
+ "rollout": {
72
+ "type": "object",
73
+ "properties": {
74
+ "enabled": {
75
+ "type": "boolean",
76
+ "default": true
77
+ }
78
+ }
55
79
  }
56
80
  }
57
81
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formthefog/stratus",
3
- "version": "2026.2.24",
3
+ "version": "2026.3.19",
4
4
  "description": "Stratus API integration for OpenClaw - action-conditioned JEPA for autonomous agent planning",
5
5
  "keywords": [
6
6
  "agent",