@alisio/alisio-code 0.1.0-alpha.23 → 0.1.0-alpha.25

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.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Pure logic of the TUI agent manager (`/agents`): argument parsing, the agent picker rows and
3
+ * their filter, and the editor form with capability-driven options. Rendering and I/O live in
4
+ * `app.ts`; agent files go through the core `AgentDefinitionService` shared with the web server.
5
+ */
6
+ import type { AgentDefinitionInfo, AgentDefinitionInput, AgentDraft, AgentModelCapabilities, AgentReasoningSummary, AgentScope, AgentTemplateInfo } from "@alisio/core";
7
+ import { type ActiveAgent } from "@alisio/core";
8
+ /** Verbs the TUI owns; any other `/agents <verb>` goes to the subagents plugin's command. */
9
+ export type AgentsCommand = {
10
+ kind: "picker";
11
+ } | {
12
+ kind: "new";
13
+ description?: string;
14
+ } | {
15
+ kind: "templates";
16
+ } | {
17
+ kind: "manage";
18
+ } | {
19
+ kind: "edit";
20
+ id: string;
21
+ scope?: AgentScope;
22
+ } | {
23
+ kind: "delete";
24
+ id: string;
25
+ scope?: AgentScope;
26
+ } | {
27
+ kind: "plugin";
28
+ args: string;
29
+ };
30
+ /**
31
+ * `/agents` → picker; `/agents new [description]` (assisted when a description is given),
32
+ * `/agents templates`, `/agents manage`, `/agents edit|delete <id> [project|global]`. Anything
33
+ * else (`list`, `defs`, `reload`, `open`, `cancel`…) belongs to the subagents plugin.
34
+ */
35
+ export declare function parseAgentsArgs(args: string): AgentsCommand;
36
+ /** "Project" / "Global" for agent files, "Built-in" / "Plugin" / "User" otherwise. */
37
+ export declare function agentBadge(agent: Pick<ActiveAgent, "source"> & {
38
+ path?: string;
39
+ }, workspace: string, home: string): string;
40
+ export interface PickerRow {
41
+ value: string;
42
+ label: string;
43
+ description?: string;
44
+ }
45
+ /** Action rows shown above the agents in the `/agents` picker. */
46
+ export declare const AGENT_PICKER_ACTIONS: PickerRow[];
47
+ /** Picker rows: name, badge and markers in the label; description below. */
48
+ export declare function agentPickerRows(agents: Array<ActiveAgent & {
49
+ path?: string;
50
+ }>, activeId: string, roots: {
51
+ workspace: string;
52
+ home: string;
53
+ }): PickerRow[];
54
+ /** Case-insensitive substring match of every query word on the label and description. */
55
+ export declare function filterAgentRows<T extends {
56
+ label: string;
57
+ description?: string;
58
+ value: string;
59
+ }>(rows: ReadonlyArray<T>, query: string): T[];
60
+ /** The editable state of one agent in the TUI editor. */
61
+ export interface AgentForm {
62
+ scope: AgentScope;
63
+ /** Set when editing a saved agent. */
64
+ id?: string;
65
+ name: string;
66
+ description: string;
67
+ instructions: string;
68
+ model: string;
69
+ reasoning: {
70
+ effort?: string;
71
+ summary?: AgentReasoningSummary;
72
+ };
73
+ text: {
74
+ format?: "text" | "json_object" | "json_schema";
75
+ verbosity?: "low" | "medium" | "high";
76
+ };
77
+ }
78
+ export declare function emptyAgentForm(scope: AgentScope, model: string): AgentForm;
79
+ export declare function formFromTemplate(template: AgentTemplateInfo, scope: AgentScope, model: string): AgentForm;
80
+ export declare function formFromDefinition(definition: AgentDefinitionInfo): AgentForm;
81
+ /** Applies a model draft to the form (keeps scope, id and model). */
82
+ export declare function applyDraft(form: AgentForm, draft: AgentDraft): AgentForm;
83
+ /** The API/service body of a form, fitted to the model's capabilities. */
84
+ export declare function formToInput(form: AgentForm, capabilities?: AgentModelCapabilities): AgentDefinitionInput;
85
+ /** Validation errors of a form (empty when it can be saved). */
86
+ export declare function formErrors(form: AgentForm): string[];
87
+ /** One-line editing of multi-line text: newlines shown and typed as `\n`. */
88
+ export declare const encodeMultiline: (text: string) => string;
89
+ export declare const decodeMultiline: (text: string) => string;
90
+ export type AgentField = "scope" | "name" | "description" | "instructions" | "model" | "effort" | "summary" | "verbosity" | "format" | "refine" | "save" | "cancel";
91
+ /**
92
+ * Editor rows. Settings the model does not support are shown as "not supported by this model"
93
+ * (their value is ignored on save); `dirty` enables the Save row.
94
+ */
95
+ export declare function agentEditorRows(form: AgentForm, capabilities: AgentModelCapabilities, options: {
96
+ creating: boolean;
97
+ scopes: AgentScope[];
98
+ dirty: boolean;
99
+ }): Array<PickerRow & {
100
+ value: AgentField;
101
+ }>;
102
+ /** Choice rows of a select field, honoring the model capabilities. */
103
+ export declare function agentFieldChoices(field: "effort" | "summary" | "verbosity" | "format", capabilities: AgentModelCapabilities): PickerRow[];
104
+ /** Whether a field can be edited for the model (unsupported settings are read-only). */
105
+ export declare function agentFieldSupported(field: "effort" | "summary" | "verbosity", capabilities: AgentModelCapabilities): boolean;
@@ -0,0 +1,269 @@
1
+ import { AGENT_REASONING_SUMMARIES, AGENT_TEXT_FORMATS, AGENT_VERBOSITIES, DEFAULT_AGENT_ID, fitAgentSettings, validateAgentDefinitionInput, } from "@alisio/core";
2
+ /**
3
+ * `/agents` → picker; `/agents new [description]` (assisted when a description is given),
4
+ * `/agents templates`, `/agents manage`, `/agents edit|delete <id> [project|global]`. Anything
5
+ * else (`list`, `defs`, `reload`, `open`, `cancel`…) belongs to the subagents plugin.
6
+ */
7
+ export function parseAgentsArgs(args) {
8
+ const text = args.trim();
9
+ if (!text)
10
+ return { kind: "picker" };
11
+ const [verb = "", ...rest] = text.split(/\s+/);
12
+ const tail = text.slice(verb.length).trim();
13
+ const scope = (value) => value === "project" || value === "global" ? value : undefined;
14
+ switch (verb.toLowerCase()) {
15
+ case "new":
16
+ case "create":
17
+ return tail ? { kind: "new", description: tail } : { kind: "new" };
18
+ case "templates":
19
+ case "template":
20
+ return { kind: "templates" };
21
+ case "manage":
22
+ return { kind: "manage" };
23
+ case "edit":
24
+ case "delete":
25
+ case "rm": {
26
+ const [id, where] = rest;
27
+ if (!id)
28
+ return { kind: "manage" };
29
+ const found = scope(where);
30
+ return {
31
+ kind: verb.toLowerCase() === "edit" ? "edit" : "delete",
32
+ id,
33
+ ...(found ? { scope: found } : {}),
34
+ };
35
+ }
36
+ default:
37
+ return { kind: "plugin", args: text };
38
+ }
39
+ }
40
+ /** "Project" / "Global" for agent files, "Built-in" / "Plugin" / "User" otherwise. */
41
+ export function agentBadge(agent, workspace, home) {
42
+ if (agent.source === "builtin")
43
+ return "Built-in";
44
+ if (agent.source === "plugin")
45
+ return "Plugin";
46
+ const path = agent.path ?? "";
47
+ if (path.startsWith(`${workspace}/`) || path.startsWith(`${workspace}\\`))
48
+ return "Project";
49
+ if (path.startsWith(`${home}/`) || path.startsWith(`${home}\\`))
50
+ return "Global";
51
+ return "User";
52
+ }
53
+ /** Action rows shown above the agents in the `/agents` picker. */
54
+ export const AGENT_PICKER_ACTIONS = [
55
+ {
56
+ value: "__agents:new",
57
+ label: "+ Create agent…",
58
+ description: "Blank, from a template or written by Alisio",
59
+ },
60
+ {
61
+ value: "__agents:manage",
62
+ label: "✎ Manage saved agents…",
63
+ description: "Edit or delete project and global agents",
64
+ },
65
+ ];
66
+ /** Picker rows: name, badge and markers in the label; description below. */
67
+ export function agentPickerRows(agents, activeId, roots) {
68
+ return agents.map((agent) => ({
69
+ value: agent.id,
70
+ label: [
71
+ agent.name,
72
+ `[${agentBadge(agent, roots.workspace, roots.home)}]`,
73
+ agent.id === activeId ? "(current)" : undefined,
74
+ agent.id === DEFAULT_AGENT_ID ? "(default)" : undefined,
75
+ agent.readOnly ? "read-only" : undefined,
76
+ ]
77
+ .filter(Boolean)
78
+ .join(" "),
79
+ description: [agent.description, agent.model ? `model: ${agent.model}` : undefined]
80
+ .filter(Boolean)
81
+ .join(" · "),
82
+ }));
83
+ }
84
+ /** Case-insensitive substring match of every query word on the label and description. */
85
+ export function filterAgentRows(rows, query) {
86
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
87
+ if (!words.length)
88
+ return [...rows];
89
+ return rows.filter((row) => {
90
+ const haystack = `${row.value} ${row.label} ${row.description ?? ""}`.toLowerCase();
91
+ return words.every((word) => haystack.includes(word));
92
+ });
93
+ }
94
+ export function emptyAgentForm(scope, model) {
95
+ return {
96
+ scope,
97
+ name: "New agent",
98
+ description: "",
99
+ instructions: "",
100
+ model,
101
+ reasoning: {},
102
+ text: {},
103
+ };
104
+ }
105
+ const settingsOf = (source) => ({
106
+ reasoning: {
107
+ ...(source.reasoning?.effort ? { effort: source.reasoning.effort } : {}),
108
+ ...(source.reasoning?.summary ? { summary: source.reasoning.summary } : {}),
109
+ },
110
+ text: {
111
+ ...(source.text?.format?.type ? { format: source.text.format.type } : {}),
112
+ ...(source.text?.verbosity ? { verbosity: source.text.verbosity } : {}),
113
+ },
114
+ });
115
+ export function formFromTemplate(template, scope, model) {
116
+ return {
117
+ scope,
118
+ name: template.name,
119
+ description: template.description,
120
+ instructions: template.instructions,
121
+ model,
122
+ ...settingsOf(template),
123
+ };
124
+ }
125
+ export function formFromDefinition(definition) {
126
+ return {
127
+ scope: definition.scope,
128
+ id: definition.id,
129
+ name: definition.name,
130
+ description: definition.description,
131
+ instructions: definition.instructions,
132
+ model: definition.model,
133
+ ...settingsOf(definition),
134
+ };
135
+ }
136
+ /** Applies a model draft to the form (keeps scope, id and model). */
137
+ export function applyDraft(form, draft) {
138
+ return {
139
+ ...form,
140
+ name: draft.name,
141
+ description: draft.description,
142
+ instructions: draft.instructions,
143
+ ...settingsOf(draft),
144
+ };
145
+ }
146
+ /** The API/service body of a form, fitted to the model's capabilities. */
147
+ export function formToInput(form, capabilities) {
148
+ const input = {
149
+ name: form.name.trim(),
150
+ ...(form.description.trim() ? { description: form.description.trim() } : {}),
151
+ ...(form.instructions.trim() ? { instructions: form.instructions.trim() } : {}),
152
+ model: form.model.trim(),
153
+ ...(form.reasoning.effort || form.reasoning.summary
154
+ ? {
155
+ reasoning: {
156
+ ...(form.reasoning.effort ? { effort: form.reasoning.effort } : {}),
157
+ ...(form.reasoning.summary ? { summary: form.reasoning.summary } : {}),
158
+ },
159
+ }
160
+ : {}),
161
+ ...(form.text.format || form.text.verbosity
162
+ ? {
163
+ text: {
164
+ ...(form.text.format ? { format: { type: form.text.format } } : {}),
165
+ ...(form.text.verbosity ? { verbosity: form.text.verbosity } : {}),
166
+ },
167
+ }
168
+ : {}),
169
+ };
170
+ return capabilities ? fitAgentSettings(input, capabilities) : input;
171
+ }
172
+ /** Validation errors of a form (empty when it can be saved). */
173
+ export function formErrors(form) {
174
+ const result = validateAgentDefinitionInput(formToInput(form));
175
+ return result.ok ? [] : result.errors.map((e) => e.message);
176
+ }
177
+ /** One-line editing of multi-line text: newlines shown and typed as `\n`. */
178
+ export const encodeMultiline = (text) => text.replace(/\\/g, "\\\\").replace(/\n/g, "\\n");
179
+ export const decodeMultiline = (text) => text.replace(/\\(\\|n)/g, (_, c) => (c === "n" ? "\n" : "\\"));
180
+ const clip = (text, max = 60) => {
181
+ const line = text.replace(/\s+/g, " ").trim();
182
+ return line.length > max ? `${line.slice(0, max - 1)}…` : line;
183
+ };
184
+ /**
185
+ * Editor rows. Settings the model does not support are shown as "not supported by this model"
186
+ * (their value is ignored on save); `dirty` enables the Save row.
187
+ */
188
+ export function agentEditorRows(form, capabilities, options) {
189
+ const unsupported = "not supported by this model";
190
+ const rows = [];
191
+ if (options.creating && options.scopes.length > 1)
192
+ rows.push({
193
+ value: "scope",
194
+ label: `Scope: ${form.scope === "project" ? "Project" : "Global"}`,
195
+ description: "Project: .agents/agents in this workspace · Global: ~/.agents/agents",
196
+ });
197
+ rows.push({ value: "name", label: `Name: ${form.name || "(empty)"}` }, {
198
+ value: "description",
199
+ label: `Description: ${clip(form.description) || "(derived from instructions)"}`,
200
+ }, {
201
+ value: "instructions",
202
+ label: `Instructions: ${clip(form.instructions) || "(empty)"}`,
203
+ description: `${form.instructions.length} characters`,
204
+ }, { value: "model", label: `Model: ${form.model || "(choose one)"}` }, {
205
+ value: "effort",
206
+ label: `Reasoning effort: ${capabilities.reasoning ? (form.reasoning.effort ?? "model default") : "—"}`,
207
+ ...(capabilities.reasoning ? {} : { description: unsupported }),
208
+ }, {
209
+ value: "summary",
210
+ label: `Reasoning summary: ${capabilities.summary ? (form.reasoning.summary ?? "auto") : "—"}`,
211
+ ...(capabilities.summary ? {} : { description: unsupported }),
212
+ }, {
213
+ value: "verbosity",
214
+ label: `Verbosity: ${capabilities.verbosity ? (form.text.verbosity ?? "default") : "—"}`,
215
+ ...(capabilities.verbosity ? {} : { description: unsupported }),
216
+ }, { value: "format", label: `Text format: ${form.text.format ?? "text"}` }, {
217
+ value: "refine",
218
+ label: "✦ Refine with Alisio…",
219
+ description: "The active model rewrites the instructions to the authoring standards",
220
+ });
221
+ const errors = formErrors(form);
222
+ rows.push({
223
+ value: "save",
224
+ label: options.dirty && !errors.length
225
+ ? "✓ Save agent definition"
226
+ : "✓ Save agent definition (nothing to save)",
227
+ ...(errors.length
228
+ ? { description: errors.join("; ") }
229
+ : options.dirty
230
+ ? {}
231
+ : { description: "No unsaved changes" }),
232
+ });
233
+ rows.push({ value: "cancel", label: "✗ Close editor" });
234
+ return rows;
235
+ }
236
+ /** Choice rows of a select field, honoring the model capabilities. */
237
+ export function agentFieldChoices(field, capabilities) {
238
+ const levels = field === "effort"
239
+ ? capabilities.effortLevels
240
+ : field === "summary"
241
+ ? AGENT_REASONING_SUMMARIES
242
+ : field === "verbosity"
243
+ ? AGENT_VERBOSITIES
244
+ : AGENT_TEXT_FORMATS.filter((format) => capabilities.textFormats.includes(format));
245
+ return [
246
+ ...levels.map((value) => ({
247
+ value,
248
+ label: field === "format"
249
+ ? value === "json_object"
250
+ ? "JSON"
251
+ : value === "json_schema"
252
+ ? "JSON Schema"
253
+ : "Text"
254
+ : value,
255
+ ...(field === "effort" && value === capabilities.defaultEffort
256
+ ? { description: "model default" }
257
+ : {}),
258
+ })),
259
+ ...(field === "format" ? [] : [{ value: "!clear", label: "(use the default)" }]),
260
+ ];
261
+ }
262
+ /** Whether a field can be edited for the model (unsupported settings are read-only). */
263
+ export function agentFieldSupported(field, capabilities) {
264
+ return field === "effort"
265
+ ? capabilities.reasoning
266
+ : field === "summary"
267
+ ? capabilities.summary
268
+ : capabilities.verbosity;
269
+ }
package/dist/tui/app.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AppOptions } from "@alisio/core";
1
+ import { type AppOptions } from "@alisio/core";
2
2
  import { type Component, type SelectItem } from "@earendil-works/pi-tui";
