@alisio/core 0.1.0-alpha.8 → 0.1.0-alpha.9

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.
@@ -1,4 +1,5 @@
1
1
  import type { AvailableProviderModel, ModelInfo, ModelProvider, Plugin, PluginCategory, ProviderConfigurationValue, ResolvedProviderModel, RunEvent } from "@alisio/sdk";
2
+ import { type SettableSettingKey } from "./config.ts";
2
3
  import type { ApprovalHandler } from "./core/contracts.ts";
3
4
  import { ToolRegistry } from "./core/registry.ts";
4
5
  import { AgentRunner } from "./core/runner.ts";
@@ -163,6 +164,18 @@ export declare function createApplication(options?: AppOptions): Promise<{
163
164
  revokeGlobalMcpConsent(): Promise<void>;
164
165
  /** Persist an MCP toggle in its defining layer, then safely update this process. */
165
166
  setMcpEnabled(id: string, enabled: boolean, connect?: boolean): Promise<import("./index.ts").McpServerInfo>;
167
+ /**
168
+ * Persist one user-facing setting to the GLOBAL user config (`<config home>/config.json`)
169
+ * and apply it to this process where the value can change live. Compaction and limits land
170
+ * in the runner (honored by the next run/compact), `context.*` lands in the project context
171
+ * (next instructions load), `websearch.provider` mutates the shared config object the search
172
+ * chain reads per call, `pluginHooks.timeoutMs` lands in the plugin host (next hook call),
173
+ * and `tui.*` is persisted for the host to apply. Nothing in this set requires a restart.
174
+ * `mcp.allow` is deliberately not routed here: it flows through
175
+ * `rememberGlobalMcpConsent`/`revokeGlobalMcpConsent` instead.
176
+ * Throws under `--read-only`. Returns the written config file.
177
+ */
178
+ updateSetting(key: SettableSettingKey, value: unknown): Promise<string>;
166
179
  provider: ActiveProvider;
167
180
  providers: ProviderRegistry;
168
181
  providerSettings: ProviderSettingsStore;
@@ -1,6 +1,6 @@
1
1
  import { homedir } from "node:os";
2
2
  import { dirname, join, resolve } from "node:path";
3
- import { configFile, configHome, loadConfigWithProvenance, overridesSavedProviderProfile, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
3
+ import { configFile, configHome, loadConfigWithProvenance, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
4
4
  import { completeText } from "./core/compaction.js";
5
5
  import { ToolRegistry } from "./core/registry.js";
6
6
  import { AgentRunner } from "./core/runner.js";
@@ -729,6 +729,69 @@ export async function createApplication(options = {}) {
729
729
  : undefined);
730
730
  return mcp.info(id);
731
731
  },
732
+ /**
733
+ * Persist one user-facing setting to the GLOBAL user config (`<config home>/config.json`)
734
+ * and apply it to this process where the value can change live. Compaction and limits land
735
+ * in the runner (honored by the next run/compact), `context.*` lands in the project context
736
+ * (next instructions load), `websearch.provider` mutates the shared config object the search
737
+ * chain reads per call, `pluginHooks.timeoutMs` lands in the plugin host (next hook call),
738
+ * and `tui.*` is persisted for the host to apply. Nothing in this set requires a restart.
739
+ * `mcp.allow` is deliberately not routed here: it flows through
740
+ * `rememberGlobalMcpConsent`/`revokeGlobalMcpConsent` instead.
741
+ * Throws under `--read-only`. Returns the written config file.
742
+ */
743
+ async updateSetting(key, value) {
744
+ if (options.readOnly)
745
+ throw new Error("Settings changes are unavailable under --read-only");
746
+ const file = await setConfigValue({ key, value });
747
+ const compactionLeaf = (leaf) => ({ [leaf]: value });
748
+ const limitLeaf = (leaf) => ({ [leaf]: value });
749
+ switch (key) {
750
+ case "compaction.auto":
751
+ case "compaction.threshold":
752
+ case "compaction.keepTurns":
753
+ case "compaction.maxOutputTokens": {
754
+ const patch = compactionLeaf(key.slice("compaction.".length));
755
+ config.compaction = { ...config.compaction, ...patch };
756
+ runner.applySettings({ compaction: patch });
757
+ break;
758
+ }
759
+ case "context.claudeMdFallback":
760
+ config.context = { ...config.context, claudeMdFallback: value === true };
761
+ context.update({ claudeMdFallback: value === true });
762
+ break;
763
+ case "context.maxBytes":
764
+ config.context = { ...config.context, maxBytes: Number(value) };
765
+ context.update({ maxBytes: Number(value) });
766
+ break;
767
+ case "limits.maxTurns":
768
+ case "limits.maxOutputTokens":
769
+ case "limits.maxContextChars":
770
+ case "limits.timeoutMs": {
771
+ const patch = limitLeaf(key.slice("limits.".length));
772
+ config.limits = { ...config.limits, ...patch };
773
+ runner.applySettings(patch);
774
+ break;
775
+ }
776
+ case "pluginHooks.timeoutMs":
777
+ config.pluginHooks = { ...config.pluginHooks, timeoutMs: Number(value) };
778
+ plugins.applyTimeoutSettings({ hookTimeoutMs: Number(value) });
779
+ break;
780
+ case "tui.paddingX":
781
+ config.tui = { ...config.tui, paddingX: Number(value) };
782
+ break;
783
+ case "tui.skillSlashCommands":
784
+ config.tui = { ...config.tui, skillSlashCommands: value === true };
785
+ break;
786
+ case "websearch.provider":
787
+ // Mutated in place (same object identity) so the tool chain's per-call
788
+ // `websearchCtx.config` sees the new provider on the very next search call.
789
+ config.websearch.provider =
790
+ value === undefined ? undefined : value;
791
+ break;
792
+ }
793
+ return file;
794
+ },
732
795
  provider,
733
796
  providers,
734
797
  providerSettings,
package/dist/config.d.ts CHANGED
@@ -172,6 +172,10 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
172
172
  maxOutputTokens: z.ZodDefault<z.ZodNumber>;
173
173
  maxTokens: z.ZodOptional<z.ZodNumber>;
174
174
  }, z.core.$strip>>;
175
+ tui: z.ZodDefault<z.ZodObject<{
176
+ paddingX: z.ZodDefault<z.ZodNumber>;
177
+ skillSlashCommands: z.ZodDefault<z.ZodBoolean>;
178
+ }, z.core.$strict>>;
175
179
  }, z.core.$strict>, z.ZodTransform<{
176
180
  schemaVersion: 1;
177
181
  provider: {
@@ -223,6 +227,10 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
223
227
  maxOutputTokens: number;
224
228
  maxTokens?: number | undefined;
225
229
  };
230
+ tui: {
231
+ paddingX: number;
232
+ skillSlashCommands: boolean;
233
+ };
226
234
  mcp: {
227
235
  allow?: boolean | undefined;
228
236
  servers: {
@@ -357,6 +365,10 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
357
365
  maxOutputTokens: number;
358
366
  maxTokens?: number | undefined;
359
367
  };
368
+ tui: {
369
+ paddingX: number;
370
+ skillSlashCommands: boolean;
371
+ };
360
372
  }>>;
361
373
  export type Config = z.infer<typeof configSchema>;
362
374
  export type ServerConfig = z.infer<typeof serverSchema>;
@@ -416,6 +428,41 @@ export declare function setMcpServerEnabled(input: {
416
428
  export declare function setGlobalMcpAllow(input: {
417
429
  allow: boolean;
418
430
  }): Promise<string>;
431
+ declare const SETTABLE_KEYS: {
432
+ readonly "compaction.auto": z.ZodDefault<z.ZodBoolean>;
433
+ readonly "compaction.threshold": z.ZodDefault<z.ZodNumber>;
434
+ readonly "compaction.keepTurns": z.ZodDefault<z.ZodNumber>;
435
+ readonly "compaction.maxOutputTokens": z.ZodDefault<z.ZodNumber>;
436
+ readonly "context.claudeMdFallback": z.ZodDefault<z.ZodBoolean>;
437
+ readonly "context.maxBytes": z.ZodDefault<z.ZodNumber>;
438
+ readonly "limits.maxTurns": z.ZodDefault<z.ZodNumber>;
439
+ readonly "limits.maxOutputTokens": z.ZodDefault<z.ZodNumber>;
440
+ readonly "limits.maxContextChars": z.ZodDefault<z.ZodNumber>;
441
+ readonly "limits.timeoutMs": z.ZodDefault<z.ZodNumber>;
442
+ readonly "pluginHooks.timeoutMs": z.ZodDefault<z.ZodNumber>;
443
+ readonly "tui.paddingX": z.ZodDefault<z.ZodNumber>;
444
+ readonly "tui.skillSlashCommands": z.ZodDefault<z.ZodBoolean>;
445
+ readonly "websearch.provider": z.ZodOptional<z.ZodEnum<{
446
+ brave: "brave";
447
+ "duckduckgo-instant": "duckduckgo-instant";
448
+ native: "native";
449
+ searxng: "searxng";
450
+ serpapi: "serpapi";
451
+ tavily: "tavily";
452
+ }>>;
453
+ };
454
+ export type SettableSettingKey = keyof typeof SETTABLE_KEYS;
455
+ export declare function isSettableSettingKey(key: string): key is SettableSettingKey;
456
+ /**
457
+ * Atomically sets one user-facing setting in the global config file (`<config home>/config.json`)
458
+ * while preserving every unrelated JSON field. The value is validated against the same zod leaf
459
+ * used by `configSchema`, so a write can never produce a config the loader would reject. The
460
+ * caller is responsible for applying the change to the running process (see `application.ts`).
461
+ */
462
+ export declare function setConfigValue(input: {
463
+ key: SettableSettingKey;
464
+ value: unknown;
465
+ }): Promise<string>;
419
466
  /** Atomically updates one project plugin override while preserving every unrelated JSON field. */
420
467
  export declare function setProjectPluginEnabled(input: {
421
468
  workspace: string;
package/dist/config.js CHANGED
@@ -164,6 +164,18 @@ const configObjectSchema = z
164
164
  maxContextChars: 160000,
165
165
  maxOutputTokens: 4096,
166
166
  })),
167
+ tui: z
168
+ .object({
169
+ /** Horizontal padding (columns) around the editor input box. */
170
+ paddingX: z.number().int().min(0).max(4).default(1),
171
+ /**
172
+ * Offer effective skills as first-class `skill:<id>` editor slash-autocomplete entries.
173
+ * Off hides those entries; the `/skills` manager and its argument completion stay available.
174
+ */
175
+ skillSlashCommands: z.boolean().default(true),
176
+ })
177
+ .strict()
178
+ .default(() => ({ paddingX: 1, skillSlashCommands: true })),
167
179
  })
