@0xmaxma/claude-gateway 2.0.4 → 2.0.6
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 +10 -1
- package/dist/agent/builtin-commands.d.ts +1 -1
- package/dist/agent/builtin-commands.d.ts.map +1 -1
- package/dist/agent/builtin-commands.js +3 -2
- package/dist/agent/builtin-commands.js.map +1 -1
- package/dist/agent/runner.d.ts +123 -9
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +215 -182
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/session-compaction-scheduler.d.ts +22 -0
- package/dist/agent/session-compaction-scheduler.d.ts.map +1 -0
- package/dist/agent/session-compaction-scheduler.js +40 -0
- package/dist/agent/session-compaction-scheduler.js.map +1 -0
- package/dist/agent/skill-learning/index.d.ts.map +1 -1
- package/dist/agent/skill-learning/index.js +27 -8
- package/dist/agent/skill-learning/index.js.map +1 -1
- package/dist/agent/skill-learning/reviewer.d.ts +3 -1
- package/dist/agent/skill-learning/reviewer.d.ts.map +1 -1
- package/dist/agent/skill-learning/reviewer.js +6 -2
- package/dist/agent/skill-learning/reviewer.js.map +1 -1
- package/dist/agent/turn-stream.d.ts +3 -1
- package/dist/agent/turn-stream.d.ts.map +1 -1
- package/dist/agent/turn-stream.js +8 -4
- package/dist/agent/turn-stream.js.map +1 -1
- package/dist/api/gateway-router.d.ts +2 -0
- package/dist/api/gateway-router.d.ts.map +1 -1
- package/dist/api/gateway-router.js +323 -16
- package/dist/api/gateway-router.js.map +1 -1
- package/dist/api/memory-activity.d.ts +18 -0
- package/dist/api/memory-activity.d.ts.map +1 -0
- package/dist/api/memory-activity.js +97 -0
- package/dist/api/memory-activity.js.map +1 -0
- package/dist/api/router.js +11 -11
- package/dist/api/router.js.map +1 -1
- package/dist/api/slack-webhook-router.js +1 -1
- package/dist/api/slack-webhook-router.js.map +1 -1
- package/dist/api/voice-router.d.ts +1 -0
- package/dist/api/voice-router.d.ts.map +1 -1
- package/dist/api/voice-router.js +129 -7
- package/dist/api/voice-router.js.map +1 -1
- package/dist/config/claude-settings.d.ts +14 -0
- package/dist/config/claude-settings.d.ts.map +1 -1
- package/dist/config/claude-settings.js +20 -0
- package/dist/config/claude-settings.js.map +1 -1
- package/dist/orchestration/agent-cli-session.d.ts +46 -0
- package/dist/orchestration/agent-cli-session.d.ts.map +1 -0
- package/dist/orchestration/agent-cli-session.js +121 -0
- package/dist/orchestration/agent-cli-session.js.map +1 -0
- package/dist/orchestration/agent-tool-schemas.d.ts +6 -0
- package/dist/orchestration/agent-tool-schemas.d.ts.map +1 -0
- package/dist/orchestration/agent-tool-schemas.js +23 -0
- package/dist/orchestration/agent-tool-schemas.js.map +1 -0
- package/dist/orchestration/bridge.d.ts +6 -3
- package/dist/orchestration/bridge.d.ts.map +1 -1
- package/dist/orchestration/bridge.js +31 -14
- package/dist/orchestration/bridge.js.map +1 -1
- package/dist/orchestration/browser-voice.d.ts +14 -0
- package/dist/orchestration/browser-voice.d.ts.map +1 -0
- package/dist/orchestration/browser-voice.js +30 -0
- package/dist/orchestration/browser-voice.js.map +1 -0
- package/dist/orchestration/compact-measurements.d.ts +21 -0
- package/dist/orchestration/compact-measurements.d.ts.map +1 -0
- package/dist/orchestration/compact-measurements.js +62 -0
- package/dist/orchestration/compact-measurements.js.map +1 -0
- package/dist/orchestration/config.d.ts +4 -0
- package/dist/orchestration/config.d.ts.map +1 -1
- package/dist/orchestration/config.js +1 -1
- package/dist/orchestration/config.js.map +1 -1
- package/dist/orchestration/context-delivery.d.ts +32 -0
- package/dist/orchestration/context-delivery.d.ts.map +1 -0
- package/dist/orchestration/context-delivery.js +101 -0
- package/dist/orchestration/context-delivery.js.map +1 -0
- package/dist/orchestration/context-footprint.d.ts +14 -0
- package/dist/orchestration/context-footprint.d.ts.map +1 -0
- package/dist/orchestration/context-footprint.js +57 -0
- package/dist/orchestration/context-footprint.js.map +1 -0
- package/dist/orchestration/conversation-intake.d.ts +10 -6
- package/dist/orchestration/conversation-intake.d.ts.map +1 -1
- package/dist/orchestration/conversation-intake.js +22 -8
- package/dist/orchestration/conversation-intake.js.map +1 -1
- package/dist/orchestration/dashboard-charts.d.ts +32 -0
- package/dist/orchestration/dashboard-charts.d.ts.map +1 -0
- package/dist/orchestration/dashboard-charts.js +77 -0
- package/dist/orchestration/dashboard-charts.js.map +1 -0
- package/dist/orchestration/dashboard-reader-worker.d.ts +2 -0
- package/dist/orchestration/dashboard-reader-worker.d.ts.map +1 -0
- package/dist/orchestration/dashboard-reader-worker.js +173 -0
- package/dist/orchestration/dashboard-reader-worker.js.map +1 -0
- package/dist/orchestration/dashboard-reader.d.ts +15 -0
- package/dist/orchestration/dashboard-reader.d.ts.map +1 -0
- package/dist/orchestration/dashboard-reader.js +87 -0
- package/dist/orchestration/dashboard-reader.js.map +1 -0
- package/dist/orchestration/decision-context.d.ts +15 -0
- package/dist/orchestration/decision-context.d.ts.map +1 -0
- package/dist/orchestration/decision-context.js +57 -0
- package/dist/orchestration/decision-context.js.map +1 -0
- package/dist/orchestration/display-stream.d.ts +21 -2
- package/dist/orchestration/display-stream.d.ts.map +1 -1
- package/dist/orchestration/display-stream.js +61 -8
- package/dist/orchestration/display-stream.js.map +1 -1
- package/dist/orchestration/inference-errors.d.ts.map +1 -1
- package/dist/orchestration/inference-errors.js +19 -21
- package/dist/orchestration/inference-errors.js.map +1 -1
- package/dist/orchestration/mutation-recovery.d.ts +13 -0
- package/dist/orchestration/mutation-recovery.d.ts.map +1 -0
- package/dist/orchestration/mutation-recovery.js +49 -0
- package/dist/orchestration/mutation-recovery.js.map +1 -0
- package/dist/orchestration/native-compact.d.ts +8 -0
- package/dist/orchestration/native-compact.d.ts.map +1 -0
- package/dist/orchestration/native-compact.js +33 -0
- package/dist/orchestration/native-compact.js.map +1 -0
- package/dist/orchestration/notification-mailbox.d.ts +3 -1
- package/dist/orchestration/notification-mailbox.d.ts.map +1 -1
- package/dist/orchestration/notification-mailbox.js +4 -2
- package/dist/orchestration/notification-mailbox.js.map +1 -1
- package/dist/orchestration/process-turn.d.ts +16 -2
- package/dist/orchestration/process-turn.d.ts.map +1 -1
- package/dist/orchestration/process-turn.js +78 -14
- package/dist/orchestration/process-turn.js.map +1 -1
- package/dist/orchestration/progress-review.d.ts +8 -18
- package/dist/orchestration/progress-review.d.ts.map +1 -1
- package/dist/orchestration/progress-review.js +12 -14
- package/dist/orchestration/progress-review.js.map +1 -1
- package/dist/orchestration/provider-message.d.ts +7 -0
- package/dist/orchestration/provider-message.d.ts.map +1 -0
- package/dist/orchestration/provider-message.js +63 -0
- package/dist/orchestration/provider-message.js.map +1 -0
- package/dist/orchestration/response-errors.d.ts +6 -0
- package/dist/orchestration/response-errors.d.ts.map +1 -0
- package/dist/orchestration/response-errors.js +88 -0
- package/dist/orchestration/response-errors.js.map +1 -0
- package/dist/orchestration/response-schema.d.ts +33 -0
- package/dist/orchestration/response-schema.d.ts.map +1 -0
- package/dist/orchestration/response-schema.js +61 -0
- package/dist/orchestration/response-schema.js.map +1 -0
- package/dist/orchestration/runtime.d.ts +79 -0
- package/dist/orchestration/runtime.d.ts.map +1 -1
- package/dist/orchestration/runtime.js +415 -68
- package/dist/orchestration/runtime.js.map +1 -1
- package/dist/orchestration/session-compaction.d.ts +59 -0
- package/dist/orchestration/session-compaction.d.ts.map +1 -0
- package/dist/orchestration/session-compaction.js +212 -0
- package/dist/orchestration/session-compaction.js.map +1 -0
- package/dist/orchestration/skills.d.ts.map +1 -1
- package/dist/orchestration/skills.js +3 -2
- package/dist/orchestration/skills.js.map +1 -1
- package/dist/orchestration/source-policy.d.ts +1 -1
- package/dist/orchestration/source-policy.d.ts.map +1 -1
- package/dist/orchestration/source-policy.js +1 -1
- package/dist/orchestration/speech.d.ts +15 -17
- package/dist/orchestration/speech.d.ts.map +1 -1
- package/dist/orchestration/speech.js +90 -30
- package/dist/orchestration/speech.js.map +1 -1
- package/dist/orchestration/store.d.ts +2 -0
- package/dist/orchestration/store.d.ts.map +1 -1
- package/dist/orchestration/store.js +16 -1
- package/dist/orchestration/store.js.map +1 -1
- package/dist/orchestration/tasks/driver.d.ts.map +1 -1
- package/dist/orchestration/tasks/driver.js +8 -3
- package/dist/orchestration/tasks/driver.js.map +1 -1
- package/dist/orchestration/tasks/pool.d.ts.map +1 -1
- package/dist/orchestration/tasks/pool.js +8 -4
- package/dist/orchestration/tasks/pool.js.map +1 -1
- package/dist/orchestration/tasks/service.d.ts +48 -1
- package/dist/orchestration/tasks/service.d.ts.map +1 -1
- package/dist/orchestration/tasks/service.js +41 -17
- package/dist/orchestration/tasks/service.js.map +1 -1
- package/dist/orchestration/token-ledger.d.ts +132 -0
- package/dist/orchestration/token-ledger.d.ts.map +1 -0
- package/dist/orchestration/token-ledger.js +233 -0
- package/dist/orchestration/token-ledger.js.map +1 -0
- package/dist/orchestration/token-usage.d.ts +46 -0
- package/dist/orchestration/token-usage.d.ts.map +1 -0
- package/dist/orchestration/token-usage.js +102 -0
- package/dist/orchestration/token-usage.js.map +1 -0
- package/dist/orchestration/tool-activity.d.ts.map +1 -1
- package/dist/orchestration/tool-activity.js +5 -3
- package/dist/orchestration/tool-activity.js.map +1 -1
- package/dist/orchestration/tool-name.d.ts +10 -0
- package/dist/orchestration/tool-name.d.ts.map +1 -0
- package/dist/orchestration/tool-name.js +14 -0
- package/dist/orchestration/tool-name.js.map +1 -0
- package/dist/orchestration/transcript-checkpoint.d.ts +14 -0
- package/dist/orchestration/transcript-checkpoint.d.ts.map +1 -0
- package/dist/orchestration/transcript-checkpoint.js +83 -0
- package/dist/orchestration/transcript-checkpoint.js.map +1 -0
- package/dist/orchestration/unanswered-inputs.d.ts +6 -0
- package/dist/orchestration/unanswered-inputs.d.ts.map +1 -0
- package/dist/orchestration/unanswered-inputs.js +18 -0
- package/dist/orchestration/unanswered-inputs.js.map +1 -0
- package/dist/session/process.d.ts +10 -0
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +91 -9
- package/dist/session/process.js.map +1 -1
- package/dist/session/request-tool-capture.d.ts +31 -0
- package/dist/session/request-tool-capture.d.ts.map +1 -0
- package/dist/session/request-tool-capture.js +194 -0
- package/dist/session/request-tool-capture.js.map +1 -0
- package/dist/session/runtime-profile.d.ts +7 -3
- package/dist/session/runtime-profile.d.ts.map +1 -1
- package/dist/session/runtime-profile.js +3 -1
- package/dist/session/runtime-profile.js.map +1 -1
- package/dist/session/store.d.ts +7 -0
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +27 -0
- package/dist/session/store.js.map +1 -1
- package/dist/shell/tailer.d.ts +7 -5
- package/dist/shell/tailer.d.ts.map +1 -1
- package/dist/shell/tailer.js +6 -17
- package/dist/shell/tailer.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/cli-viewer-ui.js +1 -1
- package/dist/ui/dashboard-charts.d.ts +3 -0
- package/dist/ui/dashboard-charts.d.ts.map +1 -0
- package/dist/ui/dashboard-charts.js +130 -0
- package/dist/ui/dashboard-charts.js.map +1 -0
- package/dist/ui/dashboard-client.d.ts +3 -0
- package/dist/ui/dashboard-client.d.ts.map +1 -0
- package/dist/ui/dashboard-client.js +184 -0
- package/dist/ui/dashboard-client.js.map +1 -0
- package/dist/ui/dashboard-controls.d.ts +4 -0
- package/dist/ui/dashboard-controls.d.ts.map +1 -0
- package/dist/ui/dashboard-controls.js +27 -0
- package/dist/ui/dashboard-controls.js.map +1 -0
- package/dist/ui/dashboard-presentation.d.ts +12 -0
- package/dist/ui/dashboard-presentation.d.ts.map +1 -0
- package/dist/ui/dashboard-presentation.js +62 -0
- package/dist/ui/dashboard-presentation.js.map +1 -0
- package/dist/ui/dashboard-range.d.ts +5 -0
- package/dist/ui/dashboard-range.d.ts.map +1 -0
- package/dist/ui/dashboard-range.js +36 -0
- package/dist/ui/dashboard-range.js.map +1 -0
- package/dist/ui/dashboard-theme.d.ts +6 -0
- package/dist/ui/dashboard-theme.d.ts.map +1 -0
- package/dist/ui/dashboard-theme.js +562 -0
- package/dist/ui/dashboard-theme.js.map +1 -0
- package/dist/ui/memory-activity.d.ts +4 -0
- package/dist/ui/memory-activity.d.ts.map +1 -0
- package/dist/ui/memory-activity.js +115 -0
- package/dist/ui/memory-activity.js.map +1 -0
- package/dist/ui/token-report-client.d.ts +3 -0
- package/dist/ui/token-report-client.d.ts.map +1 -0
- package/dist/ui/token-report-client.js +34 -0
- package/dist/ui/token-report-client.js.map +1 -0
- package/dist/ui/token-report.d.ts +90 -0
- package/dist/ui/token-report.d.ts.map +1 -0
- package/dist/ui/token-report.js +203 -0
- package/dist/ui/token-report.js.map +1 -0
- package/dist/ui/web-ui.d.ts +1 -1
- package/dist/ui/web-ui.d.ts.map +1 -1
- package/dist/ui/web-ui.js +129 -409
- package/dist/ui/web-ui.js.map +1 -1
- package/dist/voice/replay.d.ts +8 -0
- package/dist/voice/replay.d.ts.map +1 -0
- package/dist/voice/replay.js +48 -0
- package/dist/voice/replay.js.map +1 -0
- package/dist/voice/session.d.ts +9 -0
- package/dist/voice/session.d.ts.map +1 -1
- package/dist/voice/session.js +72 -14
- package/dist/voice/session.js.map +1 -1
- package/mcp/lazy-connector.test.ts +58 -0
- package/mcp/lazy-connector.ts +67 -0
- package/mcp/lazy-tools.test.ts +152 -0
- package/mcp/lazy-tools.ts +41 -0
- package/mcp/server.ts +53 -34
- package/mcp/tools/discord/commands.ts +2 -0
- package/mcp/tools/tasks/module.ts +1 -12
- package/mcp/tools/telegram/receiver-server.ts +45 -31
- package/mcp/tools/telegram/session-browser.ts +10 -0
- package/package.json +2 -1
- package/resource/dashboard/OFL.txt +93 -0
- package/resource/dashboard/poppins-300.woff2 +0 -0
- package/resource/dashboard/poppins-400.woff2 +0 -0
- package/resource/dashboard/poppins-500.woff2 +0 -0
- package/resource/dashboard/poppins-600.woff2 +0 -0
package/dist/agent/runner.js
CHANGED
|
@@ -35,8 +35,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.AgentRunner = exports.CHANNEL_COALESCE_WINDOW_MS = exports.WHATSAPP_CLOUD_WINDOW_NOTE = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
|
|
37
37
|
exports.toRelMediaFiles = toRelMediaFiles;
|
|
38
|
+
const session_compaction_scheduler_1 = require("./session-compaction-scheduler");
|
|
39
|
+
const session_compaction_1 = require("../orchestration/session-compaction");
|
|
40
|
+
const config_1 = require("./dreaming/config");
|
|
41
|
+
const response_errors_1 = require("../orchestration/response-errors");
|
|
38
42
|
const channel_media_error_1 = require("../orchestration/channel-media-error");
|
|
39
43
|
const store_1 = require("../orchestration/store");
|
|
44
|
+
const config_2 = require("../orchestration/config");
|
|
40
45
|
const channel_input_media_1 = require("../orchestration/channel-input-media");
|
|
41
46
|
const chunks_1 = require("../telegram/chunks");
|
|
42
47
|
const file_delivery_1 = require("../orchestration/file-delivery");
|
|
@@ -56,7 +61,7 @@ const config_write_lock_1 = require("../config/config-write-lock");
|
|
|
56
61
|
const logger_1 = require("../logger");
|
|
57
62
|
const process_1 = require("../session/process");
|
|
58
63
|
const store_2 = require("../session/store");
|
|
59
|
-
const
|
|
64
|
+
const native_compact_1 = require("../orchestration/native-compact");
|
|
60
65
|
const receiver_1 = require("../telegram/receiver");
|
|
61
66
|
const receiver_2 = require("../discord/receiver");
|
|
62
67
|
const line_reply_manager_1 = require("./line-reply-manager");
|
|
@@ -73,7 +78,7 @@ const skills_1 = require("../skills");
|
|
|
73
78
|
const builtin_commands_1 = require("./builtin-commands");
|
|
74
79
|
const model_catalog_1 = require("./model-catalog");
|
|
75
80
|
const safe_mode_1 = require("./safe-mode");
|
|
76
|
-
const
|
|
81
|
+
const config_3 = require("./skill-learning/config");
|
|
77
82
|
const turn_stream_1 = require("./turn-stream");
|
|
78
83
|
const recovery_executor_1 = require("./recovery-executor");
|
|
79
84
|
const recovery_policy_1 = require("./recovery-policy");
|
|
@@ -348,9 +353,21 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
348
353
|
async subscribeResponseText(sessionId, principalId, receive) {
|
|
349
354
|
return (await this.getOrchestration()).subscribeText(sessionId, principalId, receive);
|
|
350
355
|
}
|
|
356
|
+
async setBrowserVoice(sessionId, principalId, enabled) {
|
|
357
|
+
return (await this.getOrchestration()).setBrowserVoice(sessionId, principalId, enabled);
|
|
358
|
+
}
|
|
359
|
+
async pendingVoiceSpeech(sessionId, principalId, claimed = []) {
|
|
360
|
+
return (await this.getOrchestration()).pendingVoiceSpeech(sessionId, principalId, claimed);
|
|
361
|
+
}
|
|
351
362
|
async voiceReplay(sessionId, principalId, responseId) {
|
|
352
363
|
return (await this.getOrchestration()).voiceAudio(sessionId, principalId, responseId);
|
|
353
364
|
}
|
|
365
|
+
async replayableVoiceResponses(sessionId, principalId) {
|
|
366
|
+
return (await this.getOrchestration()).replayableVoiceResponses(sessionId, principalId);
|
|
367
|
+
}
|
|
368
|
+
async voiceReplaySpeech(sessionId, principalId, responseId) {
|
|
369
|
+
return (await this.getOrchestration()).voiceReplaySpeech(sessionId, principalId, responseId);
|
|
370
|
+
}
|
|
354
371
|
saveVoiceReplay(sessionId, principalId, responseId, audio) {
|
|
355
372
|
this.orchestration?.saveVoiceAudio(sessionId, principalId, responseId, audio);
|
|
356
373
|
}
|
|
@@ -642,6 +659,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
642
659
|
}
|
|
643
660
|
});
|
|
644
661
|
};
|
|
662
|
+
this.compactingSessions = new Set();
|
|
645
663
|
this.telegramCommandConfig = '';
|
|
646
664
|
this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
|
|
647
665
|
this.gatewayConfig = gatewayConfig;
|
|
@@ -691,6 +709,26 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
691
709
|
if (result.state !== 'delivered')
|
|
692
710
|
this.logger.warn('Skill notification delivery failed', { sessionId, code: result.code });
|
|
693
711
|
}
|
|
712
|
+
sessionCompactionSettings() {
|
|
713
|
+
const config = (0, session_compaction_1.resolveSessionCompaction)(this.agentConfig.sessionCompaction, this.gatewayConfig.gateway.sessionCompaction);
|
|
714
|
+
if (!this.agentConfig.orchestration?.enabled)
|
|
715
|
+
config.enabled = false;
|
|
716
|
+
const dream = (0, config_1.resolveDreamingConfig)(this.agentConfig.dreaming, this.gatewayConfig.gateway.dreaming, this.gatewayConfig.gateway.timezone);
|
|
717
|
+
return { config, hour: dream.dreamHour, minute: dream.dreamMinute, timezone: dream.dreamTimezone, stagger: dream.staggerWindowMinutes };
|
|
718
|
+
}
|
|
719
|
+
startSessionCompaction() {
|
|
720
|
+
this.sessionCompactionScheduler ?? (this.sessionCompactionScheduler = new session_compaction_scheduler_1.SessionCompactionScheduler(this.agentConfig.id, () => this.sessionCompactionSettings(), async () => {
|
|
721
|
+
if (this.stopping)
|
|
722
|
+
return;
|
|
723
|
+
const runtime = await this.getOrchestration();
|
|
724
|
+
return runtime.runSessionCompaction(this.sessionCompactionSettings().config, model => this.contextWindowFor(model, true));
|
|
725
|
+
}, error => this.logger.warn('Nightly session compaction failed', { error: String(error) })));
|
|
726
|
+
this.sessionCompactionScheduler.start();
|
|
727
|
+
}
|
|
728
|
+
async sessionCompactionReport() {
|
|
729
|
+
const cfg = this.sessionCompactionSettings();
|
|
730
|
+
return { schedule: { ...cfg.config, nextRunAt: this.sessionCompactionScheduler?.nextRunAt ?? null, timezone: cfg.timezone } };
|
|
731
|
+
}
|
|
694
732
|
setSkillLearning(manager) {
|
|
695
733
|
this.skillLearning = manager;
|
|
696
734
|
}
|
|
@@ -907,6 +945,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
907
945
|
.then(() => this.writeTypingDone(chatId))
|
|
908
946
|
.catch((err) => {
|
|
909
947
|
this.logger.error('Session command failed', { error: err.message });
|
|
948
|
+
this.writeAutoForward(chatId, 'Command failed: ' + err.message);
|
|
910
949
|
this.writeTypingDone(chatId);
|
|
911
950
|
});
|
|
912
951
|
return;
|
|
@@ -1375,7 +1414,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1375
1414
|
return;
|
|
1376
1415
|
}
|
|
1377
1416
|
const restarted = await this.restartChannelSessions();
|
|
1378
|
-
respond({ success: true, model: newModel, restarted });
|
|
1417
|
+
respond({ success: true, model: newModel, restarted, appliesNextTurn: !restarted });
|
|
1379
1418
|
return;
|
|
1380
1419
|
}
|
|
1381
1420
|
if (command === 'restart') {
|
|
@@ -1416,36 +1455,21 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1416
1455
|
const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, this.channelFor(chatId));
|
|
1417
1456
|
const meta = index.sessions.find(s => s.id === index.activeSessionId);
|
|
1418
1457
|
if (!meta) {
|
|
1419
|
-
return respond({ success: true, text: 'No active session found.' });
|
|
1420
|
-
}
|
|
1421
|
-
const effectiveModel = this.agentConfig.claude.model;
|
|
1422
|
-
const contextWindow = await this.contextWindowFor(effectiveModel);
|
|
1423
|
-
const contextTokens = meta.lastInputTokens ?? 0;
|
|
1424
|
-
const usedPct = Math.round((contextTokens / contextWindow) * 100);
|
|
1425
|
-
let msgs;
|
|
1426
|
-
if (meta.messageCount <= 0) {
|
|
1427
|
-
msgs = 'No messages yet';
|
|
1428
|
-
}
|
|
1429
|
-
else if ((meta.archivedCount ?? 0) > 0 && meta.loadedAtSpawn != null && meta.messageCountAtSpawn != null) {
|
|
1430
|
-
const newMessagesSinceSpawn = meta.messageCount - meta.messageCountAtSpawn;
|
|
1431
|
-
const inContext = meta.loadedAtSpawn + Math.max(0, newMessagesSinceSpawn);
|
|
1432
|
-
msgs = `${meta.messageCount} (${inContext} in context / ${meta.archivedCount} archived)`;
|
|
1433
|
-
}
|
|
1434
|
-
else {
|
|
1435
|
-
msgs = `${meta.messageCount}`;
|
|
1458
|
+
return respond({ success: true, sessionId: null, text: 'No active session found.' });
|
|
1436
1459
|
}
|
|
1460
|
+
const context = await this.sessionContextInfo(meta.id, meta);
|
|
1437
1461
|
const lines = [
|
|
1438
1462
|
`📌 Current Session: ${meta.name}`,
|
|
1439
1463
|
`<code>${meta.id}</code>`,
|
|
1440
1464
|
'',
|
|
1441
|
-
|
|
1442
|
-
|
|
1465
|
+
`👉 Context: ${context.text}`,
|
|
1466
|
+
`🤖 Model: ${String(this.agentConfig.claude.model).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}`,
|
|
1443
1467
|
];
|
|
1444
|
-
if (
|
|
1468
|
+
if (context.contextUsedPct != null && context.contextUsedPct >= 80) {
|
|
1445
1469
|
lines.push('', '💡 Near limit — consider /compact');
|
|
1446
1470
|
}
|
|
1447
1471
|
lines.push('', 'Commands: /sessions /new /rename /clear /compact');
|
|
1448
|
-
return respond({ success: true, text: lines.join('\n'), format: 'html' });
|
|
1472
|
+
return respond({ success: true, sessionId: meta.id, text: lines.join('\n'), format: 'html' });
|
|
1449
1473
|
}
|
|
1450
1474
|
catch {
|
|
1451
1475
|
return respond({ success: false, text: 'Failed to get session info.' });
|
|
@@ -2189,6 +2213,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2189
2213
|
// size when the cap has been lowered.
|
|
2190
2214
|
const recoveryCount = this.tooLargeRecoveries.get(mapKey) ?? 0;
|
|
2191
2215
|
proc.historyLimit = this.spawnHistoryLimit(configuredMax, recoveryCount);
|
|
2216
|
+
proc.historyRecoveryActive = recoveryCount > 0;
|
|
2192
2217
|
if (recoveryCount > 0) {
|
|
2193
2218
|
this.logger.info('Spawning with reduced history after request_too_large', {
|
|
2194
2219
|
mapKey, recoveryCount, historyLimit: proc.historyLimit,
|
|
@@ -2800,6 +2825,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2800
2825
|
}
|
|
2801
2826
|
else if (content.startsWith('/clear')) {
|
|
2802
2827
|
await this.handleCommandClear(agentId, chatId);
|
|
2828
|
+
this.writeAutoForward(chatId, 'Claude Code context reset. Chat history is unchanged. The next message loads the latest 50 messages.');
|
|
2803
2829
|
}
|
|
2804
2830
|
else if (content.startsWith('/compact')) {
|
|
2805
2831
|
await this.handleCommandCompact(agentId, chatId);
|
|
@@ -2869,7 +2895,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2869
2895
|
}
|
|
2870
2896
|
if (!this.isDirectChat(meta)) {
|
|
2871
2897
|
this.writeAutoForward(chatId, 'Switching models is only available in a direct message with the bot — '
|
|
2872
|
-
+ 'it changes the model for every chat of this agent
|
|
2898
|
+
+ 'it changes the model for every chat of this agent. '
|
|
2873
2899
|
+ 'Use /models here to see the list.');
|
|
2874
2900
|
return;
|
|
2875
2901
|
}
|
|
@@ -2885,10 +2911,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2885
2911
|
return;
|
|
2886
2912
|
}
|
|
2887
2913
|
await this.setModel(match.id);
|
|
2888
|
-
//
|
|
2889
|
-
//
|
|
2914
|
+
// Legacy sessions need respawning; managed decisions pick up the new model
|
|
2915
|
+
// on their next spawn without terminating the response in progress.
|
|
2890
2916
|
const restarted = await this.restartChannelSessions();
|
|
2891
|
-
this.writeAutoForward(chatId, `Model set to ${match.label} (${match.id}).${restarted ? ' Restarting the session…' : ''}`);
|
|
2917
|
+
this.writeAutoForward(chatId, `Model set to ${match.label} (${match.id}).${restarted ? ' Restarting the session…' : ' Applies to the next response; current work continues.'}`);
|
|
2892
2918
|
}
|
|
2893
2919
|
/**
|
|
2894
2920
|
* /sessions — list all sessions for this chat.
|
|
@@ -2910,6 +2936,23 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2910
2936
|
/**
|
|
2911
2937
|
* /session — show info about the current active session.
|
|
2912
2938
|
*/
|
|
2939
|
+
async sessionContextInfo(sessionId, meta) {
|
|
2940
|
+
let context = this.orchestration?.sessionContextWindow(sessionId);
|
|
2941
|
+
// Legacy sessions do not write the orchestration ledger. Keep their measured
|
|
2942
|
+
// usage, but never resurrect stale metadata for an orchestration-owned session.
|
|
2943
|
+
if (!context && meta && !this.orchestration?.ownsSession(sessionId) &&
|
|
2944
|
+
Number.isFinite(meta.lastInputTokens) && meta.lastInputTokens >= 0 &&
|
|
2945
|
+
Number.isFinite(meta.lastActive) && Date.now() - meta.lastActive <= 3600000) {
|
|
2946
|
+
context = { used: meta.lastInputTokens, total: null, model: meta.model ?? this.agentConfig.claude.model };
|
|
2947
|
+
}
|
|
2948
|
+
if (!context)
|
|
2949
|
+
return { contextUsedPct: null, contextTokens: null, contextWindow: null, contextModel: null, text: '—' };
|
|
2950
|
+
const total = context.model ? await this.dashboardContextWindow(context.model) : null;
|
|
2951
|
+
const pct = total && total > 0 ? Math.round(context.used / total * 100) : null;
|
|
2952
|
+
const format = (n) => n >= 1000000 ? `${Number((n / 1000000).toFixed(2))}M` : n >= 1000 ? `${Number((n / 1000).toFixed(2))}K` : String(n);
|
|
2953
|
+
return { contextUsedPct: pct, contextTokens: context.used, contextWindow: total, contextModel: context.model,
|
|
2954
|
+
text: `${format(context.used)} / ${total == null ? '—' : format(total)}${pct == null ? '' : ` · ${pct}%`}` };
|
|
2955
|
+
}
|
|
2913
2956
|
async handleCommandSessionInfo(agentId, chatId) {
|
|
2914
2957
|
const index = await this.sessionStore.listSessions(agentId, chatId, this.channelFor(chatId));
|
|
2915
2958
|
const meta = index.sessions.find(s => s.id === index.activeSessionId);
|
|
@@ -2917,31 +2960,15 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2917
2960
|
this.writeAutoForward(chatId, 'No active session found.');
|
|
2918
2961
|
return;
|
|
2919
2962
|
}
|
|
2920
|
-
const
|
|
2921
|
-
const contextWindow = await this.contextWindowFor(effectiveModel);
|
|
2922
|
-
const contextTokens = meta.lastInputTokens ?? 0;
|
|
2923
|
-
const usedPct = Math.round((contextTokens / contextWindow) * 100);
|
|
2924
|
-
let msgs;
|
|
2925
|
-
if (meta.messageCount <= 0) {
|
|
2926
|
-
msgs = 'No messages yet';
|
|
2927
|
-
}
|
|
2928
|
-
else if ((meta.archivedCount ?? 0) > 0 && meta.loadedAtSpawn != null && meta.messageCountAtSpawn != null) {
|
|
2929
|
-
const newMessagesSinceSpawn = meta.messageCount - meta.messageCountAtSpawn;
|
|
2930
|
-
const inContext = meta.loadedAtSpawn + Math.max(0, newMessagesSinceSpawn);
|
|
2931
|
-
msgs = `${meta.messageCount} (${inContext} in context / ${meta.archivedCount} archived)`;
|
|
2932
|
-
}
|
|
2933
|
-
else {
|
|
2934
|
-
msgs = `${meta.messageCount}`;
|
|
2935
|
-
}
|
|
2936
|
-
const contextLine = `${usedPct}%`;
|
|
2963
|
+
const context = await this.sessionContextInfo(meta.id, meta);
|
|
2937
2964
|
const lines = [
|
|
2938
2965
|
`📌 Current Session: ${meta.name}`,
|
|
2939
2966
|
`<code>${index.activeSessionId}</code>`,
|
|
2940
2967
|
'',
|
|
2941
|
-
|
|
2942
|
-
|
|
2968
|
+
`👉 Context: ${context.text}`,
|
|
2969
|
+
`🤖 Model: ${String(this.agentConfig.claude.model).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}`,
|
|
2943
2970
|
];
|
|
2944
|
-
if (
|
|
2971
|
+
if (context.contextUsedPct != null && context.contextUsedPct >= 80) {
|
|
2945
2972
|
lines.push('', '💡 Near limit — consider /compact');
|
|
2946
2973
|
}
|
|
2947
2974
|
lines.push('', 'Commands: /sessions /new /rename /clear /compact');
|
|
@@ -3005,87 +3032,78 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3005
3032
|
const stopped = interrupted || hadActive || hadQueued || !!buffered;
|
|
3006
3033
|
this.writeAutoForward(chatId, stopped ? 'Stopped.' : 'No turn in progress.');
|
|
3007
3034
|
}
|
|
3008
|
-
/**
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3035
|
+
/** /clear rotates model context while preserving history, media and tasks. */
|
|
3036
|
+
async clearContext(sessionId) {
|
|
3037
|
+
if (this.compactingSessions.has(sessionId))
|
|
3038
|
+
throw new Error('Context maintenance is already running for this session.');
|
|
3039
|
+
this.compactingSessions.add(sessionId);
|
|
3040
|
+
try {
|
|
3041
|
+
const runtime = this.orchestration ?? (this.agentConfig.orchestration?.enabled ? await this.getOrchestration() : undefined);
|
|
3042
|
+
if (runtime?.isBusy(sessionId))
|
|
3043
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3044
|
+
const sessions = [...this.sessions.entries()].filter(([, process]) => process.sessionId === sessionId);
|
|
3045
|
+
if (sessions.some(([, process]) => process.isProcessing))
|
|
3046
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3047
|
+
for (const [key, process] of sessions) {
|
|
3048
|
+
await process.stop();
|
|
3049
|
+
if (this.sessions.get(key) === process)
|
|
3050
|
+
this.sessions.delete(key);
|
|
3051
|
+
}
|
|
3052
|
+
// No await between persisting the seed window and dropping the resume mapping.
|
|
3053
|
+
// The marker survives gateway restarts until a successful turn consumes it.
|
|
3054
|
+
if (runtime?.isBusy(sessionId))
|
|
3055
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3056
|
+
this.sessionStore.requestContextReset(this.agentConfig.id, sessionId);
|
|
3057
|
+
if (runtime?.ownsSession(sessionId))
|
|
3058
|
+
runtime.resetSessionContext(sessionId);
|
|
3059
|
+
for (const key of [sessionId, ...sessions.map(([key]) => key)]) {
|
|
3060
|
+
this.tooLargeRecoveries.delete(key);
|
|
3061
|
+
this.tooLargeExhausted.delete(key);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
finally {
|
|
3065
|
+
this.compactingSessions.delete(sessionId);
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3012
3068
|
async handleCommandClear(agentId, chatId) {
|
|
3013
|
-
const
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3069
|
+
const sessionId = await this.sessionStore.getActiveSessionId(agentId, chatId, this.channelFor(chatId));
|
|
3070
|
+
await this.clearContext(sessionId);
|
|
3071
|
+
}
|
|
3072
|
+
/** Native CLI compaction leaves canonical chat history intact. */
|
|
3073
|
+
async compactContext(sessionId, model) {
|
|
3074
|
+
if (this.compactingSessions.has(sessionId))
|
|
3075
|
+
throw new Error('Context compaction is already running for this session.');
|
|
3076
|
+
this.compactingSessions.add(sessionId);
|
|
3077
|
+
try {
|
|
3078
|
+
const runtime = this.orchestration ?? (this.agentConfig.orchestration?.enabled ? await this.getOrchestration() : undefined);
|
|
3079
|
+
if (runtime?.ownsSession(sessionId)) {
|
|
3080
|
+
await runtime.compactSession(sessionId, model);
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
const process = [...this.sessions.values()].find(p => p.sessionId === sessionId && p.isRunning());
|
|
3084
|
+
if (!process)
|
|
3085
|
+
throw new Error('No active Claude Code context to compact. Send a message first; chat history was not changed.');
|
|
3086
|
+
if (process.isProcessing)
|
|
3087
|
+
throw new Error('The agent is responding. Try /compact after the current response finishes.');
|
|
3088
|
+
if (this.gatewayConfig.gateway.headless === false)
|
|
3089
|
+
throw new Error('Native /compact currently requires the headless Claude Code backend. Chat history was not changed.');
|
|
3090
|
+
await (0, native_compact_1.startNativeCompact)(process, true).result;
|
|
3091
|
+
}
|
|
3092
|
+
finally {
|
|
3093
|
+
this.compactingSessions.delete(sessionId);
|
|
3094
|
+
}
|
|
3035
3095
|
}
|
|
3036
|
-
/**
|
|
3037
|
-
* /compact — summarise old history and keep only recent messages.
|
|
3038
|
-
*/
|
|
3039
3096
|
async handleCommandCompact(agentId, chatId) {
|
|
3040
3097
|
const ch = this.channelFor(chatId);
|
|
3041
3098
|
const sessionId = await this.sessionStore.getActiveSessionId(agentId, chatId, ch);
|
|
3042
|
-
|
|
3043
|
-
const meta = index.sessions.find(s => s.id === sessionId);
|
|
3044
|
-
const name = meta?.name ?? 'Session';
|
|
3045
|
-
const compactModel = this.agentConfig.claude.model;
|
|
3046
|
-
const contextWindow = await this.contextWindowFor(compactModel);
|
|
3047
|
-
this.writeAutoForward(chatId, `⏳ Compacting session "${name}"...`);
|
|
3099
|
+
this.writeAutoForward(chatId, '⏳ Compacting Claude Code context…');
|
|
3048
3100
|
try {
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
// update at each ~25% step so the channel doesn't look dead mid-compact.
|
|
3052
|
-
// Throttled (not per-chunk) to avoid spamming the chat on a 16+ chunk job.
|
|
3053
|
-
let lastReportedPct = -1;
|
|
3054
|
-
const onProgress = (done, total) => {
|
|
3055
|
-
if (total <= 1)
|
|
3056
|
-
return;
|
|
3057
|
-
const pct = Math.floor((done / total) * 100);
|
|
3058
|
-
if (pct - lastReportedPct >= 25) {
|
|
3059
|
-
lastReportedPct = pct;
|
|
3060
|
-
this.writeAutoForward(chatId, `⏳ Compacting session "${name}"... ${done}/${total} parts summarized`);
|
|
3061
|
-
}
|
|
3062
|
-
};
|
|
3063
|
-
const result = await compactor.compact(agentId, chatId, sessionId, compactModel, contextWindow, ch, onProgress);
|
|
3064
|
-
await this.sessionStore.updateSessionMeta(agentId, chatId, sessionId, {
|
|
3065
|
-
loadedAtSpawn: undefined,
|
|
3066
|
-
archivedCount: undefined,
|
|
3067
|
-
messageCountAtSpawn: undefined,
|
|
3068
|
-
}, ch);
|
|
3069
|
-
await this.restartProcess(chatId);
|
|
3070
|
-
const summary = [
|
|
3071
|
-
`✅ Session compacted`,
|
|
3072
|
-
'',
|
|
3073
|
-
`Before: ${result.beforeMessages} messages (~${result.beforeTokens.toLocaleString()} tokens) → ${result.contextPctBefore}% of context`,
|
|
3074
|
-
`After: ${result.afterMessages} messages (~${result.afterTokens.toLocaleString()} tokens) → ${result.contextPctAfter}% of context`,
|
|
3075
|
-
`Reduced by: ${result.reductionPct}%`,
|
|
3076
|
-
'',
|
|
3077
|
-
'Summary preserved. Full history before compaction is archived.',
|
|
3078
|
-
].join('\n');
|
|
3079
|
-
this.writeAutoForward(chatId, summary);
|
|
3101
|
+
await this.compactContext(sessionId);
|
|
3102
|
+
this.writeAutoForward(chatId, '✅ Claude Code context compacted. Chat history is unchanged.');
|
|
3080
3103
|
}
|
|
3081
3104
|
catch (err) {
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
}
|
|
3085
|
-
else {
|
|
3086
|
-
this.logger.error('Compact failed', { error: err.message });
|
|
3087
|
-
this.writeAutoForward(chatId, `❌ Compact failed: ${err.message}\n\nYour session history is unchanged.`);
|
|
3088
|
-
}
|
|
3105
|
+
this.logger.error('Compact failed', { error: err.message });
|
|
3106
|
+
this.writeAutoForward(chatId, 'Context compaction failed: ' + err.message + '\nChat history is unchanged.');
|
|
3089
3107
|
}
|
|
3090
3108
|
}
|
|
3091
3109
|
/**
|
|
@@ -3644,6 +3662,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3644
3662
|
await this.wechat?.resumeIfLinked();
|
|
3645
3663
|
this.startIdleCleaner();
|
|
3646
3664
|
this._startCleanupScheduler();
|
|
3665
|
+
this.startSessionCompaction();
|
|
3647
3666
|
this.logger.info('AgentRunner started', { agentId: this.agentConfig.id });
|
|
3648
3667
|
}
|
|
3649
3668
|
/**
|
|
@@ -3712,6 +3731,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3712
3731
|
newConfig = (0, gateway_config_1.applyGatewayOrchestration)(newConfig, this.gatewayConfig);
|
|
3713
3732
|
this.orchestration?.updateAgentConfig(newConfig);
|
|
3714
3733
|
this.agentConfig = newConfig;
|
|
3734
|
+
this.sessionCompactionScheduler?.start();
|
|
3715
3735
|
this.refreshTelegramCommands();
|
|
3716
3736
|
// Restart LineReplyManager if the LINE config changed so the live instance
|
|
3717
3737
|
// picks up a new access token, threshold, or labels without a full restart.
|
|
@@ -4097,6 +4117,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4097
4117
|
}
|
|
4098
4118
|
async stop() {
|
|
4099
4119
|
this.stopping = true;
|
|
4120
|
+
this.sessionCompactionScheduler?.stop();
|
|
4100
4121
|
if (this.orchestrationStarting) {
|
|
4101
4122
|
const orchestration = await this.orchestrationStarting.catch(() => undefined);
|
|
4102
4123
|
await orchestration?.close();
|
|
@@ -4161,9 +4182,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4161
4182
|
// Each precedence level is validated in turn — an invalid-but-present
|
|
4162
4183
|
// cleanupTimezone does not count as "set" and falls through to gw.timezone
|
|
4163
4184
|
// rather than jumping straight to UTC (issue #462 review).
|
|
4164
|
-
const cleanupTimezone = (0,
|
|
4185
|
+
const cleanupTimezone = (0, config_3.isValidTimezone)(gw.history?.cleanupTimezone)
|
|
4165
4186
|
? gw.history.cleanupTimezone
|
|
4166
|
-
: (0,
|
|
4187
|
+
: (0, config_3.isValidTimezone)(gw.timezone)
|
|
4167
4188
|
? gw.timezone
|
|
4168
4189
|
: 'UTC';
|
|
4169
4190
|
const agentMediaRoot = media_store_1.MediaStore.agentMediaRoot(this.agentsBaseDir, this.agentConfig.id);
|
|
@@ -4185,6 +4206,23 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4185
4206
|
isRunning() {
|
|
4186
4207
|
return this.receiver?.isRunning() ?? false;
|
|
4187
4208
|
}
|
|
4209
|
+
getDashboardSource() {
|
|
4210
|
+
return { workspace: this.agentConfig.type === 'app-agent' ? undefined : this.agentConfig.workspace, filename: path.join(this.agentDir, 'orchestration.db'), historyFilename: path.join(this.agentDir, 'history.db'),
|
|
4211
|
+
enabled: Boolean(this.orchestration || this.agentConfig.orchestration?.enabled),
|
|
4212
|
+
workspaceMode: this.agentConfig.type === 'app-agent' ? 'container' : this.agentConfig.orchestration?.tasks?.workspaceMode ?? 'host',
|
|
4213
|
+
maxWorkers: this.agentConfig.orchestration?.tasks?.maxConcurrentPerAgent ?? config_2.ORCHESTRATION_DEFAULTS.tasks.maxConcurrentPerAgent,
|
|
4214
|
+
idleTtlMs: this.agentConfig.orchestration?.tasks?.workerIdleTtlMs ?? config_2.ORCHESTRATION_DEFAULTS.tasks.workerIdleTtlMs };
|
|
4215
|
+
}
|
|
4216
|
+
async dashboardContextWindow(model) {
|
|
4217
|
+
const models = await this.availableModels();
|
|
4218
|
+
return models.find(m => m.id === model)?.contextWindow
|
|
4219
|
+
?? (this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS).find(m => m.id === model)?.contextWindow
|
|
4220
|
+
?? null;
|
|
4221
|
+
}
|
|
4222
|
+
getTokenReport(sessionId) {
|
|
4223
|
+
const report = this.orchestration?.tokenReport(sessionId);
|
|
4224
|
+
return report ? { ...report, backgroundReviews: this.historyDb.listReviewRuns().filter(run => run.sessionId === sessionId) } : undefined;
|
|
4225
|
+
}
|
|
4188
4226
|
getOrchestrationSummary() {
|
|
4189
4227
|
if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
|
|
4190
4228
|
return undefined;
|
|
@@ -4224,6 +4262,29 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4224
4262
|
}
|
|
4225
4263
|
return out;
|
|
4226
4264
|
}
|
|
4265
|
+
/**
|
|
4266
|
+
* Session IDs currently alive in the in-memory session map. Display-only
|
|
4267
|
+
* liveness signal for the dashboard: a session removed by the idle cleaner
|
|
4268
|
+
* (kept-alive window elapsed) or released is no longer present here.
|
|
4269
|
+
*/
|
|
4270
|
+
liveSessionIds() {
|
|
4271
|
+
return [...this.sessions.values()].filter(p => p.isRunning()).map(p => p.sessionId);
|
|
4272
|
+
}
|
|
4273
|
+
/**
|
|
4274
|
+
* Live status of one agent session derived from the in-memory session map:
|
|
4275
|
+
* - 'running' → alive and processing a turn right now
|
|
4276
|
+
* - 'idle' → alive and kept warm, not currently processing
|
|
4277
|
+
* - 'stopped' → gone from the map (idle-timeout kill or otherwise released)
|
|
4278
|
+
* Display-only; does not touch token accounting.
|
|
4279
|
+
*/
|
|
4280
|
+
isSessionCompacting(sessionId) { return this.orchestration?.isCompacting(sessionId) ?? false; }
|
|
4281
|
+
agentSessionLiveStatus(sessionId) {
|
|
4282
|
+
for (const p of this.sessions.values()) {
|
|
4283
|
+
if (p.sessionId === sessionId && p.isRunning())
|
|
4284
|
+
return p.isProcessing ? 'running' : 'idle';
|
|
4285
|
+
}
|
|
4286
|
+
return 'stopped';
|
|
4287
|
+
}
|
|
4227
4288
|
/**
|
|
4228
4289
|
* Send a message to an API session and wait for the response.
|
|
4229
4290
|
*
|
|
@@ -4526,7 +4587,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4526
4587
|
void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
|
|
4527
4588
|
turn.emit({ ...event, type: 'tool_use' }); })
|
|
4528
4589
|
.then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
|
|
4529
|
-
.catch(error =>
|
|
4590
|
+
.catch(error => {
|
|
4591
|
+
const publicError = (0, response_errors_1.presentedResponseError)(error);
|
|
4592
|
+
this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(publicError), publicError);
|
|
4593
|
+
});
|
|
4530
4594
|
return () => turn.detach(sink);
|
|
4531
4595
|
}
|
|
4532
4596
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
@@ -5022,10 +5086,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5022
5086
|
this.historyDb.insertMessage({ chatId: dbChatId, sessionId, source: 'api', role, content, ts: Date.now() });
|
|
5023
5087
|
};
|
|
5024
5088
|
// Persist the full user command before executing so it appears in history.
|
|
5025
|
-
|
|
5026
|
-
if (cmd !== '/clear') {
|
|
5027
|
-
persist('user', opts?.displayCommand ?? command);
|
|
5028
|
-
}
|
|
5089
|
+
persist('user', opts?.displayCommand ?? command);
|
|
5029
5090
|
let result;
|
|
5030
5091
|
let responseText;
|
|
5031
5092
|
let forcePersist = false;
|
|
@@ -5087,21 +5148,13 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5087
5148
|
// The api append path doesn't maintain the index messageCount, so count the flat store
|
|
5088
5149
|
// (the real conversation) directly rather than trusting meta.messageCount.
|
|
5089
5150
|
const messageCount = (await this.sessionStore.loadSession(agentId, sessionId).catch(() => [])).length;
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
result = { sessionId, sessionName: meta.name, messageCount, archivedCount: meta.archivedCount ?? 0, contextUsedPct, model: effectiveModel };
|
|
5098
|
-
responseText = [
|
|
5099
|
-
`Session: ${meta.name ?? '(unnamed)'}`,
|
|
5100
|
-
`Messages: ${messageCount}${(meta.archivedCount ?? 0) > 0 ? ` (${meta.archivedCount} archived)` : ''}`,
|
|
5101
|
-
`Context used: ${contextUsedPct}%`,
|
|
5102
|
-
`Model: ${effectiveModel}`,
|
|
5103
|
-
].join('\n');
|
|
5104
|
-
}
|
|
5151
|
+
const context = await this.sessionContextInfo(sessionId, meta);
|
|
5152
|
+
const { text: contextText, ...contextFields } = context;
|
|
5153
|
+
result = { sessionId, sessionName: meta?.name ?? null, messageCount, archivedCount: meta?.archivedCount ?? 0,
|
|
5154
|
+
...contextFields, model: effectiveModel };
|
|
5155
|
+
responseText = [`📌 Current Session: ${meta?.name ?? '(unnamed)'}`, sessionId, '', `👉 Context: ${contextText}`, `🤖 Model: ${effectiveModel}`,
|
|
5156
|
+
...(context.contextUsedPct != null && context.contextUsedPct >= 80 ? ['', '💡 Near limit — consider /compact'] : []),
|
|
5157
|
+
'', 'Commands: /sessions /new /rename /clear /compact'].join('\n');
|
|
5105
5158
|
}
|
|
5106
5159
|
else if (cmd === '/sessions') {
|
|
5107
5160
|
// Advertised for the api channel (BUILTIN_COMMANDS), so handle it here — mirrors the
|
|
@@ -5130,36 +5183,14 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5130
5183
|
}
|
|
5131
5184
|
}
|
|
5132
5185
|
else if (cmd === '/clear') {
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
totalTokensUsed: 0,
|
|
5137
|
-
lastInputTokens: 0,
|
|
5138
|
-
archivedCount: 0,
|
|
5139
|
-
loadedAtSpawn: undefined,
|
|
5140
|
-
messageCountAtSpawn: undefined,
|
|
5141
|
-
}, ch);
|
|
5142
|
-
const mediaPaths = this.historyDb.clearSession(dbChatId, sessionId);
|
|
5143
|
-
media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, agentId, mediaPaths);
|
|
5144
|
-
this.restartProcess(sessionId).catch(() => { });
|
|
5145
|
-
result = { success: true };
|
|
5146
|
-
responseText = 'Session cleared.';
|
|
5186
|
+
await this.clearContext(sessionId);
|
|
5187
|
+
result = { success: true, historyUnchanged: true, historyLimit: 50 };
|
|
5188
|
+
responseText = 'Claude Code context reset. Chat history is unchanged. Your next message starts a new context with the latest 50 messages.';
|
|
5147
5189
|
}
|
|
5148
5190
|
else if (cmd === '/compact') {
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
const contextWindow = await this.contextWindowFor(compactEffectiveModel);
|
|
5153
|
-
const compactor = new compactor_1.SessionCompactor(this.sessionStore);
|
|
5154
|
-
const compactResult = await compactor.compact(agentId, storeChatId, sessionId, compactEffectiveModel, contextWindow, ch);
|
|
5155
|
-
await this.sessionStore.updateSessionMeta(agentId, storeChatId, sessionId, {
|
|
5156
|
-
loadedAtSpawn: undefined,
|
|
5157
|
-
archivedCount: undefined,
|
|
5158
|
-
messageCountAtSpawn: undefined,
|
|
5159
|
-
}, ch);
|
|
5160
|
-
await this.restartProcess(sessionId);
|
|
5161
|
-
result = { success: true, keptMessages: compactResult.afterMessages, archivedMessages: compactResult.beforeMessages - compactResult.afterMessages };
|
|
5162
|
-
responseText = `Session compacted. Kept ${compactResult.afterMessages} messages, archived ${compactResult.beforeMessages - compactResult.afterMessages}.`;
|
|
5191
|
+
await this.compactContext(sessionId, opts?.model);
|
|
5192
|
+
result = { success: true, native: true, historyUnchanged: true };
|
|
5193
|
+
responseText = 'Claude Code context compacted. Chat history is unchanged.';
|
|
5163
5194
|
}
|
|
5164
5195
|
else {
|
|
5165
5196
|
// Passed the gate but has no dispatch branch — BUILTIN_COMMANDS drifted from this
|
|
@@ -5205,17 +5236,20 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5205
5236
|
return kind === 'direct' || kind === 'user';
|
|
5206
5237
|
}
|
|
5207
5238
|
/**
|
|
5208
|
-
* Restart
|
|
5239
|
+
* Restart legacy non-api sessions so a model change actually takes effect.
|
|
5209
5240
|
*
|
|
5210
5241
|
* `setModel` only rewrites config — a session process was spawned with the
|
|
5211
5242
|
* old model on its command line and keeps using it until it is restarted.
|
|
5212
5243
|
* Reporting "model set" without this is a false success: the next turn still
|
|
5213
|
-
* runs on the previous model.
|
|
5244
|
+
* runs on the previous model. Managed decisions already spawn per turn and
|
|
5245
|
+
* must finish with the model they started with. Returns whether anything restarted.
|
|
5214
5246
|
*/
|
|
5215
5247
|
async restartChannelSessions() {
|
|
5216
5248
|
const restartPromises = [];
|
|
5217
5249
|
for (const [key, session] of this.sessions) {
|
|
5218
|
-
|
|
5250
|
+
// Managed decisions spawn a new process on every turn. Killing the current
|
|
5251
|
+
// one here bypasses orchestration cancellation and reports PROCESS_EXITED.
|
|
5252
|
+
if (session.source !== 'api' && !session.runtimeProfile)
|
|
5219
5253
|
restartPromises.push(this.restartProcess(key));
|
|
5220
5254
|
}
|
|
5221
5255
|
await Promise.all(restartPromises);
|
|
@@ -5237,7 +5271,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5237
5271
|
* honest for a model that exists only upstream — before, any such model fell
|
|
5238
5272
|
* through to the 200k default no matter how large its real window.
|
|
5239
5273
|
*/
|
|
5240
|
-
async contextWindowFor(modelId) {
|
|
5274
|
+
async contextWindowFor(modelId, requireKnown = false) {
|
|
5241
5275
|
const models = await this.availableModels();
|
|
5242
5276
|
// Configured list second, not just as the catalog's fallback: the catalog
|
|
5243
5277
|
// *replaces* the list rather than merging with it, so a configured model
|
|
@@ -5247,7 +5281,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5247
5281
|
// SessionCompactor a window small enough to compact far too early.
|
|
5248
5282
|
return models.find((m) => m.id === modelId)?.contextWindow
|
|
5249
5283
|
?? (this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS).find((m) => m.id === modelId)?.contextWindow
|
|
5250
|
-
?? model_catalog_1.DEFAULT_CONTEXT_WINDOW;
|
|
5284
|
+
?? (requireKnown ? 0 : model_catalog_1.DEFAULT_CONTEXT_WINDOW);
|
|
5251
5285
|
}
|
|
5252
5286
|
/**
|
|
5253
5287
|
* Models `/model <arg>` may switch to: what the catalog offers, plus the
|
|
@@ -5320,16 +5354,14 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5320
5354
|
const meta = index?.sessions.find((s) => s.id === sessionId);
|
|
5321
5355
|
if (!meta)
|
|
5322
5356
|
return null;
|
|
5323
|
-
const
|
|
5324
|
-
const contextWindow = await this.contextWindowFor(effectiveModel);
|
|
5325
|
-
const contextUsedPct = Math.round(((meta.lastInputTokens ?? 0) / contextWindow) * 100);
|
|
5357
|
+
const { text: _contextText, ...context } = await this.sessionContextInfo(sessionId, meta);
|
|
5326
5358
|
return {
|
|
5327
5359
|
sessionId: meta.id,
|
|
5328
5360
|
sessionName: meta.name,
|
|
5329
5361
|
messageCount: meta.messageCount,
|
|
5330
5362
|
archivedCount: meta.archivedCount ?? 0,
|
|
5331
|
-
|
|
5332
|
-
model:
|
|
5363
|
+
...context,
|
|
5364
|
+
model: this.agentConfig.claude.model,
|
|
5333
5365
|
};
|
|
5334
5366
|
}
|
|
5335
5367
|
async updateApiSession(chatId, sessionId, updates) {
|
|
@@ -5421,7 +5453,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5421
5453
|
drain();
|
|
5422
5454
|
}
|
|
5423
5455
|
catch { /* Preserve the original stream/provider failure. */ }
|
|
5424
|
-
|
|
5456
|
+
const publicError = (0, response_errors_1.presentedResponseError)(error);
|
|
5457
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(publicError), publicError);
|
|
5425
5458
|
close();
|
|
5426
5459
|
};
|
|
5427
5460
|
void (async () => {
|