ai-runtime-engine 1.1.1 → 1.2.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 +27 -0
- package/dist/cli/cli.js +3 -1
- package/dist/cli/commands/config.js +6 -4
- package/dist/cli/commands/executions.d.ts +2 -0
- package/dist/cli/commands/executions.js +8 -1
- package/dist/cli/commands/init.js +7 -4
- package/dist/cli/commands/phase2.js +6 -2
- package/dist/cli/interactive/repl.js +20 -0
- package/dist/cli/interactive/session.js +15 -5
- package/dist/runtime/runtime.d.ts +5 -1
- package/dist/runtime/runtime.js +28 -8
- package/docs/GUIDE.md +3 -2
- package/docs/README.md +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to `ai-runtime` are documented here. The format follows
|
|
|
5
5
|
Versioning](https://semver.org/). Development history and rationale live in
|
|
6
6
|
[docs/DECISIONS.md](docs/DECISIONS.md) and [docs/PROGRESS.md](docs/PROGRESS.md).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] — 2026-08-31
|
|
9
|
+
|
|
10
|
+
Closes the known gaps between what the CLI/REPL exposed and what the Runtime supported. Additive and
|
|
11
|
+
backward compatible, with three small behavior corrections noted under Changed.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Approve / deny from the terminal** — `resume-execution --approve` / `--deny`, and REPL `/approve <id>`
|
|
16
|
+
/ `/deny <id>`. A plan that stops at `waiting_for_approval` can now be advanced or cancelled from the
|
|
17
|
+
CLI or interactive terminal (previously reachable only programmatically or via a host `ApprovalProvider`).
|
|
18
|
+
- **Live progress in the interactive terminal** — the REPL now prints concise mode/routing/fallback lines
|
|
19
|
+
during a run by subscribing to lifecycle events (it always claimed to, but never did).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`chat` mode now honors `--dry-run` / `dryRun`** — a chat dry run makes **no** model call and captures
|
|
24
|
+
**no** memory; it reports what it would send. (Orchestration modes already did; chat used to run live.)
|
|
25
|
+
- **`ai-runtime config`** now resolves `.ai-runtime/config.yaml` through the runtime loader, matching
|
|
26
|
+
every other command. It previously used the legacy root-only loader and reported "no config file found"
|
|
27
|
+
even when the canonical file was in use.
|
|
28
|
+
- **`ai-runtime init`** now scaffolds the canonical `.ai-runtime/config.yaml` (same location as `setup`)
|
|
29
|
+
instead of a root `ai-runtime.yaml`. A root `ai-runtime.yaml` still works as a fallback if present.
|
|
30
|
+
- **`ai-runtime telemetry`** empty-state message now explains that telemetry is buffered in-process and
|
|
31
|
+
points to `telemetry: { sink: file }` for history that survives across CLI invocations.
|
|
32
|
+
|
|
8
33
|
## [1.1.1] — 2026-08-31
|
|
9
34
|
|
|
10
35
|
Documentation only — no code changes.
|
|
@@ -82,6 +107,8 @@ Initial release: the provider-agnostic AI **router** — capability-based routin
|
|
|
82
107
|
scoring, evidence validation, fallback, health tracking, learning-based scoring, multi-model verification,
|
|
83
108
|
budgets, MCP tools, OpenAPI-based adapter generation, and the `AI` class + CLI.
|
|
84
109
|
|
|
110
|
+
[1.2.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.2.0
|
|
111
|
+
[1.1.1]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.1.1
|
|
85
112
|
[1.1.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.1.0
|
|
86
113
|
[1.0.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.0.0
|
|
87
114
|
[0.1.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v0.1.0
|
package/dist/cli/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import { skillsCommand } from './commands/skills.js';
|
|
|
20
20
|
import { startRepl } from './interactive/repl.js';
|
|
21
21
|
import { printError } from './render.js';
|
|
22
22
|
const program = new Command();
|
|
23
|
-
program.name('ai-runtime').description('Universal, provider-agnostic AI Runtime & Orchestration Platform').version('1.
|
|
23
|
+
program.name('ai-runtime').description('Universal, provider-agnostic AI Runtime & Orchestration Platform').version('1.2.0');
|
|
24
24
|
const configOpt = ['-c, --config <path>', 'path to an ai-runtime config file'];
|
|
25
25
|
// Bare `ai-runtime` (no subcommand) opens the interactive terminal. `allowExcessArguments(false)` keeps
|
|
26
26
|
// a mistyped subcommand (e.g. `ai-runtime porviders`) failing fast instead of silently opening the REPL.
|
|
@@ -48,6 +48,8 @@ program
|
|
|
48
48
|
.description('Resume a persisted execution (reconciles the workspace first)')
|
|
49
49
|
.option(...configOpt)
|
|
50
50
|
.option('--answer <text>', 'answer a pending clarification')
|
|
51
|
+
.option('--approve', 'approve a plan that is waiting for approval, then continue')
|
|
52
|
+
.option('--deny', 'deny a plan waiting for approval (cancels the execution)')
|
|
51
53
|
.action((id, o) => resumeExecutionCommand(id, o));
|
|
52
54
|
program.command('init').description('Scaffold ai-runtime.yaml, .env.example, and .gitignore entries').action(() => initCommand());
|
|
53
55
|
program
|
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
* defaults). Shows weights, strategy, policy posture, and timeouts. No secrets are read or printed.
|
|
4
4
|
*/
|
|
5
5
|
import { resolveConfig } from '../../config/defaults.js';
|
|
6
|
-
import {
|
|
6
|
+
import { loadRuntimeConfig } from '../../runtime/config.js';
|
|
7
7
|
import { print } from '../render.js';
|
|
8
8
|
export function configCommand(options) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// Use the runtime loader so `.ai-runtime/config.yaml` is honored (root `ai-runtime.yaml` still works
|
|
10
|
+
// as a fallback) — matching every other command, instead of the legacy root-only loader.
|
|
11
|
+
const loaded = loadRuntimeConfig({ workspaceRoot: process.cwd(), ...(options.config ? { explicitPath: options.config } : {}) });
|
|
12
|
+
const resolved = resolveConfig(loaded.config.router);
|
|
11
13
|
if (options.json) {
|
|
12
14
|
print(JSON.stringify(resolved, null, 2));
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
|
-
print(
|
|
17
|
+
print(loaded.configFile ? `Resolved config (${loaded.configFile}):` : 'Resolved defaults (no config file found):');
|
|
16
18
|
print(` providers: ${resolved.providers.length}`);
|
|
17
19
|
print(` strategy: ${resolved.strategy}`);
|
|
18
20
|
print(` timeoutMs: ${resolved.timeoutMs}`);
|
|
@@ -14,10 +14,17 @@ export async function executionsCommand(options) {
|
|
|
14
14
|
}
|
|
15
15
|
export async function resumeExecutionCommand(id, options) {
|
|
16
16
|
const rt = await Runtime.load({ ...(options.config ? { config: options.config } : {}) });
|
|
17
|
-
|
|
17
|
+
// --approve proceeds past a waiting-for-approval gate; --deny cancels it. Neither → leave the decision.
|
|
18
|
+
const approve = options.approve ? true : options.deny ? false : undefined;
|
|
19
|
+
const r = await rt.resumeExecution(id, {
|
|
20
|
+
...(options.answer ? { clarificationAnswer: options.answer } : {}),
|
|
21
|
+
...(approve !== undefined ? { approve } : {}),
|
|
22
|
+
});
|
|
18
23
|
if (r.response?.text)
|
|
19
24
|
print(r.response.text);
|
|
20
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)');
|
|
21
28
|
if (r.clarification)
|
|
22
29
|
print(`? ${r.clarification.question}`);
|
|
23
30
|
if (!r.ok)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `ai-runtime init` — scaffold a config, an env template, and gitignore entries. Never writes secrets;
|
|
3
3
|
* the config carries only env-var NAMES, and `.env` is added to .gitignore.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
-
import { resolve } from 'node:path';
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { dirname, resolve } from 'node:path';
|
|
7
7
|
import { print } from '../render.js';
|
|
8
8
|
const EXAMPLE_YAML = `# ai-runtime configuration. Secret VALUES live in .env (gitignored); this file names env vars only.
|
|
9
9
|
strategy: best
|
|
@@ -48,6 +48,7 @@ function writeIfAbsent(file, contents) {
|
|
|
48
48
|
print(` • ${file} already exists — left unchanged`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
+
mkdirSync(dirname(full), { recursive: true });
|
|
51
52
|
writeFileSync(full, contents);
|
|
52
53
|
print(` ✓ wrote ${file}`);
|
|
53
54
|
}
|
|
@@ -65,11 +66,13 @@ function ensureGitignore() {
|
|
|
65
66
|
}
|
|
66
67
|
export function initCommand() {
|
|
67
68
|
print('Initializing ai-runtime...');
|
|
68
|
-
|
|
69
|
+
// Write the canonical `.ai-runtime/config.yaml` (the location the runtime loader prefers) so `init`
|
|
70
|
+
// and `setup` scaffold the same file. A root `ai-runtime.yaml` still works as a fallback if present.
|
|
71
|
+
writeIfAbsent('.ai-runtime/config.yaml', EXAMPLE_YAML);
|
|
69
72
|
writeIfAbsent('.env.example', EXAMPLE_ENV);
|
|
70
73
|
ensureGitignore();
|
|
71
74
|
print('\nNext steps:');
|
|
72
75
|
print(' 1. Copy .env.example to .env and fill in the keys you have');
|
|
73
76
|
print(' 2. Run `ai-runtime doctor` to verify configuration');
|
|
74
|
-
print(' 3. Run `ai-runtime
|
|
77
|
+
print(' 3. Run `ai-runtime` to open the interactive terminal (or `ai-runtime run "<task>"`)');
|
|
75
78
|
}
|
|
@@ -65,8 +65,12 @@ export function telemetryCommand(opts) {
|
|
|
65
65
|
const events = ai.telemetryEvents();
|
|
66
66
|
const limit = opts.limit ? Number(opts.limit) : 20;
|
|
67
67
|
const recent = events.slice(-limit);
|
|
68
|
-
if (recent.length === 0)
|
|
69
|
-
|
|
68
|
+
if (recent.length === 0) {
|
|
69
|
+
print('No telemetry recorded in this session.');
|
|
70
|
+
print('(Telemetry is buffered in-process, so a fresh CLI invocation starts empty. For telemetry that');
|
|
71
|
+
print(' persists across processes, set `telemetry: { sink: file }` in your config.)');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
70
74
|
for (const e of recent)
|
|
71
75
|
print(JSON.stringify(e));
|
|
72
76
|
}
|
|
@@ -15,10 +15,30 @@ function banner(rt) {
|
|
|
15
15
|
print(`mode: auto · ${rt.ai.providers().length} providers · type /help or a request, /exit to quit`);
|
|
16
16
|
print('');
|
|
17
17
|
}
|
|
18
|
+
/** A concise, redacted progress line for a lifecycle event — the REPL's live feedback during a run. */
|
|
19
|
+
function progressLine(e) {
|
|
20
|
+
switch (e.type) {
|
|
21
|
+
case 'mode.selected':
|
|
22
|
+
return e.source === 'auto' ? ` · mode: ${e.selected} (auto, ${Math.round(e.confidence * 100)}%)` : ` · mode: ${e.selected}`;
|
|
23
|
+
case 'provider.selected':
|
|
24
|
+
return ` · routed → ${e.providerId}/${e.model}`;
|
|
25
|
+
case 'provider.failed':
|
|
26
|
+
return ` · ${e.providerId}/${e.model} failed${e.category ? ` (${e.category})` : ''} — falling back`;
|
|
27
|
+
default:
|
|
28
|
+
return undefined; // runtime.started / clarification.requested / run.completed are covered by the result block
|
|
29
|
+
}
|
|
30
|
+
}
|
|
18
31
|
/** Start the interactive session. Resolves when the user exits (or stdin closes). */
|
|
19
32
|
export async function startRepl(configPath) {
|
|
20
33
|
const rt = await Runtime.load({ ...(configPath ? { config: configPath } : {}) });
|
|
21
34
|
const session = new ReplSession(rt);
|
|
35
|
+
// Live progress: print concise routing/mode lines as they happen during a run. The events are already
|
|
36
|
+
// redacted; a throwing observer can never break a run (the emitter swallows sink errors).
|
|
37
|
+
rt.on((e) => {
|
|
38
|
+
const line = progressLine(e);
|
|
39
|
+
if (line)
|
|
40
|
+
print(line);
|
|
41
|
+
});
|
|
22
42
|
banner(rt);
|
|
23
43
|
const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: '> ' });
|
|
24
44
|
rl.prompt();
|
|
@@ -36,6 +36,8 @@ const HELP = [
|
|
|
36
36
|
' /resume <id> resume a conversation',
|
|
37
37
|
' /executions list persisted executions',
|
|
38
38
|
' /resume-execution <id> resume an execution',
|
|
39
|
+
' /approve <id> approve an execution waiting for approval, then continue',
|
|
40
|
+
' /deny <id> deny an execution waiting for approval (cancels it)',
|
|
39
41
|
' /pause <id> pause an execution',
|
|
40
42
|
' /cancel <id> cancel an execution',
|
|
41
43
|
' /config show the resolved configuration',
|
|
@@ -141,6 +143,10 @@ export class ReplSession {
|
|
|
141
143
|
return this.executionsList();
|
|
142
144
|
case 'resume-execution':
|
|
143
145
|
return this.resumeExecution(args[0]);
|
|
146
|
+
case 'approve':
|
|
147
|
+
return this.resumeExecution(args[0], true);
|
|
148
|
+
case 'deny':
|
|
149
|
+
return this.resumeExecution(args[0], false);
|
|
144
150
|
case 'pause':
|
|
145
151
|
return args[0] ? { lines: [this.runtime.pauseExecution(args[0]) ? `paused ${args[0]}` : `cannot pause '${args[0]}'`] } : { lines: ['usage: /pause <execution-id>'] };
|
|
146
152
|
case 'cancel':
|
|
@@ -165,8 +171,10 @@ export class ReplSession {
|
|
|
165
171
|
lines.push(`[${result.error.category}] ${result.error.message}`);
|
|
166
172
|
if (result.mode.executed !== result.mode.selected)
|
|
167
173
|
lines.push(`(mode: ${result.mode.selected} → ${result.mode.executed})`);
|
|
168
|
-
if (result.status === 'waiting_for_approval')
|
|
169
|
-
|
|
174
|
+
if (result.status === 'waiting_for_approval') {
|
|
175
|
+
const execId = result.execution?.id;
|
|
176
|
+
lines.push(execId ? `(approval required — /approve ${execId} to proceed, or /deny ${execId})` : '(approval required — approve the execution via /executions then /approve <id>)');
|
|
177
|
+
}
|
|
170
178
|
if (result.status === 'failed')
|
|
171
179
|
lines.push('(did not complete)');
|
|
172
180
|
if (result.clarification)
|
|
@@ -249,14 +257,16 @@ export class ReplSession {
|
|
|
249
257
|
return { lines: ['no executions yet.'] };
|
|
250
258
|
return { lines: ['Executions:', ...list.map((e) => ` ${e.id} [${e.status}] ${e.mode}: ${e.goal.slice(0, 50)}`)] };
|
|
251
259
|
}
|
|
252
|
-
async resumeExecution(id) {
|
|
260
|
+
async resumeExecution(id, approve) {
|
|
253
261
|
if (!id)
|
|
254
|
-
return { lines: [
|
|
255
|
-
const r = await this.runtime.resumeExecution(id);
|
|
262
|
+
return { lines: [`usage: /${approve === undefined ? 'resume-execution' : approve ? 'approve' : 'deny'} <execution-id>`] };
|
|
263
|
+
const r = await this.runtime.resumeExecution(id, approve !== undefined ? { approve } : {});
|
|
256
264
|
const lines = [];
|
|
257
265
|
if (r.response?.text)
|
|
258
266
|
lines.push(r.response.text);
|
|
259
267
|
lines.push(`status: ${r.status}`);
|
|
268
|
+
if (r.status === 'waiting_for_approval')
|
|
269
|
+
lines.push('(waiting for approval — /approve ' + id + ' to proceed, /deny ' + id + ' to cancel)');
|
|
260
270
|
if (r.clarification)
|
|
261
271
|
lines.push(`? ${r.clarification.question}`);
|
|
262
272
|
return { lines };
|
|
@@ -186,6 +186,10 @@ export declare class Runtime {
|
|
|
186
186
|
private contextBlocks;
|
|
187
187
|
/** Best-effort estimator calibration from provider-reported input usage (estimates never claim to be exact). */
|
|
188
188
|
private calibrate;
|
|
189
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* Capture an explicit "remember …" fact and retrieve relevant facts for context. Off when disabled.
|
|
191
|
+
* `capture` is false on a dry run — retrieval is read-only, but writing a fact is a mutation the dry
|
|
192
|
+
* run must not perform.
|
|
193
|
+
*/
|
|
190
194
|
private applyMemory;
|
|
191
195
|
}
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -350,8 +350,9 @@ export class Runtime {
|
|
|
350
350
|
const clarification = buildClarification(modeResult);
|
|
351
351
|
if (clarification)
|
|
352
352
|
this.emitter.emit({ type: 'clarification.requested', runId, question: clarification.question });
|
|
353
|
-
// Memory:
|
|
354
|
-
|
|
353
|
+
// Memory: retrieve relevant facts into context, and (unless this is a dry run) capture an explicit
|
|
354
|
+
// "remember …". A dry run performs zero mutations, so it retrieves but never writes.
|
|
355
|
+
const memTrace = this.applyMemory(text, !policy.dryRun);
|
|
355
356
|
// Compile the model context (workspace summary + memory facts + user system) under a token budget.
|
|
356
357
|
const budget = resolveContextBudget({
|
|
357
358
|
...(req.context?.maxTokens !== undefined ? { perRun: req.context.maxTokens } : {}),
|
|
@@ -360,6 +361,19 @@ export class Runtime {
|
|
|
360
361
|
});
|
|
361
362
|
const compiled = compileContext(this.contextBlocks(req, context.workspace, memTrace?.retrieved), { budgetTokens: budget, estimator: this.estimator });
|
|
362
363
|
const contextReport = { metrics: compiled.metrics, validation: compiled.validation };
|
|
364
|
+
// Dry-run: chat's only "action" is the model call itself, so a dry run makes NO call. It reports
|
|
365
|
+
// what it would send (mode, compiled-context size) and performs zero mutations.
|
|
366
|
+
if (policy.dryRun) {
|
|
367
|
+
const status = clarification ? 'waiting_for_clarification' : 'completed';
|
|
368
|
+
this.emitter.emit({ type: 'run.completed', runId, ok: true, status, confidence: 1 });
|
|
369
|
+
const preview = `[dry-run] chat — no model call made. Would compile ${compiled.metrics.compiledTokens} context token(s) (budget ${budget}) and send one request to the best-matching provider. Run without --dry-run to execute.`;
|
|
370
|
+
const result = { ok: true, runId, mode: resolution, status, response: { text: preview }, context: contextReport, artifacts: [] };
|
|
371
|
+
if (clarification)
|
|
372
|
+
result.clarification = clarification;
|
|
373
|
+
if (memTrace)
|
|
374
|
+
result.memory = memTrace;
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
363
377
|
const runRequest = buildChatRequest(req, strategy, compiled.system || undefined, routing);
|
|
364
378
|
const runResult = await this._ai.run(runRequest);
|
|
365
379
|
this.calibrate(runRequest.system, text, runResult);
|
|
@@ -673,16 +687,22 @@ export class Runtime {
|
|
|
673
687
|
if (tokens && tokens > 0)
|
|
674
688
|
this.estimator.calibrate((system?.length ?? 0) + inputText.length, tokens);
|
|
675
689
|
}
|
|
676
|
-
/**
|
|
677
|
-
|
|
690
|
+
/**
|
|
691
|
+
* Capture an explicit "remember …" fact and retrieve relevant facts for context. Off when disabled.
|
|
692
|
+
* `capture` is false on a dry run — retrieval is read-only, but writing a fact is a mutation the dry
|
|
693
|
+
* run must not perform.
|
|
694
|
+
*/
|
|
695
|
+
applyMemory(text, capture = true) {
|
|
678
696
|
if (!this.memoryEnabled())
|
|
679
697
|
return undefined;
|
|
680
698
|
try {
|
|
681
699
|
const trace = { retrieved: [] };
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
700
|
+
if (capture) {
|
|
701
|
+
const candidate = classifyMemory(text);
|
|
702
|
+
if (candidate) {
|
|
703
|
+
const rec = this._memory.remember(candidate);
|
|
704
|
+
trace.captured = { id: rec.id, scope: rec.scope };
|
|
705
|
+
}
|
|
686
706
|
}
|
|
687
707
|
trace.retrieved = this._memory.search(text, { limit: 3 }).map((h) => h.text);
|
|
688
708
|
if (trace.retrieved.length === 0 && !trace.captured)
|
package/docs/GUIDE.md
CHANGED
|
@@ -66,8 +66,9 @@ You can run the whole pipeline with a **mock provider** — no credentials, no n
|
|
|
66
66
|
```js
|
|
67
67
|
import { Runtime, MockProvider, makeModel } from 'ai-runtime-engine';
|
|
68
68
|
|
|
69
|
-
// A fresh runtime
|
|
70
|
-
|
|
69
|
+
// A fresh runtime in stateless mode, so this demo writes nothing to disk.
|
|
70
|
+
// (Persistence is ON by default; pass { persistence: 'disabled' } to turn it off.)
|
|
71
|
+
const runtime = new Runtime(undefined, { persistence: 'disabled' });
|
|
71
72
|
|
|
72
73
|
// Register one fake provider with one fake model that can "reason" and output "text".
|
|
73
74
|
runtime.ai.registerProvider(new MockProvider({
|
package/docs/README.md
CHANGED
|
@@ -13,8 +13,15 @@ with the `ai-runtime-engine` package:
|
|
|
13
13
|
permissions, credentials, and the learning safety rail.
|
|
14
14
|
|
|
15
15
|
Additional development records — the append-only decision log (`DECISIONS.md`), the phase-by-phase build
|
|
16
|
-
log (`PROGRESS.md`),
|
|
17
|
-
repository and are not part of
|
|
16
|
+
log (`PROGRESS.md`), the release checklist (`NPM_PUBLISHING.md`), and the complete project **handbook**
|
|
17
|
+
(`handbook/00-index.md` and its chapters) — live in the (private) source repository and are not part of
|
|
18
|
+
the published package. The handbook is the deepest reference: architecture, a file-by-file source tour,
|
|
19
|
+
the CLI/REPL, the npm story, testing, and maintenance.
|
|
20
|
+
|
|
21
|
+
> Note: this index file (`docs/README.md`) itself *does* end up in the npm tarball, because npm always
|
|
22
|
+
> includes any `README*` file regardless of the `files` allowlist. It only links to shipped docs, so that
|
|
23
|
+
> is harmless — but keep anything private out of `README*`-named files. (The handbook index is named
|
|
24
|
+
> `00-index.md`, not `README.md`, for exactly this reason.)
|
|
18
25
|
|
|
19
26
|
Per-subsystem detail is documented at the source: each module under `src/**` opens with a doc comment
|
|
20
27
|
describing its contract and invariants, and the README's [How to use it](../README.md#how-to-use-it)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-runtime-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AI Runtime — a provider-agnostic AI runtime and orchestration platform. Point it at whatever AI providers you have; it routes each task to the best available model. Ships the `ai-runtime` CLI and the `Runtime`/`AI` library API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "ISC",
|