@alisio/alisio-code 0.1.0-alpha.3 → 0.1.0-alpha.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.
package/dist/main.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- const VERSION = "0.1.0-alpha.1";
3
+ import { loadVersion } from "./version.js";
4
+ const VERSION = loadVersion(import.meta.url);
4
5
  function parseAgents(json) {
5
6
  const value = JSON.parse(json);
6
7
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -24,7 +25,7 @@ const program = new Command();
24
25
  program
25
26
  .name("alisio")
26
27
  .description("Velocidad y eficiencia para construir — extensible coding harness")
27
- .version("0.1.0-alpha.1")
28
+ .version(VERSION)
28
29
  .option("--cwd <path>", "Working directory")
29
30
  .option("--config <path>", "Explicit trusted configuration file")
30
31
  .option("--trust-project", "Load project config and executable plugins (full process privileges)")
@@ -269,7 +270,7 @@ program.command("doctor").action(async (_opts, cmd) => {
269
270
  process.env.ALISIO_MODEL?.trim() ||
270
271
  (useSaved ? saved.profile.model : config.provider.model);
271
272
  const status = {
272
- version: "0.1.0-alpha.1",
273
+ version: VERSION,
273
274
  runtime: process.versions.bun ? `bun ${process.versions.bun}` : `node ${process.versions.node}`,
274
275
  platform: process.platform,
275
276
  workspace,
package/dist/tui/app.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { homedir } from "node:os";
2
2
  import { CombinedAutocompleteProvider, Container, Editor, getImageDimensions, getNativeClipboard, Key, matchesKey, ProcessTerminal, ScrollView, SelectList, TuiAltScreen, truncateToWidth, VStack, } from "@earendil-works/pi-tui";
3
+ import { loadVersion } from "../version.js";
3
4
  import { MAX_ATTACHMENTS_PER_MESSAGE, MAX_IMAGE_BYTES, pasteImageFromClipboard, removeLastAttachment, toApiAttachment, } from "./attachments.js";
4
5
  import { copyText, nodeSpawn } from "./clipboard.js";
5
6
  import { AttachmentsBar, BannerBlock, clock, Footer, Header, QuestionPanel, Switch, TranscriptSync, TreePanel, } from "./components.js";
@@ -10,7 +11,7 @@ import { InteractiveQueue } from "./queue.js";
10
11
  import { SkillsManager } from "./skills-manager.js";
11
12
  import { addItem, COMMANDS, configuredProviderModelItems, formatContext, formatDuration, formatTokens, hostOf, initialViewState, itemsFromHistory, lastAssistantText, mcpServerItems, mcpToolItems, parseCommand, pluginCatalogItems, pluginToggleNeedsConfirmation, providerModelItems, reduceEvent, reservedCommandNames, resolveCommand, shortenPath, shortId, summarizeToolArgs, } from "./state.js";
12
13
  import { editorTheme, selectListTheme, style } from "./theme.js";
13
- const VERSION = "0.1.0-alpha.1";
14
+ const VERSION = loadVersion(import.meta.url);
14
15
  /** Inline selection list with type-to-filter, rendered above the editor. */
15
16
  class Picker {
16
17
  title;
@@ -182,8 +183,10 @@ export async function runTui(options) {
182
183
  path.unshift(cur.label);
183
184
  const siblings = nodes.filter((n) => n.parentId === node.parentId);
184
185
  const child = viewedState();
185
- const window = app.contextWindow(child?.model ?? view.model);
186
- const pct = child?.context && window ? ` · ctx ${Math.round((child.context.used / window) * 100)}%` : "";
186
+ const budget = app.contextBudget(child?.model ?? view.model);
187
+ const pct = child?.context && budget
188
+ ? ` · ctx ${Math.round((child.context.used / budget.total) * 100)}%`
189
+ : "";
187
190
  const tokens = child
188
191
  ? ` · ↑${formatTokens(child.stats.input)} ↓${formatTokens(child.stats.output)}`
189
192
  : "";
@@ -194,7 +197,7 @@ export async function runTui(options) {
194
197
  : panelState.focus === "view"
195
198
  ? viewHint()
196
199
  : undefined;
197
- const footer = new Footer(() => viewedState() ?? view, () => app.contextWindow((viewedState() ?? view).model), () => panelHint() ?? hint, () => [...app.plugins.status.values()].map((s) => s.text));
200
+ const footer = new Footer(() => viewedState() ?? view, () => app.contextBudget((viewedState() ?? view).model), () => panelHint() ?? hint, () => [...app.plugins.status.values()].map((s) => s.text));
198
201
  const treePanel = new TreePanel(() => {
199
202
  const entry = panelEntry();
200
203
  if (!entry)
@@ -865,7 +868,7 @@ export async function runTui(options) {
865
868
  };
866
869
  const statsReport = () => {
867
870
  const s = view.stats;
868
- const window = app.contextWindow(view.model);
871
+ const budget = app.contextBudget(view.model);
869
872
  const tools = Object.entries(s.tools)
870
873
  .map(([name, t]) => `| \`${name}\` | ${t.calls} | ${t.errors} |`)
871
874
  .join("\n");
@@ -877,7 +880,7 @@ export async function runTui(options) {
877
880
  `- Tokens: in ${formatTokens(s.input)} · out ${formatTokens(s.output)} · cached ${formatTokens(s.cached)}`,
878
881
  `- Runs: ${s.runs} · turns: ${s.turns}${s.lastRunMs !== undefined ? ` · last run ${formatDuration(s.lastRunMs)}` : ""}`,
879
882
  `- Duration: ${formatDuration(Date.now() - s.startedAt)}`,
880
- `- Context: ${formatContext(view.context?.used ?? 0, window, view.context?.estimated ?? true)}`,
883
+ `- Context: ${formatContext(view.context?.used ?? 0, budget?.total, view.context?.estimated ?? true, budget?.basis)}`,
881
884
  "",
882
885
  tools ? `| Tool | Calls | Errors |\n| --- | --- | --- |\n${tools}` : "No tool calls yet.",
883
886
  "",
@@ -2,7 +2,7 @@ import type { PanelNode } from "@alisio/sdk";
2
2
  import { type Component, Container } from "@earendil-works/pi-tui";
3
3
  import { type PendingAttachment } from "./attachments.ts";
4
4
  import { type QuestionPanelState, type QuestionSpec } from "./questions.ts";
5
- import { type TranscriptItem, type ViewState } from "./state.ts";
5
+ import { type ContextBudget, type TranscriptItem, type ViewState } from "./state.ts";
6
6
  export declare const SPINNER: string[];
7
7
  /** Shared animation clock advanced by the app while work is running. */
8
8
  export declare const clock: {
@@ -31,10 +31,10 @@ export declare class Header implements Component {
31
31
  }
32
32
  export declare class Footer implements Component {
33
33
  private view;
34
- private window;
34
+ private budget;
35
35
  private hint;
36
36
  private statuses;
37
- constructor(view: () => ViewState, window: () => number | undefined, hint: () => string | undefined, statuses?: () => string[]);
37
+ constructor(view: () => ViewState, budget: () => ContextBudget | undefined, hint: () => string | undefined, statuses?: () => string[]);
38
38
  invalidate(): void;
39
39
  render(width: number): string[];
40
40
  }
@@ -60,20 +60,20 @@ export class Header {
60
60
  }
61
61
  export class Footer {
62
62
  view;
63
- window;
63
+ budget;
64
64
  hint;
65
65
  statuses;
66
- constructor(view, window, hint, statuses = () => []) {
66
+ constructor(view, budget, hint, statuses = () => []) {
67
67
  this.view = view;
68
- this.window = window;
68
+ this.budget = budget;
69
69
  this.hint = hint;
70
70
  this.statuses = statuses;
71
71
  }
72
72
  invalidate() { }
73
73
  render(width) {
74
- const v = this.view(), total = this.window(), used = v.context?.used ?? 0, estimated = v.context?.estimated ?? true;
74
+ const v = this.view(), budget = this.budget(), total = budget?.total, used = v.context?.used ?? 0, estimated = v.context?.estimated ?? true;
75
75
  const pct = contextPercent(used, total);
76
- const level = contextLevel(pct ?? 0);
76
+ const level = contextLevel(pct ?? 0, budget?.compactionAt ?? 85);
77
77
  const cells = 10, filled = pct === undefined ? 0 : Math.min(cells, Math.round((pct / 100) * cells));
78
78
  const bar = pct === undefined
79
79
  ? style.gray("░".repeat(cells))
@@ -86,7 +86,7 @@ export class Footer {
86
86
  const s = v.stats;
87
87
  const segments = [
88
88
  {
89
- text: `ctx ${formatContext(used, total, estimated)}`,
89
+ text: `ctx ${formatContext(used, total, estimated, budget?.basis)}`,
90
90
  priority: 10,
91
91
  paint: pct === undefined ? style.gray : levelColor(level),
92
92
  },
@@ -84,9 +84,18 @@ export declare function configuredProviderModelItems(catalogs: ProviderCatalogVi
84
84
  provider: string;
85
85
  model: string;
86
86
  }): ConfiguredProviderModelItem[];
87
- export declare function contextLevel(pct: number): Level;
87
+ export declare function contextLevel(pct: number, compactionAt?: number): Level;
88
88
  export declare function contextPercent(used: number, total: number | undefined): number | undefined;
89
- export declare function formatContext(used: number, total: number | undefined, estimated: boolean): string;
89
+ /** The effective total the context bar measures against, and what it is derived from. */
90
+ export interface ContextBudget {
91
+ /** Effective total in tokens (model window, or the char budget converted to tokens). */
92
+ total: number;
93
+ /** Basis of the total: the model's context window, or the char-budget fallback. */
94
+ basis: "window" | "chars";
95
+ /** Percentage of `total` at which the engine auto-compacts; the bar turns red there. */
96
+ compactionAt: number;
97
+ }
98
+ export declare function formatContext(used: number, total: number | undefined, estimated: boolean, basis?: "window" | "chars"): string;
90
99
  export declare function formatDuration(ms: number): string;
91
100
  export declare const textWidth: (text: string) => number;
92
101
  export declare function truncatePlain(text: string, width: number): string;
package/dist/tui/state.js CHANGED
@@ -117,18 +117,23 @@ export function configuredProviderModelItems(catalogs, current) {
117
117
  }
118
118
  return items;
119
119
  }
120
- export function contextLevel(pct) {
121
- return pct < 60 ? "ok" : pct < 85 ? "warn" : "danger";
120
+ export function contextLevel(pct, compactionAt = 85) {
121
+ // Warning band starts a quarter below the auto-compaction point; danger is exactly there.
122
+ const warn = Math.max(0, compactionAt - 25);
123
+ return pct < warn ? "ok" : pct < compactionAt ? "warn" : "danger";
122
124
  }
123
125
  export function contextPercent(used, total) {
124
126
  return total && total > 0 ? (used / total) * 100 : undefined;
125
127
  }
126
- export function formatContext(used, total, estimated) {
128
+ export function formatContext(used, total, estimated, basis) {
127
129
  const prefix = `${estimated ? "~" : ""}${formatTokens(used)} / `;
128
130
  const pct = contextPercent(used, total);
129
- return pct === undefined || !total
130
- ? `${prefix}unknown`
131
- : `${prefix}${formatTokens(total)} (${Math.round(pct)}%)`;
131
+ if (pct === undefined || !total)
132
+ return `${prefix}unknown`;
133
+ // The `~` marks an estimate; the "char budget" suffix tells the user the bar is measured
134
+ // against the fallback (est. tokens from limits.maxContextChars), not a model window.
135
+ const basisSuffix = basis === "chars" ? " char budget" : "";
136
+ return `${prefix}${formatTokens(total)} (${Math.round(pct)}%)${basisSuffix}`;
132
137
  }
133
138
  export function formatDuration(ms) {
134
139
  if (ms < 1000)
@@ -461,6 +466,9 @@ export function reduceEvent(state, event) {
461
466
  const checkpoint = typeof d.summarizedTokens === "number" && typeof d.checkpointTokens === "number"
462
467
  ? ` · checkpoint ~${formatTokens(d.summarizedTokens)} → ~${formatTokens(d.checkpointTokens)} tokens`
463
468
  : "";
469
+ const partial = d.partial
470
+ ? " · partial: the summary was cut by max output tokens; consider raising compaction.maxOutputTokens"
471
+ : "";
464
472
  return addItem({
465
473
  ...state,
466
474
  compacting: false,
@@ -468,7 +476,7 @@ export function reduceEvent(state, event) {
468
476
  }, {
469
477
  kind: "notice",
470
478
  text: [
471
- `Context compacted (${String(d.reason ?? "manual")}): ${String(d.replaced ?? 0)} messages summarized, ~${formatTokens(Number(d.before ?? 0))} → ~${formatTokens(Number(d.after ?? 0))} tokens${checkpoint}`,
479
+ `Context compacted (${String(d.reason ?? "manual")}): ${String(d.replaced ?? 0)} messages summarized, ~${formatTokens(Number(d.before ?? 0))} → ~${formatTokens(Number(d.after ?? 0))} tokens${checkpoint}${partial}`,
472
480
  ...reports,
473
481
  ].join("\n"),
474
482
  });
@@ -487,6 +495,11 @@ export function reduceEvent(state, event) {
487
495
  return addItem({ ...state, compacting: false }, { kind: "notice", text: `Compaction skipped: ${String(d.detail ?? "nothing to compact")}` });
488
496
  case "compaction_failed":
489
497
  return addItem({ ...state, compacting: false }, { kind: "error", text: `Compaction failed: ${String(d.error ?? "unknown error")}` });
498
+ case "response_truncated":
499
+ return addItem(state, {
500
+ kind: "notice",
501
+ text: "Response cut by max output tokens — the answer may be incomplete. Raise limits.maxOutputTokens to allow longer answers.",
502
+ });
490
503
  case "model_changed":
491
504
  return {
492
505
  ...state,
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Reads the package version at runtime so --version, doctor and MCP client metadata
3
+ * stay in sync with the published package without a build-time constant to update.
4
+ * Standalone binaries inject the version at build time via ALISIO_PACKAGE_VERSION.
5
+ */
6
+ export declare function loadVersion(fromHere: string): string;
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /** Fallback used when package.json is not readable (e.g. standalone binaries). */
5
+ const FALLBACK = "0.1.0-alpha.1";
6
+ /**
7
+ * Reads the package version at runtime so --version, doctor and MCP client metadata
8
+ * stay in sync with the published package without a build-time constant to update.
9
+ * Standalone binaries inject the version at build time via ALISIO_PACKAGE_VERSION.
10
+ */
11
+ export function loadVersion(fromHere) {
12
+ if (process.env.ALISIO_PACKAGE_VERSION)
13
+ return process.env.ALISIO_PACKAGE_VERSION;
14
+ try {
15
+ const manifest = join(dirname(fileURLToPath(fromHere)), "..", "package.json");
16
+ const parsed = JSON.parse(readFileSync(manifest, "utf8"));
17
+ return typeof parsed.version === "string" && parsed.version ? parsed.version : FALLBACK;
18
+ }
19
+ catch {
20
+ return FALLBACK;
21
+ }
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alisio/alisio-code",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.5",
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,14 +45,14 @@
45
45
  "alisio": "./dist/main.js"
46
46
  },
47
47
  "dependencies": {
48
- "@alisio/core": "0.1.0-alpha.3",
49
- "@alisio/plugin-deepseek": "0.1.0-alpha.2",
50
- "@alisio/plugin-memory": "0.1.0-alpha.3",
51
- "@alisio/plugin-openai-compatible": "0.1.0-alpha.3",
52
- "@alisio/plugin-opencode": "0.1.0-alpha.2",
53
- "@alisio/plugin-opencode-go": "0.1.0-alpha.2",
54
- "@alisio/plugin-subagents": "0.1.0-alpha.3",
55
- "@alisio/sdk": "0.1.0-alpha.2",
48
+ "@alisio/core": "0.1.0-alpha.5",
49
+ "@alisio/plugin-deepseek": "0.1.0-alpha.5",
50
+ "@alisio/plugin-memory": "0.1.0-alpha.5",
51
+ "@alisio/plugin-openai-compatible": "0.1.0-alpha.5",
52
+ "@alisio/plugin-opencode": "0.1.0-alpha.5",
53
+ "@alisio/plugin-opencode-go": "0.1.0-alpha.5",
54
+ "@alisio/plugin-subagents": "0.1.0-alpha.5",
55
+ "@alisio/sdk": "0.1.0-alpha.5",
56
56
  "@earendil-works/pi-tui": "0.87.1",
57
57
  "commander": "15.0.0"
58
58
  },