168
180
  .strict()
169
181
  .superRefine((config, context) => {
@@ -398,6 +410,89 @@ export async function setGlobalMcpAllow(input) {
398
410
  }
399
411
  return file;
400
412
  }
413
+ /**
414
+ * The user-facing settings writable through the atomic `setConfigValue` writer, with their zod
415
+ * leaf validators derived from the config schema. Only these keys are accepted; every other
416
+ * config path stays out of reach of the settings menu (unknown keys are rejected, never created).
417
+ */
418
+ const SETTABLE_SECTIONS = {
419
+ compaction: configObjectSchema.shape.compaction.removeDefault(),
420
+ context: configObjectSchema.shape.context.removeDefault(),
421
+ limits: configObjectSchema.shape.limits.removeDefault(),
422
+ pluginHooks: configObjectSchema.shape.pluginHooks.removeDefault(),
423
+ tui: configObjectSchema.shape.tui.removeDefault(),
424
+ websearch: configObjectSchema.shape.websearch.removeDefault(),
425
+ };
426
+ const SETTABLE_KEYS = {
427
+ "compaction.auto": SETTABLE_SECTIONS.compaction.shape.auto,
428
+ "compaction.threshold": SETTABLE_SECTIONS.compaction.shape.threshold,
429
+ "compaction.keepTurns": SETTABLE_SECTIONS.compaction.shape.keepTurns,
430
+ "compaction.maxOutputTokens": SETTABLE_SECTIONS.compaction.shape.maxOutputTokens,
431
+ "context.claudeMdFallback": SETTABLE_SECTIONS.context.shape.claudeMdFallback,
432
+ "context.maxBytes": SETTABLE_SECTIONS.context.shape.maxBytes,
433
+ "limits.maxTurns": SETTABLE_SECTIONS.limits.shape.maxTurns,
434
+ "limits.maxOutputTokens": SETTABLE_SECTIONS.limits.shape.maxOutputTokens,
435
+ "limits.maxContextChars": SETTABLE_SECTIONS.limits.shape.maxContextChars,
436
+ "limits.timeoutMs": SETTABLE_SECTIONS.limits.shape.timeoutMs,
437
+ "pluginHooks.timeoutMs": SETTABLE_SECTIONS.pluginHooks.shape.timeoutMs,
438
+ "tui.paddingX": SETTABLE_SECTIONS.tui.shape.paddingX,
439
+ "tui.skillSlashCommands": SETTABLE_SECTIONS.tui.shape.skillSlashCommands,
440
+ "websearch.provider": SETTABLE_SECTIONS.websearch.shape.provider,
441
+ };
442
+ export function isSettableSettingKey(key) {
443
+ return Object.prototype.hasOwnProperty.call(SETTABLE_KEYS, key);
444
+ }
445
+ /**
446
+ * Atomically sets one user-facing setting in the global config file (`<config home>/config.json`)
447
+ * while preserving every unrelated JSON field. The value is validated against the same zod leaf
448
+ * used by `configSchema`, so a write can never produce a config the loader would reject. The
449
+ * caller is responsible for applying the change to the running process (see `application.ts`).
450
+ */
451
+ export async function setConfigValue(input) {
452
+ const validator = SETTABLE_KEYS[input.key];
453
+ if (!validator)
454
+ throw new Error(`Unknown setting key: ${input.key}`);
455
+ const parsed = validator.safeParse(input.value);
456
+ if (!parsed.success) {
457
+ const detail = parsed.error.issues[0];
458
+ throw new Error(detail
459
+ ? `Invalid value for ${input.key}: ${detail.path.join(".") || "value"} ${detail.message}`
460
+ : `Invalid value for ${input.key}`);
461
+ }
462
+ const file = join(configHome(), "config.json");
463
+ let raw = {};
464
+ if (await exists(file)) {
465
+ const existing = JSON.parse(await readFile(file, "utf8"));
466
+ if (!existing || typeof existing !== "object" || Array.isArray(existing))
467
+ throw new Error("Global Alisio configuration must be a JSON object");
468
+ raw = existing;
469
+ }
470
+ const [section, leaf] = input.key.split(".");
471
+ const container = raw[section] && typeof raw[section] === "object" && !Array.isArray(raw[section])
472
+ ? raw[section]
473
+ : {};
474
+ raw[section] = { ...container, [leaf]: parsed.data };
475
+ raw.schemaVersion ??= 1;
476
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
477
+ const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
478
+ const handle = await open(temporary, "wx", 0o600);
479
+ try {
480
+ await handle.writeFile(`${JSON.stringify(raw, null, 2)}\n`, "utf8");
481
+ await handle.sync();
482
+ }
483
+ finally {
484
+ await handle.close();
485
+ }
486
+ try {
487
+ await rename(temporary, file);
488
+ await chmod(file, 0o600);
489
+ }
490
+ catch (error) {
491
+ await unlink(temporary).catch(() => { });
492
+ throw error;
493
+ }
494
+ return file;
495
+ }
401
496
  /** Atomically updates one project plugin override while preserving every unrelated JSON field. */
402
497
  export async function setProjectPluginEnabled(input) {
403
498
  const file = join(input.workspace, ".alisio", "config.json");
@@ -43,6 +43,19 @@ export interface RunnerOptions {
43
43
  */
44
44
  nativeTools?: Array<Record<string, unknown>>;
45
45
  }
46
+ /** The settings a running TUI can change live; merged into the runner options on apply. */
47
+ export interface RunnerSettingsPatch {
48
+ /** Compaction settings; merged over the current ones (any subset). */
49
+ compaction?: Partial<CompactionSettings>;
50
+ /** Maximum turns per run. */
51
+ maxTurns?: number;
52
+ /** Per-call max output tokens budget. */
53
+ maxOutputTokens?: number;
54
+ /** Post-compaction context budget in characters. */
55
+ maxContextChars?: number;
56
+ /** Per-run timeout in milliseconds; the next run honors it. */
57
+ timeoutMs?: number;
58
+ }
46
59
  /**
47
60
  * Per-run overrides used by embedders and child sessions. Callers must only NARROW: `policy`
48
61
  * and `toolFilter` are applied on top of the runner's policy and tools.
@@ -92,6 +105,11 @@ export declare class AgentRunner {
92
105
  get policy(): Readonly<Policy>;
93
106
  /** Whether write/process tools can be approved interactively. */
94
107
  get approvals(): boolean;
108
+ /**
109
+ * Live-update runnable settings so the next `run`/`compact` call honors them without restarting
110
+ * Alisio. Every patch field is merged over the current options; unrelated settings are kept.
111
+ */
112
+ applySettings(patch: RunnerSettingsPatch): void;
95
113
  private emitter;
96
114
  /** Tools offered to the model under the current policy (and optional run narrowing). */
97
115
  availableTools(run?: RunOptions): ToolDefinition[];
@@ -37,6 +37,18 @@ export class AgentRunner {
37
37
  get approvals() {
38
38
  return !!this.options.approve;
39
39
  }
40
+ /**
41
+ * Live-update runnable settings so the next `run`/`compact` call honors them without restarting
42
+ * Alisio. Every patch field is merged over the current options; unrelated settings are kept.
43
+ */
44
+ applySettings(patch) {
45
+ const { compaction, ...rest } = patch;
46
+ this.options = {
47
+ ...this.options,
48
+ ...rest,
49
+ ...(compaction ? { compaction: { ...this.options.compaction, ...compaction } } : {}),
50
+ };
51
+ }
40
52
  emitter(sessionId) {
41
53
  const runId = crypto.randomUUID();
42
54
  let seq = 0;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /** @alisio/core public API: embed the Alisio agent programmatically. */
2
2
  export * from "@alisio/sdk";
3
3
  export { type AppOptions, type BuiltinContext, type BuiltinPlugin, createApplication, enabledBuiltins, type PluginCatalogEntry, type PluginCatalogStatus, } from "./application.ts";
4
- export { type Config, type ConfigLoadResult, type ConfigProvenance, configFile, configHome, configSchema, type LoadedConfig, loadConfig, loadConfigWithProvenance, type McpServerSource, overridesSavedProviderProfile, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.ts";
4
+ export { type Config, type ConfigLoadResult, type ConfigProvenance, configFile, configHome, configSchema, isSettableSettingKey, type LoadedConfig, loadConfig, loadConfigWithProvenance, type McpServerSource, overridesSavedProviderProfile, type SettableSettingKey, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.ts";
5
5
  export { type ContextBudget, checkpointInstructions, completeText, effectiveContextBudget, estimateTokens, MAX_TRUSTED_WINDOW, parseCheckpointOutput, planCompaction, renderCheckpoint, serializeForSummary, shouldCompact, shouldCompactContext, summarize, } from "./core/compaction.ts";
6
6
  export type { ApprovalDecision, ApprovalHandler, ApprovalRequest, ContextSource, HookFailure, Policy, RunnerExtensions, Session, SessionStore, } from "./core/contracts.ts";
7
7
  export { ToolRegistry } from "./core/registry.ts";
8
- export { AgentRunner, type CompactionResult, defaultTokenBudget, type RunnerOptions, type RunOptions, } from "./core/runner.ts";
8
+ export { AgentRunner, type CompactionResult, defaultTokenBudget, type RunnerOptions, type RunnerSettingsPatch, type RunOptions, } from "./core/runner.ts";
9
9
  export { type ExtensionConflict, ExtensionRegistry } from "./extensions/registry.ts";
10
10
  export { HerdrBridge } from "./integrations/herdr.ts";
11
11
  export { McpConnector, type McpServerInfo, type McpStatus, type McpToolInfo, } from "./mcp/connector.ts";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** @alisio/core public API: embed the Alisio agent programmatically. */
2
2
  export * from "@alisio/sdk";
3
3
  export { createApplication, enabledBuiltins, } from "./application.js";
4
- export { configFile, configHome, configSchema, loadConfig, loadConfigWithProvenance, overridesSavedProviderProfile, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
4
+ export { configFile, configHome, configSchema, isSettableSettingKey, loadConfig, loadConfigWithProvenance, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
5
5
  export { checkpointInstructions, completeText, effectiveContextBudget, estimateTokens, MAX_TRUSTED_WINDOW, parseCheckpointOutput, planCompaction, renderCheckpoint, serializeForSummary, shouldCompact, shouldCompactContext, summarize, } from "./core/compaction.js";
6
6
  export { ToolRegistry } from "./core/registry.js";
7
7
  export { AgentRunner, defaultTokenBudget, } from "./core/runner.js";
@@ -91,6 +91,11 @@ export declare class PluginHost implements RunnerExtensions {
91
91
  getState(plugin: string, key: string): unknown;
92
92
  setState(plugin: string, key: string, value: unknown): void;
93
93
  }, options?: PluginHostOptions, providers?: ProviderRegistry | undefined);
94
+ /**
95
+ * Live-update host-enforced hook timeouts so the NEXT hook call honors them without restarting.
96
+ * Keeps the same options object reference the constructors of new hooks read at call time.
97
+ */
98
+ applyTimeoutSettings(patch: Partial<PluginHostOptions>): void;
94
99
  /** Binds the provider-agnostic completion service once a provider exists. */
95
100
  setCompleter(fn: (request: CompletionRequest & {
96
101
  signal: AbortSignal;
@@ -93,6 +93,13 @@ export class PluginHost {
93
93
  this.options = options;
94
94
  this.providers = providers;
95
95
  }
96
+ /**
97
+ * Live-update host-enforced hook timeouts so the NEXT hook call honors them without restarting.
98
+ * Keeps the same options object reference the constructors of new hooks read at call time.
99
+ */
100
+ applyTimeoutSettings(patch) {
101
+ this.options = { ...this.options, ...patch };
102
+ }
96
103
  /** Binds the provider-agnostic completion service once a provider exists. */
97
104
  setCompleter(fn) {
98
105
  this.completer = fn;
@@ -22,6 +22,11 @@ export declare class ProjectContext implements ContextSource {
22
22
  /** Per session: nested instruction file path → fingerprint already attached. */
23
23
  private attached;
24
24
  constructor(workspace: string, options?: ProjectContextOptions);
25
+ /**
26
+ * Live-update options so the next `instructions()` call honors them without restarting.
27
+ * Only runtime-read fields matter (`claudeMdFallback`, `maxBytes`); `cwd` stays fixed.
28
+ */
29
+ update(options: Partial<ProjectContextOptions>): void;
25
30
  private names;
26
31
  private readDir;
27
32
  /** Directories from the workspace root down to the directory containing `path`. */
@@ -28,6 +28,13 @@ export class ProjectContext {
28
28
  this.options = options;
29
29
  this.cwd = options.cwd ?? workspace;
30
30
  }
31
+ /**
32
+ * Live-update options so the next `instructions()` call honors them without restarting.
33
+ * Only runtime-read fields matter (`claudeMdFallback`, `maxBytes`); `cwd` stays fixed.
34
+ */
35
+ update(options) {
36
+ this.options = { ...this.options, ...options };
37
+ }
31
38
  names() {
32
39
  return [
33
40
  ["AGENTS.override.md", "override"],
@@ -42,6 +42,7 @@ export function welcomeSection(ctx) {
42
42
  export function infoSection(ctx, width) {
43
43
  const t = ctx.terminal;
44
44
  const label = sgr(t.color, "2");
45
+ const pad = " ".repeat(2);
45
46
  const rows = [];
46
47
  if (ctx.model)
47
48
  rows.push(["model", ctx.model]);
@@ -54,9 +55,9 @@ export function infoSection(ctx, width) {
54
55
  rows.push(["plugins", plugins ? `${plugins} loaded` : "none"]);
55
56
  const labelWidth = Math.min(12, Math.max(...rows.map(([l]) => l.length)) + 1);
56
57
  return rows.map(([l, v]) => {
57
- const room = Math.max(1, width - labelWidth - 1);
58
+ const room = Math.max(1, width - labelWidth - 1 - 4);
58
59
  const value = l === "cwd" ? shortenPath(v, room) : truncate(v, room);
59
- return `${label(padEnd(l, labelWidth))} ${value}`;
60
+ return `${pad}${label(padEnd(l, labelWidth))} ${value}${pad}`;
60
61
  });
61
62
  }
62
63
  export function pluginsSection(ctx) {
@@ -72,12 +73,12 @@ export function pluginsSection(ctx) {
72
73
  .filter((p) => !p.categories?.includes("model-provider"))
73
74
  .map((p) => `${p.id}${p.builtin ? " (builtin)" : ""}`),
74
75
  ];
75
- return [`${sgr(t.color, "2")("with")} ${names.join(", ")}`];
76
+ return [` ${sgr(t.color, "2")("with")} ${names.join(", ")} `];
76
77
  }
77
78
  export function tipsSection(ctx) {
78
79
  const t = ctx.terminal;
79
80
  const bullet = sgr(t.color, "33")(glyph(t, "›", ">"));
80
- return ctx.tips.map((tip) => `${bullet} Tip: ${tip}`);
81
+ return ctx.tips.map((tip) => ` ${bullet} Tip: ${tip} `);
81
82
  }
82
83
  export function mascotSection(ctx) {
83
84
  return [ctx.mascot.render({ terminal: ctx.terminal, version: ctx.version })].flat();
@@ -110,7 +111,7 @@ export const DefaultStartupScreen = {
110
111
  ...pluginsSection(ctx),
111
112
  ];
112
113
  const body = side ? composeSideBySide(mascot, info, columns) : [...mascot, ...info];
113
- const rule = sgr(ctx.terminal.color, "2")(glyph(ctx.terminal, "─", "-").repeat(Math.min(columns, 60)));
114
+ const rule = sgr(ctx.terminal.color, "2")(glyph(ctx.terminal, "─", "-").repeat(columns - 2));
114
115
  return [...body, "", ...tipsSection(ctx), rule].map((line) => truncate(line, columns));
115
116
  },
116
117
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alisio/core",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-alpha.9",
4
4
  "description": "Embeddable provider-agnostic Alisio agent core: tool loop, context compaction, local tools, MCP, plugin host and SQLite session store. Runs on Node >=22.16 and Bun.",
5
5
  "author": "Gustavo Gutiérrez",
6
6
  "license": "MIT",