@stratusagent/cli 0.11.3 → 0.11.5

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 (88) hide show
  1. package/README.md +15 -3
  2. package/dist/approvals.d.ts +7 -1
  3. package/dist/approvals.d.ts.map +1 -1
  4. package/dist/approvals.js +59 -6
  5. package/dist/approvals.js.map +1 -1
  6. package/dist/commands/agent-new.d.ts.map +1 -1
  7. package/dist/commands/agent-new.js +12 -7
  8. package/dist/commands/agent-new.js.map +1 -1
  9. package/dist/commands/chat.d.ts +1 -1
  10. package/dist/commands/chat.d.ts.map +1 -1
  11. package/dist/commands/chat.js +7 -4
  12. package/dist/commands/chat.js.map +1 -1
  13. package/dist/commands/doctor.d.ts.map +1 -1
  14. package/dist/commands/doctor.js +32 -5
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/grants.d.ts.map +1 -1
  17. package/dist/commands/grants.js +8 -3
  18. package/dist/commands/grants.js.map +1 -1
  19. package/dist/commands/memory.d.ts +16 -3
  20. package/dist/commands/memory.d.ts.map +1 -1
  21. package/dist/commands/memory.js +322 -16
  22. package/dist/commands/memory.js.map +1 -1
  23. package/dist/commands/plugins.d.ts +12 -0
  24. package/dist/commands/plugins.d.ts.map +1 -1
  25. package/dist/commands/plugins.js +30 -7
  26. package/dist/commands/plugins.js.map +1 -1
  27. package/dist/commands/schedules.d.ts.map +1 -1
  28. package/dist/commands/schedules.js +136 -31
  29. package/dist/commands/schedules.js.map +1 -1
  30. package/dist/commands/serve.d.ts.map +1 -1
  31. package/dist/commands/serve.js +51 -3
  32. package/dist/commands/serve.js.map +1 -1
  33. package/dist/commands/setup.d.ts.map +1 -1
  34. package/dist/commands/setup.js +87 -17
  35. package/dist/commands/setup.js.map +1 -1
  36. package/dist/commands/template.d.ts.map +1 -1
  37. package/dist/commands/template.js +28 -7
  38. package/dist/commands/template.js.map +1 -1
  39. package/dist/commands/update.d.ts +0 -17
  40. package/dist/commands/update.d.ts.map +1 -1
  41. package/dist/commands/update.js +63 -31
  42. package/dist/commands/update.js.map +1 -1
  43. package/dist/companions.d.ts +34 -0
  44. package/dist/companions.d.ts.map +1 -0
  45. package/dist/companions.js +45 -0
  46. package/dist/companions.js.map +1 -0
  47. package/dist/daemon.d.ts.map +1 -1
  48. package/dist/daemon.js +3 -0
  49. package/dist/daemon.js.map +1 -1
  50. package/dist/environment.d.ts +6 -0
  51. package/dist/environment.d.ts.map +1 -1
  52. package/dist/events.d.ts.map +1 -1
  53. package/dist/events.js +27 -9
  54. package/dist/events.js.map +1 -1
  55. package/dist/help.d.ts +1 -1
  56. package/dist/help.d.ts.map +1 -1
  57. package/dist/help.js +41 -7
  58. package/dist/help.js.map +1 -1
  59. package/dist/index.d.ts +4 -3
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +4 -3
  62. package/dist/index.js.map +1 -1
  63. package/dist/main.d.ts.map +1 -1
  64. package/dist/main.js +20 -5
  65. package/dist/main.js.map +1 -1
  66. package/dist/npm.d.ts +1 -1
  67. package/dist/npm.js +1 -1
  68. package/dist/parse.d.ts +42 -5
  69. package/dist/parse.d.ts.map +1 -1
  70. package/dist/parse.js +90 -17
  71. package/dist/parse.js.map +1 -1
  72. package/dist/plugin-catalog.d.ts +9 -0
  73. package/dist/plugin-catalog.d.ts.map +1 -1
  74. package/dist/plugin-catalog.js +12 -0
  75. package/dist/plugin-catalog.js.map +1 -1
  76. package/dist/runtime.d.ts +28 -11
  77. package/dist/runtime.d.ts.map +1 -1
  78. package/dist/runtime.js +178 -71
  79. package/dist/runtime.js.map +1 -1
  80. package/dist/service.d.ts +23 -0
  81. package/dist/service.d.ts.map +1 -1
  82. package/dist/service.js +41 -2
  83. package/dist/service.js.map +1 -1
  84. package/dist/trusted-config.d.ts +43 -1
  85. package/dist/trusted-config.d.ts.map +1 -1
  86. package/dist/trusted-config.js +99 -5
  87. package/dist/trusted-config.js.map +1 -1
  88. package/package.json +25 -23
