ai-runtime-engine 1.3.0 → 2.8.0
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/CHANGELOG.md +638 -0
- package/dist/agents/admit.d.ts +69 -0
- package/dist/agents/admit.js +129 -0
- package/dist/agents/definition.d.ts +36 -0
- package/dist/agents/definition.js +9 -0
- package/dist/agents/envelope.d.ts +53 -0
- package/dist/agents/envelope.js +68 -0
- package/dist/agents/finding.d.ts +79 -0
- package/dist/agents/finding.js +80 -0
- package/dist/agents/roles.d.ts +36 -0
- package/dist/agents/roles.js +44 -0
- package/dist/agents/synthesize.d.ts +44 -0
- package/dist/agents/synthesize.js +60 -0
- package/dist/agents/task.d.ts +112 -0
- package/dist/agents/task.js +48 -0
- package/dist/agents/worker.d.ts +91 -0
- package/dist/agents/worker.js +377 -0
- package/dist/capabilities/capability.d.ts +117 -0
- package/dist/capabilities/capability.js +66 -0
- package/dist/capabilities/registry.d.ts +139 -0
- package/dist/capabilities/registry.js +413 -0
- package/dist/capabilities/vocabulary.d.ts +32 -0
- package/dist/capabilities/vocabulary.js +34 -0
- package/dist/cli/cli.js +55 -4
- package/dist/cli/commands/cleanup.js +29 -27
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +38 -8
- package/dist/cli/commands/executions.js +34 -25
- package/dist/cli/commands/info.d.ts +1 -0
- package/dist/cli/commands/info.js +11 -9
- package/dist/cli/commands/init.js +19 -0
- package/dist/cli/commands/inspect.d.ts +40 -1
- package/dist/cli/commands/inspect.js +157 -2
- package/dist/cli/commands/mcp.d.ts +45 -0
- package/dist/cli/commands/mcp.js +148 -0
- package/dist/cli/commands/route.js +21 -0
- package/dist/cli/commands/run.d.ts +1 -0
- package/dist/cli/commands/run.js +21 -2
- package/dist/cli/commands/skills.d.ts +2 -0
- package/dist/cli/commands/skills.js +29 -7
- package/dist/cli/interactive/ansi.d.ts +41 -0
- package/dist/cli/interactive/ansi.js +43 -0
- package/dist/cli/interactive/complete.d.ts +10 -0
- package/dist/cli/interactive/complete.js +19 -0
- package/dist/cli/interactive/lanes.d.ts +69 -0
- package/dist/cli/interactive/lanes.js +181 -0
- package/dist/cli/interactive/repl.d.ts +3 -0
- package/dist/cli/interactive/repl.js +91 -13
- package/dist/cli/interactive/session.d.ts +8 -0
- package/dist/cli/interactive/session.js +73 -2
- package/dist/cli/render.d.ts +7 -0
- package/dist/cli/render.js +10 -0
- package/dist/cli/runtimeSession.d.ts +11 -0
- package/dist/cli/runtimeSession.js +17 -0
- package/dist/config/defaults.d.ts +3 -1
- package/dist/config/defaults.js +2 -0
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +2 -2
- package/dist/context/lossVerifier.d.ts +24 -0
- package/dist/context/lossVerifier.js +45 -0
- package/dist/context/summarize.d.ts +19 -0
- package/dist/context/summarize.js +53 -0
- package/dist/core/fallback/fallback.d.ts +5 -0
- package/dist/core/fallback/fallback.js +3 -1
- package/dist/core/router/router.d.ts +3 -0
- package/dist/core/router/router.js +1 -0
- package/dist/executions/agentTasks.d.ts +627 -0
- package/dist/executions/agentTasks.js +149 -0
- package/dist/executions/checkpoint.d.ts +5 -1
- package/dist/executions/checkpoint.js +13 -1
- package/dist/executions/execution.d.ts +36 -2
- package/dist/executions/store.d.ts +37 -0
- package/dist/executions/store.js +33 -0
- package/dist/generation/generateAdapter.d.ts +14 -0
- package/dist/generation/generateAdapter.js +38 -0
- package/dist/generation/generateSkill.d.ts +26 -0
- package/dist/generation/generateSkill.js +51 -0
- package/dist/index.d.ts +47 -4
- package/dist/index.js +33 -2
- package/dist/mcp/client.d.ts +70 -0
- package/dist/mcp/client.js +221 -0
- package/dist/mcp/manager.d.ts +151 -0
- package/dist/mcp/manager.js +493 -0
- package/dist/mcp/protocol.d.ts +216 -0
- package/dist/mcp/protocol.js +149 -0
- package/dist/mcp/toolAdapter.d.ts +44 -0
- package/dist/mcp/toolAdapter.js +94 -0
- package/dist/mcp/transport.d.ts +109 -0
- package/dist/mcp/transport.js +383 -0
- package/dist/memory/embedders/hash.d.ts +12 -0
- package/dist/memory/embedders/hash.js +31 -0
- package/dist/memory/embedders/http.d.ts +25 -0
- package/dist/memory/embedders/http.js +48 -0
- package/dist/memory/memory.d.ts +19 -2
- package/dist/memory/memory.js +75 -11
- package/dist/memory/semantic.d.ts +17 -0
- package/dist/memory/semantic.js +29 -0
- package/dist/orchestration/budget.d.ts +30 -0
- package/dist/orchestration/budget.js +40 -0
- package/dist/orchestration/executor.d.ts +64 -1
- package/dist/orchestration/executor.js +104 -7
- package/dist/orchestration/orchestrator.d.ts +35 -1
- package/dist/orchestration/orchestrator.js +106 -8
- package/dist/orchestration/plan.d.ts +15 -1
- package/dist/orchestration/plan.js +23 -4
- package/dist/orchestration/planner.d.ts +19 -1
- package/dist/orchestration/planner.js +25 -5
- package/dist/plugin/ai.d.ts +4 -0
- package/dist/plugin/ai.js +9 -0
- package/dist/runtime/config.js +50 -6
- package/dist/runtime/intent/aiClassifier.d.ts +19 -0
- package/dist/runtime/intent/aiClassifier.js +74 -0
- package/dist/runtime/models/modelProfile.d.ts +61 -0
- package/dist/runtime/models/modelProfile.js +139 -0
- package/dist/runtime/planning/deriveCapabilities.d.ts +95 -0
- package/dist/runtime/planning/deriveCapabilities.js +146 -0
- package/dist/runtime/policy.d.ts +10 -0
- package/dist/runtime/policy.js +9 -2
- package/dist/runtime/runtime.d.ts +233 -0
- package/dist/runtime/runtime.js +1042 -60
- package/dist/runtime/types.d.ts +88 -2
- package/dist/security/redact.js +22 -10
- package/dist/skills/manifest.d.ts +3 -0
- package/dist/skills/manifest.js +24 -0
- package/dist/skills/registry.d.ts +16 -1
- package/dist/skills/registry.js +21 -1
- package/dist/skills/skill.d.ts +6 -1
- package/dist/store/area.d.ts +15 -1
- package/dist/store/area.js +19 -8
- package/dist/store/crypto.d.ts +21 -0
- package/dist/store/crypto.js +49 -0
- package/dist/store/paths.d.ts +5 -1
- package/dist/store/paths.js +6 -0
- package/dist/store/store.d.ts +15 -3
- package/dist/store/store.js +28 -7
- package/dist/telemetry/sinks/otlp.d.ts +31 -0
- package/dist/telemetry/sinks/otlp.js +76 -0
- package/dist/tools/builtins/filesystem.js +1 -0
- package/dist/tools/builtins/git.js +1 -0
- package/dist/tools/builtins/shell.js +1 -0
- package/dist/tools/permissions.d.ts +28 -0
- package/dist/tools/permissions.js +72 -0
- package/dist/tools/registry.d.ts +18 -2
- package/dist/tools/registry.js +22 -2
- package/dist/tools/tool.d.ts +4 -0
- package/dist/types.d.ts +5 -1
- package/dist/util/flatten.d.ts +11 -0
- package/dist/util/flatten.js +18 -0
- package/dist/util/hash.d.ts +19 -0
- package/dist/util/hash.js +39 -0
- package/dist/util/semaphore.d.ts +19 -0
- package/dist/util/semaphore.js +60 -0
- package/package.json +24 -9
package/dist/cli/cli.js
CHANGED
|
@@ -17,10 +17,11 @@ import { setupCommand } from './commands/setup.js';
|
|
|
17
17
|
import { infoCommand } from './commands/info.js';
|
|
18
18
|
import { cleanupCommand } from './commands/cleanup.js';
|
|
19
19
|
import { skillsCommand } from './commands/skills.js';
|
|
20
|
+
import { mcpCommand, mcpAddCommand, mcpRemoveCommand, mcpEnableCommand, mcpTestCommand } from './commands/mcp.js';
|
|
20
21
|
import { startRepl } from './interactive/repl.js';
|
|
21
22
|
import { printError } from './render.js';
|
|
22
23
|
const program = new Command();
|
|
23
|
-
program.name('ai-runtime').description('Universal, provider-agnostic AI Runtime & Orchestration Platform').version('
|
|
24
|
+
program.name('ai-runtime').description('Universal, provider-agnostic AI Runtime & Orchestration Platform').version('2.8.0');
|
|
24
25
|
const configOpt = ['-c, --config <path>', 'path to an ai-runtime config file'];
|
|
25
26
|
// Bare `ai-runtime` (no subcommand) opens the interactive terminal. `allowExcessArguments(false)` keeps
|
|
26
27
|
// a mistyped subcommand (e.g. `ai-runtime porviders`) failing fast instead of silently opening the REPL.
|
|
@@ -36,6 +37,7 @@ program
|
|
|
36
37
|
.option('--json', 'print the full RuntimeResult as JSON')
|
|
37
38
|
.option('--dry-run', 'plan and report what would happen without making any changes')
|
|
38
39
|
.option('--stream', 'stream the answer token-by-token (text output only)')
|
|
40
|
+
.option('--partial', 'if over the call budget, run the phases that fit and pause (vs. notify-and-wait)')
|
|
39
41
|
.action((input, o) => runCommand(input, o));
|
|
40
42
|
program
|
|
41
43
|
.command('executions')
|
|
@@ -60,11 +62,57 @@ program
|
|
|
60
62
|
.action((o) => setupCommand(o));
|
|
61
63
|
program
|
|
62
64
|
.command('skills')
|
|
63
|
-
.description('List available skills; --discover scans the workspace for more
|
|
65
|
+
.description('List available skills; --discover scans the workspace for more; --scaffold drafts a new one')
|
|
64
66
|
.option(...configOpt)
|
|
65
67
|
.option('--discover', 'scan the workspace for skill files elsewhere and report them')
|
|
68
|
+
.option('--scaffold <goal>', 'draft a *.skill.yaml manifest for a goal (asks before saving)')
|
|
69
|
+
.option('-y, --yes', 'with --scaffold, save the drafted skill without prompting')
|
|
66
70
|
.option('--json', 'print as JSON')
|
|
67
71
|
.action((o) => skillsCommand(o));
|
|
72
|
+
// `mcp` — MCP servers. A subcommand-bearing group whose bare form lists, and `mcp <id>` details one.
|
|
73
|
+
// Every path releases stdio children on completion (Runtime.close), so a one-shot command never orphans.
|
|
74
|
+
const mcp = program.command('mcp').description('Inspect and manage MCP servers (tools from external servers)');
|
|
75
|
+
mcp
|
|
76
|
+
.argument('[id]', 'a server id to show in detail')
|
|
77
|
+
.option(...configOpt)
|
|
78
|
+
.option('--json', 'print as JSON')
|
|
79
|
+
.action((id, o) => mcpCommand(id, o));
|
|
80
|
+
mcp
|
|
81
|
+
.command('add')
|
|
82
|
+
.argument('<id>', 'a short id for the server (lowercase, kebab/snake)')
|
|
83
|
+
.description('Add a server (stored locally — your config file is never modified)')
|
|
84
|
+
.option('--command <argv>', 'stdio server command line, e.g. "npx -y some-mcp-server"')
|
|
85
|
+
.option('--url <url>', 'streamable-HTTP endpoint')
|
|
86
|
+
.option('--token-env <NAME>', 'env-var NAME holding the credential (never the value itself)')
|
|
87
|
+
.option('--timeout <ms>', 'per-call timeout override')
|
|
88
|
+
.option(...configOpt)
|
|
89
|
+
.option('--json', 'print as JSON')
|
|
90
|
+
.action((id, o) => mcpAddCommand(id, o));
|
|
91
|
+
mcp
|
|
92
|
+
.command('remove')
|
|
93
|
+
.argument('<id>', 'server id')
|
|
94
|
+
.description('Remove a server that was added with `mcp add`')
|
|
95
|
+
.option(...configOpt)
|
|
96
|
+
.action((id, o) => mcpRemoveCommand(id, o));
|
|
97
|
+
mcp
|
|
98
|
+
.command('enable')
|
|
99
|
+
.argument('<id>', 'server id')
|
|
100
|
+
.description('Enable a disabled server')
|
|
101
|
+
.option(...configOpt)
|
|
102
|
+
.action((id, o) => mcpEnableCommand(id, true, o));
|
|
103
|
+
mcp
|
|
104
|
+
.command('disable')
|
|
105
|
+
.argument('<id>', 'server id')
|
|
106
|
+
.description('Disable a server without removing it')
|
|
107
|
+
.option(...configOpt)
|
|
108
|
+
.action((id, o) => mcpEnableCommand(id, false, o));
|
|
109
|
+
mcp
|
|
110
|
+
.command('test')
|
|
111
|
+
.argument('<id>', 'server id')
|
|
112
|
+
.description('Connect, handshake, list tools, and ping — reporting exactly what happened')
|
|
113
|
+
.option(...configOpt)
|
|
114
|
+
.option('--json', 'print as JSON')
|
|
115
|
+
.action((id, o) => mcpTestCommand(id, o));
|
|
68
116
|
program
|
|
69
117
|
.command('info')
|
|
70
118
|
.description('Summarize the runtime: workspace, providers, skills, tools, memory, learning, storage')
|
|
@@ -90,13 +138,16 @@ program
|
|
|
90
138
|
.description('List known models across providers')
|
|
91
139
|
.option(...configOpt)
|
|
92
140
|
.option('--json', 'print as JSON')
|
|
141
|
+
.option('--profile', 'show the models.md per-mode/per-task routing profile instead')
|
|
93
142
|
.action((o) => modelsCommand(o));
|
|
94
143
|
program
|
|
95
144
|
.command('capabilities')
|
|
96
|
-
.
|
|
145
|
+
.argument('[goal]', 'with --actions: which action capabilities this goal needs (offline — no model call)')
|
|
146
|
+
.description('Show the model capability matrix; --actions shows action capabilities (read_file, run_tests, …)')
|
|
97
147
|
.option(...configOpt)
|
|
148
|
+
.option('--actions', 'show ACTION capabilities (what the runtime can do) instead of the model matrix')
|
|
98
149
|
.option('--json', 'print as JSON')
|
|
99
|
-
.action((o) => capabilitiesCommand(o));
|
|
150
|
+
.action((goal, o) => capabilitiesCommand(goal, o));
|
|
100
151
|
program
|
|
101
152
|
.command('doctor')
|
|
102
153
|
.description('Diagnose the runtime: config, .env, providers, models, store, skills, tools, permissions, git')
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Nothing is deleted without confirmation unless `--yes`; `--dry-run` only reports the plan. Integrity
|
|
5
5
|
* issues are reported, never auto-"repaired" destructively.
|
|
6
6
|
*/
|
|
7
|
-
import { Runtime } from '../../runtime/runtime.js';
|
|
8
7
|
import { TERMINAL } from '../../executions/execution.js';
|
|
9
8
|
import { confirm } from '../prompt.js';
|
|
9
|
+
import { withRuntime } from '../runtimeSession.js';
|
|
10
10
|
import { print } from '../render.js';
|
|
11
11
|
const DAY_MS = 86_400_000;
|
|
12
12
|
function numEnv(v) {
|
|
@@ -67,30 +67,32 @@ function renderPlan(plan) {
|
|
|
67
67
|
return lines;
|
|
68
68
|
}
|
|
69
69
|
export async function cleanupCommand(opts) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
70
|
+
// Every Runtime-based command releases its Runtime (MCP children included) on every path.
|
|
71
|
+
return withRuntime(opts, async (rt) => {
|
|
72
|
+
if (!rt.store.enabled) {
|
|
73
|
+
print('storage is disabled (stateless mode) — nothing to clean.');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const plan = planCleanup(rt, Date.now());
|
|
77
|
+
if (opts.json)
|
|
78
|
+
return print(JSON.stringify(plan, null, 2));
|
|
79
|
+
for (const line of renderPlan(plan))
|
|
80
|
+
print(line);
|
|
81
|
+
const removable = toRemove(plan);
|
|
82
|
+
if (removable === 0) {
|
|
83
|
+
print('\nNothing to remove.');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (opts.dryRun) {
|
|
87
|
+
print('\n(dry run — nothing was removed)');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const ok = opts.yes || (await confirm(`\nRemove ${removable} item(s)?`));
|
|
91
|
+
if (!ok) {
|
|
92
|
+
print('cancelled — nothing was removed.');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const done = applyCleanup(rt, plan);
|
|
96
|
+
print(`\nremoved: ${done.conversations} conversation(s), ${done.executions} execution(s), ${done.artifacts} artifact(s), ${done.cache} cache entr${done.cache === 1 ? 'y' : 'ies'}.`);
|
|
97
|
+
});
|
|
96
98
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Never prints a secret VALUE; all output is redacted. `--json` emits the structured report.
|
|
5
5
|
*/
|
|
6
6
|
import { Runtime } from '../../runtime/runtime.js';
|
|
7
|
+
import type { McpServerStatus } from '../../mcp/manager.js';
|
|
7
8
|
import type { IntegrityIssue } from '../../store/area.js';
|
|
8
9
|
export interface DoctorReport {
|
|
9
10
|
configFile?: string;
|
|
@@ -36,6 +37,19 @@ export interface DoctorReport {
|
|
|
36
37
|
};
|
|
37
38
|
skills: string[];
|
|
38
39
|
tools: string[];
|
|
40
|
+
/**
|
|
41
|
+
* MCP servers (Phase 3.3). ABSENT when none are configured, so a no-MCP `doctor --json` is unchanged.
|
|
42
|
+
* `connected: false` always: `doctor` is read-only and never connects (it is also the REPL `/doctor`
|
|
43
|
+
* path, and `gatherDoctor` is side-effect-free) — so states are as last recorded. `servers` reuses
|
|
44
|
+
* `McpServerStatus` VERBATIM, which is name-only for credentials (`tokenEnv` + `tokenSet`, never a
|
|
45
|
+
* value); adding any field beyond it would risk the secret-leak scan that stringifies this report.
|
|
46
|
+
*/
|
|
47
|
+
mcp?: {
|
|
48
|
+
connected: boolean;
|
|
49
|
+
servers: McpServerStatus[];
|
|
50
|
+
grants: Record<string, string>;
|
|
51
|
+
warnings: string[];
|
|
52
|
+
};
|
|
39
53
|
permissions: {
|
|
40
54
|
fsRead: boolean;
|
|
41
55
|
fsWrite: boolean;
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* health, accessible models, the local store (integrity + counts), skills, tools, permissions, and git.
|
|
4
4
|
* Never prints a secret VALUE; all output is redacted. `--json` emits the structured report.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { mcpRow } from './mcp.js';
|
|
7
7
|
import { loadRuntimeConfig } from '../../runtime/config.js';
|
|
8
|
+
import { withRuntime } from '../runtimeSession.js';
|
|
8
9
|
import { print } from '../render.js';
|
|
9
10
|
/** The credential env-var NAMES ai-runtime knows about (§16). Presence is reported, never the value. */
|
|
10
11
|
const KNOWN_KEY_ENVS = [
|
|
@@ -45,6 +46,21 @@ export async function gatherDoctor(rt, configFile, env) {
|
|
|
45
46
|
},
|
|
46
47
|
skills: rt.skills().map((s) => `${s.id}@${s.version}`),
|
|
47
48
|
tools: rt.tools().map((t) => t.id),
|
|
49
|
+
// No connectMcp(): doctor stays read-only. `list()` is id-sorted, so rendering is deterministic, and
|
|
50
|
+
// the spread makes the null-prototype grant map plain JSON.
|
|
51
|
+
...(rt.hasMcpServers()
|
|
52
|
+
? {
|
|
53
|
+
mcp: {
|
|
54
|
+
// Truthful rather than hardcoded: `doctor` never CONNECTS, but the REPL's `/doctor` runs
|
|
55
|
+
// against a session whose servers are already live, and reporting `false` there would be a
|
|
56
|
+
// lie. This says whether any server is connected right now, not whether doctor connected it.
|
|
57
|
+
connected: rt.mcp().list().some((sv) => sv.state === 'connected' || sv.state === 'degraded'),
|
|
58
|
+
servers: rt.mcp().list(),
|
|
59
|
+
grants: { ...rt.permissions().mcp.servers },
|
|
60
|
+
warnings: rt.mcpWarningsList(),
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: {}),
|
|
48
64
|
permissions: { fsRead: perms.filesystem.read, fsWrite: perms.filesystem.write, shell: perms.shell.enabled, gitCommit: perms.git.commit, gitPush: perms.git.push, network: perms.network.enabled },
|
|
49
65
|
git: { repo: !!ws?.git.isRepo, ...(ws?.git.branch ? { branch: ws.git.branch } : {}) },
|
|
50
66
|
};
|
|
@@ -69,6 +85,18 @@ export function renderDoctor(r) {
|
|
|
69
85
|
lines.push(` ${r.store.conversations} conversation(s), ${r.store.executions} execution(s), ${r.store.memory} memory record(s), ${r.store.artifacts} artifact(s)`);
|
|
70
86
|
lines.push(` integrity: ${r.store.integrity.length ? `${r.store.integrity.length} issue(s) — ${r.store.integrity.map((i) => `${i.file} (${i.problem})`).join(', ')}` : 'ok'}`);
|
|
71
87
|
}
|
|
88
|
+
if (r.mcp) {
|
|
89
|
+
lines.push('', `MCP servers (${r.mcp.connected ? 'live in this session' : 'as last recorded'} — \`doctor\` does not connect; run \`ai-runtime mcp\` for a live check):`);
|
|
90
|
+
if (!r.mcp.servers.length)
|
|
91
|
+
lines.push(' (none)');
|
|
92
|
+
for (const sv of r.mcp.servers)
|
|
93
|
+
lines.push(mcpRow(r.mcp.grants, sv));
|
|
94
|
+
const ungranted = r.mcp.servers.filter((sv) => !Object.prototype.hasOwnProperty.call(r.mcp.grants, sv.id) || r.mcp.grants[sv.id] === 'off').map((sv) => sv.id);
|
|
95
|
+
if (ungranted.length)
|
|
96
|
+
lines.push(` not permitted to execute: ${ungranted.join(', ')} — grant permissions.mcp.servers.<id>: read (or full)`);
|
|
97
|
+
for (const w of r.mcp.warnings)
|
|
98
|
+
lines.push(` ! ${w}`);
|
|
99
|
+
}
|
|
72
100
|
lines.push('', `Skills: ${r.skills.length ? r.skills.join(', ') : '(none)'}`);
|
|
73
101
|
lines.push(`Tools: ${r.tools.join(', ')}`);
|
|
74
102
|
lines.push(`Perms: fsRead=${r.permissions.fsRead} fsWrite=${r.permissions.fsWrite} shell=${r.permissions.shell} gitCommit=${r.permissions.gitCommit} gitPush=${r.permissions.gitPush} network=${r.permissions.network}`);
|
|
@@ -76,11 +104,13 @@ export function renderDoctor(r) {
|
|
|
76
104
|
return lines;
|
|
77
105
|
}
|
|
78
106
|
export async function doctorCommand(opts) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
// Every Runtime-based command releases its Runtime (MCP children included) on every path.
|
|
108
|
+
return withRuntime(opts, async (rt) => {
|
|
109
|
+
const loaded = loadRuntimeConfig({ workspaceRoot: process.cwd(), ...(opts.config ? { explicitPath: opts.config } : {}) });
|
|
110
|
+
const report = await gatherDoctor(rt, loaded.configFile, process.env);
|
|
111
|
+
if (opts.json)
|
|
112
|
+
return print(JSON.stringify(report, null, 2));
|
|
113
|
+
for (const line of renderDoctor(report))
|
|
114
|
+
print(line);
|
|
115
|
+
});
|
|
86
116
|
}
|
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
/** `ai-runtime executions` (list) and `resume-execution <id>` — inspect and resume persisted work. */
|
|
2
|
-
import { Runtime } from '../../runtime/runtime.js';
|
|
3
2
|
import { print } from '../render.js';
|
|
3
|
+
import { withRuntime } from '../runtimeSession.js';
|
|
4
4
|
export async function executionsCommand(options) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
return withRuntime(options, async (rt) => {
|
|
6
|
+
const list = rt.executions();
|
|
7
|
+
if (options.json)
|
|
8
|
+
return print(JSON.stringify(list, null, 2));
|
|
9
|
+
if (!list.length)
|
|
10
|
+
return print('No executions.');
|
|
11
|
+
print('Executions:');
|
|
12
|
+
for (const e of list)
|
|
13
|
+
print(` ${e.id} [${e.status}] ${e.mode}: ${e.goal.slice(0, 60)}`);
|
|
14
|
+
});
|
|
14
15
|
}
|
|
15
16
|
export async function resumeExecutionCommand(id, options) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
return withRuntime(options, async (rt) => {
|
|
18
|
+
// A resumed plan can execute MCP tool steps, so its servers must be connected — and released on the
|
|
19
|
+
// way out (withRuntime), or a live stdio child keeps the CLI from ever exiting.
|
|
20
|
+
if (rt.hasMcpServers()) {
|
|
21
|
+
await rt.connectMcp();
|
|
22
|
+
for (const w of rt.mcpWarningsList())
|
|
23
|
+
print(`! ${w}`);
|
|
24
|
+
}
|
|
25
|
+
// --approve proceeds past a waiting-for-approval gate; --deny cancels it. Neither → leave the decision.
|
|
26
|
+
const approve = options.approve ? true : options.deny ? false : undefined;
|
|
27
|
+
const r = await rt.resumeExecution(id, {
|
|
28
|
+
...(options.answer ? { clarificationAnswer: options.answer } : {}),
|
|
29
|
+
...(approve !== undefined ? { approve } : {}),
|
|
30
|
+
});
|
|
31
|
+
if (r.response?.text)
|
|
32
|
+
print(r.response.text);
|
|
33
|
+
print(`status: ${r.status}`);
|
|
34
|
+
if (r.status === 'waiting_for_approval')
|
|
35
|
+
print('(waiting for approval — re-run with --approve to proceed or --deny to cancel)');
|
|
36
|
+
if (r.clarification)
|
|
37
|
+
print(`? ${r.clarification.question}`);
|
|
38
|
+
if (!r.ok)
|
|
39
|
+
process.exitCode = 1;
|
|
22
40
|
});
|
|
23
|
-
if (r.response?.text)
|
|
24
|
-
print(r.response.text);
|
|
25
|
-
print(`status: ${r.status}`);
|
|
26
|
-
if (r.status === 'waiting_for_approval')
|
|
27
|
-
print('(waiting for approval — re-run with --approve to proceed or --deny to cancel)');
|
|
28
|
-
if (r.clarification)
|
|
29
|
-
print(`? ${r.clarification.question}`);
|
|
30
|
-
if (!r.ok)
|
|
31
|
-
process.exitCode = 1;
|
|
32
41
|
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* providers/models, skills, tools, memory + learning stats, and storage paths. Makes NO live calls and
|
|
4
4
|
* mutates nothing. `--json` prints the structured report.
|
|
5
5
|
*/
|
|
6
|
-
import { Runtime } from '../../runtime/runtime.js';
|
|
7
6
|
import { resolveContextBudget } from '../../context/budget.js';
|
|
8
7
|
import { summarizeWorkspace } from '../../runtime/workspace/workspace.js';
|
|
8
|
+
import { withRuntime } from '../runtimeSession.js';
|
|
9
9
|
import { print } from '../render.js';
|
|
10
10
|
/** Gather the read-only info report from a loaded runtime. Pure aside from reading the local store. */
|
|
11
11
|
export async function gatherInfo(rt) {
|
|
@@ -31,7 +31,7 @@ export async function gatherInfo(rt) {
|
|
|
31
31
|
tools: rt.tools().map((t) => t.id),
|
|
32
32
|
memory: { enabled: rt.memory.enabled, facts },
|
|
33
33
|
learning: { enabled: rt.learning.enabled, entries: learningSnap.length, preferredProviders: rt.learning.enabled ? rt.learning.preferredProviders() : [] },
|
|
34
|
-
storage: { home: paths.home, project: paths.project, enabled: rt.store.enabled },
|
|
34
|
+
storage: { home: paths.home, project: paths.project, enabled: rt.store.enabled, ...(paths.organization ? { organization: paths.organization } : {}) },
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
export function renderInfo(r) {
|
|
@@ -40,14 +40,16 @@ export function renderInfo(r) {
|
|
|
40
40
|
lines.push(`workspace: ${r.workspace.name}${r.workspace.branch ? ` @ ${r.workspace.branch}` : ''}`, ` ${r.workspace.summary}`);
|
|
41
41
|
else
|
|
42
42
|
lines.push('workspace: (none detected)');
|
|
43
|
-
lines.push(`mode: ${r.defaultMode}`, `context: ${r.contextBudgetTokens} token budget`, `providers: ${r.providers.total} configured (${r.providers.enabled} enabled), ${r.models} models`, `skills: ${r.skills.length ? r.skills.join(', ') : '(none — register tools to enable)'}`, `tools: ${r.tools.join(', ')}`, `memory: ${r.memory.enabled ? `${r.memory.facts} fact(s)` : 'disabled'}`, `learning: ${r.learning.enabled ? `${r.learning.entries} record(s)${r.learning.preferredProviders.length ? `, prefers ${r.learning.preferredProviders.join(', ')}` : ''}` : 'disabled'}`, `storage: ${r.storage.enabled ? r.storage.project : 'disabled (stateless)'}`);
|
|
43
|
+
lines.push(`mode: ${r.defaultMode}`, `context: ${r.contextBudgetTokens} token budget`, `providers: ${r.providers.total} configured (${r.providers.enabled} enabled), ${r.models} models`, `skills: ${r.skills.length ? r.skills.join(', ') : '(none — register tools to enable)'}`, `tools: ${r.tools.join(', ')}`, `memory: ${r.memory.enabled ? `${r.memory.facts} fact(s)` : 'disabled'}`, `learning: ${r.learning.enabled ? `${r.learning.entries} record(s)${r.learning.preferredProviders.length ? `, prefers ${r.learning.preferredProviders.join(', ')}` : ''}` : 'disabled'}`, `storage: ${r.storage.enabled ? r.storage.project : 'disabled (stateless)'}`, ...(r.storage.organization ? [`organization: ${r.storage.organization}`] : []));
|
|
44
44
|
return lines;
|
|
45
45
|
}
|
|
46
46
|
export async function infoCommand(opts) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
// Every Runtime-based command releases its Runtime (MCP children included) on every path.
|
|
48
|
+
return withRuntime(opts, async (rt) => {
|
|
49
|
+
const report = await gatherInfo(rt);
|
|
50
|
+
if (opts.json)
|
|
51
|
+
return print(JSON.stringify(report, null, 2));
|
|
52
|
+
for (const line of renderInfo(report))
|
|
53
|
+
print(line);
|
|
54
|
+
});
|
|
53
55
|
}
|
|
@@ -41,6 +41,24 @@ GEMINI_API_KEY=
|
|
|
41
41
|
GROQ_API_KEY=
|
|
42
42
|
ANTHROPIC_API_KEY=
|
|
43
43
|
`;
|
|
44
|
+
const EXAMPLE_MODELS = `# models.md — optional per-mode / per-task model routing (Phase 23). Uncomment and edit.
|
|
45
|
+
# Each entry is a soft prefer (default), a hard pin (=provider:model), or a strategy keyword
|
|
46
|
+
# (best|fastest|cheapest|highest-quality|local-only|cloud-only|privacy-first|provider-specific|
|
|
47
|
+
# fallback-only, or 'free' → cheapest). Explicit per-run choices always win over this file.
|
|
48
|
+
#
|
|
49
|
+
# What applies where:
|
|
50
|
+
# - chat mode + '## By task' (router tasks via 'ai-runtime route <task>'): prefer, pin, AND strategy.
|
|
51
|
+
# - orchestration modes (plan/execute/orchestrate/agent/debug): soft PREFER only — a hard pin or a
|
|
52
|
+
# strategy is NOT applied there (the planner and skills pick their own models). Use prefer for those.
|
|
53
|
+
#
|
|
54
|
+
# ## By mode
|
|
55
|
+
# - chat: free # chat: cheapest/local models (strategy works here)
|
|
56
|
+
# - plan: anthropic:claude-sonnet-5 # orchestration: soft prefer a strong planning model
|
|
57
|
+
# - execute: openai:gpt-5 # orchestration: soft prefer
|
|
58
|
+
#
|
|
59
|
+
# ## By task
|
|
60
|
+
# - generate-playwright-tests: =anthropic:claude-opus-5 # via 'ai-runtime route generate-playwright-tests'
|
|
61
|
+
`;
|
|
44
62
|
const GITIGNORE_ENTRIES = ['.env', '.env.*', '!.env.example', '.ai-runtime/'];
|
|
45
63
|
function writeIfAbsent(file, contents) {
|
|
46
64
|
const full = resolve(process.cwd(), file);
|
|
@@ -70,6 +88,7 @@ export function initCommand() {
|
|
|
70
88
|
// and `setup` scaffold the same file. A root `ai-runtime.yaml` still works as a fallback if present.
|
|
71
89
|
writeIfAbsent('.ai-runtime/config.yaml', EXAMPLE_YAML);
|
|
72
90
|
writeIfAbsent('.env.example', EXAMPLE_ENV);
|
|
91
|
+
writeIfAbsent('models.md', EXAMPLE_MODELS);
|
|
73
92
|
ensureGitignore();
|
|
74
93
|
print('\nNext steps:');
|
|
75
94
|
print(' 1. Copy .env.example to .env and fill in the keys you have');
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* `ai-runtime providers` / `models` / `capabilities` — read-only inspection of the configured fleet.
|
|
3
3
|
* All go through the key-free facade views and redacted output.
|
|
4
4
|
*/
|
|
5
|
+
import { Runtime } from '../../runtime/runtime.js';
|
|
6
|
+
import type { CapabilityGapReason, GapSuggestion, MissingCapabilityReport } from '../../capabilities/capability.js';
|
|
5
7
|
export declare function providersCommand(opts: {
|
|
6
8
|
config?: string;
|
|
7
9
|
json?: boolean;
|
|
@@ -9,8 +11,45 @@ export declare function providersCommand(opts: {
|
|
|
9
11
|
export declare function modelsCommand(opts: {
|
|
10
12
|
config?: string;
|
|
11
13
|
json?: boolean;
|
|
14
|
+
profile?: boolean;
|
|
12
15
|
}): Promise<void>;
|
|
13
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Gather the ACTION-capability table (Phase 3.1). Uses a full Runtime (like `doctor`) so autoloaded
|
|
18
|
+
* skills appear — a listing that omitted them would misreport availability.
|
|
19
|
+
*/
|
|
20
|
+
export declare function actionCapabilityRows(rt: Runtime): Array<{
|
|
21
|
+
id: string;
|
|
22
|
+
provider: string;
|
|
23
|
+
source: string;
|
|
24
|
+
effects: string;
|
|
25
|
+
availability: string;
|
|
26
|
+
evidence: string;
|
|
27
|
+
description: string;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function gatherActionCapabilities(config?: string): Promise<Array<{
|
|
30
|
+
id: string;
|
|
31
|
+
provider: string;
|
|
32
|
+
source: string;
|
|
33
|
+
effects: string;
|
|
34
|
+
availability: string;
|
|
35
|
+
evidence: string;
|
|
36
|
+
description: string;
|
|
37
|
+
}>>;
|
|
38
|
+
/** Render the action-capability table as aligned text (shared by the CLI and the REPL). */
|
|
39
|
+
export declare function renderActionCapabilities(rows: Awaited<ReturnType<typeof gatherActionCapabilities>>): string[];
|
|
40
|
+
/** One-line gloss per gap reason (exhaustive over CapabilityGapReason). */
|
|
41
|
+
export declare const GAP_REASON_GLOSS: Record<CapabilityGapReason, string>;
|
|
42
|
+
/** One actionable line per GapSuggestion (exhaustive over all four arms). */
|
|
43
|
+
export declare function renderGapSuggestion(s: GapSuggestion): string;
|
|
44
|
+
/**
|
|
45
|
+
* Render a MissingCapabilityReport. THE one gap renderer: `run`, the REPL result tail,
|
|
46
|
+
* `capabilities --actions <goal>` and `/capabilities <goal>` all call this, so they cannot drift.
|
|
47
|
+
* Every id, hint, and policyKey passes `displaySafe` — a gap's capabilityId originates in the model's
|
|
48
|
+
* plan JSON, and `sanitizeSegment` restricts its charset but sets no length bound.
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderCapabilityGaps(report: MissingCapabilityReport): string[];
|
|
51
|
+
export declare function capabilitiesCommand(goal: string | undefined, opts: {
|
|
14
52
|
config?: string;
|
|
15
53
|
json?: boolean;
|
|
54
|
+
actions?: boolean;
|
|
16
55
|
}): Promise<void>;
|