aiden-runtime 4.0.2 → 4.1.1
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 +19 -11
- package/config/hardware.json +2 -2
- package/dist/api/server.js +50 -52
- package/dist/cli/v4/aidenCLI.js +424 -7
- 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 +256 -55
- 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 +16 -1
- package/dist/cli/v4/commands/mcp.js +358 -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 +297 -13
- package/dist/cli/v4/doctor.js +102 -1
- package/dist/cli/v4/doctorLiveness.js +329 -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/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/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 +118 -0
- package/dist/core/v4/skillMining/proposalBuilder.js +140 -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/moat/dangerousPatterns.js +1 -1
- package/dist/providers/v4/codexResponsesAdapter.js +7 -2
- package/dist/providers/v4/errors.js +51 -1
- package/dist/providers/v4/ollamaPromptToolsAdapter.js +9 -2
- package/dist/tools/v4/index.js +32 -1
- package/dist/tools/v4/subagent/subagentFanout.js +190 -0
- package/package.json +11 -2
package/dist/cli/v4/aidenCLI.js
CHANGED
|
@@ -62,7 +62,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
62
62
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
63
63
|
};
|
|
64
64
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
65
|
-
exports.getEnvSource = exports.loadAidenEnvFile = void 0;
|
|
65
|
+
exports.getEnvSource = exports.loadAidenEnvFile = exports.AIDEN_UI_BUILD = void 0;
|
|
66
66
|
exports.main = main;
|
|
67
67
|
exports.buildAgentRuntime = buildAgentRuntime;
|
|
68
68
|
exports.runInteractiveChat = runInteractiveChat;
|
|
@@ -81,6 +81,10 @@ const display_1 = require("./display");
|
|
|
81
81
|
const skinEngine_1 = require("./skinEngine");
|
|
82
82
|
const commandRegistry_1 = require("./commandRegistry");
|
|
83
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; } });
|
|
84
88
|
const setupWizard_1 = require("./setupWizard");
|
|
85
89
|
const doctor_1 = require("./doctor");
|
|
86
90
|
const modelPicker_1 = require("./commands/modelPicker");
|
|
@@ -92,6 +96,7 @@ const sessionStore_1 = require("../../core/v4/sessionStore");
|
|
|
92
96
|
const sessionManager_1 = require("../../core/v4/sessionManager");
|
|
93
97
|
const toolRegistry_1 = require("../../core/v4/toolRegistry");
|
|
94
98
|
const skillLoader_1 = require("../../core/v4/skillLoader");
|
|
99
|
+
const index_1 = require("../../tools/v4/index");
|
|
95
100
|
const skillCommands_1 = require("../../core/v4/skillCommands");
|
|
96
101
|
const aidenAgent_1 = require("../../core/v4/aidenAgent");
|
|
97
102
|
const promptBuilder_1 = require("../../core/v4/promptBuilder");
|
|
@@ -102,18 +107,31 @@ const approvalEngine_1 = require("../../moat/approvalEngine");
|
|
|
102
107
|
const plannerGuard_1 = require("../../moat/plannerGuard");
|
|
103
108
|
const honestyEnforcement_1 = require("../../moat/honestyEnforcement");
|
|
104
109
|
const skillTeacher_1 = require("../../moat/skillTeacher");
|
|
110
|
+
const skillMiner_1 = require("../../core/v4/skillMining/skillMiner");
|
|
111
|
+
const uiBuild_2 = require("./uiBuild");
|
|
105
112
|
const memoryGuard_1 = require("../../moat/memoryGuard");
|
|
106
113
|
const ssrfProtection_1 = require("../../moat/ssrfProtection");
|
|
107
114
|
const tirithScanner_1 = require("../../moat/tirithScanner");
|
|
108
115
|
const credentialResolver_1 = require("../../providers/v4/credentialResolver");
|
|
109
116
|
const runtimeResolver_1 = require("../../providers/v4/runtimeResolver");
|
|
110
117
|
const chatCompletionsAdapter_1 = require("../../providers/v4/chatCompletionsAdapter");
|
|
118
|
+
const modelCatalog_1 = require("../../providers/v4/modelCatalog");
|
|
111
119
|
const providerFallback_1 = require("../../core/v4/providerFallback");
|
|
112
120
|
const skillBundledRestore_1 = require("../../core/v4/skillBundledRestore");
|
|
113
121
|
const providerDetection_1 = require("../../core/v4/firstRun/providerDetection");
|
|
114
122
|
const aidenLogger_1 = require("../../core/v4/aidenLogger");
|
|
115
123
|
const plugins_1 = require("../../core/v4/plugins");
|
|
116
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");
|
|
117
135
|
const v4_1 = require("../../tools/v4");
|
|
118
136
|
const mcpSetup_1 = require("../../tools/v4/mcpSetup");
|
|
119
137
|
const skillCommandHandler_1 = require("./commands/skillCommandHandler");
|
|
@@ -196,7 +214,24 @@ async function main(argv, opts = {}) {
|
|
|
196
214
|
.option('--planner-guard <mode>', 'PlannerGuard mode: off | rule_based | llm_classified')
|
|
197
215
|
.option('--honesty <mode>', 'HonestyEnforcement mode: off | detect | enforce')
|
|
198
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')
|
|
199
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';
|
|
200
235
|
const cliOpts = program.opts();
|
|
201
236
|
if (opts.runChatHook) {
|
|
202
237
|
await opts.runChatHook(cliOpts);
|
|
@@ -204,6 +239,16 @@ async function main(argv, opts = {}) {
|
|
|
204
239
|
}
|
|
205
240
|
await runInteractiveChat(cliOpts, opts);
|
|
206
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
|
+
});
|
|
207
252
|
program
|
|
208
253
|
.command('setup')
|
|
209
254
|
.description('Run the setup wizard (provider + model + API key)')
|
|
@@ -237,12 +282,13 @@ async function main(argv, opts = {}) {
|
|
|
237
282
|
program
|
|
238
283
|
.command('doctor')
|
|
239
284
|
.description('Run diagnostic checks')
|
|
240
|
-
.
|
|
285
|
+
.option('--providers', 'Also ping each configured / authed provider and report live status (deep check). Slower; useful before shipping or when a provider regression is suspected.')
|
|
286
|
+
.action(async (cmdOpts) => {
|
|
241
287
|
if (opts.runDoctorHook) {
|
|
242
288
|
await opts.runDoctorHook();
|
|
243
289
|
return;
|
|
244
290
|
}
|
|
245
|
-
await (0, doctor_1.runDoctorCli)();
|
|
291
|
+
await (0, doctor_1.runDoctorCli)({ liveness: cmdOpts.providers === true });
|
|
246
292
|
});
|
|
247
293
|
program
|
|
248
294
|
.command('sessions <action> [arg]')
|
|
@@ -266,17 +312,82 @@ async function main(argv, opts = {}) {
|
|
|
266
312
|
});
|
|
267
313
|
program
|
|
268
314
|
.command('mcp <action>')
|
|
269
|
-
.description('
|
|
315
|
+
.description('MCP server mode (Phase v4.1-mcp). Actions: serve, status, tools.')
|
|
270
316
|
.action(async (action) => {
|
|
271
317
|
if (opts.runMcpHook) {
|
|
272
318
|
await opts.runMcpHook(action);
|
|
273
319
|
return;
|
|
274
320
|
}
|
|
275
|
-
|
|
276
|
-
|
|
321
|
+
// Lazy-load so the rest of the CLI does not pay the import cost
|
|
322
|
+
// for `setup`, `doctor`, `model`, etc. on every invocation.
|
|
323
|
+
const { runMcpSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/mcp')));
|
|
324
|
+
const code = await runMcpSubcommand(action, {
|
|
325
|
+
writeOut: opts.writeOut,
|
|
326
|
+
writeErr: (t) => process.stderr.write(t),
|
|
327
|
+
});
|
|
328
|
+
if (code !== 0)
|
|
329
|
+
process.exit(code);
|
|
330
|
+
});
|
|
331
|
+
program
|
|
332
|
+
.command('voice [args...]')
|
|
333
|
+
.description('Voice diagnostics + one-shot TTS / transcribe (Phase v4.1-voice-cli). ' +
|
|
334
|
+
'Usage: aiden voice doctor | tts "<text>" | transcribe <file>')
|
|
335
|
+
.allowUnknownOption()
|
|
336
|
+
.action(async (args) => {
|
|
337
|
+
const { runVoiceSubcommand } = await Promise.resolve().then(() => __importStar(require('./voiceCli')));
|
|
338
|
+
const action = (args[0] ?? 'doctor').toLowerCase();
|
|
339
|
+
const rest = args.slice(1);
|
|
340
|
+
const code = await runVoiceSubcommand(action, rest, {
|
|
341
|
+
writeOut: opts.writeOut,
|
|
342
|
+
writeErr: (t) => process.stderr.write(t),
|
|
343
|
+
});
|
|
344
|
+
if (code !== 0)
|
|
345
|
+
process.exit(code);
|
|
346
|
+
});
|
|
347
|
+
program
|
|
348
|
+
.command('subagent <action>')
|
|
349
|
+
.description('Subagent fanout diagnostics (Phase v4.1-subagent). Actions: status, tools.')
|
|
350
|
+
.action(async (action) => {
|
|
351
|
+
const { runSubagentSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/subagent')));
|
|
352
|
+
const code = await runSubagentSubcommand(action, {
|
|
353
|
+
writeOut: opts.writeOut,
|
|
354
|
+
writeErr: (t) => process.stderr.write(t),
|
|
355
|
+
});
|
|
356
|
+
if (code !== 0)
|
|
357
|
+
process.exit(code);
|
|
358
|
+
});
|
|
359
|
+
program
|
|
360
|
+
.command('fanout [args...]')
|
|
361
|
+
.description('Run a parallel agent fanout (Phase v4.1-subagent). ' +
|
|
362
|
+
'Usage: aiden fanout "<query>" --n=3 --merge=combine [--mode=ensemble] [--dry-run]')
|
|
363
|
+
.allowUnknownOption()
|
|
364
|
+
.action(async (args) => {
|
|
365
|
+
const { runFanoutCli } = await Promise.resolve().then(() => __importStar(require('./commands/fanout')));
|
|
366
|
+
const code = await runFanoutCli(args, {
|
|
367
|
+
writeOut: opts.writeOut,
|
|
368
|
+
writeErr: (t) => process.stderr.write(t),
|
|
369
|
+
});
|
|
370
|
+
if (code !== 0)
|
|
371
|
+
process.exit(code);
|
|
277
372
|
});
|
|
278
373
|
// v4.1 placeholders. (`tui` graduated to a real flag in Phase 15.)
|
|
279
|
-
|
|
374
|
+
program
|
|
375
|
+
.command('cron [args...]')
|
|
376
|
+
.description('Cron diagnostics + one-shot list / run (Phase v4.1 hardened cron). ' +
|
|
377
|
+
'Usage: aiden cron status | list | run <id>')
|
|
378
|
+
.allowUnknownOption()
|
|
379
|
+
.action(async (args) => {
|
|
380
|
+
const { runCronSubcommand } = await Promise.resolve().then(() => __importStar(require('./cronCli')));
|
|
381
|
+
const action = (args[0] ?? 'status').toLowerCase();
|
|
382
|
+
const rest = args.slice(1);
|
|
383
|
+
const code = await runCronSubcommand(action, rest, {
|
|
384
|
+
writeOut: opts.writeOut,
|
|
385
|
+
writeErr: (t) => process.stderr.write(t),
|
|
386
|
+
});
|
|
387
|
+
if (code !== 0)
|
|
388
|
+
process.exit(code);
|
|
389
|
+
});
|
|
390
|
+
for (const cmd of ['batch', 'gateway', 'pairing', 'update']) {
|
|
280
391
|
program
|
|
281
392
|
.command(cmd)
|
|
282
393
|
.description(`(deferred to v4.1)`)
|
|
@@ -777,6 +888,14 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
777
888
|
personalityOverlay: activeOverlay,
|
|
778
889
|
modelId,
|
|
779
890
|
};
|
|
891
|
+
// ── Phase v4.1-skill-mining ──────────────────────────────────────────
|
|
892
|
+
// Construct the miner once — it owns its in-memory session counter +
|
|
893
|
+
// CandidateStore handle. Skipped entirely in MCP serve mode (the
|
|
894
|
+
// serve binary doesn't run the agent loop the same way and shouldn't
|
|
895
|
+
// mutate skill state from inside JSON-RPC handling).
|
|
896
|
+
const skillMiner = (0, uiBuild_2.isMcpServeMode)()
|
|
897
|
+
? undefined
|
|
898
|
+
: new skillMiner_1.SkillMiner({ auxiliaryClient });
|
|
780
899
|
// ── Build agent with all moat layers attached ────────────────────────
|
|
781
900
|
const agent = new aidenAgent_1.AidenAgent({
|
|
782
901
|
provider: adapter,
|
|
@@ -787,6 +906,13 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
787
906
|
plannerGuard,
|
|
788
907
|
honestyEnforcement,
|
|
789
908
|
skillTeacher,
|
|
909
|
+
skillMiner,
|
|
910
|
+
onSkillCandidate: (candidate) => {
|
|
911
|
+
try {
|
|
912
|
+
callbacks.onSkillCandidate?.(candidate);
|
|
913
|
+
}
|
|
914
|
+
catch { /* notification must not break the turn */ }
|
|
915
|
+
},
|
|
790
916
|
// Phase 23.5: tool event rows. CliCallbacks.onToolCall
|
|
791
917
|
// emits a single line per call — `· tool <name> <args> [running]`
|
|
792
918
|
// mutates to `[ok 220ms]` / `[fail 1.4s]` / `[blocked]` on resolve.
|
|
@@ -840,6 +966,236 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
840
966
|
memoryManager.onMutation((file) => {
|
|
841
967
|
agent.markMemoryDirty(file === 'user' ? 'user' : 'memory');
|
|
842
968
|
});
|
|
969
|
+
// ── Phase v4.1-subagent.1 — subagent_fanout wiring is below
|
|
970
|
+
// (after `bootLogger` is declared and the gateway processor is set
|
|
971
|
+
// up). Stub registered at boot is replaced there with the real
|
|
972
|
+
// closures over adapter / sessionManager / promptBuilder / etc.
|
|
973
|
+
// Phase v4.1-1.3a — boot a unified mode-aware Logger ('cli-interactive'
|
|
974
|
+
// mode = no stdout sinks, REPL is sacred). Declared here so the gateway
|
|
975
|
+
// processor + channel manager (later in the function) can both attach
|
|
976
|
+
// scoped child loggers to the same root.
|
|
977
|
+
const { logger: bootLogger } = (0, logger_1.createBootLogger)({
|
|
978
|
+
mode: 'cli-interactive',
|
|
979
|
+
logsDir: paths.logsDir,
|
|
980
|
+
});
|
|
981
|
+
// Wire the gateway singleton's logger BEFORE registering its processor
|
|
982
|
+
// so register / unregister channel events are scoped correctly.
|
|
983
|
+
gateway_1.gateway.attachLogger(bootLogger.child('gateway'));
|
|
984
|
+
// ── Phase v4.1-subagent.1 — replace subagent_fanout stub with wired version
|
|
985
|
+
//
|
|
986
|
+
// tools/v4/index.ts registers a stub at boot so the schema is visible
|
|
987
|
+
// to MCP / /tools immediately. NOW that the runtime has a provider
|
|
988
|
+
// adapter, an active model, sessions, memory, and a built agent, we
|
|
989
|
+
// re-register subagent_fanout with the real callbacks so live calls
|
|
990
|
+
// (from REPL, MCP, or `aiden fanout`) actually execute children
|
|
991
|
+
// instead of returning the "not wired" stub error.
|
|
992
|
+
//
|
|
993
|
+
// The closures capture parent runtime handles. Each fanout call
|
|
994
|
+
// builds a fresh AidenAgent per child — the AidenAgent constructor
|
|
995
|
+
// is cheap (per-instance state, no module singletons), so N=5
|
|
996
|
+
// children = 5 instances + 5 cloned FallbackAdapters. The heavy
|
|
997
|
+
// shared subsystems (registry, skillLoader, paths, memoryManager,
|
|
998
|
+
// promptBuilder, promptBuilderOptions) are read-only and pass by
|
|
999
|
+
// reference.
|
|
1000
|
+
toolRegistry.register((0, index_1.makeSubagentFanoutTool)({
|
|
1001
|
+
logger: bootLogger.child('subagent'),
|
|
1002
|
+
resolveActiveModel: () => ({ providerId, modelId }),
|
|
1003
|
+
aggregatorAdapter: adapter,
|
|
1004
|
+
resolveProviders: () => {
|
|
1005
|
+
// When the parent uses FallbackAdapter, expose every key-present
|
|
1006
|
+
// slot's (providerId, modelId) so rotation can spread children
|
|
1007
|
+
// across distinct providers / keys. Otherwise just the active
|
|
1008
|
+
// provider+model pair — single-provider rotation falls back to
|
|
1009
|
+
// slot rotation within the FallbackAdapter at run time, OR to
|
|
1010
|
+
// pure same-provider sampling (singleProviderWarning fires).
|
|
1011
|
+
if (adapter instanceof providerFallback_1.FallbackAdapter) {
|
|
1012
|
+
const diag = adapter.getDiagnostics();
|
|
1013
|
+
const live = diag.slots.filter((s) => s.keyPresent);
|
|
1014
|
+
if (live.length > 0) {
|
|
1015
|
+
return live.map((s) => ({
|
|
1016
|
+
providerId: s.providerId,
|
|
1017
|
+
modelId: s.modelId,
|
|
1018
|
+
label: s.id,
|
|
1019
|
+
}));
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return [{ providerId, modelId }];
|
|
1023
|
+
},
|
|
1024
|
+
runChild: async (childOpts) => {
|
|
1025
|
+
// Per-child context: paths / skillLoader / memoryManager / processes
|
|
1026
|
+
// are SAFE to share (read-only or per-call by design). The approval
|
|
1027
|
+
// engine is intentionally OMITTED — N children competing for one
|
|
1028
|
+
// stdin REPL would deadlock.
|
|
1029
|
+
const childCtx = {
|
|
1030
|
+
cwd: process.cwd(),
|
|
1031
|
+
paths,
|
|
1032
|
+
sessions: sessionManager,
|
|
1033
|
+
memory: memoryManager,
|
|
1034
|
+
skillLoader,
|
|
1035
|
+
// approvalEngine, ssrfProtection, tirithScanner, memoryGuard:
|
|
1036
|
+
// SSRF + Tirith would be safe to share but adding them now
|
|
1037
|
+
// expands the per-child surface; keep lean for v4.1-subagent.1
|
|
1038
|
+
// and revisit when fanout actually exercises network or shell
|
|
1039
|
+
// tools (gated by ALLOW_DESTRUCTIVE).
|
|
1040
|
+
};
|
|
1041
|
+
// Filter the tool surface. Default-safe: read-only tools only.
|
|
1042
|
+
// AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE=1 mirrors the MCP env from
|
|
1043
|
+
// v4.1-mcp — predictable, env-driven.
|
|
1044
|
+
const allowDestructive = process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === '1' ||
|
|
1045
|
+
process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === 'true';
|
|
1046
|
+
const childToolNames = [];
|
|
1047
|
+
for (const name of toolRegistry.list()) {
|
|
1048
|
+
const h = toolRegistry.get(name);
|
|
1049
|
+
if (!h)
|
|
1050
|
+
continue;
|
|
1051
|
+
if (h.mutates && !allowDestructive)
|
|
1052
|
+
continue;
|
|
1053
|
+
// Avoid recursive fanout this phase — children cannot spawn
|
|
1054
|
+
// their own children. Recursion was capped at depth 1 by
|
|
1055
|
+
// default in prior multi-agent systems for the same reason;
|
|
1056
|
+
// v3 starved nested spawns.
|
|
1057
|
+
if (name === 'subagent_fanout')
|
|
1058
|
+
continue;
|
|
1059
|
+
childToolNames.push(name);
|
|
1060
|
+
}
|
|
1061
|
+
const childExecutor = toolRegistry.buildExecutor(childCtx);
|
|
1062
|
+
const childTools = childToolNames
|
|
1063
|
+
.map((n) => toolRegistry.get(n)?.schema)
|
|
1064
|
+
.filter((s) => !!s);
|
|
1065
|
+
// Provider isolation: clone the FallbackAdapter so per-child
|
|
1066
|
+
// rate-limit state doesn't pollute the parent or siblings.
|
|
1067
|
+
// Non-Fallback adapters are stateless by spec (providers/v4/
|
|
1068
|
+
// types.ts:190) so direct reuse is safe.
|
|
1069
|
+
const childProvider = adapter instanceof providerFallback_1.FallbackAdapter
|
|
1070
|
+
? adapter.clone()
|
|
1071
|
+
: adapter;
|
|
1072
|
+
// Build per-child AidenAgent. Skip the moat layers (PlannerGuard,
|
|
1073
|
+
// HonestyEnforcement, SkillTeacher, SkillEnforcementTracker) —
|
|
1074
|
+
// they're parent-loop concerns and add cost without value at the
|
|
1075
|
+
// child scale. Skip promptBuilder too: children get a SHORT
|
|
1076
|
+
// system prompt (brief identity + role) instead of the parent's
|
|
1077
|
+
// full SOUL.md + 72-skills inventory + memory snapshot. The
|
|
1078
|
+
// tradeoff is deliberate — children answer the GOAL, not "be
|
|
1079
|
+
// Aiden". With the full prompt, trivial queries take 30s+ for
|
|
1080
|
+
// children to generate verbose self-introductions; the lean
|
|
1081
|
+
// child prompt brings n=2 trivial fanouts under 12s. Parent
|
|
1082
|
+
// should pass any context children genuinely need via the
|
|
1083
|
+
// `query` / `tasks[].context` argument.
|
|
1084
|
+
const child = new aidenAgent_1.AidenAgent({
|
|
1085
|
+
provider: childProvider,
|
|
1086
|
+
tools: childTools,
|
|
1087
|
+
toolExecutor: childExecutor,
|
|
1088
|
+
maxTurns: childOpts.maxIterations,
|
|
1089
|
+
providerId: childOpts.provider.providerId,
|
|
1090
|
+
modelId: childOpts.provider.modelId,
|
|
1091
|
+
// No promptBuilder — childSystemPrompt prepended manually below.
|
|
1092
|
+
// No fallback strategy — child failures bubble up to the
|
|
1093
|
+
// orchestrator, which surfaces them in the result envelope.
|
|
1094
|
+
});
|
|
1095
|
+
// Honour the abort signal — if the parent aborts mid-call (or the
|
|
1096
|
+
// per-child timeout fires), short-circuit before dispatching to
|
|
1097
|
+
// the provider. AidenAgent doesn't take an AbortSignal directly;
|
|
1098
|
+
// the AbortController plumbing through fetch is the
|
|
1099
|
+
// v4.1-subagent.2 / v4.2 hardening pass. Pre-check here for the
|
|
1100
|
+
// synchronous path.
|
|
1101
|
+
if (childOpts.signal.aborted) {
|
|
1102
|
+
throw new Error('aborted before dispatch');
|
|
1103
|
+
}
|
|
1104
|
+
// Brief, role-aware system prompt — drops 5KB+ of Aiden identity
|
|
1105
|
+
// boilerplate that would otherwise inflate every child to 30s+
|
|
1106
|
+
// wall-clock for a trivial query. The parent agent retains the
|
|
1107
|
+
// full prompt when it's the orchestrator; children answer the
|
|
1108
|
+
// goal directly.
|
|
1109
|
+
const roleLine = childOpts.role
|
|
1110
|
+
? `Role: ${childOpts.role}. `
|
|
1111
|
+
: '';
|
|
1112
|
+
const childSystemPrompt = `You are one of ${childOpts.index >= 0 ? 'N' : '?'} parallel subagents. ` +
|
|
1113
|
+
`${roleLine}Answer the user's request concisely. Use available tools when ` +
|
|
1114
|
+
`the answer requires real-world information you don't have memorized.`;
|
|
1115
|
+
const history = [
|
|
1116
|
+
{ role: 'system', content: childSystemPrompt },
|
|
1117
|
+
{ role: 'user', content: childOpts.prompt },
|
|
1118
|
+
];
|
|
1119
|
+
const result = await child.runConversation(history);
|
|
1120
|
+
return result.finalContent;
|
|
1121
|
+
},
|
|
1122
|
+
}));
|
|
1123
|
+
bootLogger.child('subagent').info('subagent_fanout: wired (replaces stub)', {
|
|
1124
|
+
providerId,
|
|
1125
|
+
modelId,
|
|
1126
|
+
fallback: adapter instanceof providerFallback_1.FallbackAdapter ? 'FallbackAdapter' : 'direct',
|
|
1127
|
+
});
|
|
1128
|
+
// ── Phase v4.1-2.1: gateway message processor ────────────────────
|
|
1129
|
+
//
|
|
1130
|
+
// Channel adapters call `gateway.routeMessage(...)` for every inbound
|
|
1131
|
+
// message; the gateway then invokes the registered processor — that's
|
|
1132
|
+
// the bridge from channel-side I/O to the agent loop. `aiden serve`
|
|
1133
|
+
// wires its own processor in `api/server.ts` (HTTP-hops to /api/chat
|
|
1134
|
+
// because Express is already up). The CLI host (Phase v4.1-1.1)
|
|
1135
|
+
// never had one, so every Telegram inbound was throwing
|
|
1136
|
+
// "No message processor registered" and the user saw the
|
|
1137
|
+
// friendly-fallback "Something went wrong" reply.
|
|
1138
|
+
//
|
|
1139
|
+
// The closure mirrors the api/server processor's intent — one agent
|
|
1140
|
+
// turn per inbound — but invokes `agent.runConversation()` directly
|
|
1141
|
+
// instead of round-tripping through HTTP. Per-(channel, channelId)
|
|
1142
|
+
// history persists through the same SessionStore the REPL uses, so
|
|
1143
|
+
// a Telegram conversation accumulates context across messages, and
|
|
1144
|
+
// a future `/sessions` listing surfaces those threads alongside REPL
|
|
1145
|
+
// sessions.
|
|
1146
|
+
const gatewayProcessorLog = bootLogger.child('gateway.processor');
|
|
1147
|
+
// gateway sessionId (`session_<ts>`) → sessionStore session id.
|
|
1148
|
+
// In-memory only; restart re-creates a fresh sessionStore session
|
|
1149
|
+
// for the same channel+user pair.
|
|
1150
|
+
const gatewaySessionMap = new Map();
|
|
1151
|
+
gateway_1.gateway.setProcessor(async (message) => {
|
|
1152
|
+
try {
|
|
1153
|
+
// 1. Resolve a sessionStore session for this gateway session.
|
|
1154
|
+
// sessionManager.startSession opens a new row; we cache the
|
|
1155
|
+
// mapping so subsequent messages from the same chat append
|
|
1156
|
+
// to the same history.
|
|
1157
|
+
const gatewaySid = message.sessionId
|
|
1158
|
+
?? `${message.channel}_${message.channelId}`;
|
|
1159
|
+
let storeSid = gatewaySessionMap.get(gatewaySid);
|
|
1160
|
+
if (!storeSid) {
|
|
1161
|
+
const created = sessionManager.startSession({
|
|
1162
|
+
providerId,
|
|
1163
|
+
modelId,
|
|
1164
|
+
title: `${message.channel}:${message.channelId}`,
|
|
1165
|
+
});
|
|
1166
|
+
storeSid = created.id;
|
|
1167
|
+
gatewaySessionMap.set(gatewaySid, storeSid);
|
|
1168
|
+
}
|
|
1169
|
+
// 2. Load past messages for this session and append the new
|
|
1170
|
+
// user turn so the agent sees full context. We drop tool /
|
|
1171
|
+
// system rows on load — the agent's prompt builder rebuilds
|
|
1172
|
+
// those from scratch each call.
|
|
1173
|
+
// Provider Message union has tool-specific variants; we only
|
|
1174
|
+
// load the user/assistant turns and cast to satisfy the union.
|
|
1175
|
+
// Tool-call replay across adapter restarts isn't a feature we
|
|
1176
|
+
// need for chat-channel UX (Phase v4.1-2.1 scope).
|
|
1177
|
+
const past = store.getMessages(storeSid)
|
|
1178
|
+
.filter((r) => r.role === 'user' || r.role === 'assistant')
|
|
1179
|
+
.map((r) => ({ role: r.role, content: r.content }));
|
|
1180
|
+
const userTurn = { role: 'user', content: message.text };
|
|
1181
|
+
const history = [...past, userTurn];
|
|
1182
|
+
// 3. Run one agent turn.
|
|
1183
|
+
const result = await agent.runConversation(history);
|
|
1184
|
+
// 4. Persist the new tail (everything past the loaded history) so
|
|
1185
|
+
// the next inbound resumes seamlessly. Mirror chatSession's
|
|
1186
|
+
// record-turn slice convention.
|
|
1187
|
+
const newSlice = result.messages.slice(history.length - 1);
|
|
1188
|
+
sessionManager.recordTurn(storeSid, newSlice, result.totalUsage);
|
|
1189
|
+
return result.finalContent || '(no response)';
|
|
1190
|
+
}
|
|
1191
|
+
catch (err) {
|
|
1192
|
+
// Diagnostics route through the unified logger — nothing reaches
|
|
1193
|
+
// stdout / stderr so the REPL stays clean. The gateway's own
|
|
1194
|
+
// catch returns the friendly fallback to the user.
|
|
1195
|
+
gatewayProcessorLog.error(`processor failed: ${err?.message ?? String(err)}`, { channel: message.channel, channelId: message.channelId });
|
|
1196
|
+
throw err;
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
843
1199
|
// Command registry.
|
|
844
1200
|
const commandRegistry = new commandRegistry_1.CommandRegistry();
|
|
845
1201
|
for (const cmd of commands_1.allCommands)
|
|
@@ -866,6 +1222,59 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
866
1222
|
catch (err) {
|
|
867
1223
|
display.dim(`(skill commands unavailable: ${err.message})`);
|
|
868
1224
|
}
|
|
1225
|
+
// ── Phase v4.1-1.1: CLI-side channel manager ──────────────────────
|
|
1226
|
+
//
|
|
1227
|
+
// Build a manager local to this CLI process and register the env-driven
|
|
1228
|
+
// adapters that don't need an Express app (Telegram is the only one in
|
|
1229
|
+
// Phase 1 — Discord/Slack/etc. land iteratively). Webhook/Twilio stay
|
|
1230
|
+
// out of CLI scope because they need an HTTP listener.
|
|
1231
|
+
//
|
|
1232
|
+
// Conflict guard: when `aiden serve` is already running locally, BOTH
|
|
1233
|
+
// processes would race the same Telegram bot's long-poll, and the
|
|
1234
|
+
// server would lose every other update with a 409. Probe localhost:4200
|
|
1235
|
+
// briefly; if the server answers, skip auto-start in CLI but still
|
|
1236
|
+
// build the manager so /channel list / status work as a read-only view.
|
|
1237
|
+
//
|
|
1238
|
+
// Phase v4.1-1.3a — `bootLogger` + gateway logger were attached
|
|
1239
|
+
// earlier (right after the agent was constructed) so the gateway
|
|
1240
|
+
// processor closure could share the same scoped sink chain. Here
|
|
1241
|
+
// we just plumb the channels child logger into the manager.
|
|
1242
|
+
const channelManager = new manager_1.ChannelManager({ logger: bootLogger.child('channels') });
|
|
1243
|
+
// Phase v4.1-4.1 — wire active-model lookup into the Telegram
|
|
1244
|
+
// adapter. The closure captures `providerId` / `modelId` from
|
|
1245
|
+
// this scope so the photo-vision module can decide native vs
|
|
1246
|
+
// text routing (and pdf-extract can compute the truncation
|
|
1247
|
+
// budget) using the SAME model the chat path already uses.
|
|
1248
|
+
channelManager.register(new telegram_1.TelegramAdapter({
|
|
1249
|
+
activeModelInfo: () => ({
|
|
1250
|
+
providerId,
|
|
1251
|
+
modelId,
|
|
1252
|
+
contextWindow: (0, modelCatalog_1.findModel)(providerId, modelId)?.contextLength,
|
|
1253
|
+
}),
|
|
1254
|
+
}));
|
|
1255
|
+
let serverIsHosting = false;
|
|
1256
|
+
try {
|
|
1257
|
+
const ctrl = new AbortController();
|
|
1258
|
+
const timer = setTimeout(() => ctrl.abort(), 250);
|
|
1259
|
+
const probe = await fetch('http://127.0.0.1:4200/health', {
|
|
1260
|
+
signal: ctrl.signal,
|
|
1261
|
+
}).catch(() => null);
|
|
1262
|
+
clearTimeout(timer);
|
|
1263
|
+
if (probe && (probe.status >= 200 && probe.status < 500)) {
|
|
1264
|
+
serverIsHosting = true;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
catch {
|
|
1268
|
+
/* no server — fall through to CLI-host */
|
|
1269
|
+
}
|
|
1270
|
+
if (serverIsHosting) {
|
|
1271
|
+
display.dim('[channels] aiden serve is running locally — channel adapters hosted there, /channel commands stay read-only.');
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
// start() resolves quickly when no token is set (logs "Disabled" and
|
|
1275
|
+
// returns). Errors don't crash boot.
|
|
1276
|
+
channelManager.startAll().catch((e) => display.dim(`[channels] startAll error: ${e.message}`));
|
|
1277
|
+
}
|
|
869
1278
|
return {
|
|
870
1279
|
paths,
|
|
871
1280
|
config,
|
|
@@ -900,6 +1309,7 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
900
1309
|
personalityManager,
|
|
901
1310
|
pluginLoader,
|
|
902
1311
|
exploreMode,
|
|
1312
|
+
channelManager,
|
|
903
1313
|
};
|
|
904
1314
|
}
|
|
905
1315
|
async function runInteractiveChat(cliOpts, opts) {
|
|
@@ -929,6 +1339,9 @@ async function runInteractiveChat(cliOpts, opts) {
|
|
|
929
1339
|
// Phase 30.2.1 — boot card renders "model not configured" and
|
|
930
1340
|
// chat attempts get the friendly NotConfiguredError message.
|
|
931
1341
|
unconfigured: runtime.exploreMode,
|
|
1342
|
+
// Phase v4.1-1.1 — live ChannelManager so /channel commands can
|
|
1343
|
+
// list, add, remove, and inspect adapters without an external server.
|
|
1344
|
+
channelManager: runtime.channelManager,
|
|
932
1345
|
};
|
|
933
1346
|
if (cliOpts.tui) {
|
|
934
1347
|
await (0, aidenTUI_1.runTuiMode)({
|
|
@@ -946,6 +1359,10 @@ async function runInteractiveChat(cliOpts, opts) {
|
|
|
946
1359
|
// Phase 17 Task 5: fire onTeardown so plugins (e.g. CDP browser) can
|
|
947
1360
|
// close their resources before the process exits.
|
|
948
1361
|
await runtime.pluginLoader.teardown().catch(() => undefined);
|
|
1362
|
+
// Phase v4.1-1.1 — stop polling adapters before exit so Telegram's
|
|
1363
|
+
// long-poll TCP connection closes cleanly. stopAll() is best-effort
|
|
1364
|
+
// and never throws.
|
|
1365
|
+
await runtime.channelManager.stopAll().catch(() => undefined);
|
|
949
1366
|
runtime.store.close?.();
|
|
950
1367
|
}
|
|
951
1368
|
// ─── setup ─────────────────────────────────────────────────────────────
|