3
3
  /**
4
4
  * Inline selection list with type-to-filter, rendered above the editor.
@@ -15,6 +15,8 @@ export declare class Picker implements Component {
15
15
  private readonly onCancelPick;
16
16
  private filterable;
17
17
  private detail?;
18
+ /** Custom filter (default: value prefix, keeping group headers). */
19
+ private readonly match?;
18
20
  private list;
19
21
  private filter;
20
22
  /** Full item sequence (group headers + rows) before any filtering. */
@@ -25,7 +27,9 @@ export declare class Picker implements Component {
25
27
  private index;
26
28
  /** Picker height target: counted on real rows only, so headers never stretch the list. */
27
29
  private readonly maxVisible;
28
- constructor(title: string, items: SelectItem[], onSelectItem: (item: SelectItem) => void, onCancelPick: () => void, filterable?: boolean, detail?: string | undefined);
30
+ constructor(title: string, items: SelectItem[], onSelectItem: (item: SelectItem) => void, onCancelPick: () => void, filterable?: boolean, detail?: string | undefined,
31
+ /** Custom filter (default: value prefix, keeping group headers). */
32
+ match?: ((items: SelectItem[], filter: string) => SelectItem[]) | undefined);
29
33
  /** Copies items, dimming group-header labels (style helpers stay plain under NO_COLOR). */
30
34
  private static displayItems;
31
35
  /** (Re)builds the SelectList over the filtered display sequence; selection starts on a row. */
package/dist/tui/app.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { homedir } from "node:os";
2
+ import { AGENT_TEMPLATES, agentAuthoringGuidance, agentCommandDescriptors, agentIdFromCommand, agentModelCapabilities, agentModelLabels, agentModelOptions, generateAgentDraft, } from "@alisio/core";
2
3
  import { CombinedAutocompleteProvider, Container, Editor, getImageDimensions, getNativeClipboard, Key, MouseRegion, matchesKey, ProcessTerminal, ScrollView, SelectList, TuiAltScreen, truncateToWidth, VStack, } from "@earendil-works/pi-tui";
3
4
  import { loadVersion } from "../version.js";
4
- import { activeAgentCatalog, agentPickerItems, agentRunOptions, mainAgentFromRecord, resolveActiveAgent, } from "./agents.js";
5
+ import { AGENT_PICKER_ACTIONS, agentEditorRows, agentFieldChoices, agentFieldSupported, agentPickerRows, applyDraft, decodeMultiline, emptyAgentForm, encodeMultiline, filterAgentRows, formErrors, formFromDefinition, formFromTemplate, formToInput, parseAgentsArgs, } from "./agent-manager.js";
6
+ import { activeAgentCatalog, agentRunOptions, mainAgentFromRecord, resolveActiveAgent, } from "./agents.js";
5
7
  import { MAX_ATTACHMENTS_PER_MESSAGE, MAX_IMAGE_BYTES, pasteImageFromClipboard, removeLastAttachment, toApiAttachment, } from "./attachments.js";
6
8
  import { BTW_DESCRIPTION, BTW_USAGE, btwBrowser, btwPending } from "./btw.js";
7
9
  import { copyText, nodeSpawn } from "./clipboard.js";
@@ -33,6 +35,7 @@ export class Picker {
33
35
  onCancelPick;
34
36
  filterable;
35
37
  detail;
38
+ match;
36
39
  list = new SelectList([], 1, selectListTheme);
37
40
  filter = "";
38
41
  /** Full item sequence (group headers + rows) before any filtering. */
@@ -43,12 +46,15 @@ export class Picker {
43
46
  index = 0;
44
47
  /** Picker height target: counted on real rows only, so headers never stretch the list. */
45
48
  maxVisible;
46
- constructor(title, items, onSelectItem, onCancelPick, filterable = false, detail) {
49
+ constructor(title, items, onSelectItem, onCancelPick, filterable = false, detail,
50
+ /** Custom filter (default: value prefix, keeping group headers). */
51
+ match) {
47
52
  this.title = title;
48
53
  this.onSelectItem = onSelectItem;
49
54
  this.onCancelPick = onCancelPick;
50
55
  this.filterable = filterable;
51
56
  this.detail = detail;
57
+ this.match = match;
52
58
  this.allItems = items;
53
59
  this.maxVisible = Math.min(10, Math.max(1, items.filter((item) => !isGroupHeader(item)).length));
54
60
  this.resetList();
@@ -59,7 +65,11 @@ export class Picker {
59
65
  }
60
66
  /** (Re)builds the SelectList over the filtered display sequence; selection starts on a row. */
61
67
  resetList() {
62
- this.items = Picker.displayItems(this.filter ? visibleGroupedItems(this.allItems, this.filter) : this.allItems);
68
+ this.items = Picker.displayItems(this.filter
69
+ ? this.match
70
+ ? this.match(this.allItems, this.filter)
71
+ : visibleGroupedItems(this.allItems, this.filter)
72
+ : this.allItems);
63
73
  this.list = new SelectList(this.items, this.maxVisible, selectListTheme);
64
74
  this.list.onSelect = (item) => {
65
75
  if (!isGroupHeader(item))
@@ -684,7 +694,8 @@ export async function runTui(options) {
684
694
  // read-only agent narrows the run to reads (no approvals). The persisted reasoning effort is
685
695
  // resolved against the ACTIVE model and sent when the model advertises supported levels.
686
696
  const agent = currentAgent();
687
- const effort = currentEffort();
697
+ // An explicit /effort wins; otherwise the agent's own default effort (agentRunOptions) is kept.
698
+ const effort = app.config.agents.effort || !agent.effort ? currentEffort() : undefined;
688
699
  return task((signal) => app.runner.run(session, prompt, signal, {
689
700
  ...agentRunOptions(agent),
690
701
  ...(effort ? { reasoningEffort: effort } : {}),
@@ -1160,11 +1171,20 @@ export async function runTui(options) {
1160
1171
  const openAgentsPicker = () => {
1161
1172
  const agents = mainAgents();
1162
1173
  const current = currentAgent();
1163
- showPicker(new Picker("Active agent", agentPickerItems(agents, current.id), (item) => {
1174
+ const rows = [...AGENT_PICKER_ACTIONS, ...agentPickerRows(agents, current.id, agentRoots())];
1175
+ showPicker(new Picker("Agents", rows, (item) => {
1176
+ if (item.value === "__agents:new") {
1177
+ closePicker();
1178
+ return void newAgentFlow().catch(error);
1179
+ }
1180
+ if (item.value === "__agents:manage") {
1181
+ closePicker();
1182
+ return void manageAgents().catch(error);
1183
+ }
1164
1184
  const agent = agents.find((candidate) => candidate.id === item.value);
1165
1185
  if (agent)
1166
1186
  void applyActiveAgent(agent);
1167
- }, closePicker, true, "Changing the active agent takes effect from the next prompt; the current session is kept unless the agent declares a different model."));
1187
+ }, closePicker, true, "Type to search by name or description. Selecting an agent activates it from the next prompt; build (default) returns to the default Alisio agent.", (items, filter) => filterAgentRows(items, filter)));
1168
1188
  };
1169
1189
  const applyActiveAgent = (agent) => {
1170
1190
  void (async () => {
@@ -1200,6 +1220,424 @@ export async function runTui(options) {
1200
1220
  }
1201
1221
  })();
1202
1222
  };
1223
+ // ---- Agent manager: project/global `.agents/agents` files (shared core service) ----------
1224
+ const agentRoots = () => ({ workspace: app.workspace, home: homedir() });
1225
+ let agentModels;
1226
+ /** Configured models with capabilities (cached per process; the running model always listed). */
1227
+ const loadAgentModels = async () => {
1228
+ if (agentModels)
1229
+ return agentModels;
1230
+ let options = [];
1231
+ try {
1232
+ options = agentModelOptions(await app.listAvailableModels(), {
1233
+ ...(app.providerInfo?.profileName ? { profile: app.providerInfo.profileName } : {}),
1234
+ ...(app.providerInfo?.id ? { provider: app.providerInfo.id } : {}),
1235
+ model: view.model,
1236
+ });
1237
+ }
1238
+ catch {
1239
+ /* configured catalogs unavailable: offer the running model only */
1240
+ }
1241
+ if (!options.some((option) => option.active)) {
1242
+ const capabilities = agentModelCapabilities(modelCatalog.get(view.model) ?? { id: view.model });
1243
+ options.unshift({
1244
+ reference: view.model,
1245
+ provider: app.provider.id,
1246
+ profile: app.providerInfo?.profileName ?? "",
1247
+ providerName: app.providers.get(activeProvider?.id ?? "")?.name ?? app.provider.id,
1248
+ id: view.model,
1249
+ active: true,
1250
+ capabilities,
1251
+ labels: agentModelLabels(capabilities),
1252
+ });
1253
+ }
1254
+ agentModels = options;
1255
+ return options;
1256
+ };
1257
+ const capabilitiesOf = (models, reference) => models.find((m) => m.reference === reference || m.id === reference)?.capabilities ??
1258
+ agentModelCapabilities(undefined);
1259
+ const defaultAgentModel = (models) => models.find((m) => m.active)?.reference ?? view.model;
1260
+ /**
1261
+ * "Building with Alisio": one tool-less call of the ACTIVE model writes (or refines) the agent
1262
+ * with the authoring guidance (a discovered `create-agent` skill, else the bundled one). Runs as
1263
+ * a task, so Esc cancels it; failures are reported and leave the form untouched.
1264
+ */
1265
+ const buildWithAlisio = async (description, base) => {
1266
+ let draft;
1267
+ flashHint("◐ Building with Alisio… writing the agent instructions · Esc to cancel", 180_000);
1268
+ await task(async (signal) => {
1269
+ draft = await generateAgentDraft(app.provider, description, {
1270
+ signal,
1271
+ guidance: await agentAuthoringGuidance(app.skills),
1272
+ ...(base
1273
+ ? {
1274
+ base: {
1275
+ name: base.name,
1276
+ description: base.description,
1277
+ instructions: base.instructions,
1278
+ },
1279
+ }
1280
+ : {}),
1281
+ });
1282
+ });
1283
+ flashHint(draft
1284
+ ? `Draft by ${draft.generatedBy} (${draft.guidance}). Review it, then save.`
1285
+ : "Building with Alisio stopped; nothing was changed.", 5000);
1286
+ return draft;
1287
+ };
1288
+ /** Saves a form; reports honestly whether the running registry already uses the change. */
1289
+ const saveAgentForm = async (form, models) => {
1290
+ const input = formToInput(form, capabilitiesOf(models, form.model));
1291
+ const change = form.id
1292
+ ? await app.agentDefinitions.update(form.scope, form.id, input)
1293
+ : await app.agentDefinitions.create(form.scope, input);
1294
+ editor.setAutocompleteProvider(buildSlashCompletionProvider());
1295
+ notice(change.live
1296
+ ? `Agent ${change.result.name} saved to ${change.result.path} and available now: /agent:${change.result.id}.`
1297
+ : `Agent ${change.result.name} saved to ${change.result.path}. Restart Alisio to use it${form.scope === "project" ? " (project agents also need a trusted workspace)" : ""}.`);
1298
+ return change;
1299
+ };
1300
+ /** Starts a fresh session with `id` as the active agent (the "Try it" action). */
1301
+ const tryAgentInNewSession = async (id) => {
1302
+ const agent = mainAgents().find((candidate) => candidate.id === id);
1303
+ if (!agent)
1304
+ return notice(`Agent ${id} is not loaded in this process; restart Alisio to try it.`);
1305
+ await endSession("clear");
1306
+ await app.updateSetting("agents.active", agent.id);
1307
+ let created;
1308
+ if (agent.model && agent.model !== view.model)
1309
+ created = await app.switchModel(agent.model).catch(() => undefined);
1310
+ if (created) {
1311
+ activeProvider = app.providerInfo;
1312
+ modelList = undefined;
1313
+ session = created.id;
1314
+ reset(initialViewState(created.model));
1315
+ }
1316
+ else {
1317
+ session = app.store.create(app.workspace, app.provider.id, view.model).id;
1318
+ reset(initialViewState(view.model));
1319
+ }
1320
+ notice(`New session ${session} with agent ${agent.name}. Type a prompt to try it.`);
1321
+ void app.herdr.report("idle", session);
1322
+ refreshEffort();
1323
+ refreshEstimate();
1324
+ };
1325
+ /** After the first save: try the agent in a new session (default) or keep editing. */
1326
+ const offerTryAgent = async (form, models, live) => {
1327
+ const choice = await askChoice(`Agent ${form.name} created`, live
1328
+ ? [
1329
+ { value: "try", label: "▶ Try it in a new session", description: "Default" },
1330
+ { value: "stay", label: "Stay in editor" },
1331
+ ]
1332
+ : [{ value: "stay", label: "Stay in editor", description: "Restart Alisio to try it" }]);
1333
+ if (choice === "try" && form.id)
1334
+ return tryAgentInNewSession(form.id);
1335
+ if (choice === "stay")
1336
+ return openAgentEditor(form, models, form);
1337
+ };
1338
+ /** The editor: one picker row per field; each edit returns to it. Esc closes. */
1339
+ const openAgentEditor = async (initial, models, saved) => {
1340
+ let form = initial;
1341
+ let baseline = saved ? JSON.stringify(saved) : undefined;
1342
+ const show = () => {
1343
+ const capabilities = capabilitiesOf(models, form.model);
1344
+ const dirty = baseline === undefined || JSON.stringify(form) !== baseline;
1345
+ showPicker(new Picker(form.id ? `Agents › ${form.name}` : "Agents › New agent", agentEditorRows(form, capabilities, {
1346
+ creating: !form.id,
1347
+ scopes: app.agentDefinitions.scopes,
1348
+ dirty,
1349
+ }), (item) => {
1350
+ closePicker();
1351
+ void edit(item.value, dirty).catch((cause) => {
1352
+ error(cause);
1353
+ show();
1354
+ });
1355
+ }, closePicker, false, `${form.scope === "project" ? "Project" : "Global"} scope · ${shortenPath(app.agentDefinitions.dir(form.scope), homedir())}`));
1356
+ };
1357
+ const input = (label, initialValue, placeholder, hint) => askInput({
1358
+ provider: "agent",
1359
+ title: form.id ? `Agents › ${form.name}` : "Agents › New agent",
1360
+ label,
1361
+ initial: initialValue,
1362
+ placeholder,
1363
+ ...(hint ? { hint } : {}),
1364
+ step: 1,
1365
+ steps: 1,
1366
+ });
1367
+ const edit = async (field, dirty) => {
1368
+ const capabilities = capabilitiesOf(models, form.model);
1369
+ switch (field) {
1370
+ case "scope": {
1371
+ const scope = await askChoice("Scope", app.agentDefinitions.scopes.map((value) => ({
1372
+ value,
1373
+ label: value === "project" ? "Project" : "Global",
1374
+ description: app.agentDefinitions.dir(value),
1375
+ })));
1376
+ if (scope === "project" || scope === "global")
1377
+ form = { ...form, scope };
1378
+ break;
1379
+ }
1380
+ case "name": {
1381
+ const value = await input("Name", form.name, "New agent");
1382
+ if (value !== undefined)
1383
+ form = { ...form, name: value.trim() };
1384
+ break;
1385
+ }
1386
+ case "description": {
1387
+ const value = await input("Description", form.description, "One sentence: what this agent does");
1388
+ if (value !== undefined)
1389
+ form = { ...form, description: value.trim() };
1390
+ break;
1391
+ }
1392
+ case "instructions": {
1393
+ const value = await input("Instructions", encodeMultiline(form.instructions), "Describe desired model behavior (tone, tool usage, response style)", "Type \\n for a new line. ✦ Refine with Alisio writes them for you.");
1394
+ if (value !== undefined)
1395
+ form = { ...form, instructions: decodeMultiline(value) };
1396
+ break;
1397
+ }
1398
+ case "model": {
1399
+ const value = await askChoice("Model", models.map((m) => ({
1400
+ value: m.reference,
1401
+ label: `${m.name ?? m.id} · ${m.providerName}${m.active ? " (active)" : ""}${m.reference === form.model ? " ✓" : ""}`,
1402
+ description: m.labels.join(" · ") || "capabilities not declared by the provider",
1403
+ })));
1404
+ if (value) {
1405
+ const fitted = formToInput({ ...form, model: value }, capabilitiesOf(models, value));
1406
+ form = {
1407
+ ...form,
1408
+ model: value,
1409
+ reasoning: { ...(fitted.reasoning ?? {}) },
1410
+ text: {
1411
+ ...(fitted.text?.format ? { format: fitted.text.format.type } : {}),
1412
+ ...(fitted.text?.verbosity ? { verbosity: fitted.text.verbosity } : {}),
1413
+ },
1414
+ };
1415
+ }
1416
+ break;
1417
+ }
1418
+ case "effort":
1419
+ case "summary":
1420
+ case "verbosity":
1421
+ case "format": {
1422
+ if (field !== "format" && !agentFieldSupported(field, capabilities)) {
1423
+ flashHint(`${form.model} does not support this setting.`);
1424
+ break;
1425
+ }
1426
+ const value = await askChoice(field, agentFieldChoices(field, capabilities));
1427
+ if (value === undefined)
1428
+ break;
1429
+ const clear = value === "!clear" ? undefined : value;
1430
+ if (field === "effort")
1431
+ form = { ...form, reasoning: { ...form.reasoning, effort: clear } };
1432
+ if (field === "summary")
1433
+ form = {
1434
+ ...form,
1435
+ reasoning: { ...form.reasoning, summary: clear },
1436
+ };
1437
+ if (field === "verbosity")
1438
+ form = {
1439
+ ...form,
1440
+ text: { ...form.text, verbosity: clear },
1441
+ };
1442
+ if (field === "format")
1443
+ form = {
1444
+ ...form,
1445
+ text: { ...form.text, format: clear },
1446
+ };
1447
+ break;
1448
+ }
1449
+ case "refine": {
1450
+ const changes = await input("What should change? (optional)", "", "e.g. focus on security, answer in Spanish");
1451
+ if (changes === undefined)
1452
+ break;
1453
+ const draft = await buildWithAlisio(changes, form);
1454
+ if (draft)
1455
+ form = applyDraft(form, draft);
1456
+ break;
1457
+ }
1458
+ case "save": {
1459
+ const errors = formErrors(form);
1460
+ if (errors.length) {
1461
+ flashHint(errors.join("; "), 5000);
1462
+ break;
1463
+ }
1464
+ if (!dirty) {
1465
+ flashHint("No unsaved changes");
1466
+ break;
1467
+ }
1468
+ const creating = !form.id;
1469
+ const change = await saveAgentForm(form, models);
1470
+ form = { ...form, id: change.result.id, name: change.result.name };
1471
+ baseline = JSON.stringify(form);
1472
+ if (creating)
1473
+ return offerTryAgent(form, models, change.live);
1474
+ break;
1475
+ }
1476
+ case "cancel": {
1477
+ if (dirty) {
1478
+ const discard = await askChoice("Discard unsaved changes?", [
1479
+ { value: "keep", label: "Keep editing" },
1480
+ { value: "discard", label: "Discard and close" },
1481
+ ]);
1482
+ if (discard === "discard")
1483
+ return;
1484
+ break;
1485
+ }
1486
+ return;
1487
+ }
1488
+ }
1489
+ show();
1490
+ };
1491
+ show();
1492
+ };
1493
+ /** `/agents new [description]`: assisted (with a description), template or blank. */
1494
+ const newAgentFlow = async (description) => {
1495
+ const models = await loadAgentModels();
1496
+ const blank = emptyAgentForm(app.agentDefinitions.defaultScope, defaultAgentModel(models));
1497
+ let mode = description ? "assisted" : undefined;
1498
+ if (!mode)
1499
+ mode = await askChoice("Agents › New agent", [
1500
+ {
1501
+ value: "assisted",
1502
+ label: "✦ Describe it — Alisio writes the agent",
1503
+ description: "The active model drafts the name, instructions and settings",
1504
+ },
1505
+ { value: "template", label: "From a template…" },
1506
+ { value: "blank", label: "Blank agent" },
1507
+ ]);
1508
+ if (mode === "template")
1509
+ return chooseAgentTemplate();
1510
+ if (mode === "blank")
1511
+ return openAgentEditor(blank, models);
1512
+ if (mode !== "assisted")
1513
+ return;
1514
+ const text = description ??
1515
+ (await askInput({
1516
+ provider: "agent",
1517
+ title: "Agents › New agent",
1518
+ label: "Describe the agent you want",
1519
+ initial: "",
1520
+ placeholder: "e.g. reviews pull requests for security issues",
1521
+ step: 1,
1522
+ steps: 1,
1523
+ }));
1524
+ if (!text?.trim())
1525
+ return;
1526
+ const draft = await buildWithAlisio(text);
1527
+ if (!draft)
1528
+ return;
1529
+ const form = applyDraft(blank, draft);
1530
+ if (formErrors(form).length)
1531
+ return openAgentEditor(form, models);
1532
+ const change = await saveAgentForm(form, models);
1533
+ return offerTryAgent({ ...form, id: change.result.id }, models, change.live);
1534
+ };
1535
+ /** `/agents templates`: pick a template, then customize it (and optionally refine it). */
1536
+ const chooseAgentTemplate = async () => {
1537
+ const models = await loadAgentModels();
1538
+ showPicker(new Picker("Agents › Templates", AGENT_TEMPLATES.map((template) => ({
1539
+ value: template.id,
1540
+ label: template.name,
1541
+ description: template.description,
1542
+ })), (item) => {
1543
+ closePicker();
1544
+ const template = AGENT_TEMPLATES.find((candidate) => candidate.id === item.value);
1545
+ if (template)
1546
+ void openAgentEditor(formFromTemplate(template, app.agentDefinitions.defaultScope, defaultAgentModel(models)), models).catch(error);
1547
+ }, closePicker, true, "Type to search. The template opens in the editor; ✦ Refine with Alisio adapts it to your changes.", (items, filter) => filterAgentRows(items, filter)));
1548
+ };
1549
+ /** `/agents manage`: saved project/global agents → edit, activate, try or delete. */
1550
+ const manageAgents = async () => {
1551
+ const list = await app.agentDefinitions.list();
1552
+ if (!list.length)
1553
+ return notice("No saved agents yet. Create one with /agents new.");
1554
+ showPicker(new Picker("Agents › Saved agents", list.map((a) => ({
1555
+ value: `${a.scope}:${a.id}`,
1556
+ label: `${a.name} [${a.scope === "project" ? "Project" : "Global"}]${a.overridesGlobal ? " overrides global" : ""}${a.overriddenByProject ? " (overridden by project)" : ""}`,
1557
+ description: [a.description, a.model].filter(Boolean).join(" · "),
1558
+ })), (item) => {
1559
+ closePicker();
1560
+ const found = list.find((a) => `${a.scope}:${a.id}` === item.value);
1561
+ if (found)
1562
+ void agentActions(found).catch(error);
1563
+ }, closePicker, true, `Project: ${app.agentDefinitions.scopes.includes("project") ? app.agentDefinitions.dir("project") : "—"} · Global: ${app.agentDefinitions.dir("global")}`, (items, filter) => filterAgentRows(items, filter)));
1564
+ };
1565
+ const agentActions = async (definition) => {
1566
+ const action = await askChoice(`${definition.name} [${definition.scope}]`, [
1567
+ { value: "edit", label: "Edit" },
1568
+ { value: "activate", label: "Activate in this session", description: "From the next prompt" },
1569
+ { value: "try", label: "Try it in a new session" },
1570
+ { value: "delete", label: "Delete", description: definition.path },
1571
+ ]);
1572
+ if (action === "edit") {
1573
+ const form = formFromDefinition(definition);
1574
+ return openAgentEditor(form, await loadAgentModels(), form);
1575
+ }
1576
+ if (action === "try")
1577
+ return tryAgentInNewSession(definition.id);
1578
+ if (action === "activate") {
1579
+ const agent = mainAgents().find((candidate) => candidate.id === definition.id);
1580
+ if (!agent)
1581
+ return notice(`Agent ${definition.id} is not loaded; restart Alisio to use it.`);
1582
+ return applyActiveAgent(agent);
1583
+ }
1584
+ if (action === "delete") {
1585
+ const confirm = await askChoice(`Delete ${definition.path}?`, [
1586
+ { value: "no", label: "Keep it" },
1587
+ { value: "yes", label: "Delete the file" },
1588
+ ]);
1589
+ if (confirm !== "yes")
1590
+ return;
1591
+ const change = await app.agentDefinitions.delete(definition.scope, definition.id);
1592
+ editor.setAutocompleteProvider(buildSlashCompletionProvider());
1593
+ notice(change.live
1594
+ ? `Deleted agent ${definition.name}; it is no longer offered.`
1595
+ : `Deleted ${definition.path}. Restart Alisio to drop it from the running agents.`);
1596
+ }
1597
+ };
1598
+ /** Routes `/agents [verb]`: TUI manager verbs, else the subagents plugin's verbs. */
1599
+ const agentsCommand = async (args, raw) => {
1600
+ const command = parseAgentsArgs(args);
1601
+ if (command.kind === "plugin") {
1602
+ const handler = app.plugins.commands.get("agents");
1603
+ if (!handler)
1604
+ return error("Subagent task management needs the Subagents plugin.");
1605
+ const output = await handler(command.args, { sessionId: session });
1606
+ if (/^reload\b/.test(command.args))
1607
+ editor.setAutocompleteProvider(buildSlashCompletionProvider());
1608
+ return info(output);
1609
+ }
1610
+ if (busy) {
1611
+ editor.setText(raw);
1612
+ flashHint("A turn is running: wait for it to finish before managing agents");
1613
+ return;
1614
+ }
1615
+ switch (command.kind) {
1616
+ case "picker":
1617
+ // Pick up files edited outside Alisio before listing.
1618
+ await app.agentDefinitions.reload();
1619
+ return openAgentsPicker();
1620
+ case "new":
1621
+ return newAgentFlow(command.description);
1622
+ case "templates":
1623
+ return chooseAgentTemplate();
1624
+ case "manage":
1625
+ return manageAgents();
1626
+ case "edit":
1627
+ case "delete": {
1628
+ const list = await app.agentDefinitions.list();
1629
+ const found = list.find((a) => a.id === command.id && (!command.scope || a.scope === command.scope)) ??
1630
+ undefined;
1631
+ if (!found)
1632
+ return error(`No saved agent ${command.id}. See /agents manage.`);
1633
+ if (command.kind === "edit") {
1634
+ const form = formFromDefinition(found);
1635
+ return openAgentEditor(form, await loadAgentModels(), form);
1636
+ }
1637
+ return agentActions(found);
1638
+ }
1639
+ }
1640
+ };
1203
1641
  /**
1204
1642
  * `/effort [level]`: without an argument, a picker over the ACTIVE model's advertised effort
1205
1643
  * levels (marking the model default); with an argument, the level is validated against
@@ -1537,6 +1975,19 @@ export async function runTui(options) {
1537
1975
  return error(`Unknown plugin command: ${command ?? ""}`);
1538
1976
  return info(await handler(rest.join(" ")));
1539
1977
  }
1978
+ const agentId = agentIdFromCommand(parsed.name);
1979
+ if (agentId) {
1980
+ // `/agent:<id>`: activates a loaded agent from the next prompt (as the /agents picker).
1981
+ if (busy) {
1982
+ editor.setText(raw);
1983
+ flashHint("A turn is running: wait for it to finish before switching the active agent");
1984
+ return;
1985
+ }
1986
+ const agent = mainAgents().find((candidate) => candidate.id === agentId);
1987
+ if (!agent)
1988
+ return error(`Unknown agent ${agentId}. Open /agents to see the loaded agents.`);
1989
+ return applyActiveAgent(agent);
1990
+ }
1540
1991
  switch (name) {
1541
1992
  case "help":
1542
1993
  return info(helpReport());
@@ -1586,23 +2037,11 @@ export async function runTui(options) {
1586
2037
  `(reuse the user's own wording for the question text). Do not answer in plain text ` +
1587
2038
  `first; call the tool right away.`);
1588
2039
  }
1589
- case "agents": {
1590
- // The subagents plugin registers its own built-in `/agents` command for subagent task
1591
- // management (list/open/cancel/kill/resume/merge/discard/defs). With arguments, route
1592
- // to it verbatim (still available while a turn runs, exactly as before); without
1593
- // arguments, this opens the ACTIVE-agent picker.
1594
- if (parsed.args) {
1595
- const handler = app.plugins.commands.get("agents");
1596
- if (handler)
1597
- return info(await handler(parsed.args, { sessionId: session }));
1598
- }
1599
- if (busy) {
1600
- editor.setText(raw);
1601
- flashHint("A turn is running: wait for it to finish before switching the active agent");
1602
- return;
1603
- }
1604
- return openAgentsPicker();
1605
- }
2040
+ case "agents":
2041
+ // TUI manager verbs (picker, new, templates, manage, edit, delete); other verbs go to the
2042
+ // subagents plugin's own `/agents` command (list/open/cancel/kill/resume/merge/discard/
2043
+ // defs/reload), still available while a turn runs.
2044
+ return await agentsCommand(parsed.args, raw);
1606
2045
  case "effort":
1607
2046
  if (busy) {
1608
2047
  editor.setText(raw);
@@ -1666,6 +2105,11 @@ export async function runTui(options) {
1666
2105
  description: c.description ?? `plugin ${c.plugin}`,
1667
2106
  ...(c.argumentHint ? { argumentHint: c.argumentHint } : {}),
1668
2107
  })),
2108
+ // `/agent:<id>` for every loaded agent (namespaced: never collides with other commands).
2109
+ ...agentCommandDescriptors(mainAgents()).map((d) => ({
2110
+ name: d.name,
2111
+ description: d.description,
2112
+ })),
1669
2113
  ];
1670
2114
  return new CombinedAutocompleteProvider(slashCompletionCommands(sources, {
1671
2115
  sessions: (prefix) => workspaceSessions()
@@ -26,6 +26,8 @@ export declare class ConnectInputPrompt implements Component {
26
26
  hint?: string;
27
27
  step: number;
28
28
  steps: number;
29
+ /** Dialog title (default `Connect · <provider>`). */
30
+ title?: string;
29
31
  onSubmit: (value: string) => void;
30
32
  onCancel: () => void;
31
33
  });
@@ -92,7 +92,7 @@ export class ConnectInputPrompt {
92
92
  const rule = "─".repeat(Math.max(1, Math.min(56, width - 2)));
93
93
  return [
94
94
  truncateToWidth(style.cyan(`┌${rule}┐`), width),
95
- truncateToWidth(`│ ${style.bold(`Connect · ${this.options.provider}`)} ${style.dim(`(${this.options.step}/${this.options.steps})`)}`, width),
95
+ truncateToWidth(`│ ${style.bold(this.options.title ?? `Connect · ${this.options.provider}`)}${this.options.title && this.options.steps <= 1 ? "" : ` ${style.dim(`(${this.options.step}/${this.options.steps})`)}`}`, width),
96
96
  truncateToWidth(`│ ${style.brightCyan("❯")} ${style.bold(this.options.label)}`, width),
97
97
  truncateToWidth(`│ ${field}`, width),
98
98
  ...(this.options.hint ? [truncateToWidth(`│ ${style.dim(this.options.hint)}`, width)] : []),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alisio/alisio-code",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.25",
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.20",
48
+ "@alisio/core": "0.1.0-alpha.21",
49
49
  "@alisio/plugin-memory": "0.1.0-alpha.9",
50
50
  "@alisio/plugin-openai-compatible": "0.1.0-alpha.9",
51
- "@alisio/plugin-subagents": "0.1.0-alpha.10",
52
- "@alisio/sdk": "0.1.0-alpha.16",
53
- "@alisio/server": "0.1.0-alpha.3",
51
+ "@alisio/plugin-subagents": "0.1.0-alpha.11",
52
+ "@alisio/sdk": "0.1.0-alpha.17",
53
+ "@alisio/server": "0.1.0-alpha.5",
54
54
  "@earendil-works/pi-tui": "0.87.1",
55
55
  "commander": "15.0.0"
56
56
  },