@0xmaxma/claude-gateway 1.0.4 → 1.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 CHANGED
@@ -26,6 +26,7 @@ A self-hosted multi-agent gateway for Claude Code. Connect Claude agents to Tele
26
26
  - **Config auto-migration** — automatic schema migration when config format changes
27
27
  - **Access control** — allowlist, open, or pairing-based Telegram access policies
28
28
  - **HTTP API** — REST API with key-based auth for external integrations
29
+ - **Self-update API** — check for newer versions of `claude-gateway` and `claude-code` and trigger an update via a single API call; no SSH or shell access needed
29
30
  - **Session persistence** — conversation history saved and restored across restarts
30
31
 
31
32
  ---
@@ -1,8 +1,9 @@
1
1
  import { EventEmitter } from 'events';
2
- import { AgentConfig, GatewayConfig, Logger, StreamEvent } from '../types';
2
+ import { AgentConfig, GatewayConfig, Logger, ModelConfig, StreamEvent } from '../types';
3
3
  import { type SkillRegistry } from '../skills';
4
4
  import { HistoryDB } from '../history/db';
5
5
  export declare const MAX_IMAGE_SIZE_BYTES: number;
6
+ export declare const DEFAULT_MODELS: ModelConfig[];
6
7
  export declare class AgentRunner extends EventEmitter {
7
8
  private readonly agentConfig;
8
9
  private readonly gatewayConfig;
@@ -25,6 +26,7 @@ export declare class AgentRunner extends EventEmitter {
25
26
  private readonly maxConcurrent;
26
27
  private idleCleanerTimer;
27
28
  private readonly pendingApiSessions;
29
+ private readonly sessionSpawnLocks;
28
30
  private readonly pendingImagePaths;
29
31
  private skillRegistry;
30
32
  private readonly configPath;
@@ -56,6 +58,7 @@ export declare class AgentRunner extends EventEmitter {
56
58
  private resolveMediaPaths;
57
59
  private static buildChannelXml;
58
60
  private getOrSpawnSession;
61
+ private spawnSession;
59
62
  /**
60
63
  * Returns true if the message content is a session management command.
61
64
  */
@@ -150,6 +153,7 @@ export declare class AgentRunner extends EventEmitter {
150
153
  timeoutMs: number;
151
154
  allowTools?: boolean;
152
155
  mediaFiles?: string[];
156
+ model?: string;
153
157
  }): Promise<string>;
154
158
  /**
155
159
  * Send a message to an API session and stream back events via callbacks.
@@ -165,6 +169,7 @@ export declare class AgentRunner extends EventEmitter {
165
169
  timeoutMs: number;
166
170
  allowTools?: boolean;
167
171
  mediaFiles?: string[];
172
+ model?: string;
168
173
  }): Promise<() => void>;
169
174
  /**
170
175
  * Check if a session has a pending API request (for preflight conflict check).
@@ -173,14 +178,21 @@ export declare class AgentRunner extends EventEmitter {
173
178
  getAgentsBaseDir(): string;
174
179
  getAgentDir(): string;
175
180
  getHistoryDb(): HistoryDB;
176
- getAllSessionNames(): Promise<Map<string, string>>;
181
+ getAllSessionMeta(): Promise<Map<string, {
182
+ name: string;
183
+ }>>;
177
184
  listSessionsForChat(chatId: string, channel: 'telegram' | 'discord'): Promise<import('../types').SessionIndex>;
178
185
  executeApiCommand(sessionId: string, chatId: string, command: string): Promise<Record<string, unknown>>;
179
186
  setModel(newModel: string): Promise<void>;
180
187
  listApiSessions(chatId: string): Promise<import('../types').SessionIndex>;
181
188
  createApiSession(chatId: string, prompt?: string, name?: string): Promise<import('../types').SessionMeta>;
182
189
  getApiSessionInfo(chatId: string, sessionId: string): Promise<Record<string, unknown> | null>;
183
- renameApiSession(chatId: string, sessionId: string, sessionName: string): Promise<void>;
190
+ updateApiSession(chatId: string, sessionId: string, updates: {
191
+ sessionName?: string;
192
+ }): Promise<{
193
+ sessionId: string;
194
+ sessionName?: string;
195
+ }>;
184
196
  deleteApiSession(chatId: string, sessionId: string): Promise<void>;
185
197
  /**
186
198
  * Send a message into an existing channel session (cross-channel continuation from UI).
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAW,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQjG,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQ1C,eAAO,MAAM,oBAAoB,QAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAAW,EAKvC,CAAC;AAoDF,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,SAAS,CAAoC;IAErD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IACjC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAEvC,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6C;IAC9E,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,gBAAgB,CAA+C;IAGvE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IAGxD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA8C;IAGhF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA+B;IAGjE,OAAO,CAAC,aAAa,CAAwC;IAG7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAGpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAGtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAGlC,OAAO,CAAC,aAAa,CAA6B;gBAEtC,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM;IAsBnF;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAI/C,gBAAgB,IAAI,aAAa;IAIjC;;;OAGG;YACW,mBAAmB;IAyJjC;;;OAGG;YACW,oBAAoB;IAsNlC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,MAAM,CAAC,eAAe;YAwChB,iBAAiB;YAyCjB,YAAY;IA+N1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;YACW,oBAAoB;IAsBlC;;OAEG;YACW,qBAAqB;IAiBnC;;OAEG;YACW,wBAAwB;IA+CtC;;OAEG;YACW,gBAAgB;IAM9B;;OAEG;YACW,mBAAmB;IAUjC;;;OAGG;YACW,iBAAiB;IAM/B;;;OAGG;YACW,kBAAkB;IAyBhC;;OAEG;YACW,oBAAoB;IA4ClC;;;OAGG;YACW,aAAa;IAS3B;;;OAGG;YACW,cAAc;IAS5B;;;;;;;;;;;OAWG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBrC;;OAEG;IACH,OAAO,CAAC,SAAS;IAWjB;;;;OAIG;IACD,OAAO,CAAC,YAAY;IAMtB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,eAAe;YAST,wBAAwB;IA2BtC,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,gBAAgB;IAYlB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB5B,oBAAoB,IAAI,IAAI;IAWtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,OAAO,CAAC,sBAAsB;IAqBxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B,SAAS,IAAI,OAAO;IAIpB;;;;;;;OAOG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACvF,OAAO,CAAC,MAAM,CAAC;IAiLlB;;;;;OAKG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE;QACT,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;QACtC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;QACnC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;KAC/B,EACD,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACvF,OAAO,CAAC,MAAM,IAAI,CAAC;IA8NtB;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/C,gBAAgB,IAAI,MAAM;IAI1B,WAAW,IAAI,MAAM;IAIrB,YAAY,IAAI,SAAS;IAIzB,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAIrD,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,UAAU,EAAE,YAAY,CAAC;IAI9G,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IA0EvG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWzC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,UAAU,EAAE,YAAY,CAAC;IAIzE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC;IAqBzG,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAmB7F,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAO5I,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxE;;;;OAIG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,EAAE;QACT,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;QACtC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;QACnC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;KAC/B,EACD,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAC1B,OAAO,CAAC,MAAM,IAAI,CAAC;IAmHtB;;;OAGG;IACH,eAAe,IAAI,MAAM;IAIzB;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAoBnC"}
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.AgentRunner = exports.MAX_IMAGE_SIZE_BYTES = void 0;
36
+ exports.AgentRunner = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
37
37
  const events_1 = require("events");
38
38
  const fs = __importStar(require("fs"));
39
39
  const fsPromises = __importStar(require("fs/promises"));
@@ -54,7 +54,7 @@ const cleanup_1 = require("../history/cleanup");
54
54
  const DEFAULT_IDLE_TIMEOUT_MINUTES = 30;
55
55
  const DEFAULT_MAX_CONCURRENT = 20;
56
56
  exports.MAX_IMAGE_SIZE_BYTES = 10 * 1024 * 1024;
57
- const DEFAULT_MODELS = [
57
+ exports.DEFAULT_MODELS = [
58
58
  { id: 'claude-opus-4-7', label: 'Opus 4.7', alias: 'opus', contextWindow: 1000000 },
59
59
  { id: 'claude-opus-4-6', label: 'Opus 4.6', alias: 'opus46', contextWindow: 1000000 },
60
60
  { id: 'claude-sonnet-4-6', label: 'Sonnet 4.6', alias: 'sonnet', contextWindow: 1000000 },
@@ -121,6 +121,8 @@ class AgentRunner extends events_1.EventEmitter {
121
121
  this.idleCleanerTimer = null;
122
122
  // Tracks session IDs with an in-flight API request (prevents concurrent turns)
123
123
  this.pendingApiSessions = new Set();
124
+ // Serialises concurrent getOrSpawnSession calls for the same key to prevent double-spawn
125
+ this.sessionSpawnLocks = new Map();
124
126
  // Tracks pending Telegram image paths per chatId (queue) for size accumulation after each turn.
125
127
  this.pendingImagePaths = new Map();
126
128
  // Skill registry for detecting /skill-name commands in user messages
@@ -249,6 +251,7 @@ class AgentRunner extends events_1.EventEmitter {
249
251
  this.imageSizePerChat.delete(chatId);
250
252
  }
251
253
  // Route to session process (map key = chatId, actual sessionId passed separately)
254
+ // Channel sessions use agent-level model (not per-session)
252
255
  const session = await this.getOrSpawnSession(chatId, channelSource, sessionId);
253
256
  let channelXml = AgentRunner.buildChannelXml(params);
254
257
  // Detect skill commands and inject skill content
@@ -319,42 +322,38 @@ class AgentRunner extends events_1.EventEmitter {
319
322
  }
320
323
  if (command === 'set_model') {
321
324
  const newModel = typeof body.payload?.model === 'string' ? body.payload.model : '';
322
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
323
- const valid = availableModels.find(m => m.id === newModel);
324
- if (!valid) {
325
- respond({ success: false, error: 'Unknown model' });
326
- return;
327
- }
328
- // Update in-memory config
329
- this.agentConfig.claude.model = newModel;
330
- // Persist to config.json (atomic write)
331
325
  try {
332
- this.persistModelToConfig(newModel);
326
+ await this.setModel(newModel);
333
327
  }
334
328
  catch (err) {
335
- this.logger.error('Failed to persist model to config', { error: err.message });
329
+ respond({ success: false, error: err.message });
330
+ return;
336
331
  }
337
- // Graceful restart all sessions with notify payload
332
+ // Graceful restart ALL channel sessions via signal files so they pick up the new agent-level model.
333
+ // Signal file approach: chokidar watcher in SessionProcess detects the file, reads the notify
334
+ // payload, finishes in-flight work, then restarts — ensuring no mid-response interruption.
338
335
  const chatId = body.chat_id ?? '';
339
336
  let restarted = false;
340
- for (const [sessionId, session] of this.sessions) {
341
- restarted = true;
342
- const notifyPayload = JSON.stringify({
343
- notify: {
344
- chat_id: chatId,
345
- text: `Model changed to ${newModel} back online!`,
346
- },
347
- });
348
- const signalPath = path.join(this.agentConfig.workspace, '.telegram-state', `restart-${sessionId}`);
349
- try {
350
- fs.mkdirSync(path.dirname(signalPath), { recursive: true });
351
- fs.writeFileSync(signalPath, notifyPayload);
352
- }
353
- catch (err) {
354
- this.logger.error('Failed to write restart signal', {
355
- sessionId,
356
- error: err.message,
337
+ for (const [key, session] of this.sessions) {
338
+ // Only restart channel sessions (not API sessions which use per-session model)
339
+ if (session.source !== 'api') {
340
+ restarted = true;
341
+ const channel = this.channelSourceMap.get(key) ?? 'telegram';
342
+ const stateDir = channel === 'discord' ? '.discord-state' : '.telegram-state';
343
+ const signalPayload = JSON.stringify({
344
+ notify: { chat_id: chatId, text: `✅ Model changed to ${newModel}` },
357
345
  });
346
+ const signalPath = path.join(this.agentConfig.workspace, stateDir, `restart-${key}`);
347
+ try {
348
+ fs.mkdirSync(path.dirname(signalPath), { recursive: true });
349
+ fs.writeFileSync(signalPath, signalPayload);
350
+ }
351
+ catch (err) {
352
+ this.logger.error('Failed to write restart signal for set_model', {
353
+ sessionKey: key,
354
+ error: err.message,
355
+ });
356
+ }
358
357
  }
359
358
  }
360
359
  respond({ success: true, model: newModel, restarted });
@@ -414,8 +413,9 @@ class AgentRunner extends events_1.EventEmitter {
414
413
  if (!meta) {
415
414
  return respond({ success: true, text: 'No active session found.' });
416
415
  }
417
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
418
- const modelConfig = availableModels.find(m => m.id === this.agentConfig.claude.model);
416
+ const effectiveModel = this.agentConfig.claude.model;
417
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
418
+ const modelConfig = availableModels.find(m => m.id === effectiveModel);
419
419
  const contextWindow = modelConfig?.contextWindow ?? 200000;
420
420
  const contextTokens = meta.lastInputTokens ?? 0;
421
421
  const usedPct = Math.round((contextTokens / contextWindow) * 100);
@@ -560,10 +560,45 @@ class AgentRunner extends events_1.EventEmitter {
560
560
  `ts="${meta['ts'] ?? new Date().toISOString()}"${optionalAttrs}>${repliedBlock}${params.content ?? ''}</channel>`);
561
561
  }
562
562
  async getOrSpawnSession(mapKey, // Map lookup key (chatId for telegram/discord, sessionId for API)
563
- source, sessionId) {
563
+ source, sessionId, // actual session UUID (only for channel sessions; equals mapKey for API)
564
+ modelOverride) {
565
+ // If a spawn is already in progress for this key, wait for it instead of spawning a second one
566
+ const pending = this.sessionSpawnLocks.get(mapKey);
567
+ if (pending)
568
+ return pending;
564
569
  const existing = this.sessions.get(mapKey);
565
- if (existing)
566
- return existing;
570
+ if (existing) {
571
+ // If model changed, restart the session with the new model
572
+ if (modelOverride && existing.modelOverride !== modelOverride) {
573
+ this.logger.info('Model changed, restarting session', { mapKey, oldModel: existing.modelOverride, newModel: modelOverride });
574
+ const respawn = (async () => {
575
+ await existing.stop();
576
+ this.sessions.delete(mapKey);
577
+ return this.spawnSession(mapKey, source, sessionId, modelOverride);
578
+ })();
579
+ this.sessionSpawnLocks.set(mapKey, respawn);
580
+ try {
581
+ return await respawn;
582
+ }
583
+ finally {
584
+ this.sessionSpawnLocks.delete(mapKey);
585
+ }
586
+ }
587
+ else {
588
+ return existing;
589
+ }
590
+ }
591
+ // No existing session — acquire lock for the spawn path
592
+ const spawnPromise = this.spawnSession(mapKey, source, sessionId, modelOverride);
593
+ this.sessionSpawnLocks.set(mapKey, spawnPromise);
594
+ try {
595
+ return await spawnPromise;
596
+ }
597
+ finally {
598
+ this.sessionSpawnLocks.delete(mapKey);
599
+ }
600
+ }
601
+ async spawnSession(mapKey, source, sessionId, modelOverride) {
567
602
  // Evict oldest idle session if at capacity
568
603
  if (this.sessions.size >= this.maxConcurrent) {
569
604
  const sorted = [...this.sessions.entries()].sort(([, a], [, b]) => a.lastActivityAt - b.lastActivityAt);
@@ -580,6 +615,9 @@ class AgentRunner extends events_1.EventEmitter {
580
615
  const actualSessionId = sessionId ?? mapKey;
581
616
  const chatId = source !== 'api' ? mapKey : undefined;
582
617
  const proc = new process_1.SessionProcess(actualSessionId, source, this.agentConfig, this.gatewayConfig, this.sessionStore, chatId);
618
+ // Apply per-session model override if provided
619
+ if (modelOverride)
620
+ proc.modelOverride = modelOverride;
583
621
  await proc.start();
584
622
  // Forward all session output lines so listeners on AgentRunner (GatewayRouter,
585
623
  // CronScheduler, tests) receive them without needing individual session references.
@@ -822,8 +860,9 @@ class AgentRunner extends events_1.EventEmitter {
822
860
  this.writeAutoForward(chatId, 'No active session found.');
823
861
  return;
824
862
  }
825
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
826
- const modelConfig = availableModels.find(m => m.id === this.agentConfig.claude.model);
863
+ const effectiveModel = this.agentConfig.claude.model;
864
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
865
+ const modelConfig = availableModels.find(m => m.id === effectiveModel);
827
866
  const contextWindow = modelConfig?.contextWindow ?? 200000;
828
867
  const contextTokens = meta.lastInputTokens ?? 0;
829
868
  const usedPct = Math.round((contextTokens / contextWindow) * 100);
@@ -916,13 +955,14 @@ class AgentRunner extends events_1.EventEmitter {
916
955
  const index = await this.sessionStore.listSessions(agentId, chatId, ch);
917
956
  const meta = index.sessions.find(s => s.id === sessionId);
918
957
  const name = meta?.name ?? 'Session';
919
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
920
- const modelConfig = availableModels.find(m => m.id === this.agentConfig.claude.model);
958
+ const compactModel = this.agentConfig.claude.model;
959
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
960
+ const modelConfig = availableModels.find(m => m.id === compactModel);
921
961
  const contextWindow = modelConfig?.contextWindow ?? 200000;
922
962
  this.writeAutoForward(chatId, `⏳ Compacting session "${name}"...`);
923
963
  try {
924
964
  const compactor = new compactor_1.SessionCompactor(this.sessionStore);
925
- const result = await compactor.compact(agentId, chatId, sessionId, this.agentConfig.claude.model, contextWindow, ch);
965
+ const result = await compactor.compact(agentId, chatId, sessionId, compactModel, contextWindow, ch);
926
966
  await this.sessionStore.updateSessionMeta(agentId, chatId, sessionId, {
927
967
  loadedAtSpawn: undefined,
928
968
  archivedCount: undefined,
@@ -1172,12 +1212,13 @@ class AgentRunner extends events_1.EventEmitter {
1172
1212
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
1173
1213
  throw err;
1174
1214
  }
1175
- const session = await this.getOrSpawnSession(sessionId, 'api');
1176
1215
  // Register session in api-{chatId} index.json on first use
1177
1216
  const internalChatIdForSession = `api-${chatId}`;
1178
1217
  await this.sessionStore.ensureApiSession(this.agentConfig.id, internalChatIdForSession, sessionId).catch((err) => {
1179
1218
  this.logger.warn('Failed to register API session in index', { agentId: this.agentConfig.id, chatId, sessionId, error: err.message });
1180
1219
  });
1220
+ // Use model from request body if provided, otherwise use agent default
1221
+ const session = await this.getOrSpawnSession(sessionId, 'api', undefined, opts.model);
1181
1222
  // Promote UI-uploaded files from staging to permanent per-session storage
1182
1223
  const finalMediaFiles = opts.mediaFiles?.length
1183
1224
  ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger)
@@ -1333,12 +1374,13 @@ class AgentRunner extends events_1.EventEmitter {
1333
1374
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
1334
1375
  throw err;
1335
1376
  }
1336
- const session = await this.getOrSpawnSession(sessionId, 'api');
1337
1377
  // Register session in api-{chatId} index.json on first use
1338
1378
  const internalChatIdStream = `api-${chatId}`;
1339
1379
  await this.sessionStore.ensureApiSession(this.agentConfig.id, internalChatIdStream, sessionId).catch((err) => {
1340
1380
  this.logger.warn('Failed to register API session in index', { agentId: this.agentConfig.id, chatId, sessionId, error: err.message });
1341
1381
  });
1382
+ // Use model from request body if provided, otherwise use agent default
1383
+ const session = await this.getOrSpawnSession(sessionId, 'api', undefined, opts.model);
1342
1384
  // Promote UI-uploaded files from staging to permanent per-session storage
1343
1385
  const finalMediaFilesStream = opts.mediaFiles?.length
1344
1386
  ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger)
@@ -1538,8 +1580,8 @@ class AgentRunner extends events_1.EventEmitter {
1538
1580
  getHistoryDb() {
1539
1581
  return this.historyDb;
1540
1582
  }
1541
- getAllSessionNames() {
1542
- return this.sessionStore.getAllSessionNames(this.agentConfig.id);
1583
+ getAllSessionMeta() {
1584
+ return this.sessionStore.getAllSessionMeta(this.agentConfig.id);
1543
1585
  }
1544
1586
  async listSessionsForChat(chatId, channel) {
1545
1587
  return this.sessionStore.listSessions(this.agentConfig.id, chatId, channel);
@@ -1562,10 +1604,11 @@ class AgentRunner extends events_1.EventEmitter {
1562
1604
  if (command === '/session') {
1563
1605
  const index = await this.sessionStore.listSessions(agentId, internalChatId, 'api').catch(() => null);
1564
1606
  const meta = index?.sessions.find((s) => s.id === sessionId);
1607
+ const effectiveModel = this.agentConfig.claude.model;
1565
1608
  if (!meta)
1566
- return { sessionId, sessionName: null, messageCount: 0, archivedCount: 0, contextUsedPct: 0, model: this.agentConfig.claude.model };
1567
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
1568
- const modelConfig = availableModels.find((m) => m.id === this.agentConfig.claude.model);
1609
+ return { sessionId, sessionName: null, messageCount: 0, archivedCount: 0, contextUsedPct: 0, model: effectiveModel };
1610
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
1611
+ const modelConfig = availableModels.find((m) => m.id === effectiveModel);
1569
1612
  const contextWindow = modelConfig?.contextWindow ?? 200000;
1570
1613
  const contextUsedPct = Math.round(((meta.lastInputTokens ?? 0) / contextWindow) * 100);
1571
1614
  return {
@@ -1574,7 +1617,7 @@ class AgentRunner extends events_1.EventEmitter {
1574
1617
  messageCount: meta.messageCount,
1575
1618
  archivedCount: meta.archivedCount ?? 0,
1576
1619
  contextUsedPct,
1577
- model: this.agentConfig.claude.model,
1620
+ model: effectiveModel,
1578
1621
  };
1579
1622
  }
1580
1623
  if (command === '/clear') {
@@ -1594,11 +1637,12 @@ class AgentRunner extends events_1.EventEmitter {
1594
1637
  }
1595
1638
  if (command === '/compact') {
1596
1639
  const ch = 'api';
1597
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
1598
- const modelConfig = availableModels.find((m) => m.id === this.agentConfig.claude.model);
1640
+ const compactEffectiveModel = this.agentConfig.claude.model;
1641
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
1642
+ const modelConfig = availableModels.find((m) => m.id === compactEffectiveModel);
1599
1643
  const contextWindow = modelConfig?.contextWindow ?? 200000;
1600
1644
  const compactor = new compactor_1.SessionCompactor(this.sessionStore);
1601
- const result = await compactor.compact(agentId, internalChatId, sessionId, this.agentConfig.claude.model, contextWindow, ch);
1645
+ const result = await compactor.compact(agentId, internalChatId, sessionId, compactEffectiveModel, contextWindow, ch);
1602
1646
  await this.sessionStore.updateSessionMeta(agentId, internalChatId, sessionId, {
1603
1647
  loadedAtSpawn: undefined,
1604
1648
  archivedCount: undefined,
@@ -1610,7 +1654,7 @@ class AgentRunner extends events_1.EventEmitter {
1610
1654
  throw new Error(`Unknown command: ${command}`);
1611
1655
  }
1612
1656
  async setModel(newModel) {
1613
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
1657
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
1614
1658
  if (!availableModels.find((m) => m.id === newModel)) {
1615
1659
  throw Object.assign(new Error(`Unknown model: ${newModel}`), { code: 'UNKNOWN_MODEL' });
1616
1660
  }
@@ -1647,8 +1691,9 @@ class AgentRunner extends events_1.EventEmitter {
1647
1691
  const meta = index?.sessions.find((s) => s.id === sessionId);
1648
1692
  if (!meta)
1649
1693
  return null;
1650
- const availableModels = this.gatewayConfig.gateway.models ?? DEFAULT_MODELS;
1651
- const modelConfig = availableModels.find((m) => m.id === this.agentConfig.claude.model);
1694
+ const effectiveModel = this.agentConfig.claude.model;
1695
+ const availableModels = this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS;
1696
+ const modelConfig = availableModels.find((m) => m.id === effectiveModel);
1652
1697
  const contextWindow = modelConfig?.contextWindow ?? 200000;
1653
1698
  const contextUsedPct = Math.round(((meta.lastInputTokens ?? 0) / contextWindow) * 100);
1654
1699
  return {
@@ -1657,11 +1702,14 @@ class AgentRunner extends events_1.EventEmitter {
1657
1702
  messageCount: meta.messageCount,
1658
1703
  archivedCount: meta.archivedCount ?? 0,
1659
1704
  contextUsedPct,
1660
- model: this.agentConfig.claude.model,
1705
+ model: effectiveModel,
1661
1706
  };
1662
1707
  }
1663
- async renameApiSession(chatId, sessionId, sessionName) {
1664
- await this.sessionStore.updateSessionMeta(this.agentConfig.id, `api-${chatId}`, sessionId, { name: sessionName }, 'api');
1708
+ async updateApiSession(chatId, sessionId, updates) {
1709
+ if (updates.sessionName) {
1710
+ await this.sessionStore.updateSessionMeta(this.agentConfig.id, `api-${chatId}`, sessionId, { name: updates.sessionName }, 'api');
1711
+ }
1712
+ return { sessionId, ...(updates.sessionName ? { sessionName: updates.sessionName } : {}) };
1665
1713
  }
1666
1714
  async deleteApiSession(chatId, sessionId) {
1667
1715
  await this.sessionStore.deleteTelegramSession(this.agentConfig.id, `api-${chatId}`, sessionId, 'api');
@@ -1674,6 +1722,7 @@ class AgentRunner extends events_1.EventEmitter {
1674
1722
  async sendMessageToSession(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
1675
1723
  // Ensure the session process uses the correct channel source
1676
1724
  this.channelSourceMap.set(rawChatId, channel);
1725
+ // Channel sessions use agent-level model (not per-session)
1677
1726
  const session = await this.getOrSpawnSession(rawChatId, channel, sessionId);
1678
1727
  // Persist user message (Layer 1 session JSON + Layer 2 history DB)
1679
1728
  const uiUserTs = Date.now();