@alisio/core 0.4.5 → 0.5.1

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.
@@ -1306,6 +1306,10 @@ export async function createApplication(options = {}) {
1306
1306
  config.tasks = { ...config.tasks, [leaf]: Number(value) };
1307
1307
  break;
1308
1308
  }
1309
+ case "web.iconTheme":
1310
+ // Read live by the icon-theme routes on the next request; `"none"` clears the theme.
1311
+ config.web = { ...config.web, iconTheme: String(value) };
1312
+ break;
1309
1313
  case "websearch.provider":
1310
1314
  // Mutated in place (same object identity) so the tool chain's per-call
1311
1315
  // `websearchCtx.config` sees the new provider on the very next search call.
package/dist/config.d.ts CHANGED
@@ -255,6 +255,9 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
255
255
  active: z.ZodDefault<z.ZodString>;
256
256
  effort: z.ZodOptional<z.ZodString>;
257
257
  }, z.core.$strict>>;
258
+ web: z.ZodDefault<z.ZodObject<{
259
+ iconTheme: z.ZodDefault<z.ZodString>;
260
+ }, z.core.$strict>>;
258
261
  }, z.core.$strict>, z.ZodTransform<{
259
262
  schemaVersion: 1;
260
263
  provider: {
@@ -376,6 +379,9 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
376
379
  active: string;
377
380
  effort?: string | undefined;
378
381
  };
382
+ web: {
383
+ iconTheme: string;
384
+ };
379
385
  mcp: {
380
386
  allow?: boolean | undefined;
381
387
  servers: {
@@ -580,6 +586,9 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
580
586
  active: string;
581
587
  effort?: string | undefined;
582
588
  };
589
+ web: {
590
+ iconTheme: string;
591
+ };
583
592
  }>>;
584
593
  export type Config = z.infer<typeof configSchema>;
585
594
  export type ServerConfig = z.infer<typeof serverSchema>;
@@ -695,6 +704,7 @@ declare const SETTABLE_KEYS: {
695
704
  readonly "goal.blockedRepeats": z.ZodDefault<z.ZodNumber>;
696
705
  readonly "plan.diagrams": z.ZodDefault<z.ZodBoolean>;
697
706
  readonly "plan.maxDiagrams": z.ZodDefault<z.ZodNumber>;
707
+ readonly "web.iconTheme": z.ZodDefault<z.ZodString>;
698
708
  };
699
709
  export type SettableSettingKey = keyof typeof SETTABLE_KEYS;
700
710
  export declare function isSettableSettingKey(key: string): key is SettableSettingKey;
package/dist/config.js CHANGED
@@ -85,6 +85,8 @@ export const OCI_IMAGE_PATTERN = /^[a-zA-Z0-9][^\s@]*@sha256:[a-f0-9]{64}$/;
85
85
  const GLOBAL_ONLY_ANALYSIS = ["runtime", "oci", "retention"];
86
86
  /** Decision Intelligence keys only the user (global) layer decides: the provider and telemetry. */
87
87
  const GLOBAL_ONLY_DECISIONS = ["provider", "telemetry"];
88
+ /** Web UI keys only the user (global) layer decides: the server picks which theme it serves. */
89
+ const GLOBAL_ONLY_WEB = ["iconTheme"];
88
90
  /** JSON values a plugin may receive as options (`pluginOverrides[id].options`). */
89
91
  const jsonValueSchema = z.lazy(() => z.union([
90
92
  z.string(),
@@ -503,6 +505,17 @@ const configObjectSchema = z
503
505
  })
504
506
  .strict()
505
507
  .default(() => ({ active: "build" })),
508
+ /**
509
+ * Web UI preferences. `iconTheme` names the active `icon-theme` extension point provider
510
+ * (`"none"` keeps the built-in inline icons). GLOBAL ONLY: the server decides which theme its
511
+ * files serve, so a repository cannot pick one.
512
+ */
513
+ web: z
514
+ .object({
515
+ iconTheme: z.string().trim().min(1).max(100).default("none"),
516
+ })
517
+ .strict()
518
+ .default(() => ({ iconTheme: "none" })),
506
519
  })
507
520
  .strict()
508
521
  .superRefine((config, context) => {
@@ -618,6 +631,9 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
618
631
  const decisionsRaw = object.decisions && typeof object.decisions === "object" && !Array.isArray(object.decisions)
619
632
  ? object.decisions
620
633
  : {};
634
+ const webRaw = object.web && typeof object.web === "object" && !Array.isArray(object.web)
635
+ ? object.web
636
+ : {};
621
637
  const overridesRaw = object.pluginOverrides &&
622
638
  typeof object.pluginOverrides === "object" &&
623
639
  !Array.isArray(object.pluginOverrides)
@@ -626,6 +642,7 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
626
642
  const ignored = [
627
643
  ...GLOBAL_ONLY_ANALYSIS.filter((key) => key in analysisRaw).map((key) => `analysis.${key}`),
628
644
  ...GLOBAL_ONLY_DECISIONS.filter((key) => key in decisionsRaw).map((key) => `decisions.${key}`),
645
+ ...GLOBAL_ONLY_WEB.filter((key) => key in webRaw).map((key) => `web.${key}`),
629
646
  ...Object.entries(overridesRaw)
630
647
  .filter(([, entry]) => !!entry && typeof entry === "object" && !Array.isArray(entry) && "options" in entry)
631
648
  .map(([id]) => `pluginOverrides.${id}.options`),
@@ -738,6 +755,11 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
738
755
  };
739
756
  continue;
740
757
  }
758
+ if (key === "web") {
759
+ // `iconTheme` is global-only (the server decides which theme its files serve).
760
+ overlaid.web = { ...selected.config.web, iconTheme: global.config.web.iconTheme };
761
+ continue;
762
+ }
741
763
  overlaid[key] = selected.config[key];
742
764
  }
