aiden-runtime 4.0.1 → 4.1.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/README.md +11 -7
- package/config/hardware.json +2 -2
- package/dist/api/server.js +50 -52
- package/dist/cli/v4/aidenCLI.js +513 -14
- package/dist/cli/v4/aidenPrompt.js +317 -0
- package/dist/cli/v4/box.js +105 -39
- package/dist/cli/v4/callbacks.js +39 -6
- package/dist/cli/v4/chatSession.js +269 -52
- package/dist/cli/v4/citationFooter.js +97 -0
- package/dist/cli/v4/commands/channel.js +656 -0
- package/dist/cli/v4/commands/clear.js +1 -1
- package/dist/cli/v4/commands/compress.js +1 -1
- package/dist/cli/v4/commands/cron.js +44 -16
- package/dist/cli/v4/commands/fanout.js +236 -0
- package/dist/cli/v4/commands/help.js +15 -4
- package/dist/cli/v4/commands/history.js +84 -0
- package/dist/cli/v4/commands/index.js +19 -1
- package/dist/cli/v4/commands/mcp.js +358 -0
- package/dist/cli/v4/commands/setup.js +34 -0
- package/dist/cli/v4/commands/show.js +43 -0
- package/dist/cli/v4/commands/skills.js +169 -4
- package/dist/cli/v4/commands/status.js +84 -0
- package/dist/cli/v4/commands/subagent.js +78 -0
- package/dist/cli/v4/commands/verbose.js +1 -1
- package/dist/cli/v4/commands/voice.js +218 -0
- package/dist/cli/v4/cronCli.js +103 -0
- package/dist/cli/v4/display.js +300 -14
- package/dist/cli/v4/doctor.js +41 -0
- package/dist/cli/v4/envSources.js +105 -0
- package/dist/cli/v4/ghostMatch.js +74 -0
- package/dist/cli/v4/historyStore.js +163 -0
- package/dist/cli/v4/pasteCompression.js +124 -0
- package/dist/cli/v4/pasteIntercept.js +203 -0
- package/dist/cli/v4/replyRenderer.js +209 -0
- package/dist/cli/v4/resizeGuard.js +92 -0
- package/dist/cli/v4/setupWizard.js +466 -232
- package/dist/cli/v4/shellInterpolation.js +139 -0
- package/dist/cli/v4/skinEngine.js +21 -1
- package/dist/cli/v4/streamingPrefix.js +121 -0
- package/dist/cli/v4/syntaxHighlight.js +345 -0
- package/dist/cli/v4/table.js +216 -0
- package/dist/cli/v4/themeDetect.js +81 -0
- package/dist/cli/v4/uiBuild.js +74 -0
- package/dist/cli/v4/voiceCli.js +113 -0
- package/dist/cli/v4/voicePromptApi.js +196 -0
- package/dist/core/channels/discord.js +16 -10
- package/dist/core/channels/email.js +13 -9
- package/dist/core/channels/imessage.js +13 -9
- package/dist/core/channels/manager.js +25 -7
- package/dist/core/channels/pdf-extract.js +180 -0
- package/dist/core/channels/photo-vision.js +157 -0
- package/dist/core/channels/signal.js +11 -7
- package/dist/core/channels/slack.js +13 -10
- package/dist/core/channels/telegram-commands.js +154 -0
- package/dist/core/channels/telegram-groups.js +198 -0
- package/dist/core/channels/telegram-rate-limit.js +124 -0
- package/dist/core/channels/telegram.js +1980 -0
- package/dist/core/channels/twilio.js +11 -7
- package/dist/core/channels/webhook.js +9 -5
- package/dist/core/channels/whatsapp.js +15 -11
- package/dist/core/channels/whisper-transcribe.js +163 -0
- package/dist/core/cronManager.js +33 -294
- package/dist/core/gateway.js +29 -8
- package/dist/core/playwrightBridge.js +90 -0
- package/dist/core/v4/aidenAgent.js +35 -0
- package/dist/core/v4/auxiliaryClient.js +2 -2
- package/dist/core/v4/cron/atomicWrite.js +18 -4
- package/dist/core/v4/cron/cronExecute.js +300 -0
- package/dist/core/v4/cron/cronManager.js +502 -0
- package/dist/core/v4/cron/cronState.js +314 -0
- package/dist/core/v4/cron/cronTick.js +90 -0
- package/dist/core/v4/cron/diagnostics.js +104 -0
- package/dist/core/v4/cron/graceWindow.js +79 -0
- package/dist/core/v4/firstRun/providerDetection.js +287 -0
- package/dist/core/v4/logger/factory.js +110 -0
- package/dist/core/v4/logger/index.js +22 -0
- package/dist/core/v4/logger/logger.js +101 -0
- package/dist/core/v4/logger/sinks/fileSink.js +110 -0
- package/dist/core/v4/logger/sinks/multiSink.js +43 -0
- package/dist/core/v4/logger/sinks/nullSink.js +53 -0
- package/dist/core/v4/logger/sinks/stdSink.js +81 -0
- package/dist/core/v4/mcp/server/diagnostics.js +40 -0
- package/dist/core/v4/mcp/server/skillBridge.js +94 -0
- package/dist/core/v4/mcp/server/stdioServer.js +119 -0
- package/dist/core/v4/mcp/server/toolBridge.js +168 -0
- package/dist/core/v4/platformPaths.js +105 -0
- package/dist/core/v4/providerFallback.js +25 -0
- package/dist/core/v4/skillLoader.js +21 -5
- package/dist/core/v4/skillMining/candidateStore.js +164 -0
- package/dist/core/v4/skillMining/extractorPrompt.js +111 -0
- package/dist/core/v4/skillMining/proposalBuilder.js +139 -0
- package/dist/core/v4/skillMining/skillMiner.js +191 -0
- package/dist/core/v4/skillMining/traceFingerprint.js +51 -0
- package/dist/core/v4/subagent/budget.js +76 -0
- package/dist/core/v4/subagent/diagnostics.js +22 -0
- package/dist/core/v4/subagent/fanout.js +216 -0
- package/dist/core/v4/subagent/merger.js +148 -0
- package/dist/core/v4/subagent/providerRotation.js +54 -0
- package/dist/core/v4/voice/audioStream.js +373 -0
- package/dist/core/v4/voice/cliVoice.js +393 -0
- package/dist/core/v4/voice/diagnostics.js +66 -0
- package/dist/core/v4/voice/ttsStream.js +193 -0
- package/dist/core/version.js +1 -1
- package/dist/core/visionAnalyze.js +291 -90
- package/dist/core/voice/audio.js +61 -5
- package/dist/core/voice/audioBackend.js +134 -0
- package/dist/core/voice/stt.js +61 -6
- package/dist/core/voice/tts.js +19 -3
- package/dist/providers/v4/nullAdapter.js +58 -0
- package/dist/tools/v4/index.js +32 -1
- package/dist/tools/v4/subagent/subagentFanout.js +166 -0
- package/package.json +11 -2
package/dist/cli/v4/aidenCLI.js
CHANGED
|
@@ -58,8 +58,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
58
58
|
return result;
|
|
59
59
|
};
|
|
60
60
|
})();
|
|
61
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
62
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
63
|
+
};
|
|
61
64
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
-
exports.getEnvSource = exports.loadAidenEnvFile = void 0;
|
|
65
|
+
exports.getEnvSource = exports.loadAidenEnvFile = exports.AIDEN_UI_BUILD = void 0;
|
|
63
66
|
exports.main = main;
|
|
64
67
|
exports.buildAgentRuntime = buildAgentRuntime;
|
|
65
68
|
exports.runInteractiveChat = runInteractiveChat;
|
|
@@ -71,12 +74,17 @@ exports.runSkillsSubcommand = runSkillsSubcommand;
|
|
|
71
74
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
72
75
|
const commander_1 = require("commander");
|
|
73
76
|
const node_fs_1 = require("node:fs");
|
|
77
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
74
78
|
const chatSession_1 = require("./chatSession");
|
|
75
79
|
const aidenTUI_1 = require("./aidenTUI");
|
|
76
80
|
const display_1 = require("./display");
|
|
77
81
|
const skinEngine_1 = require("./skinEngine");
|
|
78
82
|
const commandRegistry_1 = require("./commandRegistry");
|
|
79
83
|
const callbacks_1 = require("./callbacks");
|
|
84
|
+
// Tier-3.1 (v4.1-tier3.1) — re-export the build fingerprint so the
|
|
85
|
+
// runtime smoke can find it in the bundled artifact.
|
|
86
|
+
const uiBuild_1 = require("./uiBuild");
|
|
87
|
+
Object.defineProperty(exports, "AIDEN_UI_BUILD", { enumerable: true, get: function () { return uiBuild_1.AIDEN_UI_BUILD; } });
|
|
80
88
|
const setupWizard_1 = require("./setupWizard");
|
|
81
89
|
const doctor_1 = require("./doctor");
|
|
82
90
|
const modelPicker_1 = require("./commands/modelPicker");
|
|
@@ -88,6 +96,7 @@ const sessionStore_1 = require("../../core/v4/sessionStore");
|
|
|
88
96
|
const sessionManager_1 = require("../../core/v4/sessionManager");
|
|
89
97
|
const toolRegistry_1 = require("../../core/v4/toolRegistry");
|
|
90
98
|
const skillLoader_1 = require("../../core/v4/skillLoader");
|
|
99
|
+
const index_1 = require("../../tools/v4/index");
|
|
91
100
|
const skillCommands_1 = require("../../core/v4/skillCommands");
|
|
92
101
|
const aidenAgent_1 = require("../../core/v4/aidenAgent");
|
|
93
102
|
const promptBuilder_1 = require("../../core/v4/promptBuilder");
|
|
@@ -98,17 +107,31 @@ const approvalEngine_1 = require("../../moat/approvalEngine");
|
|
|
98
107
|
const plannerGuard_1 = require("../../moat/plannerGuard");
|
|
99
108
|
const honestyEnforcement_1 = require("../../moat/honestyEnforcement");
|
|
100
109
|
const skillTeacher_1 = require("../../moat/skillTeacher");
|
|
110
|
+
const skillMiner_1 = require("../../core/v4/skillMining/skillMiner");
|
|
111
|
+
const uiBuild_2 = require("./uiBuild");
|
|
101
112
|
const memoryGuard_1 = require("../../moat/memoryGuard");
|
|
102
113
|
const ssrfProtection_1 = require("../../moat/ssrfProtection");
|
|
103
114
|
const tirithScanner_1 = require("../../moat/tirithScanner");
|
|
104
115
|
const credentialResolver_1 = require("../../providers/v4/credentialResolver");
|
|
105
116
|
const runtimeResolver_1 = require("../../providers/v4/runtimeResolver");
|
|
106
117
|
const chatCompletionsAdapter_1 = require("../../providers/v4/chatCompletionsAdapter");
|
|
118
|
+
const modelCatalog_1 = require("../../providers/v4/modelCatalog");
|
|
107
119
|
const providerFallback_1 = require("../../core/v4/providerFallback");
|
|
108
120
|
const skillBundledRestore_1 = require("../../core/v4/skillBundledRestore");
|
|
121
|
+
const providerDetection_1 = require("../../core/v4/firstRun/providerDetection");
|
|
109
122
|
const aidenLogger_1 = require("../../core/v4/aidenLogger");
|
|
110
123
|
const plugins_1 = require("../../core/v4/plugins");
|
|
111
124
|
const providerAuth_1 = require("../../core/v4/auth/providerAuth");
|
|
125
|
+
// Phase v4.1-1.1 — CLI-side ChannelManager. The same singleton lives in
|
|
126
|
+
// the API server process; in a CLI-only session we host it here so
|
|
127
|
+
// /channel commands operate without a separate server. When `aiden serve`
|
|
128
|
+
// runs in the SAME process tree on the same machine, polling-based
|
|
129
|
+
// adapters (Telegram) must only be started in one of the two — we
|
|
130
|
+
// gate startup on a heuristic below to avoid 409 polling-conflict.
|
|
131
|
+
const manager_1 = require("../../core/channels/manager");
|
|
132
|
+
const telegram_1 = require("../../core/channels/telegram");
|
|
133
|
+
const gateway_1 = require("../../core/gateway");
|
|
134
|
+
const logger_1 = require("../../core/v4/logger");
|
|
112
135
|
const v4_1 = require("../../tools/v4");
|
|
113
136
|
const mcpSetup_1 = require("../../tools/v4/mcpSetup");
|
|
114
137
|
const skillCommandHandler_1 = require("./commands/skillCommandHandler");
|
|
@@ -191,7 +214,24 @@ async function main(argv, opts = {}) {
|
|
|
191
214
|
.option('--planner-guard <mode>', 'PlannerGuard mode: off | rule_based | llm_classified')
|
|
192
215
|
.option('--honesty <mode>', 'HonestyEnforcement mode: off | detect | enforce')
|
|
193
216
|
.option('--skill-teacher <tier>', 'SkillTeacher tier: off | tier_3_propose | tier_4_auto')
|
|
217
|
+
.option('--no-ui', 'Disable Tier-3 UI polish (autosuggest ghost text, inline status line); fall back to legacy rendering')
|
|
194
218
|
.action(async () => {
|
|
219
|
+
// Tier-3.1: argv discipline. If positional args were passed but
|
|
220
|
+
// no subcommand matched, error to stderr and exit non-zero
|
|
221
|
+
// rather than silently booting the REPL — a typo'd subcommand
|
|
222
|
+
// otherwise produces a hung foreground.
|
|
223
|
+
const leftover = program.args ?? [];
|
|
224
|
+
if (leftover.length > 0) {
|
|
225
|
+
process.stderr.write(`error: unknown command "${leftover[0]}"\n`);
|
|
226
|
+
process.stderr.write(`Run 'aiden --help' for available commands.\n`);
|
|
227
|
+
process.exit(2);
|
|
228
|
+
}
|
|
229
|
+
// Tier-3.1: surface --no-ui as an env var so downstream modules
|
|
230
|
+
// (which import uiBuild.ts) see the flag without threading it
|
|
231
|
+
// through every call site.
|
|
232
|
+
const o = program.opts();
|
|
233
|
+
if (o.ui === false)
|
|
234
|
+
process.env.AIDEN_NO_UI = '1';
|
|
195
235
|
const cliOpts = program.opts();
|
|
196
236
|
if (opts.runChatHook) {
|
|
197
237
|
await opts.runChatHook(cliOpts);
|
|
@@ -199,6 +239,16 @@ async function main(argv, opts = {}) {
|
|
|
199
239
|
}
|
|
200
240
|
await runInteractiveChat(cliOpts, opts);
|
|
201
241
|
});
|
|
242
|
+
// Tier-3.1: hidden one-shot — emits the UI build fingerprint and
|
|
243
|
+
// exits. Used by the runtime smoke to verify the bundled artifact
|
|
244
|
+
// matches the expected sub-phase without parsing other output.
|
|
245
|
+
program
|
|
246
|
+
.command('print-ui-build', { hidden: true })
|
|
247
|
+
.description('Print the v4.1 tier-3 UI build fingerprint and exit.')
|
|
248
|
+
.action(() => {
|
|
249
|
+
process.stdout.write(`${uiBuild_1.AIDEN_UI_BUILD}\n`);
|
|
250
|
+
process.exit(0);
|
|
251
|
+
});
|
|
202
252
|
program
|
|
203
253
|
.command('setup')
|
|
204
254
|
.description('Run the setup wizard (provider + model + API key)')
|
|
@@ -261,17 +311,82 @@ async function main(argv, opts = {}) {
|
|
|
261
311
|
});
|
|
262
312
|
program
|
|
263
313
|
.command('mcp <action>')
|
|
264
|
-
.description('
|
|
314
|
+
.description('MCP server mode (Phase v4.1-mcp). Actions: serve, status, tools.')
|
|
265
315
|
.action(async (action) => {
|
|
266
316
|
if (opts.runMcpHook) {
|
|
267
317
|
await opts.runMcpHook(action);
|
|
268
318
|
return;
|
|
269
319
|
}
|
|
270
|
-
|
|
271
|
-
|
|
320
|
+
// Lazy-load so the rest of the CLI does not pay the import cost
|
|
321
|
+
// for `setup`, `doctor`, `model`, etc. on every invocation.
|
|
322
|
+
const { runMcpSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/mcp')));
|
|
323
|
+
const code = await runMcpSubcommand(action, {
|
|
324
|
+
writeOut: opts.writeOut,
|
|
325
|
+
writeErr: (t) => process.stderr.write(t),
|
|
326
|
+
});
|
|
327
|
+
if (code !== 0)
|
|
328
|
+
process.exit(code);
|
|
329
|
+
});
|
|
330
|
+
program
|
|
331
|
+
.command('voice [args...]')
|
|
332
|
+
.description('Voice diagnostics + one-shot TTS / transcribe (Phase v4.1-voice-cli). ' +
|
|
333
|
+
'Usage: aiden voice doctor | tts "<text>" | transcribe <file>')
|
|
334
|
+
.allowUnknownOption()
|
|
335
|
+
.action(async (args) => {
|
|
336
|
+
const { runVoiceSubcommand } = await Promise.resolve().then(() => __importStar(require('./voiceCli')));
|
|
337
|
+
const action = (args[0] ?? 'doctor').toLowerCase();
|
|
338
|
+
const rest = args.slice(1);
|
|
339
|
+
const code = await runVoiceSubcommand(action, rest, {
|
|
340
|
+
writeOut: opts.writeOut,
|
|
341
|
+
writeErr: (t) => process.stderr.write(t),
|
|
342
|
+
});
|
|
343
|
+
if (code !== 0)
|
|
344
|
+
process.exit(code);
|
|
345
|
+
});
|
|
346
|
+
program
|
|
347
|
+
.command('subagent <action>')
|
|
348
|
+
.description('Subagent fanout diagnostics (Phase v4.1-subagent). Actions: status, tools.')
|
|
349
|
+
.action(async (action) => {
|
|
350
|
+
const { runSubagentSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/subagent')));
|
|
351
|
+
const code = await runSubagentSubcommand(action, {
|
|
352
|
+
writeOut: opts.writeOut,
|
|
353
|
+
writeErr: (t) => process.stderr.write(t),
|
|
354
|
+
});
|
|
355
|
+
if (code !== 0)
|
|
356
|
+
process.exit(code);
|
|
357
|
+
});
|
|
358
|
+
program
|
|
359
|
+
.command('fanout [args...]')
|
|
360
|
+
.description('Run a parallel agent fanout (Phase v4.1-subagent). ' +
|
|
361
|
+
'Usage: aiden fanout "<query>" --n=3 --merge=combine [--mode=ensemble] [--dry-run]')
|
|
362
|
+
.allowUnknownOption()
|
|
363
|
+
.action(async (args) => {
|
|
364
|
+
const { runFanoutCli } = await Promise.resolve().then(() => __importStar(require('./commands/fanout')));
|
|
365
|
+
const code = await runFanoutCli(args, {
|
|
366
|
+
writeOut: opts.writeOut,
|
|
367
|
+
writeErr: (t) => process.stderr.write(t),
|
|
368
|
+
});
|
|
369
|
+
if (code !== 0)
|
|
370
|
+
process.exit(code);
|
|
272
371
|
});
|
|
273
372
|
// v4.1 placeholders. (`tui` graduated to a real flag in Phase 15.)
|
|
274
|
-
|
|
373
|
+
program
|
|
374
|
+
.command('cron [args...]')
|
|
375
|
+
.description('Cron diagnostics + one-shot list / run (Phase v4.1 hardened cron). ' +
|
|
376
|
+
'Usage: aiden cron status | list | run <id>')
|
|
377
|
+
.allowUnknownOption()
|
|
378
|
+
.action(async (args) => {
|
|
379
|
+
const { runCronSubcommand } = await Promise.resolve().then(() => __importStar(require('./cronCli')));
|
|
380
|
+
const action = (args[0] ?? 'status').toLowerCase();
|
|
381
|
+
const rest = args.slice(1);
|
|
382
|
+
const code = await runCronSubcommand(action, rest, {
|
|
383
|
+
writeOut: opts.writeOut,
|
|
384
|
+
writeErr: (t) => process.stderr.write(t),
|
|
385
|
+
});
|
|
386
|
+
if (code !== 0)
|
|
387
|
+
process.exit(code);
|
|
388
|
+
});
|
|
389
|
+
for (const cmd of ['batch', 'gateway', 'pairing', 'update']) {
|
|
275
390
|
program
|
|
276
391
|
.command(cmd)
|
|
277
392
|
.description(`(deferred to v4.1)`)
|
|
@@ -351,9 +466,70 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
351
466
|
}
|
|
352
467
|
const config = new config_1.ConfigManager(paths);
|
|
353
468
|
await config.load();
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
469
|
+
// Phase 30.2 — fresh-user UX. Detection extends the old
|
|
470
|
+
// `isFreshInstall`-only gate so we cover three new failure modes:
|
|
471
|
+
// 1. fresh user with no env / no OAuth / no config → wizard fires
|
|
472
|
+
// (was working under the old gate; still does).
|
|
473
|
+
// 2. user with config.yaml pointing at chatgpt-plus but a stale /
|
|
474
|
+
// missing OAuth token file → wizard fires (was NOT under old
|
|
475
|
+
// code — it saw config.yaml present and proceeded into a
|
|
476
|
+
// broken resolve, which surfaced as a confusing rate-limit
|
|
477
|
+
// error on the user's first chat).
|
|
478
|
+
// 3. user with no config but Ollama running OR an env API key
|
|
479
|
+
// → wizard fires anyway. ConfigManager's DEFAULT_CONFIG points
|
|
480
|
+
// at `anthropic / claude-opus-4-7`, which doesn't match the
|
|
481
|
+
// detected env / Ollama, so skipping the wizard would surface
|
|
482
|
+
// the same confusing "missing ANTHROPIC_API_KEY" error.
|
|
483
|
+
// 4. moat-boot test fixtures that stub `providers.fake.apiKey`
|
|
484
|
+
// inline in config.yaml count as configured — `isFreshInstall`
|
|
485
|
+
// already returns false for them so `wizardNeeded` stays false.
|
|
486
|
+
const detection = await (0, providerDetection_1.detectAvailableProviders)({ paths });
|
|
487
|
+
const configuredProviderBroken = !!detection.configProvider &&
|
|
488
|
+
!detection.configuredProviderHasCredentials;
|
|
489
|
+
const wizardNeeded = !detection.hasAnyProvider ||
|
|
490
|
+
configuredProviderBroken ||
|
|
491
|
+
(await (0, setupWizard_1.isFreshInstall)(paths));
|
|
492
|
+
// Phase 30.2.1: when the wizard returns 'skipped' (explore mode) we
|
|
493
|
+
// boot the REPL with a NullAdapter instead of trying to resolve a
|
|
494
|
+
// real provider. Flagged here, set inside the wizard block, and
|
|
495
|
+
// consumed when building the adapter.
|
|
496
|
+
let exploreMode = false;
|
|
497
|
+
if (wizardNeeded) {
|
|
498
|
+
if (!detection.hasAnyProvider) {
|
|
499
|
+
// Truly empty: no env, no OAuth, no Ollama, no inline config.
|
|
500
|
+
process.stdout.write(`\n${(0, providerDetection_1.summarizeDetection)(detection)}\n`);
|
|
501
|
+
}
|
|
502
|
+
else if (configuredProviderBroken) {
|
|
503
|
+
// Config points at a provider we can't credential-resolve.
|
|
504
|
+
process.stdout.write(`\nConfigured provider '${detection.configProvider}' has no usable credentials ` +
|
|
505
|
+
`at ${node_path_1.default.join(paths.root, 'auth', `${detection.configProvider}.json`)}.\n`);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
// Detected something (env / oauth / ollama) but config.yaml is
|
|
509
|
+
// missing or empty — DEFAULT_CONFIG would route to anthropic and
|
|
510
|
+
// the resolver would fail. Surface the detection so the user
|
|
511
|
+
// sees what we found, then walk them through proper setup.
|
|
512
|
+
process.stdout.write(`\n${(0, providerDetection_1.summarizeDetection)(detection)}\n`);
|
|
513
|
+
process.stdout.write('config.yaml is empty — let\'s pick a provider that matches.\n');
|
|
514
|
+
}
|
|
515
|
+
process.stdout.write('Launching setup wizard…\n\n');
|
|
516
|
+
const result = await (0, setupWizard_1.runSetupWizard)({ paths });
|
|
517
|
+
// Phase 30.2.1: three exit states.
|
|
518
|
+
if (result.status === 'exited') {
|
|
519
|
+
// Recovery option [5] — clean exit, no REPL.
|
|
520
|
+
process.exit(0);
|
|
521
|
+
}
|
|
522
|
+
if (result.status === 'skipped') {
|
|
523
|
+
// Recovery option [4] "explore mode" OR Ctrl+C cancellation.
|
|
524
|
+
// Boot continues into the REPL with a NullAdapter; chat is
|
|
525
|
+
// intercepted by ChatSession, slash commands work normally.
|
|
526
|
+
// Flagged here and consumed below where the adapter is built.
|
|
527
|
+
exploreMode = true;
|
|
528
|
+
}
|
|
529
|
+
// 'configured' (or 'skipped' — we still want the env/.env reload
|
|
530
|
+
// for slash commands like /providers that read fresh state) →
|
|
531
|
+
// re-load both so the resolver sees what the wizard wrote.
|
|
532
|
+
(0, envSources_1.loadAidenEnvFile)(paths.envFile);
|
|
357
533
|
await config.load();
|
|
358
534
|
}
|
|
359
535
|
const providerId = cliOpts.provider ??
|
|
@@ -385,19 +561,32 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
385
561
|
const credentialResolver = new credentialResolver_1.CredentialResolver(paths.authJson);
|
|
386
562
|
const resolver = new runtimeResolver_1.RuntimeResolver(credentialResolver);
|
|
387
563
|
let adapter;
|
|
388
|
-
|
|
389
|
-
|
|
564
|
+
if (exploreMode) {
|
|
565
|
+
// Phase 30.2.1 — wizard skipped. Use a NullAdapter so AidenAgent
|
|
566
|
+
// construction succeeds; ChatSession will intercept chat attempts
|
|
567
|
+
// BEFORE calling the adapter and surface the friendly message.
|
|
568
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
569
|
+
const { NullAdapter } = require('../../providers/v4/nullAdapter');
|
|
570
|
+
adapter = new NullAdapter();
|
|
390
571
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
572
|
+
else {
|
|
573
|
+
try {
|
|
574
|
+
adapter = await resolver.resolve({ providerId, modelId, config, paths });
|
|
575
|
+
}
|
|
576
|
+
catch (err) {
|
|
577
|
+
display.printError(`Could not resolve provider '${providerId}' / model '${modelId}': ${err.message}`, 'Run `aiden model` to pick a valid provider, or `aiden doctor`.');
|
|
578
|
+
process.exit(1);
|
|
579
|
+
}
|
|
394
580
|
}
|
|
395
581
|
// Phase 16b.1: wrap chat_completions providers in a FallbackAdapter so
|
|
396
582
|
// 429s on Groq slot 1 transparently retry Groq slot 2/3 and Together.
|
|
397
583
|
// Only activates when there's at least one *additional* slot configured
|
|
398
584
|
// beyond the primary — otherwise the wrapper would just rethrow.
|
|
585
|
+
// Phase 30.2.1: skip in explore mode — wrapping a NullAdapter in
|
|
586
|
+
// FallbackAdapter would just defer the friendly error one layer.
|
|
399
587
|
let fallbackAdapter = null;
|
|
400
|
-
if (
|
|
588
|
+
if (!exploreMode &&
|
|
589
|
+
adapter.apiMode === 'chat_completions' &&
|
|
401
590
|
(providerId === 'groq' || providerId === 'together')) {
|
|
402
591
|
const slots = buildAgentFallbackSlots(adapter, providerId, modelId);
|
|
403
592
|
const reachable = slots.filter((s) => s.keyPresent);
|
|
@@ -698,6 +887,14 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
698
887
|
personalityOverlay: activeOverlay,
|
|
699
888
|
modelId,
|
|
700
889
|
};
|
|
890
|
+
// ── Phase v4.1-skill-mining ──────────────────────────────────────────
|
|
891
|
+
// Construct the miner once — it owns its in-memory session counter +
|
|
892
|
+
// CandidateStore handle. Skipped entirely in MCP serve mode (the
|
|
893
|
+
// serve binary doesn't run the agent loop the same way and shouldn't
|
|
894
|
+
// mutate skill state from inside JSON-RPC handling).
|
|
895
|
+
const skillMiner = (0, uiBuild_2.isMcpServeMode)()
|
|
896
|
+
? undefined
|
|
897
|
+
: new skillMiner_1.SkillMiner({ auxiliaryClient });
|
|
701
898
|
// ── Build agent with all moat layers attached ────────────────────────
|
|
702
899
|
const agent = new aidenAgent_1.AidenAgent({
|
|
703
900
|
provider: adapter,
|
|
@@ -708,6 +905,13 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
708
905
|
plannerGuard,
|
|
709
906
|
honestyEnforcement,
|
|
710
907
|
skillTeacher,
|
|
908
|
+
skillMiner,
|
|
909
|
+
onSkillCandidate: (candidate) => {
|
|
910
|
+
try {
|
|
911
|
+
callbacks.onSkillCandidate?.(candidate);
|
|
912
|
+
}
|
|
913
|
+
catch { /* notification must not break the turn */ }
|
|
914
|
+
},
|
|
711
915
|
// Phase 23.5: tool event rows. CliCallbacks.onToolCall
|
|
712
916
|
// emits a single line per call — `· tool <name> <args> [running]`
|
|
713
917
|
// mutates to `[ok 220ms]` / `[fail 1.4s]` / `[blocked]` on resolve.
|
|
@@ -761,6 +965,236 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
761
965
|
memoryManager.onMutation((file) => {
|
|
762
966
|
agent.markMemoryDirty(file === 'user' ? 'user' : 'memory');
|
|
763
967
|
});
|
|
968
|
+
// ── Phase v4.1-subagent.1 — subagent_fanout wiring is below
|
|
969
|
+
// (after `bootLogger` is declared and the gateway processor is set
|
|
970
|
+
// up). Stub registered at boot is replaced there with the real
|
|
971
|
+
// closures over adapter / sessionManager / promptBuilder / etc.
|
|
972
|
+
// Phase v4.1-1.3a — boot a unified mode-aware Logger ('cli-interactive'
|
|
973
|
+
// mode = no stdout sinks, REPL is sacred). Declared here so the gateway
|
|
974
|
+
// processor + channel manager (later in the function) can both attach
|
|
975
|
+
// scoped child loggers to the same root.
|
|
976
|
+
const { logger: bootLogger } = (0, logger_1.createBootLogger)({
|
|
977
|
+
mode: 'cli-interactive',
|
|
978
|
+
logsDir: paths.logsDir,
|
|
979
|
+
});
|
|
980
|
+
// Wire the gateway singleton's logger BEFORE registering its processor
|
|
981
|
+
// so register / unregister channel events are scoped correctly.
|
|
982
|
+
gateway_1.gateway.attachLogger(bootLogger.child('gateway'));
|
|
983
|
+
// ── Phase v4.1-subagent.1 — replace subagent_fanout stub with wired version
|
|
984
|
+
//
|
|
985
|
+
// tools/v4/index.ts registers a stub at boot so the schema is visible
|
|
986
|
+
// to MCP / /tools immediately. NOW that the runtime has a provider
|
|
987
|
+
// adapter, an active model, sessions, memory, and a built agent, we
|
|
988
|
+
// re-register subagent_fanout with the real callbacks so live calls
|
|
989
|
+
// (from REPL, MCP, or `aiden fanout`) actually execute children
|
|
990
|
+
// instead of returning the "not wired" stub error.
|
|
991
|
+
//
|
|
992
|
+
// The closures capture parent runtime handles. Each fanout call
|
|
993
|
+
// builds a fresh AidenAgent per child — the AidenAgent constructor
|
|
994
|
+
// is cheap (per-instance state, no module singletons), so N=5
|
|
995
|
+
// children = 5 instances + 5 cloned FallbackAdapters. The heavy
|
|
996
|
+
// shared subsystems (registry, skillLoader, paths, memoryManager,
|
|
997
|
+
// promptBuilder, promptBuilderOptions) are read-only and pass by
|
|
998
|
+
// reference.
|
|
999
|
+
toolRegistry.register((0, index_1.makeSubagentFanoutTool)({
|
|
1000
|
+
logger: bootLogger.child('subagent'),
|
|
1001
|
+
resolveActiveModel: () => ({ providerId, modelId }),
|
|
1002
|
+
aggregatorAdapter: adapter,
|
|
1003
|
+
resolveProviders: () => {
|
|
1004
|
+
// When the parent uses FallbackAdapter, expose every key-present
|
|
1005
|
+
// slot's (providerId, modelId) so rotation can spread children
|
|
1006
|
+
// across distinct providers / keys. Otherwise just the active
|
|
1007
|
+
// provider+model pair — single-provider rotation falls back to
|
|
1008
|
+
// slot rotation within the FallbackAdapter at run time, OR to
|
|
1009
|
+
// pure same-provider sampling (singleProviderWarning fires).
|
|
1010
|
+
if (adapter instanceof providerFallback_1.FallbackAdapter) {
|
|
1011
|
+
const diag = adapter.getDiagnostics();
|
|
1012
|
+
const live = diag.slots.filter((s) => s.keyPresent);
|
|
1013
|
+
if (live.length > 0) {
|
|
1014
|
+
return live.map((s) => ({
|
|
1015
|
+
providerId: s.providerId,
|
|
1016
|
+
modelId: s.modelId,
|
|
1017
|
+
label: s.id,
|
|
1018
|
+
}));
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return [{ providerId, modelId }];
|
|
1022
|
+
},
|
|
1023
|
+
runChild: async (childOpts) => {
|
|
1024
|
+
// Per-child context: paths / skillLoader / memoryManager / processes
|
|
1025
|
+
// are SAFE to share (read-only or per-call by design). The approval
|
|
1026
|
+
// engine is intentionally OMITTED — N children competing for one
|
|
1027
|
+
// stdin REPL would deadlock.
|
|
1028
|
+
const childCtx = {
|
|
1029
|
+
cwd: process.cwd(),
|
|
1030
|
+
paths,
|
|
1031
|
+
sessions: sessionManager,
|
|
1032
|
+
memory: memoryManager,
|
|
1033
|
+
skillLoader,
|
|
1034
|
+
// approvalEngine, ssrfProtection, tirithScanner, memoryGuard:
|
|
1035
|
+
// SSRF + Tirith would be safe to share but adding them now
|
|
1036
|
+
// expands the per-child surface; keep lean for v4.1-subagent.1
|
|
1037
|
+
// and revisit when fanout actually exercises network or shell
|
|
1038
|
+
// tools (gated by ALLOW_DESTRUCTIVE).
|
|
1039
|
+
};
|
|
1040
|
+
// Filter the tool surface. Default-safe: read-only tools only.
|
|
1041
|
+
// AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE=1 mirrors the MCP env from
|
|
1042
|
+
// v4.1-mcp — predictable, env-driven.
|
|
1043
|
+
const allowDestructive = process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === '1' ||
|
|
1044
|
+
process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === 'true';
|
|
1045
|
+
const childToolNames = [];
|
|
1046
|
+
for (const name of toolRegistry.list()) {
|
|
1047
|
+
const h = toolRegistry.get(name);
|
|
1048
|
+
if (!h)
|
|
1049
|
+
continue;
|
|
1050
|
+
if (h.mutates && !allowDestructive)
|
|
1051
|
+
continue;
|
|
1052
|
+
// Avoid recursive fanout this phase — children cannot spawn
|
|
1053
|
+
// their own children. Recursion was capped at depth 1 by
|
|
1054
|
+
// default in prior multi-agent systems for the same reason;
|
|
1055
|
+
// v3 starved nested spawns.
|
|
1056
|
+
if (name === 'subagent_fanout')
|
|
1057
|
+
continue;
|
|
1058
|
+
childToolNames.push(name);
|
|
1059
|
+
}
|
|
1060
|
+
const childExecutor = toolRegistry.buildExecutor(childCtx);
|
|
1061
|
+
const childTools = childToolNames
|
|
1062
|
+
.map((n) => toolRegistry.get(n)?.schema)
|
|
1063
|
+
.filter((s) => !!s);
|
|
1064
|
+
// Provider isolation: clone the FallbackAdapter so per-child
|
|
1065
|
+
// rate-limit state doesn't pollute the parent or siblings.
|
|
1066
|
+
// Non-Fallback adapters are stateless by spec (providers/v4/
|
|
1067
|
+
// types.ts:190) so direct reuse is safe.
|
|
1068
|
+
const childProvider = adapter instanceof providerFallback_1.FallbackAdapter
|
|
1069
|
+
? adapter.clone()
|
|
1070
|
+
: adapter;
|
|
1071
|
+
// Build per-child AidenAgent. Skip the moat layers (PlannerGuard,
|
|
1072
|
+
// HonestyEnforcement, SkillTeacher, SkillEnforcementTracker) —
|
|
1073
|
+
// they're parent-loop concerns and add cost without value at the
|
|
1074
|
+
// child scale. Skip promptBuilder too: children get a SHORT
|
|
1075
|
+
// system prompt (brief identity + role) instead of the parent's
|
|
1076
|
+
// full SOUL.md + 72-skills inventory + memory snapshot. The
|
|
1077
|
+
// tradeoff is deliberate — children answer the GOAL, not "be
|
|
1078
|
+
// Aiden". With the full prompt, trivial queries take 30s+ for
|
|
1079
|
+
// children to generate verbose self-introductions; the lean
|
|
1080
|
+
// child prompt brings n=2 trivial fanouts under 12s. Parent
|
|
1081
|
+
// should pass any context children genuinely need via the
|
|
1082
|
+
// `query` / `tasks[].context` argument.
|
|
1083
|
+
const child = new aidenAgent_1.AidenAgent({
|
|
1084
|
+
provider: childProvider,
|
|
1085
|
+
tools: childTools,
|
|
1086
|
+
toolExecutor: childExecutor,
|
|
1087
|
+
maxTurns: childOpts.maxIterations,
|
|
1088
|
+
providerId: childOpts.provider.providerId,
|
|
1089
|
+
modelId: childOpts.provider.modelId,
|
|
1090
|
+
// No promptBuilder — childSystemPrompt prepended manually below.
|
|
1091
|
+
// No fallback strategy — child failures bubble up to the
|
|
1092
|
+
// orchestrator, which surfaces them in the result envelope.
|
|
1093
|
+
});
|
|
1094
|
+
// Honour the abort signal — if the parent aborts mid-call (or the
|
|
1095
|
+
// per-child timeout fires), short-circuit before dispatching to
|
|
1096
|
+
// the provider. AidenAgent doesn't take an AbortSignal directly;
|
|
1097
|
+
// the AbortController plumbing through fetch is the
|
|
1098
|
+
// v4.1-subagent.2 / v4.2 hardening pass. Pre-check here for the
|
|
1099
|
+
// synchronous path.
|
|
1100
|
+
if (childOpts.signal.aborted) {
|
|
1101
|
+
throw new Error('aborted before dispatch');
|
|
1102
|
+
}
|
|
1103
|
+
// Brief, role-aware system prompt — drops 5KB+ of Aiden identity
|
|
1104
|
+
// boilerplate that would otherwise inflate every child to 30s+
|
|
1105
|
+
// wall-clock for a trivial query. The parent agent retains the
|
|
1106
|
+
// full prompt when it's the orchestrator; children answer the
|
|
1107
|
+
// goal directly.
|
|
1108
|
+
const roleLine = childOpts.role
|
|
1109
|
+
? `Role: ${childOpts.role}. `
|
|
1110
|
+
: '';
|
|
1111
|
+
const childSystemPrompt = `You are one of ${childOpts.index >= 0 ? 'N' : '?'} parallel subagents. ` +
|
|
1112
|
+
`${roleLine}Answer the user's request concisely. Use available tools when ` +
|
|
1113
|
+
`the answer requires real-world information you don't have memorized.`;
|
|
1114
|
+
const history = [
|
|
1115
|
+
{ role: 'system', content: childSystemPrompt },
|
|
1116
|
+
{ role: 'user', content: childOpts.prompt },
|
|
1117
|
+
];
|
|
1118
|
+
const result = await child.runConversation(history);
|
|
1119
|
+
return result.finalContent;
|
|
1120
|
+
},
|
|
1121
|
+
}));
|
|
1122
|
+
bootLogger.child('subagent').info('subagent_fanout: wired (replaces stub)', {
|
|
1123
|
+
providerId,
|
|
1124
|
+
modelId,
|
|
1125
|
+
fallback: adapter instanceof providerFallback_1.FallbackAdapter ? 'FallbackAdapter' : 'direct',
|
|
1126
|
+
});
|
|
1127
|
+
// ── Phase v4.1-2.1: gateway message processor ────────────────────
|
|
1128
|
+
//
|
|
1129
|
+
// Channel adapters call `gateway.routeMessage(...)` for every inbound
|
|
1130
|
+
// message; the gateway then invokes the registered processor — that's
|
|
1131
|
+
// the bridge from channel-side I/O to the agent loop. `aiden serve`
|
|
1132
|
+
// wires its own processor in `api/server.ts` (HTTP-hops to /api/chat
|
|
1133
|
+
// because Express is already up). The CLI host (Phase v4.1-1.1)
|
|
1134
|
+
// never had one, so every Telegram inbound was throwing
|
|
1135
|
+
// "No message processor registered" and the user saw the
|
|
1136
|
+
// friendly-fallback "Something went wrong" reply.
|
|
1137
|
+
//
|
|
1138
|
+
// The closure mirrors the api/server processor's intent — one agent
|
|
1139
|
+
// turn per inbound — but invokes `agent.runConversation()` directly
|
|
1140
|
+
// instead of round-tripping through HTTP. Per-(channel, channelId)
|
|
1141
|
+
// history persists through the same SessionStore the REPL uses, so
|
|
1142
|
+
// a Telegram conversation accumulates context across messages, and
|
|
1143
|
+
// a future `/sessions` listing surfaces those threads alongside REPL
|
|
1144
|
+
// sessions.
|
|
1145
|
+
const gatewayProcessorLog = bootLogger.child('gateway.processor');
|
|
1146
|
+
// gateway sessionId (`session_<ts>`) → sessionStore session id.
|
|
1147
|
+
// In-memory only; restart re-creates a fresh sessionStore session
|
|
1148
|
+
// for the same channel+user pair.
|
|
1149
|
+
const gatewaySessionMap = new Map();
|
|
1150
|
+
gateway_1.gateway.setProcessor(async (message) => {
|
|
1151
|
+
try {
|
|
1152
|
+
// 1. Resolve a sessionStore session for this gateway session.
|
|
1153
|
+
// sessionManager.startSession opens a new row; we cache the
|
|
1154
|
+
// mapping so subsequent messages from the same chat append
|
|
1155
|
+
// to the same history.
|
|
1156
|
+
const gatewaySid = message.sessionId
|
|
1157
|
+
?? `${message.channel}_${message.channelId}`;
|
|
1158
|
+
let storeSid = gatewaySessionMap.get(gatewaySid);
|
|
1159
|
+
if (!storeSid) {
|
|
1160
|
+
const created = sessionManager.startSession({
|
|
1161
|
+
providerId,
|
|
1162
|
+
modelId,
|
|
1163
|
+
title: `${message.channel}:${message.channelId}`,
|
|
1164
|
+
});
|
|
1165
|
+
storeSid = created.id;
|
|
1166
|
+
gatewaySessionMap.set(gatewaySid, storeSid);
|
|
1167
|
+
}
|
|
1168
|
+
// 2. Load past messages for this session and append the new
|
|
1169
|
+
// user turn so the agent sees full context. We drop tool /
|
|
1170
|
+
// system rows on load — the agent's prompt builder rebuilds
|
|
1171
|
+
// those from scratch each call.
|
|
1172
|
+
// Provider Message union has tool-specific variants; we only
|
|
1173
|
+
// load the user/assistant turns and cast to satisfy the union.
|
|
1174
|
+
// Tool-call replay across adapter restarts isn't a feature we
|
|
1175
|
+
// need for chat-channel UX (Phase v4.1-2.1 scope).
|
|
1176
|
+
const past = store.getMessages(storeSid)
|
|
1177
|
+
.filter((r) => r.role === 'user' || r.role === 'assistant')
|
|
1178
|
+
.map((r) => ({ role: r.role, content: r.content }));
|
|
1179
|
+
const userTurn = { role: 'user', content: message.text };
|
|
1180
|
+
const history = [...past, userTurn];
|
|
1181
|
+
// 3. Run one agent turn.
|
|
1182
|
+
const result = await agent.runConversation(history);
|
|
1183
|
+
// 4. Persist the new tail (everything past the loaded history) so
|
|
1184
|
+
// the next inbound resumes seamlessly. Mirror chatSession's
|
|
1185
|
+
// record-turn slice convention.
|
|
1186
|
+
const newSlice = result.messages.slice(history.length - 1);
|
|
1187
|
+
sessionManager.recordTurn(storeSid, newSlice, result.totalUsage);
|
|
1188
|
+
return result.finalContent || '(no response)';
|
|
1189
|
+
}
|
|
1190
|
+
catch (err) {
|
|
1191
|
+
// Diagnostics route through the unified logger — nothing reaches
|
|
1192
|
+
// stdout / stderr so the REPL stays clean. The gateway's own
|
|
1193
|
+
// catch returns the friendly fallback to the user.
|
|
1194
|
+
gatewayProcessorLog.error(`processor failed: ${err?.message ?? String(err)}`, { channel: message.channel, channelId: message.channelId });
|
|
1195
|
+
throw err;
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
764
1198
|
// Command registry.
|
|
765
1199
|
const commandRegistry = new commandRegistry_1.CommandRegistry();
|
|
766
1200
|
for (const cmd of commands_1.allCommands)
|
|
@@ -787,6 +1221,59 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
787
1221
|
catch (err) {
|
|
788
1222
|
display.dim(`(skill commands unavailable: ${err.message})`);
|
|
789
1223
|
}
|
|
1224
|
+
// ── Phase v4.1-1.1: CLI-side channel manager ──────────────────────
|
|
1225
|
+
//
|
|
1226
|
+
// Build a manager local to this CLI process and register the env-driven
|
|
1227
|
+
// adapters that don't need an Express app (Telegram is the only one in
|
|
1228
|
+
// Phase 1 — Discord/Slack/etc. land iteratively). Webhook/Twilio stay
|
|
1229
|
+
// out of CLI scope because they need an HTTP listener.
|
|
1230
|
+
//
|
|
1231
|
+
// Conflict guard: when `aiden serve` is already running locally, BOTH
|
|
1232
|
+
// processes would race the same Telegram bot's long-poll, and the
|
|
1233
|
+
// server would lose every other update with a 409. Probe localhost:4200
|
|
1234
|
+
// briefly; if the server answers, skip auto-start in CLI but still
|
|
1235
|
+
// build the manager so /channel list / status work as a read-only view.
|
|
1236
|
+
//
|
|
1237
|
+
// Phase v4.1-1.3a — `bootLogger` + gateway logger were attached
|
|
1238
|
+
// earlier (right after the agent was constructed) so the gateway
|
|
1239
|
+
// processor closure could share the same scoped sink chain. Here
|
|
1240
|
+
// we just plumb the channels child logger into the manager.
|
|
1241
|
+
const channelManager = new manager_1.ChannelManager({ logger: bootLogger.child('channels') });
|
|
1242
|
+
// Phase v4.1-4.1 — wire active-model lookup into the Telegram
|
|
1243
|
+
// adapter. The closure captures `providerId` / `modelId` from
|
|
1244
|
+
// this scope so the photo-vision module can decide native vs
|
|
1245
|
+
// text routing (and pdf-extract can compute the truncation
|
|
1246
|
+
// budget) using the SAME model the chat path already uses.
|
|
1247
|
+
channelManager.register(new telegram_1.TelegramAdapter({
|
|
1248
|
+
activeModelInfo: () => ({
|
|
1249
|
+
providerId,
|
|
1250
|
+
modelId,
|
|
1251
|
+
contextWindow: (0, modelCatalog_1.findModel)(providerId, modelId)?.contextLength,
|
|
1252
|
+
}),
|
|
1253
|
+
}));
|
|
1254
|
+
let serverIsHosting = false;
|
|
1255
|
+
try {
|
|
1256
|
+
const ctrl = new AbortController();
|
|
1257
|
+
const timer = setTimeout(() => ctrl.abort(), 250);
|
|
1258
|
+
const probe = await fetch('http://127.0.0.1:4200/health', {
|
|
1259
|
+
signal: ctrl.signal,
|
|
1260
|
+
}).catch(() => null);
|
|
1261
|
+
clearTimeout(timer);
|
|
1262
|
+
if (probe && (probe.status >= 200 && probe.status < 500)) {
|
|
1263
|
+
serverIsHosting = true;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
catch {
|
|
1267
|
+
/* no server — fall through to CLI-host */
|
|
1268
|
+
}
|
|
1269
|
+
if (serverIsHosting) {
|
|
1270
|
+
display.dim('[channels] aiden serve is running locally — channel adapters hosted there, /channel commands stay read-only.');
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
// start() resolves quickly when no token is set (logs "Disabled" and
|
|
1274
|
+
// returns). Errors don't crash boot.
|
|
1275
|
+
channelManager.startAll().catch((e) => display.dim(`[channels] startAll error: ${e.message}`));
|
|
1276
|
+
}
|
|
790
1277
|
return {
|
|
791
1278
|
paths,
|
|
792
1279
|
config,
|
|
@@ -820,6 +1307,8 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
820
1307
|
fallbackAdapter,
|
|
821
1308
|
personalityManager,
|
|
822
1309
|
pluginLoader,
|
|
1310
|
+
exploreMode,
|
|
1311
|
+
channelManager,
|
|
823
1312
|
};
|
|
824
1313
|
}
|
|
825
1314
|
async function runInteractiveChat(cliOpts, opts) {
|
|
@@ -846,6 +1335,12 @@ async function runInteractiveChat(cliOpts, opts) {
|
|
|
846
1335
|
paths: runtime.paths,
|
|
847
1336
|
personalityManager: runtime.personalityManager,
|
|
848
1337
|
pluginLoader: runtime.pluginLoader,
|
|
1338
|
+
// Phase 30.2.1 — boot card renders "model not configured" and
|
|
1339
|
+
// chat attempts get the friendly NotConfiguredError message.
|
|
1340
|
+
unconfigured: runtime.exploreMode,
|
|
1341
|
+
// Phase v4.1-1.1 — live ChannelManager so /channel commands can
|
|
1342
|
+
// list, add, remove, and inspect adapters without an external server.
|
|
1343
|
+
channelManager: runtime.channelManager,
|
|
849
1344
|
};
|
|
850
1345
|
if (cliOpts.tui) {
|
|
851
1346
|
await (0, aidenTUI_1.runTuiMode)({
|
|
@@ -863,6 +1358,10 @@ async function runInteractiveChat(cliOpts, opts) {
|
|
|
863
1358
|
// Phase 17 Task 5: fire onTeardown so plugins (e.g. CDP browser) can
|
|
864
1359
|
// close their resources before the process exits.
|
|
865
1360
|
await runtime.pluginLoader.teardown().catch(() => undefined);
|
|
1361
|
+
// Phase v4.1-1.1 — stop polling adapters before exit so Telegram's
|
|
1362
|
+
// long-poll TCP connection closes cleanly. stopAll() is best-effort
|
|
1363
|
+
// and never throws.
|
|
1364
|
+
await runtime.channelManager.stopAll().catch(() => undefined);
|
|
866
1365
|
runtime.store.close?.();
|
|
867
1366
|
}
|
|
868
1367
|
// ─── setup ─────────────────────────────────────────────────────────────
|