@@ -4,7 +4,7 @@ import { preflightPlugin, readPluginManifest, PluginConfigError, PluginManifestE
4
4
  import { DEFAULT_ANTHROPIC_MODEL } from '@stratusagent/provider-anthropic';
5
5
  import { DEFAULT_CODEX_MODEL } from '@stratusagent/provider-codex';
6
6
  import { formatSoul, parseSoul } from '@stratusagent/agents';
7
- import { claimSoulFile, collectAvailableModels as collectModels, credentialsPath, defaultApiKeyEnvName, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_STRATUS_AGENT, globalConfigPath, loadChannelCredentials, loadCredentials, loadRosterSouls, loadSoulFile, readNonEmptyString, readProcessEnv, readWorkingDirectory, resolveAgentApprovals, DEFAULT_CONFIG_FILENAME, loadConfigFile, saveChannelCredentials, saveConfigFile, saveCredentials, CREDENTIAL_PROVIDER_NAMES, verifyProviderKey, workspacesDirPath, } from '@stratusagent/state';
7
+ import { claimSoulFile, collectAvailableModels as collectModels, credentialsPath, defaultApiKeyEnvName, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_STRATUS_AGENT, globalConfigPath, loadChannelCredentials, loadCredentials, loadRosterSouls, loadSoulFile, isRegisteredProviderName, readNonEmptyString, registeredProviderNameOf, readProcessEnv, readWorkingDirectory, resolveAgentApprovals, DEFAULT_CONFIG_FILENAME, loadConfigFile, saveChannelCredentials, saveConfigFile, saveCredentials, CREDENTIAL_PROVIDER_NAMES, verifyProviderKey, } from '@stratusagent/state';
8
8
  import { installService, readServiceStatus, servicePlatform, serviceUnitPath, uninstallService, } from "../service.js";
9
9
  import { classifyApprovalReach, unattendedReachParts } from "../approvals.js";
10
10
  import { serviceEnvFor } from "../daemon.js";
@@ -101,6 +101,17 @@ export const runSetup = async (command, streams, env = {}) => {
101
101
  ...(existing.approvals !== undefined ? { approvals: existing.approvals } : {}),
102
102
  ...(existing.api !== undefined ? { api: existing.api } : {}),
103
103
  ...(existing.principals !== undefined ? { principals: existing.principals } : {}),
104
+ ...(existing.slack !== undefined ? { slack: existing.slack } : {}),
105
+ // Settings setup has no menu for, carried through a save untouched: a
106
+ // re-run that dropped them would put the operator's agents back on the
107
+ // host and the file store without a word — and, for the two bounds,
108
+ // back on defaults their model cannot serve. A `maxTokens` set because
109
+ // a proxy's ceiling is under 16000 would silently return to 16000 and
110
+ // fail every request; a raised `maxTurns` would return to 8.
111
+ ...(existing.executor !== undefined ? { executor: existing.executor } : {}),
112
+ ...(existing.memoryStore !== undefined ? { memoryStore: existing.memoryStore } : {}),
113
+ ...(existing.maxTokens !== undefined ? { maxTokens: existing.maxTokens } : {}),
114
+ ...(existing.maxTurns !== undefined ? { maxTurns: existing.maxTurns } : {}),
104
115
  credentials: await loadCredentials(env),
105
116
  credentialsDirty: false,
106
117
  channels: await loadChannelCredentials(env),
@@ -126,14 +137,24 @@ export const runSetup = async (command, streams, env = {}) => {
126
137
  header: stratusHeaderLines,
127
138
  consumeNotices: () => recentNotices.splice(0),
128
139
  });
129
- const defaultModelFor = (provider) => provider === 'openai'
140
+ const builtInDefaultModel = (provider) => provider === 'openai'
130
141
  ? DEFAULT_OPENAI_MODEL
131
142
  : provider === 'codex'
132
143
  ? DEFAULT_CODEX_MODEL
133
144
  : DEFAULT_ANTHROPIC_MODEL;
145
+ // A contributed provider's default is its own, so setup has none to
146
+ // name for it — and none to write into a config or a soul.
147
+ const defaultModelFor = (provider) => isRegisteredProviderName(provider) ? undefined : builtInDefaultModel(provider);
134
148
  // Widened to CliProviderName only so setup can ask about the provider it
135
149
  // currently has selected, `demo` included; the rule itself is the shared one.
136
- const defaultKeyEnvFor = (provider) => defaultApiKeyEnvName(provider === 'demo' ? 'anthropic' : provider);
150
+ const defaultKeyEnvFor = (provider) => defaultApiKeyEnvName(credentialProviderOf(provider) ?? 'anthropic');
151
+ /**
152
+ * The sign-in a selection is stored under, or nothing: the demo provider
153
+ * has none, and a provider a plugin registered brings its own through
154
+ * its plugin's config and credentials — setup neither stores nor checks
155
+ * one for it.
156
+ */
157
+ const credentialProviderOf = (provider) => provider === 'demo' || isRegisteredProviderName(provider) ? undefined : provider;
137
158
  const credentialLabel = (provider, credential) => credential.type === 'oauth_token'
138
159
  ? (provider === 'codex' ? 'ChatGPT sign-in' : 'Claude subscription')
139
160
  : 'API key';
@@ -177,6 +198,9 @@ export const runSetup = async (command, streams, env = {}) => {
177
198
  if (state.provider === 'demo') {
178
199
  return 'no account needed';
179
200
  }
201
+ if (isRegisteredProviderName(state.provider)) {
202
+ return `served by the plugin that registers ${registeredProviderNameOf(state.provider)}, on its own sign-in`;
203
+ }
180
204
  const credential = state.credentials[state.provider];
181
205
  if (credential) {
182
206
  return credential.type === 'oauth_token'
@@ -412,7 +436,13 @@ export const runSetup = async (command, streams, env = {}) => {
412
436
  ? readNonEmptyString(processEnv.STRATUS_API_KEY_ENV)
413
437
  ?? state.apiKeyEnv
414
438
  : undefined;
415
- return state.credentials[provider] !== undefined
439
+ const stored = credentialProviderOf(provider);
440
+ // A contributed provider brings its own sign-in; setup cannot check
441
+ // it and does not claim to.
442
+ if (stored === undefined) {
443
+ return isRegisteredProviderName(provider);
444
+ }
445
+ return state.credentials[stored] !== undefined
416
446
  || readNonEmptyString(processEnv.STRATUS_API_KEY) !== undefined
417
447
  || (keyEnvSelector ? readNonEmptyString(processEnv[String(keyEnvSelector)]) !== undefined : false)
418
448
  || readNonEmptyString(processEnv[defaultKeyEnvFor(provider)]) !== undefined;
@@ -503,7 +533,8 @@ export const runSetup = async (command, streams, env = {}) => {
503
533
  // provider, wherever the default currently points.
504
534
  const listed = available.find((entry) => entry.id === typed);
505
535
  const inferred = listed?.provider
506
- ?? (state.provider !== 'demo' ? state.provider : available[0].provider);
536
+ ?? credentialProviderOf(state.provider)
537
+ ?? available[0].provider;
507
538
  return { provider: inferred, id: typed };
508
539
  };
509
540
  let choice;
@@ -611,8 +642,9 @@ export const runSetup = async (command, streams, env = {}) => {
611
642
  const name = await prompter.ask('Name your agent (Enter to have one generated): ');
612
643
  const instructions = await prompter.ask('Describe their personality in a sentence or two (Enter for a starter you can edit later): ');
613
644
  const persona = instructions || DEFAULT_SOUL_STARTER;
645
+ const pinnedModel = state.model ?? defaultModelFor(state.provider);
614
646
  const pin = state.provider !== 'demo'
615
- ? { provider: state.provider, model: state.model ?? defaultModelFor(state.provider) }
647
+ ? { provider: state.provider, ...(pinnedModel !== undefined ? { model: pinnedModel } : {}) }
616
648
  : {};
617
649
  const claimed = await claimSoulFile(env, { ...(name ? { name } : {}), instructions: persona }, (agent) => formatSoul({ agent, ...pin }), (message) => writeLine(streams.stdout, message));
618
650
  const { agent, soulPath } = claimed;
@@ -626,7 +658,13 @@ export const runSetup = async (command, streams, env = {}) => {
626
658
  if (!state.fallbackModel || state.provider === 'demo') {
627
659
  return undefined;
628
660
  }
629
- const fallbackProvider = (state.fallbackProvider ?? state.provider);
661
+ const selectedFallback = state.fallbackProvider ?? state.provider;
662
+ if (isRegisteredProviderName(selectedFallback)) {
663
+ // A contributed fallback brings its own sign-in, as a contributed
664
+ // primary does; the test run selects it by name and no more.
665
+ return { provider: selectedFallback, model: state.fallbackModel };
666
+ }
667
+ const fallbackProvider = selectedFallback;
630
668
  const envKey = (fallbackProvider === state.provider
631
669
  ? readNonEmptyString(processEnv.STRATUS_API_KEY)
632
670
  : undefined)
@@ -689,9 +727,25 @@ export const runSetup = async (command, streams, env = {}) => {
689
727
  ?? defaultKeyEnvFor(state.provider);
690
728
  const envKey = readNonEmptyString(processEnv.STRATUS_API_KEY)
691
729
  ?? readNonEmptyString(processEnv[String(keyEnv)]);
692
- const credential = envKey ? undefined : state.credentials[state.provider];
730
+ const storedFor = credentialProviderOf(state.provider);
731
+ const credential = envKey || storedFor === undefined ? undefined : state.credentials[storedFor];
693
732
  const boundUrl = credential?.type === 'api_key' ? credential.baseUrl : undefined;
694
- const model = state.model ?? defaultModelFor(state.provider);
733
+ if (isRegisteredProviderName(state.provider)) {
734
+ // A contributed provider brings its own sign-in and its own default
735
+ // model; the test run selects it and no more, exactly as a real run
736
+ // resolves it — the configured fallback included, so the test
737
+ // exercises the failover the saved config would perform.
738
+ const fallback = buildTestFallback();
739
+ return {
740
+ provider: state.provider,
741
+ ...(state.model ? { model: state.model } : {}),
742
+ ...(state.systemPrompt ? { systemPrompt: state.systemPrompt } : {}),
743
+ ...(env.fetch ? { fetch: env.fetch } : {}),
744
+ ...(soul ? { soul } : {}),
745
+ ...(fallback ? { fallback } : {}),
746
+ };
747
+ }
748
+ const model = state.model ?? builtInDefaultModel(state.provider);
695
749
  if (state.provider === 'anthropic') {
696
750
  const apiKey = envKey ?? (credential?.type === 'api_key' ? credential.value : undefined);
697
751
  const authToken = credential?.type === 'oauth_token' ? credential.value : undefined;
@@ -1039,7 +1093,7 @@ export const runSetup = async (command, streams, env = {}) => {
1039
1093
  const { manifest, directory } = await readPluginManifest(name, {
1040
1094
  resolve: (target) => import.meta.resolve(target),
1041
1095
  });
1042
- await preflightPlugin(manifest, directory, block, workspacesDirPath(env));
1096
+ await preflightPlugin(manifest, directory, block);
1043
1097
  return undefined;
1044
1098
  }
1045
1099
  catch (error) {
@@ -2135,8 +2189,9 @@ export const runSetup = async (command, streams, env = {}) => {
2135
2189
  // below are objects, and a `Record<string, string | boolean>` was what
2136
2190
  // made dropping them the path of least resistance.
2137
2191
  const config = { provider: state.provider };
2138
- if (state.provider !== 'demo') {
2139
- config.model = state.model ?? defaultModelFor(state.provider);
2192
+ const savedModel = state.model ?? defaultModelFor(state.provider);
2193
+ if (state.provider !== 'demo' && savedModel !== undefined) {
2194
+ config.model = savedModel;
2140
2195
  }
2141
2196
  if (state.provider === 'openai') {
2142
2197
  config.baseUrl = state.baseUrl ?? state.credentials.openai?.baseUrl ?? DEFAULT_OPENAI_BASE_URL;
@@ -2171,8 +2226,8 @@ export const runSetup = async (command, streams, env = {}) => {
2171
2226
  if (state.promptCacheTtl !== undefined) {
2172
2227
  config.promptCacheTtl = state.promptCacheTtl;
2173
2228
  }
2174
- // `plugins` and `approvals` have menus above; `api` and `principals`
2175
- // do not and are written back exactly as they were read. Both cases
2229
+ // `plugins` and `approvals` have menus above; `api`, `principals`, and
2230
+ // `slack` do not and are written back exactly as they were read. Both cases
2176
2231
  // land here the same way, because the menus edit this state rather
2177
2232
  // than the file — so there is nothing to merge, only to not lose.
2178
2233
  if (state.plugins !== undefined) {
@@ -2187,6 +2242,21 @@ export const runSetup = async (command, streams, env = {}) => {
2187
2242
  if (state.principals !== undefined) {
2188
2243
  config.principals = state.principals;
2189
2244
  }
2245
+ if (state.slack !== undefined) {
2246
+ config.slack = state.slack;
2247
+ }
2248
+ if (state.executor !== undefined) {
2249
+ config.executor = state.executor;
2250
+ }
2251
+ if (state.memoryStore !== undefined) {
2252
+ config.memoryStore = state.memoryStore;
2253
+ }
2254
+ if (state.maxTokens !== undefined) {
2255
+ config.maxTokens = state.maxTokens;
2256
+ }
2257
+ if (state.maxTurns !== undefined) {
2258
+ config.maxTurns = state.maxTurns;
2259
+ }
2190
2260
  await saveConfigFile(configPath, config);
2191
2261
  writeLine(streams.stdout);
2192
2262
  writeLine(streams.stdout, `Wrote ${configPath}`);
@@ -2287,8 +2357,8 @@ export const runSetup = async (command, streams, env = {}) => {
2287
2357
  // would make `stratus run` behave differently from what was just saved.
2288
2358
  const conflicts = [
2289
2359
  detectEnvOverride('STRATUS_PROVIDER', state.provider, '--provider'),
2290
- ...(state.provider !== 'demo'
2291
- ? [detectEnvOverride('STRATUS_MODEL', state.model ?? defaultModelFor(state.provider), '--model')]
2360
+ ...(state.provider !== 'demo' && (state.model ?? defaultModelFor(state.provider)) !== undefined
2361
+ ? [detectEnvOverride('STRATUS_MODEL', String(state.model ?? defaultModelFor(state.provider)), '--model')]
2292
2362
  : []),
2293
2363
  ...(state.provider === 'openai'
2294
2364
  ? [detectEnvOverride('STRATUS_BASE_URL', state.baseUrl ?? DEFAULT_OPENAI_BASE_URL, '--base-url')]
@@ -2311,7 +2381,7 @@ export const runSetup = async (command, streams, env = {}) => {
2311
2381
  else if (readNonEmptyString(processEnv.STRATUS_API_KEY)) {
2312
2382
  writeLine(streams.stdout, 'STRATUS_API_KEY is exported and takes precedence over your saved sign-in. You are ready to go. Try:');
2313
2383
  }
2314
- else if (state.credentials[state.provider]) {
2384
+ else if (isRegisteredProviderName(state.provider) || state.credentials[credentialProviderOf(state.provider) ?? 'anthropic']) {
2315
2385
  writeLine(streams.stdout, `You are ${signInSummary()} — ready to go. Try:`);
2316
2386
  }
2317
2387
  else {