743
765
  if (selected.keys.has("mcp") || selected.keys.has("mcpServers"))
@@ -889,6 +911,7 @@ const SETTABLE_SECTIONS = {
889
911
  tasks: configObjectSchema.shape.tasks.removeDefault(),
890
912
  goal: configObjectSchema.shape.goal.removeDefault(),
891
913
  plan: configObjectSchema.shape.plan.removeDefault(),
914
+ web: configObjectSchema.shape.web.removeDefault(),
892
915
  };
893
916
  const ANALYSIS_LIMITS = SETTABLE_SECTIONS.analysis.shape.limits.removeDefault();
894
917
  const ANALYSIS_RETENTION = SETTABLE_SECTIONS.analysis.shape.retention.removeDefault();
@@ -938,6 +961,7 @@ const SETTABLE_KEYS = {
938
961
  "goal.blockedRepeats": SETTABLE_SECTIONS.goal.shape.blockedRepeats,
939
962
  "plan.diagrams": SETTABLE_SECTIONS.plan.shape.diagrams,
940
963
  "plan.maxDiagrams": SETTABLE_SECTIONS.plan.shape.maxDiagrams,
964
+ "web.iconTheme": SETTABLE_SECTIONS.web.shape.iconTheme,
941
965
  };
942
966
  export function isSettableSettingKey(key) {
943
967
  return Object.prototype.hasOwnProperty.call(SETTABLE_KEYS, key);
@@ -1,8 +1,3 @@
1
- /**
2
- * Generic, typed extension registry. Resolution is deterministic and independent of plugin
3
- * load order: highest priority, then plugin id (lexicographic), then registration order
4
- * within the plugin. Fallbacks (built-in defaults) only win when nothing else is registered.
5
- */
6
1
  import type { ExtensionPoints } from "@alisio/sdk";
7
2
  export interface ExtensionConflict {
8
3
  point: string;
@@ -23,6 +18,14 @@ export declare class ExtensionRegistry {
23
18
  provider: ExtensionPoints[K];
24
19
  plugin: string;
25
20
  } | undefined;
21
+ /**
22
+ * Every provider of a point in resolution order (the same ranking `resolve` uses): highest
23
+ * priority first, plugin id and registration order as tie-breaks, fallbacks last.
24
+ */
25
+ list<K extends keyof ExtensionPoints>(point: K): Array<{
26
+ provider: ExtensionPoints[K];
27
+ plugin: string;
28
+ }>;
26
29
  /** Equal-priority, non-fallback competitors for each point's winning slot. */
27
30
  conflicts(point?: keyof ExtensionPoints): ExtensionConflict[];
28
31
  }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Generic, typed extension registry. Resolution is deterministic and independent of plugin
3
+ * load order: highest priority, then plugin id (lexicographic), then registration order
4
+ * within the plugin. Fallbacks (built-in defaults) only win when nothing else is registered.
5
+ */
6
+ import { isAbsolute } from "node:path";
1
7
  const key = (e) => `${e.plugin}/${e.provider.id}`;
2
8
  function compare(a, b) {
3
9
  if (a.fallback !== b.fallback)
@@ -17,6 +23,14 @@ export class ExtensionRegistry {
17
23
  // a throwing or malformed provider already falls back there (see `startup/index.ts`).
18
24
  if (!provider || typeof provider.id !== "string")
19
25
  throw new Error(`Invalid ${point} provider: it needs an id`);
26
+ if (point === "icon-theme") {
27
+ // The server reads these absolute paths: an empty id or a relative path can never resolve.
28
+ const theme = provider;
29
+ if (!theme.id.trim())
30
+ throw new Error("Invalid icon-theme provider: it needs a non-empty id");
31
+ if (!isAbsolute(theme.manifestPath) || !isAbsolute(theme.iconsDir))
32
+ throw new Error("Invalid icon-theme provider: manifestPath and iconsDir must be absolute paths");
33
+ }
20
34
  const priority = options.priority ?? 0;
21
35
  if (!Number.isFinite(priority))
22
36
  throw new Error("Extension priority must be a finite number");
@@ -40,6 +54,16 @@ export class ExtensionRegistry {
40
54
  const top = this.ranked(point)[0];
41
55
  return top ? { provider: top.provider, plugin: top.plugin } : undefined;
42
56
  }
57
+ /**
58
+ * Every provider of a point in resolution order (the same ranking `resolve` uses): highest
59
+ * priority first, plugin id and registration order as tie-breaks, fallbacks last.
60
+ */
61
+ list(point) {
62
+ return this.ranked(point).map((entry) => ({
63
+ provider: entry.provider,
64
+ plugin: entry.plugin,
65
+ }));
66
+ }
43
67
  /** Equal-priority, non-fallback competitors for each point's winning slot. */
44
68
  conflicts(point) {
45
69
  const points = point ? [point] : [...new Set(this.entries.map((e) => e.point))];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alisio/core",
3
- "version": "0.4.5",
3
+ "version": "0.5.1",
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",
@@ -41,7 +41,7 @@
41
41
  "node": ">=22.16"
42
42
  },
43
43
  "dependencies": {
44
- "@alisio/sdk": "0.4.5",
44
+ "@alisio/sdk": "0.5.1",
45
45
  "@mixmark-io/domino": "2.2.0",
46
46
  "@modelcontextprotocol/client": "2.1.0",
47
47
  "ajv": "8.20.0",