@alisio/alisio-code 0.1.0-alpha.27 → 0.1.0-alpha.28

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/dist/main.js CHANGED
@@ -131,6 +131,10 @@ async function run(cmd, prompt, sessionId) {
131
131
  const retry = event.data;
132
132
  process.stderr.write(`\nThe model did not respond; retrying (${retry.attempt}/${retry.of})…\n`);
133
133
  }
134
+ else if (event.type === "truncation_recovery") {
135
+ const recovery = event.data;
136
+ process.stderr.write(`\nThe response was cut off by the output limit; retrying in smaller steps (${recovery.attempt}/${recovery.of})…\n`);
137
+ }
134
138
  else if (event.type === "tool_started")
135
139
  process.stderr.write(`\n→ ${event.data.name}\n`);
136
140
  else if (event.type === "artifact_published")
@@ -318,7 +322,7 @@ program
318
322
  console.log('To generate AGENTS.md for this project, run /init inside alisio (or: alisio run "/init" --allow-write).');
319
323
  });
320
324
  program.command("doctor").action(async (_opts, cmd) => {
321
- const { findWorkspace, loadConfigWithProvenance, overridesSavedProviderProfile, ProviderSettingsStore, RIPGREP_INSTALL_HINT, which, } = await import("@alisio/core");
325
+ const { DEFAULT_MAX_OUTPUT_TOKENS, findWorkspace, loadConfigWithProvenance, MAX_AUTO_OUTPUT_TOKENS, overridesSavedProviderProfile, ProviderSettingsStore, RIPGREP_INSTALL_HINT, which, } = await import("@alisio/core");
322
326
  const o = options(cmd);
323
327
  const workspace = await findWorkspace(o.cwd ?? process.cwd());
324
328
  const { config, provenance } = await loadConfigWithProvenance(workspace, {
@@ -341,6 +345,14 @@ program.command("doctor").action(async (_opts, cmd) => {
341
345
  workspace,
342
346
  git: (await which("git")) ?? null,
343
347
  ripgrep: (await which("rg")) ?? null,
348
+ // The per-request output budget is resolved when a run starts, from the model's catalog;
349
+ // doctor does not contact the provider, so it reports what the configuration decides.
350
+ outputTokens: config.limits.maxOutputTokens !== undefined
351
+ ? { limit: config.limits.maxOutputTokens, source: "set by you (limits.maxOutputTokens)" }
352
+ : {
353
+ limit: null,
354
+ source: `the model catalog's declared limit (capped at ${MAX_AUTO_OUTPUT_TOKENS}), else ${DEFAULT_MAX_OUTPUT_TOKENS}`,
355
+ },
344
356
  provider: {
345
357
  id: useSaved ? saved.profile.provider : "openai-compatible",
346
358
  baseURL: useSaved ? saved.profile.values.baseURL : config.provider.baseURL,
@@ -50,7 +50,8 @@ export interface SettingsConfigView {
50
50
  };
51
51
  limits: {
52
52
  maxTurns: number;
53
- maxOutputTokens: number;
53
+ /** Only present when the user set it; unset means "the model's declared limit, else 16384". */
54
+ maxOutputTokens?: number | undefined;
54
55
  maxContextChars: number;
55
56
  timeoutMs: number;
56
57
  };
@@ -12,7 +12,7 @@ export const WEBSEARCH_PROVIDERS = [
12
12
  export const defaultConfig = {
13
13
  compaction: { auto: true, threshold: 0.85, keepTurns: 2, maxOutputTokens: 16_000 },
14
14
  context: { claudeMdFallback: false, maxBytes: 32 * 1024 },
15
- limits: { maxTurns: 100, maxOutputTokens: 16_384, maxContextChars: 800_000, timeoutMs: 300_000 },
15
+ limits: { maxTurns: 100, maxContextChars: 800_000, timeoutMs: 300_000 },
16
16
  tui: { paddingX: 1, contentPaddingX: 2, skillSlashCommands: true },
17
17
  mcp: { allow: false },
18
18
  websearch: { provider: undefined },
@@ -125,9 +125,10 @@ export const SETTINGS_DEFINITIONS = [
125
125
  label: "Agent max output tokens",
126
126
  category: "Limits",
127
127
  valueType: "number",
128
- values: [1_024, 2_048, 4_096, 8_192, 16_384],
129
- read: (config) => config.limits.maxOutputTokens,
130
- description: "Per-call output token budget for agent turns. A cut response shows a notice suggesting a higher value. Applied from the next run.",
128
+ values: [1_024, 2_048, 4_096, 8_192, 16_384, 32_768, 65_536],
129
+ // Unset: each request uses the model's declared limit (capped at 65536), else 16384.
130
+ read: (config) => config.limits.maxOutputTokens ?? 16_384,
131
+ description: "Per-call output token budget for agent turns. While you leave it unchanged, each request uses the output limit the model's catalog declares (capped at 65536), or 16384 when it declares none; a value set here always wins. A cut response shows a notice suggesting a higher value. Applied from the next run.",
131
132
  },
132
133
  {
133
134
  id: "limits.maxContextChars",
@@ -348,6 +348,11 @@ export interface ViewState {
348
348
  attempt: number;
349
349
  of: number;
350
350
  };
351
+ /** The response was cut by the output limit and is requested again (`truncation_recovery`). */
352
+ recovery?: {
353
+ attempt: number;
354
+ of: number;
355
+ };
351
356
  model: string;
352
357
  /** Context size in tokens: provider-reported, or estimated (`~`). */
353
358
  context?: {
package/dist/tui/state.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Pure presentation logic for the TUI: formatting, command parsing and the reduction of
3
3
  * versioned runner events into a view model. No terminal or pi-tui imports here.
4
4
  */
5
- import { CommandCatalog } from "@alisio/core";
5
+ import { CommandCatalog, describeOutputLimitSource } from "@alisio/core";
6
6
  const trimZero = (value) => value.replace(/\.0$/, "");
7
7
  export function formatTokens(n) {
8
8
  if (!Number.isFinite(n) || n < 0)
@@ -583,7 +583,7 @@ export function reduceEvent(state, event) {
583
583
  event.type === "run_cancelled" ||
584
584
  event.type === "run_turns_exceeded";
585
585
  if (terminal) {
586
- const { lastEventAt: _ended, retry: _retry, ...rest } = next;
586
+ const { lastEventAt: _ended, retry: _retry, recovery: _recovery, ...rest } = next;
587
587
  return rest;
588
588
  }
589
589
  return { ...next, lastEventAt: Date.parse(event.timestamp) || Date.now() };
@@ -612,6 +612,11 @@ export function runPhase(view) {
612
612
  label: `the model did not respond; retrying (${view.retry.attempt}/${view.retry.of})`,
613
613
  silentByDesign: false,
614
614
  };
615
+ if (view.recovery)
616
+ return {
617
+ label: `the response was cut off; retrying in smaller steps (${view.recovery.attempt}/${view.recovery.of})`,
618
+ silentByDesign: false,
619
+ };
615
620
  const tool = view.items.findLast((i) => i.kind === "tool" && i.status !== "ok" && i.status !== "error");
616
621
  if (tool?.kind === "tool") {
617
622
  const name = tool.name.replace(/^p_[0-9a-f]{10}_/, "");
@@ -658,8 +663,8 @@ const RETRY_ENDERS = new Set([
658
663
  "compaction_started",
659
664
  ]);
660
665
  function reduceEventItems(state, event) {
661
- if (state.retry && RETRY_ENDERS.has(event.type)) {
662
- const { retry: _retry, ...rest } = state;
666
+ if ((state.retry || state.recovery) && RETRY_ENDERS.has(event.type)) {
667
+ const { retry: _retry, recovery: _recovery, ...rest } = state;
663
668
  return reduceEventItems(rest, event);
664
669
  }
665
670
  const d = (event.data ?? {});
@@ -764,6 +769,8 @@ function reduceEventItems(state, event) {
764
769
  }
765
770
  return ended;
766
771
  }
772
+ case "truncation_recovery":
773
+ return { ...state, recovery: { attempt: Number(d.attempt) || 1, of: Number(d.of) || 1 } };
767
774
  case "request_retry":
768
775
  return { ...state, retry: { attempt: Number(d.attempt) || 1, of: Number(d.of) || 1 } };
769
776
  case "compaction_started":
@@ -807,7 +814,9 @@ function reduceEventItems(state, event) {
807
814
  case "response_truncated":
808
815
  return addItem(state, {
809
816
  kind: "notice",
810
- text: "Response cut by max output tokens — the answer may be incomplete. Raise limits.maxOutputTokens (/settings → Agent max output tokens) to allow longer answers.",
817
+ text: `Response cut by max output tokens${typeof d.maxOutputTokens === "number"
818
+ ? ` (${d.maxOutputTokens} tokens, ${describeOutputLimitSource(d.source)})`
819
+ : ""} — the answer may be incomplete. Raise limits.maxOutputTokens (/settings → Agent max output tokens) to allow longer answers.`,
811
820
  });
812
821
  case "model_changed":
813
822
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alisio/alisio-code",
3
- "version": "0.1.0-alpha.27",
3
+ "version": "0.1.0-alpha.28",
4
4
  "description": "Alisio: an extensible, provider-agnostic coding-agent harness for your terminal. TUI, OpenAI-compatible providers, permissioned local tools, context compaction, persistent memory and a typed plugin SDK.",
5
5
  "author": "Gustavo Gutiérrez",
6
6
  "license": "MIT",
@@ -45,12 +45,12 @@
45
45
  "alisio": "./dist/main.js"
46
46
  },
47
47
  "dependencies": {
48
- "@alisio/core": "0.1.0-alpha.23",
48
+ "@alisio/core": "0.1.0-alpha.24",
49
49
  "@alisio/plugin-memory": "0.1.0-alpha.9",
50
- "@alisio/plugin-openai-compatible": "0.1.0-alpha.9",
50
+ "@alisio/plugin-openai-compatible": "0.1.0-alpha.10",
51
51
  "@alisio/plugin-subagents": "0.1.0-alpha.11",
52
- "@alisio/sdk": "0.1.0-alpha.19",
53
- "@alisio/server": "0.1.0-alpha.7",
52
+ "@alisio/sdk": "0.1.0-alpha.20",
53
+ "@alisio/server": "0.1.0-alpha.8",
54
54
  "@earendil-works/pi-tui": "0.87.1",
55
55
  "commander": "15.0.0"
56
56
  },