stratum-cli 0.8.0-beta.2 → 0.8.0-beta.3

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.
@@ -144,7 +144,9 @@
144
144
  "agents": {
145
145
  "defaultProfile": "general",
146
146
  "maxConcurrency": 1,
147
- "subagentRetentionDays": 30
147
+ "subagentRetentionDays": 30,
148
+ "subagentTimeoutMs": 1800000,
149
+ "subagentMaxIterations": 50
148
150
  },
149
151
  "skills": {
150
152
  "enabled": true,
package/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y l
6
6
 
7
7
  > Las versiones anteriores a la 0.2.1-beta.0 solo existen como tags de git: la primera publicación en npm fue la 0.2.1-beta.0.
8
8
 
9
+ ## [0.8.0-beta.3] — 2026-10-12
10
+
11
+ Cuarta pre-release (`npm install -g stratum-cli@beta`).
12
+
13
+ ### Cambiado
14
+ - **Los subagentes tienen más margen**: el tiempo máximo por defecto pasa de 5 a 30 minutos y las iteraciones de 25 a 50. Ambos se ajustan en la config con `agents.subagentTimeoutMs` (`0` quita el límite de tiempo) y `agents.subagentMaxIterations`; lo que un perfil declare en su `budget` sigue ganando.
15
+
16
+ ### Corregido
17
+ - Un subagente que agotaba su tiempo aparecía como «cancelado», como si alguien lo hubiera parado. Ahora termina como presupuesto agotado, y el agente principal recibe el límite que se alcanzó, el aviso de que el trabajo parcial puede estar en disco y dónde se cambia.
18
+
9
19
  ## [0.8.0-beta.2] — 2026-10-12
10
20
 
11
21
  Tercera pre-release (`npm install -g stratum-cli@beta`), con más arreglos de lo visto al probar la beta.
@@ -293,6 +303,7 @@ Primera versión etiquetada. Hitos 0 y 1.
293
303
  - `/init` para generar o actualizar `STRATUM.md`.
294
304
  - Render de markdown de las respuestas.
295
305
 
306
+ [0.8.0-beta.3]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.2...v0.8.0-beta.3
296
307
  [0.8.0-beta.2]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.1...v0.8.0-beta.2
297
308
  [0.8.0-beta.1]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.0...v0.8.0-beta.1
298
309
  [0.8.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.7.0...v0.8.0-beta.0
package/dist/index.js CHANGED
@@ -643,7 +643,15 @@ var init_schema = __esm({
643
643
  * Días que se conservan los registros de `.stratum/subagents/` (8B endurecido).
644
644
  * `chat` purga al arrancar lo que lleve más sin tocarse; `0` lo desactiva.
645
645
  */
646
- subagentRetentionDays: z.number().int().nonnegative().default(30)
646
+ subagentRetentionDays: z.number().int().nonnegative().default(30),
647
+ /**
648
+ * Tiempo máximo de un subagente en ms, para los perfiles que no declaran
649
+ * `budget.timeoutMs`. `0` quita el límite: solo lo paran sus iteraciones,
650
+ * la cancelación del usuario o el fin del turno.
651
+ */
652
+ subagentTimeoutMs: z.number().int().nonnegative().default(30 * 60 * 1e3),
653
+ /** Iteraciones máximas de un subagente, para los perfiles sin `budget.maxIterations`. */
654
+ subagentMaxIterations: z.number().int().positive().default(50)
647
655
  }).default({}),
648
656
  /**
649
657
  * Registro de skills (Hito 12). Las skills en sí son ficheros sueltos
@@ -15921,6 +15929,15 @@ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync a
15921
15929
  import { homedir as homedir4 } from "os";
15922
15930
  import { join as join16, resolve as resolve12 } from "path";
15923
15931
  import { z as z25 } from "zod";
15932
+ function resolveSubagentBudget(profile, agents) {
15933
+ const declared = profile.declaredBudget ?? {};
15934
+ const own = (key) => declared[key] ?? (profile.budget[key] !== DEFAULT_BUDGET[key] ? profile.budget[key] : void 0);
15935
+ return {
15936
+ maxIterations: own("maxIterations") ?? agents.subagentMaxIterations,
15937
+ maxTokens: declared.maxTokens ?? profile.budget.maxTokens,
15938
+ timeoutMs: own("timeoutMs") ?? agents.subagentTimeoutMs
15939
+ };
15940
+ }
15924
15941
  function isValidProfileName(name) {
15925
15942
  return PROFILE_NAME_RE.test(name) && !RESERVED_PROFILE_NAMES.has(name);
15926
15943
  }
@@ -15964,6 +15981,7 @@ function parseProfileDetailed(name, raw) {
15964
15981
  destructivePolicy: fm.destructivePolicy,
15965
15982
  budget,
15966
15983
  budgetDeclared: fm.budget !== void 0,
15984
+ declaredBudget: fm.budget ?? {},
15967
15985
  systemPromptFragment: body.trim() || GENERAL_PROFILE.systemPromptFragment
15968
15986
  }
15969
15987
  };
@@ -15986,7 +16004,7 @@ var init_profiles = __esm({
15986
16004
  init_frontmatter();
15987
16005
  init_logging();
15988
16006
  log15 = getLogger("agent");
15989
- DEFAULT_BUDGET = { maxIterations: 25, timeoutMs: 3e5 };
16007
+ DEFAULT_BUDGET = { maxIterations: 50, timeoutMs: 30 * 60 * 1e3 };
15990
16008
  PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
15991
16009
  RESERVED_PROFILE_NAMES = /* @__PURE__ */ new Set(["off", "default"]);
15992
16010
  DESCRIPTION_CAP = 160;
@@ -16309,10 +16327,10 @@ async function runSubagent(opts) {
16309
16327
  let tokenStatus = "unavailable";
16310
16328
  let currentText = "";
16311
16329
  let lastError;
16330
+ const budget = resolveSubagentBudget(profile, config.agents);
16312
16331
  let signal = parentSignal;
16313
- if (profile.budget.timeoutMs && profile.budget.timeoutMs > 0) {
16314
- signal = AbortSignal.any([parentSignal, AbortSignal.timeout(profile.budget.timeoutMs)]);
16315
- }
16332
+ const timeLimit = budget.timeoutMs && budget.timeoutMs > 0 ? AbortSignal.timeout(budget.timeoutMs) : null;
16333
+ if (timeLimit) signal = AbortSignal.any([parentSignal, timeLimit]);
16316
16334
  let router;
16317
16335
  try {
16318
16336
  if (opts.makeRouter) {
@@ -16384,8 +16402,8 @@ async function runSubagent(opts) {
16384
16402
  jobs: opts.jobs,
16385
16403
  jobScope: task.id,
16386
16404
  inbox: opts.inbox,
16387
- maxIterations: profile.budget.maxIterations,
16388
- maxTokens: profile.budget.maxTokens,
16405
+ maxIterations: budget.maxIterations,
16406
+ maxTokens: budget.maxTokens,
16389
16407
  trace: opts.trace
16390
16408
  };
16391
16409
  log17.info("subagent run", {
@@ -16393,7 +16411,8 @@ async function runSubagent(opts) {
16393
16411
  profile: profile.name,
16394
16412
  provider: router.providerName,
16395
16413
  model: router.model,
16396
- maxIterations: profile.budget.maxIterations
16414
+ maxIterations: budget.maxIterations,
16415
+ timeoutMs: budget.timeoutMs
16397
16416
  });
16398
16417
  let stopReason = "stop";
16399
16418
  try {
@@ -16463,7 +16482,8 @@ async function runSubagent(opts) {
16463
16482
  if (accounting.status !== "reported") {
16464
16483
  log17.debug("subagent tokens sin dato", { id: task.id, tokenStatus: accounting.status });
16465
16484
  }
16466
- const status = stopReason === "cancelled" ? "cancelled" : stopReason === "max_iterations" || stopReason === "budget_tokens" ? "budget_exceeded" : stopReason === "error" ? "failed" : "completed";
16485
+ const timedOut = timeLimit?.aborted === true && !parentSignal.aborted;
16486
+ const status = timedOut ? "budget_exceeded" : stopReason === "cancelled" ? "cancelled" : stopReason === "max_iterations" || stopReason === "budget_tokens" ? "budget_exceeded" : stopReason === "error" ? "failed" : "completed";
16467
16487
  const summary = truncateToolOutput(
16468
16488
  currentText.trim() || "El subagente termin\xF3 sin un resumen textual.",
16469
16489
  RESULT_SUMMARY_CAP
@@ -16476,7 +16496,7 @@ async function runSubagent(opts) {
16476
16496
  filesChanged: toFilesArray(filesChanged),
16477
16497
  decisions: decisions.length ? decisions : void 0,
16478
16498
  usage: { iterations, tokens, tokenStatus, durationMs: Date.now() - start },
16479
- error: status === "failed" && lastError ? `Subagent failed: ${lastError}` : statusErrorText(status)
16499
+ error: status === "failed" && lastError ? `Subagent failed: ${lastError}` : timedOut ? `Subagent ran out of time (${Math.round((budget.timeoutMs ?? 0) / 1e3)} s limit) before finishing. Its partial work may be on disk: check before redoing it. The limit is agents.subagentTimeoutMs in .stratumrc.json (0 = no limit), or budget.timeoutMs in the profile.` : statusErrorText(status)
16480
16500
  };
16481
16501
  }
16482
16502
  function canonicalChangePath(target, baseCwd, rawPath, cwd) {
@@ -16720,7 +16740,7 @@ async function* executeDelegations(jobs, ctx) {
16720
16740
  task: job.taskText,
16721
16741
  profile: job.profile.name,
16722
16742
  context: job.context,
16723
- budget: job.profile.budget
16743
+ budget: resolveSubagentBudget(job.profile, ctx.config.agents)
16724
16744
  },
16725
16745
  profile: job.profile,
16726
16746
  registry: ctx.registry,
@@ -27019,7 +27039,7 @@ async function settle(step, fn) {
27019
27039
  }
27020
27040
  }
27021
27041
  function resolveVersion() {
27022
- if (true) return "0.8.0-beta.2";
27042
+ if (true) return "0.8.0-beta.3";
27023
27043
  try {
27024
27044
  const thisDir = dirname14(fileURLToPath2(import.meta.url));
27025
27045
  const pkg = JSON.parse(
@@ -32874,7 +32894,7 @@ function resolveResult(ref, dir) {
32874
32894
  }
32875
32895
 
32876
32896
  // src/cli/commands/eval.ts
32877
- var VERSION = true ? "0.8.0-beta.2" : "dev";
32897
+ var VERSION = true ? "0.8.0-beta.3" : "dev";
32878
32898
  var fail2 = (message) => {
32879
32899
  process.stderr.write(`${message}
32880
32900
  `);
@@ -33487,7 +33507,7 @@ var statsCommand = new Command14("stats").description(
33487
33507
  init_loader();
33488
33508
  init_schema_version();
33489
33509
  function resolveVersion2() {
33490
- if (true) return "0.8.0-beta.2";
33510
+ if (true) return "0.8.0-beta.3";
33491
33511
  const thisDir = dirname19(fileURLToPath5(import.meta.url));
33492
33512
  const pkg = JSON.parse(readFileSync26(join32(thisDir, "..", "..", "package.json"), "utf-8"));
33493
33513
  return pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stratum-cli",
3
- "version": "0.8.0-beta.2",
3
+ "version": "0.8.0-beta.3",
4
4
  "description": "Agente CLI extensible sobre un loop ReAct — provider-agnostic (Ollama, llama.cpp, vLLM, LiteLLM, OpenAI), con plan-and-execute, subagentes, MCP y SSH nativo",
5
5
  "keywords": [
6
6
  "cli",