@alisio/core 0.1.0-alpha.16 → 0.1.0-alpha.18
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/agents/active.d.ts +66 -0
- package/dist/agents/active.js +113 -0
- package/dist/application.d.ts +18 -3
- package/dist/application.js +44 -6
- package/dist/commands/builtins.d.ts +10 -0
- package/dist/commands/builtins.js +115 -0
- package/dist/commands/catalog.d.ts +83 -0
- package/dist/commands/catalog.js +268 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +105 -5
- package/dist/core/contracts.d.ts +109 -2
- package/dist/core/runner.d.ts +10 -0
- package/dist/core/runner.js +86 -12
- package/dist/index.d.ts +9 -4
- package/dist/index.js +8 -3
- package/dist/mcp/rich.js +68 -0
- package/dist/plugins/host.d.ts +4 -0
- package/dist/plugins/host.js +22 -8
- package/dist/providers/settings.d.ts +15 -0
- package/dist/providers/settings.js +38 -0
- package/dist/resources/context.js +6 -1
- package/dist/runtime/access.d.ts +62 -0
- package/dist/runtime/access.js +115 -0
- package/dist/runtime/blobs.d.ts +29 -0
- package/dist/runtime/blobs.js +94 -0
- package/dist/runtime/diff.d.ts +23 -0
- package/dist/runtime/diff.js +154 -0
- package/dist/runtime/paths.d.ts +6 -0
- package/dist/runtime/paths.js +11 -1
- package/dist/runtime/store.d.ts +69 -3
- package/dist/runtime/store.js +292 -11
- package/dist/tools/execute.d.ts +5 -0
- package/dist/tools/execute.js +1 -0
- package/dist/tools/standard.d.ts +4 -1
- package/dist/tools/standard.js +99 -18
- package/package.json +2 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic for the ACTIVE (main-session) agent: the built-in catalog, user/plugin
|
|
3
|
+
* contributions, resolution and the per-run options an agent implies. No terminal or pi-tui
|
|
4
|
+
* imports here — the Picker and status-line rendering live in each surface (e.g. the TUI shell).
|
|
5
|
+
*
|
|
6
|
+
* The active agent drives the MAIN session (its `instructions` are appended per run and an
|
|
7
|
+
* agent-specific `model` is offered on selection). This is deliberately separate from the
|
|
8
|
+
* delegated child-session agents of the subagents plugin: a subagent definition becomes a main
|
|
9
|
+
* agent only when it is marked main-capable (`mode: primary|all`), which the subagents plugin
|
|
10
|
+
* publishes for the host to read.
|
|
11
|
+
*/
|
|
12
|
+
export interface ActiveAgent {
|
|
13
|
+
/** Stable id, also the persisted `agents.active` value. */
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
/** System prompt appended to the main session's instructions on every run. */
|
|
18
|
+
instructions?: string;
|
|
19
|
+
/** Optional default model selector (`provider/model` or an unambiguous model id). */
|
|
20
|
+
model?: string;
|
|
21
|
+
/** Read-only agents run with write/process/external tools disabled and approvals off. */
|
|
22
|
+
readOnly?: boolean;
|
|
23
|
+
/** Product built-ins are shown as built-in and never shadowed by contributed definitions. */
|
|
24
|
+
source: "builtin" | "user" | "plugin";
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_AGENT_ID = "build";
|
|
27
|
+
/** Product built-ins. `build` keeps the CURRENT behavior exactly: no added persona, full power. */
|
|
28
|
+
export declare const BUILTIN_AGENTS: ActiveAgent[];
|
|
29
|
+
export interface MainCapableAgentRecord {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
prompt: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
readOnly?: boolean;
|
|
35
|
+
source: string;
|
|
36
|
+
path?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Maps a main-capable definition published by the subagents plugin to an active agent. */
|
|
39
|
+
export declare function mainAgentFromRecord(record: MainCapableAgentRecord): ActiveAgent;
|
|
40
|
+
/** Builds the selectable catalog from the subagents plugin's published state (best-effort). */
|
|
41
|
+
export declare function agentCatalogFromState(untrusted: unknown): ActiveAgent[];
|
|
42
|
+
/**
|
|
43
|
+
* The full selectable catalog: built-ins first, then contributed main-capable definitions.
|
|
44
|
+
* A contributed definition may never shadow a built-in id; later duplicates of a contributed id
|
|
45
|
+
* are dropped (the subagents plugin already resolved precedence by its own discovery order).
|
|
46
|
+
*/
|
|
47
|
+
export declare function activeAgentCatalog(contributions?: ActiveAgent[]): ActiveAgent[];
|
|
48
|
+
/** Resolves the persisted agent id; unknown ids fall back to the built-in default (`build`). */
|
|
49
|
+
export declare function resolveActiveAgent(agents: ActiveAgent[], id: string | undefined): ActiveAgent;
|
|
50
|
+
/** The run options an active agent implies. An undefined agent yields the current behavior. */
|
|
51
|
+
export declare function agentRunOptions(agent: ActiveAgent | undefined): {
|
|
52
|
+
instructions?: string;
|
|
53
|
+
policy?: {
|
|
54
|
+
write: false;
|
|
55
|
+
process: false;
|
|
56
|
+
external: false;
|
|
57
|
+
};
|
|
58
|
+
approvals?: false;
|
|
59
|
+
};
|
|
60
|
+
export interface AgentPickerItem {
|
|
61
|
+
value: string;
|
|
62
|
+
label: string;
|
|
63
|
+
description: string;
|
|
64
|
+
}
|
|
65
|
+
/** Picker rows for `/agents`: name with (current), built-in/default and read-only markers. */
|
|
66
|
+
export declare function agentPickerItems(agents: ActiveAgent[], activeId: string): AgentPickerItem[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export const DEFAULT_AGENT_ID = "build";
|
|
2
|
+
const PLAN_INSTRUCTIONS = `You are the "plan" agent: a read-only planning assistant for this main session.
|
|
3
|
+
|
|
4
|
+
Your job is analysis and planning only. Investigate the repository with read-only tools
|
|
5
|
+
(list_files, search_text, read_file, git_status, git_diff) and return a concrete, ordered plan:
|
|
6
|
+
files to change, functions to touch, risks and verification steps. You may run context and
|
|
7
|
+
inspection tools that do not modify anything.
|
|
8
|
+
|
|
9
|
+
You never edit, write or delete files, and you never start processes that change the workspace
|
|
10
|
+
or the network. If a task requires changes, hand back a precise plan (and the exact commands)
|
|
11
|
+
instead of making them.`;
|
|
12
|
+
/** Product built-ins. `build` keeps the CURRENT behavior exactly: no added persona, full power. */
|
|
13
|
+
export const BUILTIN_AGENTS = [
|
|
14
|
+
{
|
|
15
|
+
id: DEFAULT_AGENT_ID,
|
|
16
|
+
name: "build",
|
|
17
|
+
description: "Default full-power agent: edits code and files, runs processes and verifies its work through the normal permission flow.",
|
|
18
|
+
source: "builtin",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "plan",
|
|
22
|
+
name: "plan",
|
|
23
|
+
description: "Read-only planning agent: analyzes the codebase and returns an implementation plan without modifying anything.",
|
|
24
|
+
instructions: PLAN_INSTRUCTIONS,
|
|
25
|
+
readOnly: true,
|
|
26
|
+
source: "builtin",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
/** Maps a main-capable definition published by the subagents plugin to an active agent. */
|
|
30
|
+
export function mainAgentFromRecord(record) {
|
|
31
|
+
const source = String(record.source ?? "");
|
|
32
|
+
return {
|
|
33
|
+
id: record.name,
|
|
34
|
+
name: record.name,
|
|
35
|
+
description: record.description,
|
|
36
|
+
...(record.prompt ? { instructions: record.prompt } : {}),
|
|
37
|
+
...(record.model ? { model: record.model } : {}),
|
|
38
|
+
...(record.readOnly ? { readOnly: true } : {}),
|
|
39
|
+
...(source.startsWith("plugin:")
|
|
40
|
+
? { source: "plugin" }
|
|
41
|
+
: { source: "user" }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Builds the selectable catalog from the subagents plugin's published state (best-effort). */
|
|
45
|
+
export function agentCatalogFromState(untrusted) {
|
|
46
|
+
if (!Array.isArray(untrusted))
|
|
47
|
+
return activeAgentCatalog([]);
|
|
48
|
+
const records = untrusted.filter((entry) => !!entry &&
|
|
49
|
+
typeof entry === "object" &&
|
|
50
|
+
typeof entry.name === "string" &&
|
|
51
|
+
typeof entry.description === "string" &&
|
|
52
|
+
entry.description.length > 0 &&
|
|
53
|
+
typeof entry.prompt === "string");
|
|
54
|
+
return activeAgentCatalog(records.map(mainAgentFromRecord));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The full selectable catalog: built-ins first, then contributed main-capable definitions.
|
|
58
|
+
* A contributed definition may never shadow a built-in id; later duplicates of a contributed id
|
|
59
|
+
* are dropped (the subagents plugin already resolved precedence by its own discovery order).
|
|
60
|
+
*/
|
|
61
|
+
export function activeAgentCatalog(contributions = []) {
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const agent of [...BUILTIN_AGENTS, ...contributions]) {
|
|
65
|
+
if (seen.has(agent.id))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(agent.id);
|
|
68
|
+
out.push(agent);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/** Resolves the persisted agent id; unknown ids fall back to the built-in default (`build`). */
|
|
73
|
+
export function resolveActiveAgent(agents, id) {
|
|
74
|
+
const found = id ? agents.find((a) => a.id === id) : undefined;
|
|
75
|
+
if (found)
|
|
76
|
+
return found;
|
|
77
|
+
return agents.find((a) => a.id === DEFAULT_AGENT_ID) ?? agents[0] ?? BUILTIN_AGENTS[0];
|
|
78
|
+
}
|
|
79
|
+
/** The run options an active agent implies. An undefined agent yields the current behavior. */
|
|
80
|
+
export function agentRunOptions(agent) {
|
|
81
|
+
if (!agent)
|
|
82
|
+
return {};
|
|
83
|
+
return {
|
|
84
|
+
...(agent.instructions ? { instructions: agent.instructions } : {}),
|
|
85
|
+
...(agent.readOnly
|
|
86
|
+
? {
|
|
87
|
+
policy: { write: false, process: false, external: false },
|
|
88
|
+
approvals: false,
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Picker rows for `/agents`: name with (current), built-in/default and read-only markers. */
|
|
94
|
+
export function agentPickerItems(agents, activeId) {
|
|
95
|
+
return agents.map((agent) => ({
|
|
96
|
+
value: agent.id,
|
|
97
|
+
label: [
|
|
98
|
+
agent.name,
|
|
99
|
+
agent.id === activeId ? "(current)" : undefined,
|
|
100
|
+
agent.id === DEFAULT_AGENT_ID ? "(default)" : undefined,
|
|
101
|
+
agent.readOnly ? "read-only" : undefined,
|
|
102
|
+
]
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.join(" · "),
|
|
105
|
+
description: [
|
|
106
|
+
agent.description,
|
|
107
|
+
agent.model ? `default model: ${agent.model}` : undefined,
|
|
108
|
+
agent.instructions ? "injects its system prompt from the next prompt" : undefined,
|
|
109
|
+
]
|
|
110
|
+
.filter(Boolean)
|
|
111
|
+
.join(" · "),
|
|
112
|
+
}));
|
|
113
|
+
}
|
package/dist/application.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { ActiveProvider, ProviderRegistry } from "./providers/registry.ts";
|
|
|
10
10
|
import { ProviderSettingsStore } from "./providers/settings.ts";
|
|
11
11
|
import { ProjectContext } from "./resources/context.ts";
|
|
12
12
|
import { Skills } from "./resources/skills.ts";
|
|
13
|
+
import { type ExternalDirectoryHandler, PathAccess } from "./runtime/access.ts";
|
|
14
|
+
import { BlobStore } from "./runtime/blobs.ts";
|
|
13
15
|
import { SQLiteStore } from "./runtime/store.ts";
|
|
14
16
|
export interface AppOptions {
|
|
15
17
|
cwd?: string;
|
|
@@ -23,6 +25,8 @@ export interface AppOptions {
|
|
|
23
25
|
allowMcp?: boolean;
|
|
24
26
|
readOnly?: boolean;
|
|
25
27
|
allowAgents?: boolean;
|
|
28
|
+
/** Extra directories the tools may touch outside the workspace (per-run `--add-dir`). */
|
|
29
|
+
addDirs?: string[];
|
|
26
30
|
noHerdr?: boolean;
|
|
27
31
|
model?: string;
|
|
28
32
|
baseURL?: string;
|
|
@@ -45,6 +49,11 @@ export interface AppOptions {
|
|
|
45
49
|
disablePlugins?: string[];
|
|
46
50
|
/** Interactive approval for write/process tools; ignored in read-only mode. */
|
|
47
51
|
approve?: ApprovalHandler;
|
|
52
|
+
/**
|
|
53
|
+
* Interactive approval for a tool path outside the workspace and every declared extra root,
|
|
54
|
+
* scoped to the containing directory (allow once / session / deny); ignored in read-only mode.
|
|
55
|
+
*/
|
|
56
|
+
approveExternalDirectory?: ExternalDirectoryHandler;
|
|
48
57
|
}
|
|
49
58
|
export interface BuiltinContext {
|
|
50
59
|
workspace: string;
|
|
@@ -95,6 +104,7 @@ export declare function enabledBuiltins(available: BuiltinPlugin[], config: Reco
|
|
|
95
104
|
} & Record<string, unknown>>, disabled?: string[]): BuiltinPlugin[];
|
|
96
105
|
export declare function createApplication(options?: AppOptions): Promise<{
|
|
97
106
|
workspace: string;
|
|
107
|
+
pathAccess: PathAccess;
|
|
98
108
|
config: import("./config.ts").LoadedConfig;
|
|
99
109
|
prompts: {
|
|
100
110
|
templates: Map<string, import("./index.ts").PromptTemplate>;
|
|
@@ -108,6 +118,8 @@ export declare function createApplication(options?: AppOptions): Promise<{
|
|
|
108
118
|
template: import("./index.ts").PromptTemplate;
|
|
109
119
|
} | undefined;
|
|
110
120
|
store: SQLiteStore;
|
|
121
|
+
/** Uploaded attachment bytes; resolve a `BlobRef` with `blobs.attachment(ref)` before a run. */
|
|
122
|
+
blobs: BlobStore;
|
|
111
123
|
registry: ToolRegistry;
|
|
112
124
|
context: ProjectContext;
|
|
113
125
|
skills: Skills;
|
|
@@ -150,13 +162,16 @@ export declare function createApplication(options?: AppOptions): Promise<{
|
|
|
150
162
|
* (only when runtime permission was granted). Never includes credentials or command paths.
|
|
151
163
|
*/
|
|
152
164
|
mcpStartupFailures(): string[];
|
|
153
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* Host-owned interactive consent boundary: the TUI and the web UI (`alisio serve`, after an
|
|
167
|
+
* explicit confirmation dialog) may grant. Headless callers remain blocked unless flagged.
|
|
168
|
+
*/
|
|
154
169
|
grantMcpRuntimePermission(request: {
|
|
155
|
-
source: "interactive-tui";
|
|
170
|
+
source: "interactive-tui" | "interactive-web";
|
|
156
171
|
confirmed: boolean;
|
|
157
172
|
}): "granted" | "not-granted" | "read-only";
|
|
158
173
|
/** Persist global consent atomically, then grant runtime permission for this process. */
|
|
159
|
-
rememberGlobalMcpConsent(): Promise<void>;
|
|
174
|
+
rememberGlobalMcpConsent(source?: "interactive-tui" | "interactive-web"): Promise<void>;
|
|
160
175
|
/**
|
|
161
176
|
* Clear the persisted global consent atomically and drop the runtime permission (disconnecting
|
|
162
177
|
* servers as cleanup does). An explicit this-run `--allow-mcp` flag keeps the grant alive.
|
package/dist/application.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
3
|
import { dirname, join, resolve } from "node:path";
|
|
3
4
|
import { configFile, configHome, loadConfigWithProvenance, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
|
|
@@ -13,6 +14,8 @@ import { ProviderSettingsStore } from "./providers/settings.js";
|
|
|
13
14
|
import { ProjectContext } from "./resources/context.js";
|
|
14
15
|
import { expandSlashPrompt, loadPromptTemplates, promptSources } from "./resources/prompts.js";
|
|
15
16
|
import { Skills, skillRoots } from "./resources/skills.js";
|
|
17
|
+
import { PathAccess } from "./runtime/access.js";
|
|
18
|
+
import { BlobStore } from "./runtime/blobs.js";
|
|
16
19
|
import { isPathSpec } from "./runtime/modules.js";
|
|
17
20
|
import { findWorkspace } from "./runtime/paths.js";
|
|
18
21
|
import { SQLiteStore } from "./runtime/store.js";
|
|
@@ -59,7 +62,28 @@ export async function createApplication(options = {}) {
|
|
|
59
62
|
// a project value (see loadConfigWithProvenance), so a project cannot grant itself MCP consent.
|
|
60
63
|
const mcpAllow = config.mcp.allow === true;
|
|
61
64
|
let persistedMcpAllow = mcpAllow;
|
|
65
|
+
// Extra tool roots: config `additionalDirectories` (already canonicalized on load) plus per-run
|
|
66
|
+
// `--add-dir` (resolved against the current directory). Ignored entirely under --read-only, so
|
|
67
|
+
// the flag and the config key can never widen a locked session.
|
|
68
|
+
const addDirs = options.readOnly
|
|
69
|
+
? []
|
|
70
|
+
: await Promise.all((options.addDirs ?? []).map((dir) => realpath(resolve(cwd, dir)).catch(() => resolve(cwd, dir))));
|
|
71
|
+
const pathAccess = new PathAccess({
|
|
72
|
+
workspace,
|
|
73
|
+
extraRoots: [...config.additionalDirectories, ...addDirs],
|
|
74
|
+
...(options.approveExternalDirectory && !options.readOnly
|
|
75
|
+
? { approve: options.approveExternalDirectory }
|
|
76
|
+
: {}),
|
|
77
|
+
readOnly: !!options.readOnly,
|
|
78
|
+
});
|
|
62
79
|
const store = new SQLiteStore(options.db ?? join(stateHome(), "sessions.sqlite"));
|
|
80
|
+
// Content-addressed attachment bytes next to the session database (no I/O until `put`).
|
|
81
|
+
const blobs = new BlobStore({
|
|
82
|
+
root: options.db && options.db !== ":memory:"
|
|
83
|
+
? join(dirname(options.db), "blobs")
|
|
84
|
+
: join(stateHome(), "blobs"),
|
|
85
|
+
db: store.db,
|
|
86
|
+
});
|
|
63
87
|
const registry = new ToolRegistry(), skills = new Skills({ overrides: config.skillOverrides }), context = new ProjectContext(workspace, {
|
|
64
88
|
globalDir: configHome(),
|
|
65
89
|
cwd,
|
|
@@ -241,7 +265,7 @@ export async function createApplication(options = {}) {
|
|
|
241
265
|
}, {
|
|
242
266
|
config: config.websearch,
|
|
243
267
|
resolveExtension: () => plugins.extensions.resolve("websearch"),
|
|
244
|
-
});
|
|
268
|
+
}, pathAccess);
|
|
245
269
|
const { registerPluginInstallTool } = await import("./plugins/install.js");
|
|
246
270
|
registerPluginInstallTool(registry, { readOnly: !!options.readOnly });
|
|
247
271
|
if ((options.allowMcp || mcpAllow) && !options.readOnly)
|
|
@@ -503,6 +527,7 @@ export async function createApplication(options = {}) {
|
|
|
503
527
|
extensions: plugins,
|
|
504
528
|
contextWindow,
|
|
505
529
|
...(options.approve && !options.readOnly ? { approve: options.approve } : {}),
|
|
530
|
+
pathAccess,
|
|
506
531
|
...(config.websearch.provider === "native"
|
|
507
532
|
? { nativeTools: [{ type: config.websearch.nativeToolType }] }
|
|
508
533
|
: {}),
|
|
@@ -515,6 +540,10 @@ export async function createApplication(options = {}) {
|
|
|
515
540
|
// Child sessions (generic delegation service for plugins). Contexts are cached per workspace
|
|
516
541
|
// so a child in a git worktree reads that worktree's AGENTS.md files.
|
|
517
542
|
const contexts = new Map([[workspace, context]]);
|
|
543
|
+
// Startup reconciliation: runs left queued/running by a dead process become `interrupted`
|
|
544
|
+
// (runs of this process, e.g. another Application in a server, are kept). Child sessions are
|
|
545
|
+
// reconciled by `interruptStale()` in the ChildSessions constructor right below.
|
|
546
|
+
store.interruptRuns();
|
|
518
547
|
plugins.setSessions(new ChildSessions({
|
|
519
548
|
store,
|
|
520
549
|
runner,
|
|
@@ -583,10 +612,13 @@ export async function createApplication(options = {}) {
|
|
|
583
612
|
}
|
|
584
613
|
return {
|
|
585
614
|
workspace,
|
|
615
|
+
pathAccess,
|
|
586
616
|
config,
|
|
587
617
|
prompts,
|
|
588
618
|
expandPrompt,
|
|
589
619
|
store,
|
|
620
|
+
/** Uploaded attachment bytes; resolve a `BlobRef` with `blobs.attachment(ref)` before a run. */
|
|
621
|
+
blobs,
|
|
590
622
|
registry,
|
|
591
623
|
context,
|
|
592
624
|
skills,
|
|
@@ -679,12 +711,15 @@ export async function createApplication(options = {}) {
|
|
|
679
711
|
mcpStartupFailures() {
|
|
680
712
|
return [...mcpStartupFailures];
|
|
681
713
|
},
|
|
682
|
-
/**
|
|
714
|
+
/**
|
|
715
|
+
* Host-owned interactive consent boundary: the TUI and the web UI (`alisio serve`, after an
|
|
716
|
+
* explicit confirmation dialog) may grant. Headless callers remain blocked unless flagged.
|
|
717
|
+
*/
|
|
683
718
|
grantMcpRuntimePermission(request) {
|
|
684
719
|
if (!request.confirmed)
|
|
685
720
|
return mcpRuntimePermission;
|
|
686
|
-
if (request.source !== "interactive-tui")
|
|
687
|
-
throw new Error("MCP runtime permission may only be granted by
|
|
721
|
+
if (request.source !== "interactive-tui" && request.source !== "interactive-web")
|
|
722
|
+
throw new Error("MCP runtime permission may only be granted by an interactive UI");
|
|
688
723
|
if (options.readOnly)
|
|
689
724
|
throw new Error("MCP is unavailable under --read-only");
|
|
690
725
|
if (mcpRuntimePermission === "granted")
|
|
@@ -695,12 +730,12 @@ export async function createApplication(options = {}) {
|
|
|
695
730
|
return mcpRuntimePermission;
|
|
696
731
|
},
|
|
697
732
|
/** Persist global consent atomically, then grant runtime permission for this process. */
|
|
698
|
-
async rememberGlobalMcpConsent() {
|
|
733
|
+
async rememberGlobalMcpConsent(source = "interactive-tui") {
|
|
699
734
|
if (options.readOnly)
|
|
700
735
|
throw new Error("MCP is unavailable under --read-only");
|
|
701
736
|
await setGlobalMcpAllow({ allow: true });
|
|
702
737
|
persistedMcpAllow = true;
|
|
703
|
-
this.grantMcpRuntimePermission({ source
|
|
738
|
+
this.grantMcpRuntimePermission({ source, confirmed: true });
|
|
704
739
|
},
|
|
705
740
|
/**
|
|
706
741
|
* Clear the persisted global consent atomically and drop the runtime permission (disconnecting
|
|
@@ -789,6 +824,9 @@ export async function createApplication(options = {}) {
|
|
|
789
824
|
case "tui.paddingX":
|
|
790
825
|
config.tui = { ...config.tui, paddingX: Number(value) };
|
|
791
826
|
break;
|
|
827
|
+
case "tui.contentPaddingX":
|
|
828
|
+
config.tui = { ...config.tui, contentPaddingX: Number(value) };
|
|
829
|
+
break;
|
|
792
830
|
case "tui.skillSlashCommands":
|
|
793
831
|
config.tui = { ...config.tui, skillSlashCommands: value === true };
|
|
794
832
|
break;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CommandDescriptor } from "@alisio/sdk";
|
|
2
|
+
/** A built-in slash command: a descriptor without `source`/`owner` (always `builtin`). */
|
|
3
|
+
export type BuiltinCommand = Omit<CommandDescriptor, "source" | "owner">;
|
|
4
|
+
/**
|
|
5
|
+
* Built-in slash commands, in the order the TUI lists them. Names, aliases, descriptions and
|
|
6
|
+
* argument hints are the TUI's `COMMANDS` moved here as data (the TUI re-exports them).
|
|
7
|
+
* `execution: "core"` commands have a handler in `CommandCatalog.execute`; `surface` commands
|
|
8
|
+
* (clipboard, exit, settings/connect wizards, help, `/ask`) are handled by each UI.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BUILTIN_COMMANDS: readonly BuiltinCommand[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const ALL = ["tui", "web", "api"];
|
|
2
|
+
const TUI = ["tui"];
|
|
3
|
+
const INTERACTIVE = ["tui", "web"];
|
|
4
|
+
/**
|
|
5
|
+
* Built-in slash commands, in the order the TUI lists them. Names, aliases, descriptions and
|
|
6
|
+
* argument hints are the TUI's `COMMANDS` moved here as data (the TUI re-exports them).
|
|
7
|
+
* `execution: "core"` commands have a handler in `CommandCatalog.execute`; `surface` commands
|
|
8
|
+
* (clipboard, exit, settings/connect wizards, help, `/ask`) are handled by each UI.
|
|
9
|
+
*/
|
|
10
|
+
export const BUILTIN_COMMANDS = [
|
|
11
|
+
{
|
|
12
|
+
name: "help",
|
|
13
|
+
description: "Show commands and keys",
|
|
14
|
+
surfaces: INTERACTIVE,
|
|
15
|
+
execution: "surface",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "connect",
|
|
19
|
+
description: "Configure a provider and choose its active model",
|
|
20
|
+
surfaces: TUI,
|
|
21
|
+
execution: "surface",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "model",
|
|
25
|
+
description: "Switch provider and model",
|
|
26
|
+
aliases: ["models"],
|
|
27
|
+
surfaces: ALL,
|
|
28
|
+
execution: "core",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "compact",
|
|
32
|
+
description: "Summarize older history",
|
|
33
|
+
argumentHint: "[focus]",
|
|
34
|
+
surfaces: ALL,
|
|
35
|
+
execution: "core",
|
|
36
|
+
},
|
|
37
|
+
{ name: "stats", description: "Session statistics", surfaces: ALL, execution: "core" },
|
|
38
|
+
{
|
|
39
|
+
name: "clear",
|
|
40
|
+
description: "Start a new session",
|
|
41
|
+
aliases: ["new"],
|
|
42
|
+
surfaces: ALL,
|
|
43
|
+
execution: "core",
|
|
44
|
+
},
|
|
45
|
+
{ name: "sessions", description: "List recent sessions", surfaces: ALL, execution: "core" },
|
|
46
|
+
{
|
|
47
|
+
name: "resume",
|
|
48
|
+
description: "Resume a session by ID or prefix",
|
|
49
|
+
argumentHint: "<id>",
|
|
50
|
+
surfaces: ALL,
|
|
51
|
+
execution: "core",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "tools",
|
|
55
|
+
description: "List tools and permission state",
|
|
56
|
+
surfaces: ALL,
|
|
57
|
+
execution: "core",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "plugins",
|
|
61
|
+
description: "Browse and manage project plugins",
|
|
62
|
+
aliases: ["plugin"],
|
|
63
|
+
surfaces: ALL,
|
|
64
|
+
execution: "core",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "skills",
|
|
68
|
+
description: "Browse and manage effective skills",
|
|
69
|
+
aliases: ["skill"],
|
|
70
|
+
surfaces: ALL,
|
|
71
|
+
execution: "core",
|
|
72
|
+
},
|
|
73
|
+
{ name: "mcp", description: "Browse and manage MCP servers", surfaces: ALL, execution: "core" },
|
|
74
|
+
{
|
|
75
|
+
name: "settings",
|
|
76
|
+
description: "Open the settings menu",
|
|
77
|
+
aliases: ["prefs"],
|
|
78
|
+
surfaces: TUI,
|
|
79
|
+
execution: "surface",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "copy",
|
|
83
|
+
description: "Copy the last assistant response to the clipboard",
|
|
84
|
+
surfaces: TUI,
|
|
85
|
+
execution: "surface",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "agents",
|
|
89
|
+
description: "List and switch the active agent (applies from the next prompt)",
|
|
90
|
+
argumentHint: "[<verb>]",
|
|
91
|
+
surfaces: ALL,
|
|
92
|
+
execution: "core",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "effort",
|
|
96
|
+
description: "Set the reasoning effort level for the active model",
|
|
97
|
+
argumentHint: "[level]",
|
|
98
|
+
surfaces: ALL,
|
|
99
|
+
execution: "core",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "ask",
|
|
103
|
+
description: "Ask the agent to turn your question into a multiple-choice ask_user_question",
|
|
104
|
+
argumentHint: "<question>",
|
|
105
|
+
surfaces: INTERACTIVE,
|
|
106
|
+
execution: "surface",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "exit",
|
|
110
|
+
description: "Exit Alisio",
|
|
111
|
+
aliases: ["quit"],
|
|
112
|
+
surfaces: TUI,
|
|
113
|
+
execution: "surface",
|
|
114
|
+
},
|
|
115
|
+
];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One catalog of slash commands for every surface (TUI, web, API): built-ins, plugin commands,
|
|
3
|
+
* prompt templates and skills, with the pure core handlers of `execution: "core"` commands.
|
|
4
|
+
* The catalog reads its sources live from the host on each call, so plugin/template/skill
|
|
5
|
+
* changes show up without rebuilding it.
|
|
6
|
+
*/
|
|
7
|
+
import type { CommandDescriptor, ToolDefinition } from "@alisio/sdk";
|
|
8
|
+
import type { PluginCatalogEntry } from "../application.ts";
|
|
9
|
+
import type { SessionStore } from "../core/contracts.ts";
|
|
10
|
+
import type { AgentRunner } from "../core/runner.ts";
|
|
11
|
+
import type { McpServerInfo } from "../mcp/connector.ts";
|
|
12
|
+
import type { PromptTemplate } from "../resources/prompts.ts";
|
|
13
|
+
import type { SkillCatalogEntry } from "../resources/skills.ts";
|
|
14
|
+
import { BUILTIN_COMMANDS, type BuiltinCommand } from "./builtins.ts";
|
|
15
|
+
export { BUILTIN_COMMANDS, type BuiltinCommand };
|
|
16
|
+
export type CommandSurface = CommandDescriptor["surfaces"][number];
|
|
17
|
+
type PluginCommandHandler = (args: string, context?: {
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
}) => Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* What the catalog needs from an application. `createApplication()`'s result satisfies it; the
|
|
22
|
+
* optional members only enable the matching sources and handlers.
|
|
23
|
+
*/
|
|
24
|
+
export interface CommandHost {
|
|
25
|
+
workspace: string;
|
|
26
|
+
store: SessionStore;
|
|
27
|
+
runner: Pick<AgentRunner, "policy" | "approvals" | "compact" | "setModel">;
|
|
28
|
+
registry: {
|
|
29
|
+
list(): ToolDefinition[];
|
|
30
|
+
};
|
|
31
|
+
provider: {
|
|
32
|
+
id: string;
|
|
33
|
+
};
|
|
34
|
+
plugins?: {
|
|
35
|
+
commands: ReadonlyMap<string, PluginCommandHandler>;
|
|
36
|
+
commandInfo: ReadonlyMap<string, {
|
|
37
|
+
plugin: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
argumentHint?: string;
|
|
40
|
+
}>;
|
|
41
|
+
pluginState?(plugin: string, key: string): unknown;
|
|
42
|
+
};
|
|
43
|
+
prompts?: {
|
|
44
|
+
templates: ReadonlyMap<string, PromptTemplate>;
|
|
45
|
+
};
|
|
46
|
+
skillCatalog?(): SkillCatalogEntry[];
|
|
47
|
+
pluginCatalog?(): PluginCatalogEntry[];
|
|
48
|
+
mcp?: {
|
|
49
|
+
list(): McpServerInfo[];
|
|
50
|
+
};
|
|
51
|
+
config?: {
|
|
52
|
+
agents: {
|
|
53
|
+
active?: string;
|
|
54
|
+
effort?: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
updateSetting?(key: "agents.effort", value: unknown): Promise<string>;
|
|
58
|
+
}
|
|
59
|
+
export interface CommandExecutionContext {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
}
|
|
63
|
+
export interface CommandResult {
|
|
64
|
+
/** Markdown for the user. */
|
|
65
|
+
text?: string;
|
|
66
|
+
/** `notice` is a short status line; `info` (default) a report. */
|
|
67
|
+
tone?: "info" | "notice";
|
|
68
|
+
/** The session the surface should switch to (`clear`/`new`, `resume`). */
|
|
69
|
+
sessionId?: string;
|
|
70
|
+
/** Structured result for programmatic callers (e.g. a `CompactionResult`). */
|
|
71
|
+
data?: unknown;
|
|
72
|
+
}
|
|
73
|
+
export declare class CommandCatalog {
|
|
74
|
+
private readonly host?;
|
|
75
|
+
constructor(host?: CommandHost | undefined);
|
|
76
|
+
private builtins;
|
|
77
|
+
/** Every descriptor (optionally for one surface). Precedence: builtin > plugin > prompt > skill. */
|
|
78
|
+
list(surface?: CommandSurface): CommandDescriptor[];
|
|
79
|
+
/** A command by name or alias (built-in names and aliases are case-insensitive). */
|
|
80
|
+
resolve(name: string): CommandDescriptor | undefined;
|
|
81
|
+
/** Runs an `execution: "core"` command. Surface commands and unknown names throw. */
|
|
82
|
+
execute(name: string, args: string, ctx: CommandExecutionContext): Promise<CommandResult>;
|
|
83
|
+
}
|