@0xmaxma/claude-gateway 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -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 +109 -9
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +211 -135
- package/dist/agent/runner.js.map +1 -1
- 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 +229 -12
- package/dist/api/gateway-router.js.map +1 -1
- 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 +7 -3
- package/dist/orchestration/bridge.d.ts.map +1 -1
- package/dist/orchestration/bridge.js +93 -66
- 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/channel-input-media.d.ts +6 -0
- package/dist/orchestration/channel-input-media.d.ts.map +1 -1
- package/dist/orchestration/channel-input-media.js +35 -11
- package/dist/orchestration/channel-input-media.js.map +1 -1
- package/dist/orchestration/channel-media-error.d.ts +11 -0
- package/dist/orchestration/channel-media-error.d.ts.map +1 -0
- package/dist/orchestration/channel-media-error.js +38 -0
- package/dist/orchestration/channel-media-error.js.map +1 -0
- package/dist/orchestration/channel-media.d.ts +1 -1
- package/dist/orchestration/channel-media.d.ts.map +1 -1
- package/dist/orchestration/channel-media.js +37 -7
- package/dist/orchestration/channel-media.js.map +1 -1
- 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 +1 -1
- package/dist/orchestration/conversation-intake.d.ts.map +1 -1
- package/dist/orchestration/conversation-intake.js +1 -1
- 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 +155 -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/delivery.d.ts +3 -0
- package/dist/orchestration/delivery.d.ts.map +1 -1
- package/dist/orchestration/delivery.js +14 -5
- package/dist/orchestration/delivery.js.map +1 -1
- 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 +56 -3
- package/dist/orchestration/inference-errors.js.map +1 -1
- package/dist/orchestration/mutation-recovery.d.ts +12 -0
- package/dist/orchestration/mutation-recovery.d.ts.map +1 -0
- package/dist/orchestration/mutation-recovery.js +41 -0
- package/dist/orchestration/mutation-recovery.js.map +1 -0
- package/dist/orchestration/native-compact.d.ts +5 -0
- package/dist/orchestration/native-compact.d.ts.map +1 -0
- package/dist/orchestration/native-compact.js +29 -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 +15 -2
- package/dist/orchestration/process-turn.d.ts.map +1 -1
- package/dist/orchestration/process-turn.js +103 -16
- 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 +69 -0
- package/dist/orchestration/runtime.d.ts.map +1 -1
- package/dist/orchestration/runtime.js +347 -98
- package/dist/orchestration/runtime.js.map +1 -1
- 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 +19 -0
- package/dist/orchestration/store.d.ts.map +1 -1
- package/dist/orchestration/store.js +44 -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 +125 -0
- package/dist/orchestration/token-ledger.d.ts.map +1 -0
- package/dist/orchestration/token-ledger.js +232 -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/ui/cli-viewer-ui.js +1 -1
- 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 +188 -0
- package/dist/ui/dashboard-client.js.map +1 -0
- package/dist/ui/dashboard-presentation.d.ts +9 -0
- package/dist/ui/dashboard-presentation.d.ts.map +1 -0
- package/dist/ui/dashboard-presentation.js +56 -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 +20 -0
- package/dist/ui/dashboard-range.js.map +1 -0
- package/dist/ui/dashboard-theme.d.ts +4 -0
- package/dist/ui/dashboard-theme.d.ts.map +1 -0
- package/dist/ui/dashboard-theme.js +485 -0
- package/dist/ui/dashboard-theme.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 +89 -0
- package/dist/ui/token-report.d.ts.map +1 -0
- package/dist/ui/token-report.js +200 -0
- package/dist/ui/token-report.js.map +1 -0
- package/dist/ui/web-ui.d.ts.map +1 -1
- package/dist/ui/web-ui.js +124 -181
- 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/discord/inbound.ts +1 -1
- package/mcp/tools/discord/receiver-server.ts +1 -1
- package/mcp/tools/discord/types.ts +1 -1
- package/mcp/tools/receiver-spool.test.ts +8 -2
- package/mcp/tools/receiver-spool.ts +198 -25
- package/mcp/tools/tasks/module.ts +1 -12
- package/mcp/tools/telegram/media-group.ts +3 -3
- package/mcp/tools/telegram/receiver-server.ts +15 -15
- package/mcp/types.ts +1 -1
- 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,6 +35,10 @@ 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 response_errors_1 = require("../orchestration/response-errors");
|
|
39
|
+
const channel_media_error_1 = require("../orchestration/channel-media-error");
|
|
40
|
+
const store_1 = require("../orchestration/store");
|
|
41
|
+
const config_1 = require("../orchestration/config");
|
|
38
42
|
const channel_input_media_1 = require("../orchestration/channel-input-media");
|
|
39
43
|
const chunks_1 = require("../telegram/chunks");
|
|
40
44
|
const file_delivery_1 = require("../orchestration/file-delivery");
|
|
@@ -53,8 +57,8 @@ const path = __importStar(require("path"));
|
|
|
53
57
|
const config_write_lock_1 = require("../config/config-write-lock");
|
|
54
58
|
const logger_1 = require("../logger");
|
|
55
59
|
const process_1 = require("../session/process");
|
|
56
|
-
const
|
|
57
|
-
const
|
|
60
|
+
const store_2 = require("../session/store");
|
|
61
|
+
const native_compact_1 = require("../orchestration/native-compact");
|
|
58
62
|
const receiver_1 = require("../telegram/receiver");
|
|
59
63
|
const receiver_2 = require("../discord/receiver");
|
|
60
64
|
const line_reply_manager_1 = require("./line-reply-manager");
|
|
@@ -71,7 +75,7 @@ const skills_1 = require("../skills");
|
|
|
71
75
|
const builtin_commands_1 = require("./builtin-commands");
|
|
72
76
|
const model_catalog_1 = require("./model-catalog");
|
|
73
77
|
const safe_mode_1 = require("./safe-mode");
|
|
74
|
-
const
|
|
78
|
+
const config_2 = require("./skill-learning/config");
|
|
75
79
|
const turn_stream_1 = require("./turn-stream");
|
|
76
80
|
const recovery_executor_1 = require("./recovery-executor");
|
|
77
81
|
const recovery_policy_1 = require("./recovery-policy");
|
|
@@ -346,9 +350,21 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
346
350
|
async subscribeResponseText(sessionId, principalId, receive) {
|
|
347
351
|
return (await this.getOrchestration()).subscribeText(sessionId, principalId, receive);
|
|
348
352
|
}
|
|
353
|
+
async setBrowserVoice(sessionId, principalId, enabled) {
|
|
354
|
+
return (await this.getOrchestration()).setBrowserVoice(sessionId, principalId, enabled);
|
|
355
|
+
}
|
|
356
|
+
async pendingVoiceSpeech(sessionId, principalId, claimed = []) {
|
|
357
|
+
return (await this.getOrchestration()).pendingVoiceSpeech(sessionId, principalId, claimed);
|
|
358
|
+
}
|
|
349
359
|
async voiceReplay(sessionId, principalId, responseId) {
|
|
350
360
|
return (await this.getOrchestration()).voiceAudio(sessionId, principalId, responseId);
|
|
351
361
|
}
|
|
362
|
+
async replayableVoiceResponses(sessionId, principalId) {
|
|
363
|
+
return (await this.getOrchestration()).replayableVoiceResponses(sessionId, principalId);
|
|
364
|
+
}
|
|
365
|
+
async voiceReplaySpeech(sessionId, principalId, responseId) {
|
|
366
|
+
return (await this.getOrchestration()).voiceReplaySpeech(sessionId, principalId, responseId);
|
|
367
|
+
}
|
|
352
368
|
saveVoiceReplay(sessionId, principalId, responseId, audio) {
|
|
353
369
|
this.orchestration?.saveVoiceAudio(sessionId, principalId, responseId, audio);
|
|
354
370
|
}
|
|
@@ -640,6 +656,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
640
656
|
}
|
|
641
657
|
});
|
|
642
658
|
};
|
|
659
|
+
this.compactingSessions = new Set();
|
|
643
660
|
this.telegramCommandConfig = '';
|
|
644
661
|
this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
|
|
645
662
|
this.gatewayConfig = gatewayConfig;
|
|
@@ -650,7 +667,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
650
667
|
// agentDir is workspace/.. — used for HistoryDB so DB is at <agentDir>/history.db
|
|
651
668
|
// This avoids requiring workspace to be nested at exactly <base>/<agentId>/workspace.
|
|
652
669
|
this.agentDir = path.resolve(agentConfig.workspace, '..');
|
|
653
|
-
this.sessionStore = new
|
|
670
|
+
this.sessionStore = new store_2.SessionStore(agentsBaseDir);
|
|
654
671
|
// config.json lives 3 levels above workspace: <base>/<agentId>/workspace -> <base>/config.json
|
|
655
672
|
this.configPath = path.resolve(agentConfig.workspace, '..', '..', '..', 'config.json');
|
|
656
673
|
this.historyDb = db_1.HistoryDB.forDir(this.agentDir, agentConfig.id);
|
|
@@ -746,6 +763,47 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
746
763
|
const channelSource = ((0, types_1.isChatChannel)(meta['source']) ? meta['source'] : 'telegram');
|
|
747
764
|
this.channelSourceMap.set(chatId, channelSource);
|
|
748
765
|
const channelOrchestration = this.orchestration?.ownsChannel(channelSource, chatId) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
|
|
766
|
+
// Resolve a receiver receipt before fetching media again: an ACK may have
|
|
767
|
+
// been lost after the original file was admitted or staging was removed.
|
|
768
|
+
let ingress;
|
|
769
|
+
if (channelOrchestration || meta.ingress_recovery_batch) {
|
|
770
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
771
|
+
const runtime = await this.getOrchestration();
|
|
772
|
+
const senderId = meta.user_id ?? meta.user ?? chatId;
|
|
773
|
+
const scope = { agentId: this.agentConfig.id, agentSessionId: sessionId, source: channelSource,
|
|
774
|
+
accountId: this.agentConfig.id, chatId, threadKey: channelSource === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.thread_ts ?? meta.message_thread_id ?? '', principalId: `${channelSource}:${senderId}` };
|
|
775
|
+
const { ingress_recovery_batch, ...originalMeta } = meta;
|
|
776
|
+
let recoveryBatch = ingress_recovery_batch, recoveryKey = meta.message_id;
|
|
777
|
+
const fingerprint = (0, store_1.payloadHash)({ content, meta: originalMeta });
|
|
778
|
+
const platformMessageIds = meta.message_ids_json ? JSON.parse(meta.message_ids_json) : undefined;
|
|
779
|
+
ingress = { runtime, scope, fingerprint, platformMessageIds };
|
|
780
|
+
const prior = runtime.store.channelReceipt(scope, meta.message_id, fingerprint, platformMessageIds);
|
|
781
|
+
if (prior && !prior.envelopeConflict) {
|
|
782
|
+
res.writeHead(200);
|
|
783
|
+
res.end('ok');
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (prior?.envelopeConflict) {
|
|
787
|
+
// Keep changed/legacy album envelopes without repeating accepted work.
|
|
788
|
+
recoveryKey = `channel-recovery:${fingerprint}`;
|
|
789
|
+
recoveryBatch = `conflict:${(0, store_1.payloadHash)(meta.message_id)}`;
|
|
790
|
+
const recovered = runtime.store.channelReceipt(scope, recoveryKey, fingerprint);
|
|
791
|
+
if (recovered && !recovered.envelopeConflict) {
|
|
792
|
+
res.writeHead(200);
|
|
793
|
+
res.end('recovered');
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (recoveryBatch) {
|
|
798
|
+
runtime.recoverChannelInput({ scope, text: `[Recovered queued message; not executed. A fresh request is required.]\n${content}`,
|
|
799
|
+
ingressKey: recoveryKey, trustedChannelMember: true,
|
|
800
|
+
metadata: { channelIngressFingerprint: fingerprint, recoveryBatch, recoveredChannelInput: { content, meta: originalMeta },
|
|
801
|
+
platformMessageId: meta.message_id, senderId, senderName: meta.sender_name } });
|
|
802
|
+
res.writeHead(200);
|
|
803
|
+
res.end('recovered');
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
749
807
|
if (channelSource !== 'telegram' && this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && /^\/sessions?(?:\s|$)/.test(content.trim())) {
|
|
750
808
|
const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, channelSource);
|
|
751
809
|
const current = index.sessions.find(session => session.id === index.activeSessionId);
|
|
@@ -805,16 +863,18 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
805
863
|
}
|
|
806
864
|
}
|
|
807
865
|
if (channelOrchestration && !(0, builtin_commands_1.isBuiltinCommand)(content.trim(), channelSource)) {
|
|
808
|
-
const
|
|
809
|
-
const
|
|
810
|
-
const { media, quoted, details } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta,
|
|
866
|
+
const orchestration = ingress.runtime;
|
|
867
|
+
const cleanup = [];
|
|
868
|
+
const { media, quoted, details, unavailable, primaryDirectMedia } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta, path => cleanup.push(path));
|
|
811
869
|
const senderId = meta.user_id ?? meta.user ?? chatId;
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
text: content || '[Attachment inspection requested]', attachmentIds: media, trustedChannelMember: true,
|
|
815
|
-
modality:
|
|
816
|
-
ingressKey: meta.message_id, metadata: { senderId, senderName: meta.sender_name, platformMessageId: meta.message_id, platformMessageIds:
|
|
870
|
+
const voiceNote = Boolean(primaryDirectMedia && ['voice', 'audio'].includes(meta.media_type ?? meta.attachment_kind));
|
|
871
|
+
const accepted = orchestration.submitInput({ scope: ingress.scope,
|
|
872
|
+
text: content || '[Attachment inspection requested]', attachmentIds: voiceNote ? [primaryDirectMedia, ...media.filter(ref => ref !== primaryDirectMedia)] : media, trustedChannelMember: true,
|
|
873
|
+
modality: voiceNote ? 'voice_note' : 'text',
|
|
874
|
+
ingressKey: meta.message_id, metadata: { channelIngressFingerprint: ingress.fingerprint, unavailableAttachments: unavailable, senderId, senderName: meta.sender_name, platformMessageId: meta.message_id, platformMessageIds: ingress.platformMessageIds, mediaGroupId: meta.media_group_id,
|
|
817
875
|
attachmentName: meta.attachment_name, mediaType: meta.media_type ?? meta.attachment_kind, repliedText: meta.replied_text, repliedMessageId: meta.replied_message_id, repliedSender: meta.replied_user, repliedAttachmentIds: quoted, attachmentDetails: details, attachmentError: meta.attachment_error } }, { execute: this.agentConfig.allow_tools !== false, writeMemory: this.agentConfig.allow_tools !== false });
|
|
876
|
+
for (const path of cleanup)
|
|
877
|
+
AgentRunner.discardEphemeralStaging(path);
|
|
818
878
|
// The durable receipt precedes receiver acknowledgment and inference.
|
|
819
879
|
res.writeHead(200);
|
|
820
880
|
res.end('ok');
|
|
@@ -862,6 +922,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
862
922
|
.then(() => this.writeTypingDone(chatId))
|
|
863
923
|
.catch((err) => {
|
|
864
924
|
this.logger.error('Session command failed', { error: err.message });
|
|
925
|
+
this.writeAutoForward(chatId, 'Command failed: ' + err.message);
|
|
865
926
|
this.writeTypingDone(chatId);
|
|
866
927
|
});
|
|
867
928
|
return;
|
|
@@ -894,13 +955,15 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
894
955
|
return;
|
|
895
956
|
}
|
|
896
957
|
catch (err) {
|
|
958
|
+
const mediaError = err instanceof channel_media_error_1.ChannelMediaError ? err : undefined;
|
|
959
|
+
const code = mediaError?.code ?? (err instanceof SyntaxError ? 'INVALID_CALLBACK_JSON' : 'CHANNEL_ADMISSION_FAILED');
|
|
897
960
|
if (!res.headersSent) {
|
|
898
|
-
|
|
899
|
-
|
|
961
|
+
if (mediaError?.retryAfterMs !== undefined)
|
|
962
|
+
res.setHeader('Retry-After', String(Math.ceil(mediaError.retryAfterMs / 1000)));
|
|
963
|
+
res.writeHead(err instanceof SyntaxError ? 400 : mediaError?.status === 429 ? 429 : 503);
|
|
964
|
+
res.end(JSON.stringify({ accepted: false, code, retryable: !(err instanceof SyntaxError) }));
|
|
900
965
|
}
|
|
901
|
-
this.logger.warn('
|
|
902
|
-
error: err.message,
|
|
903
|
-
});
|
|
966
|
+
this.logger.warn('Channel callback admission failed', { code, retryable: !(err instanceof SyntaxError), inputHash: (0, store_1.payloadHash)(raw).slice(0, 16) });
|
|
904
967
|
}
|
|
905
968
|
});
|
|
906
969
|
});
|
|
@@ -1328,7 +1391,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1328
1391
|
return;
|
|
1329
1392
|
}
|
|
1330
1393
|
const restarted = await this.restartChannelSessions();
|
|
1331
|
-
respond({ success: true, model: newModel, restarted });
|
|
1394
|
+
respond({ success: true, model: newModel, restarted, appliesNextTurn: !restarted });
|
|
1332
1395
|
return;
|
|
1333
1396
|
}
|
|
1334
1397
|
if (command === 'restart') {
|
|
@@ -2142,6 +2205,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2142
2205
|
// size when the cap has been lowered.
|
|
2143
2206
|
const recoveryCount = this.tooLargeRecoveries.get(mapKey) ?? 0;
|
|
2144
2207
|
proc.historyLimit = this.spawnHistoryLimit(configuredMax, recoveryCount);
|
|
2208
|
+
proc.historyRecoveryActive = recoveryCount > 0;
|
|
2145
2209
|
if (recoveryCount > 0) {
|
|
2146
2210
|
this.logger.info('Spawning with reduced history after request_too_large', {
|
|
2147
2211
|
mapKey, recoveryCount, historyLimit: proc.historyLimit,
|
|
@@ -2753,6 +2817,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2753
2817
|
}
|
|
2754
2818
|
else if (content.startsWith('/clear')) {
|
|
2755
2819
|
await this.handleCommandClear(agentId, chatId);
|
|
2820
|
+
this.writeAutoForward(chatId, 'Claude Code context reset. Chat history is unchanged. The next message loads the latest 50 messages.');
|
|
2756
2821
|
}
|
|
2757
2822
|
else if (content.startsWith('/compact')) {
|
|
2758
2823
|
await this.handleCommandCompact(agentId, chatId);
|
|
@@ -2822,7 +2887,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2822
2887
|
}
|
|
2823
2888
|
if (!this.isDirectChat(meta)) {
|
|
2824
2889
|
this.writeAutoForward(chatId, 'Switching models is only available in a direct message with the bot — '
|
|
2825
|
-
+ 'it changes the model for every chat of this agent
|
|
2890
|
+
+ 'it changes the model for every chat of this agent. '
|
|
2826
2891
|
+ 'Use /models here to see the list.');
|
|
2827
2892
|
return;
|
|
2828
2893
|
}
|
|
@@ -2838,10 +2903,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2838
2903
|
return;
|
|
2839
2904
|
}
|
|
2840
2905
|
await this.setModel(match.id);
|
|
2841
|
-
//
|
|
2842
|
-
//
|
|
2906
|
+
// Legacy sessions need respawning; managed decisions pick up the new model
|
|
2907
|
+
// on their next spawn without terminating the response in progress.
|
|
2843
2908
|
const restarted = await this.restartChannelSessions();
|
|
2844
|
-
this.writeAutoForward(chatId, `Model set to ${match.label} (${match.id}).${restarted ? ' Restarting the session…' : ''}`);
|
|
2909
|
+
this.writeAutoForward(chatId, `Model set to ${match.label} (${match.id}).${restarted ? ' Restarting the session…' : ' Applies to the next response; current work continues.'}`);
|
|
2845
2910
|
}
|
|
2846
2911
|
/**
|
|
2847
2912
|
* /sessions — list all sessions for this chat.
|
|
@@ -2958,87 +3023,76 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2958
3023
|
const stopped = interrupted || hadActive || hadQueued || !!buffered;
|
|
2959
3024
|
this.writeAutoForward(chatId, stopped ? 'Stopped.' : 'No turn in progress.');
|
|
2960
3025
|
}
|
|
2961
|
-
/**
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3026
|
+
/** /clear rotates model context while preserving history, media and tasks. */
|
|
3027
|
+
async clearContext(sessionId) {
|
|
3028
|
+
if (this.compactingSessions.has(sessionId))
|
|
3029
|
+
throw new Error('Context maintenance is already running for this session.');
|
|
3030
|
+
this.compactingSessions.add(sessionId);
|
|
3031
|
+
try {
|
|
3032
|
+
const runtime = this.orchestration ?? (this.agentConfig.orchestration?.enabled ? await this.getOrchestration() : undefined);
|
|
3033
|
+
if (runtime?.isBusy(sessionId))
|
|
3034
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3035
|
+
const sessions = [...this.sessions.entries()].filter(([, process]) => process.sessionId === sessionId);
|
|
3036
|
+
if (sessions.some(([, process]) => process.isProcessing))
|
|
3037
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3038
|
+
for (const [key, process] of sessions) {
|
|
3039
|
+
await process.stop();
|
|
3040
|
+
if (this.sessions.get(key) === process)
|
|
3041
|
+
this.sessions.delete(key);
|
|
3042
|
+
}
|
|
3043
|
+
// No await between persisting the seed window and dropping the resume mapping.
|
|
3044
|
+
// The marker survives gateway restarts until a successful turn consumes it.
|
|
3045
|
+
if (runtime?.isBusy(sessionId))
|
|
3046
|
+
throw new Error('The agent is responding. Try /clear after the current response finishes.');
|
|
3047
|
+
this.sessionStore.requestContextReset(this.agentConfig.id, sessionId);
|
|
3048
|
+
if (runtime?.ownsSession(sessionId))
|
|
3049
|
+
runtime.resetSessionContext(sessionId);
|
|
3050
|
+
for (const key of [sessionId, ...sessions.map(([key]) => key)]) {
|
|
3051
|
+
this.tooLargeRecoveries.delete(key);
|
|
3052
|
+
this.tooLargeExhausted.delete(key);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
finally {
|
|
3056
|
+
this.compactingSessions.delete(sessionId);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
2965
3059
|
async handleCommandClear(agentId, chatId) {
|
|
2966
|
-
const
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3060
|
+
const sessionId = await this.sessionStore.getActiveSessionId(agentId, chatId, this.channelFor(chatId));
|
|
3061
|
+
await this.clearContext(sessionId);
|
|
3062
|
+
}
|
|
3063
|
+
/** Native CLI compaction leaves canonical chat history intact. */
|
|
3064
|
+
async compactContext(sessionId, model) {
|
|
3065
|
+
if (this.compactingSessions.has(sessionId))
|
|
3066
|
+
throw new Error('Context compaction is already running for this session.');
|
|
3067
|
+
this.compactingSessions.add(sessionId);
|
|
3068
|
+
try {
|
|
3069
|
+
const runtime = this.orchestration ?? (this.agentConfig.orchestration?.enabled ? await this.getOrchestration() : undefined);
|
|
3070
|
+
if (runtime?.ownsSession(sessionId))
|
|
3071
|
+
return await runtime.compactSession(sessionId, model);
|
|
3072
|
+
const process = [...this.sessions.values()].find(p => p.sessionId === sessionId && p.isRunning());
|
|
3073
|
+
if (!process)
|
|
3074
|
+
throw new Error('No active Claude Code context to compact. Send a message first; chat history was not changed.');
|
|
3075
|
+
if (process.isProcessing)
|
|
3076
|
+
throw new Error('The agent is responding. Try /compact after the current response finishes.');
|
|
3077
|
+
if (this.gatewayConfig.gateway.headless === false)
|
|
3078
|
+
throw new Error('Native /compact currently requires the headless Claude Code backend. Chat history was not changed.');
|
|
3079
|
+
await (0, native_compact_1.startNativeCompact)(process, true).result;
|
|
3080
|
+
}
|
|
3081
|
+
finally {
|
|
3082
|
+
this.compactingSessions.delete(sessionId);
|
|
3083
|
+
}
|
|
2988
3084
|
}
|
|
2989
|
-
/**
|
|
2990
|
-
* /compact — summarise old history and keep only recent messages.
|
|
2991
|
-
*/
|
|
2992
3085
|
async handleCommandCompact(agentId, chatId) {
|
|
2993
3086
|
const ch = this.channelFor(chatId);
|
|
2994
3087
|
const sessionId = await this.sessionStore.getActiveSessionId(agentId, chatId, ch);
|
|
2995
|
-
|
|
2996
|
-
const meta = index.sessions.find(s => s.id === sessionId);
|
|
2997
|
-
const name = meta?.name ?? 'Session';
|
|
2998
|
-
const compactModel = this.agentConfig.claude.model;
|
|
2999
|
-
const contextWindow = await this.contextWindowFor(compactModel);
|
|
3000
|
-
this.writeAutoForward(chatId, `⏳ Compacting session "${name}"...`);
|
|
3088
|
+
this.writeAutoForward(chatId, '⏳ Compacting Claude Code context…');
|
|
3001
3089
|
try {
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
// update at each ~25% step so the channel doesn't look dead mid-compact.
|
|
3005
|
-
// Throttled (not per-chunk) to avoid spamming the chat on a 16+ chunk job.
|
|
3006
|
-
let lastReportedPct = -1;
|
|
3007
|
-
const onProgress = (done, total) => {
|
|
3008
|
-
if (total <= 1)
|
|
3009
|
-
return;
|
|
3010
|
-
const pct = Math.floor((done / total) * 100);
|
|
3011
|
-
if (pct - lastReportedPct >= 25) {
|
|
3012
|
-
lastReportedPct = pct;
|
|
3013
|
-
this.writeAutoForward(chatId, `⏳ Compacting session "${name}"... ${done}/${total} parts summarized`);
|
|
3014
|
-
}
|
|
3015
|
-
};
|
|
3016
|
-
const result = await compactor.compact(agentId, chatId, sessionId, compactModel, contextWindow, ch, onProgress);
|
|
3017
|
-
await this.sessionStore.updateSessionMeta(agentId, chatId, sessionId, {
|
|
3018
|
-
loadedAtSpawn: undefined,
|
|
3019
|
-
archivedCount: undefined,
|
|
3020
|
-
messageCountAtSpawn: undefined,
|
|
3021
|
-
}, ch);
|
|
3022
|
-
await this.restartProcess(chatId);
|
|
3023
|
-
const summary = [
|
|
3024
|
-
`✅ Session compacted`,
|
|
3025
|
-
'',
|
|
3026
|
-
`Before: ${result.beforeMessages} messages (~${result.beforeTokens.toLocaleString()} tokens) → ${result.contextPctBefore}% of context`,
|
|
3027
|
-
`After: ${result.afterMessages} messages (~${result.afterTokens.toLocaleString()} tokens) → ${result.contextPctAfter}% of context`,
|
|
3028
|
-
`Reduced by: ${result.reductionPct}%`,
|
|
3029
|
-
'',
|
|
3030
|
-
'Summary preserved. Full history before compaction is archived.',
|
|
3031
|
-
].join('\n');
|
|
3032
|
-
this.writeAutoForward(chatId, summary);
|
|
3090
|
+
await this.compactContext(sessionId);
|
|
3091
|
+
this.writeAutoForward(chatId, '✅ Claude Code context compacted. Chat history is unchanged.');
|
|
3033
3092
|
}
|
|
3034
3093
|
catch (err) {
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
}
|
|
3038
|
-
else {
|
|
3039
|
-
this.logger.error('Compact failed', { error: err.message });
|
|
3040
|
-
this.writeAutoForward(chatId, `❌ Compact failed: ${err.message}\n\nYour session history is unchanged.`);
|
|
3041
|
-
}
|
|
3094
|
+
this.logger.error('Compact failed', { error: err.message });
|
|
3095
|
+
this.writeAutoForward(chatId, 'Context compaction failed: ' + err.message + '\nChat history is unchanged.');
|
|
3042
3096
|
}
|
|
3043
3097
|
}
|
|
3044
3098
|
/**
|
|
@@ -4114,9 +4168,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4114
4168
|
// Each precedence level is validated in turn — an invalid-but-present
|
|
4115
4169
|
// cleanupTimezone does not count as "set" and falls through to gw.timezone
|
|
4116
4170
|
// rather than jumping straight to UTC (issue #462 review).
|
|
4117
|
-
const cleanupTimezone = (0,
|
|
4171
|
+
const cleanupTimezone = (0, config_2.isValidTimezone)(gw.history?.cleanupTimezone)
|
|
4118
4172
|
? gw.history.cleanupTimezone
|
|
4119
|
-
: (0,
|
|
4173
|
+
: (0, config_2.isValidTimezone)(gw.timezone)
|
|
4120
4174
|
? gw.timezone
|
|
4121
4175
|
: 'UTC';
|
|
4122
4176
|
const agentMediaRoot = media_store_1.MediaStore.agentMediaRoot(this.agentsBaseDir, this.agentConfig.id);
|
|
@@ -4138,6 +4192,23 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4138
4192
|
isRunning() {
|
|
4139
4193
|
return this.receiver?.isRunning() ?? false;
|
|
4140
4194
|
}
|
|
4195
|
+
getDashboardSource() {
|
|
4196
|
+
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'),
|
|
4197
|
+
enabled: Boolean(this.orchestration || this.agentConfig.orchestration?.enabled),
|
|
4198
|
+
workspaceMode: this.agentConfig.type === 'app-agent' ? 'container' : this.agentConfig.orchestration?.tasks?.workspaceMode ?? 'host',
|
|
4199
|
+
maxWorkers: this.agentConfig.orchestration?.tasks?.maxConcurrentPerAgent ?? config_1.ORCHESTRATION_DEFAULTS.tasks.maxConcurrentPerAgent,
|
|
4200
|
+
idleTtlMs: this.agentConfig.orchestration?.tasks?.workerIdleTtlMs ?? config_1.ORCHESTRATION_DEFAULTS.tasks.workerIdleTtlMs };
|
|
4201
|
+
}
|
|
4202
|
+
async dashboardContextWindow(model) {
|
|
4203
|
+
const models = await this.availableModels();
|
|
4204
|
+
return models.find(m => m.id === model)?.contextWindow
|
|
4205
|
+
?? (this.gatewayConfig.gateway.models ?? exports.DEFAULT_MODELS).find(m => m.id === model)?.contextWindow
|
|
4206
|
+
?? null;
|
|
4207
|
+
}
|
|
4208
|
+
getTokenReport(sessionId) {
|
|
4209
|
+
const report = this.orchestration?.tokenReport(sessionId);
|
|
4210
|
+
return report ? { ...report, backgroundReviews: this.historyDb.listReviewRuns().filter(run => run.sessionId === sessionId) } : undefined;
|
|
4211
|
+
}
|
|
4141
4212
|
getOrchestrationSummary() {
|
|
4142
4213
|
if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
|
|
4143
4214
|
return undefined;
|
|
@@ -4177,6 +4248,29 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4177
4248
|
}
|
|
4178
4249
|
return out;
|
|
4179
4250
|
}
|
|
4251
|
+
/**
|
|
4252
|
+
* Session IDs currently alive in the in-memory session map. Display-only
|
|
4253
|
+
* liveness signal for the dashboard: a session removed by the idle cleaner
|
|
4254
|
+
* (kept-alive window elapsed) or released is no longer present here.
|
|
4255
|
+
*/
|
|
4256
|
+
liveSessionIds() {
|
|
4257
|
+
return [...this.sessions.values()].filter(p => p.isRunning()).map(p => p.sessionId);
|
|
4258
|
+
}
|
|
4259
|
+
/**
|
|
4260
|
+
* Live status of one agent session derived from the in-memory session map:
|
|
4261
|
+
* - 'running' → alive and processing a turn right now
|
|
4262
|
+
* - 'idle' → alive and kept warm, not currently processing
|
|
4263
|
+
* - 'stopped' → gone from the map (idle-timeout kill or otherwise released)
|
|
4264
|
+
* Display-only; does not touch token accounting.
|
|
4265
|
+
*/
|
|
4266
|
+
isSessionCompacting(sessionId) { return this.orchestration?.isCompacting(sessionId) ?? false; }
|
|
4267
|
+
agentSessionLiveStatus(sessionId) {
|
|
4268
|
+
for (const p of this.sessions.values()) {
|
|
4269
|
+
if (p.sessionId === sessionId && p.isRunning())
|
|
4270
|
+
return p.isProcessing ? 'running' : 'idle';
|
|
4271
|
+
}
|
|
4272
|
+
return 'stopped';
|
|
4273
|
+
}
|
|
4180
4274
|
/**
|
|
4181
4275
|
* Send a message to an API session and wait for the response.
|
|
4182
4276
|
*
|
|
@@ -4479,7 +4573,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4479
4573
|
void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
|
|
4480
4574
|
turn.emit({ ...event, type: 'tool_use' }); })
|
|
4481
4575
|
.then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
|
|
4482
|
-
.catch(error =>
|
|
4576
|
+
.catch(error => {
|
|
4577
|
+
const publicError = (0, response_errors_1.presentedResponseError)(error);
|
|
4578
|
+
this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(publicError), publicError);
|
|
4579
|
+
});
|
|
4483
4580
|
return () => turn.detach(sink);
|
|
4484
4581
|
}
|
|
4485
4582
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
@@ -4975,10 +5072,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4975
5072
|
this.historyDb.insertMessage({ chatId: dbChatId, sessionId, source: 'api', role, content, ts: Date.now() });
|
|
4976
5073
|
};
|
|
4977
5074
|
// Persist the full user command before executing so it appears in history.
|
|
4978
|
-
|
|
4979
|
-
if (cmd !== '/clear') {
|
|
4980
|
-
persist('user', opts?.displayCommand ?? command);
|
|
4981
|
-
}
|
|
5075
|
+
persist('user', opts?.displayCommand ?? command);
|
|
4982
5076
|
let result;
|
|
4983
5077
|
let responseText;
|
|
4984
5078
|
let forcePersist = false;
|
|
@@ -5083,36 +5177,14 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5083
5177
|
}
|
|
5084
5178
|
}
|
|
5085
5179
|
else if (cmd === '/clear') {
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
totalTokensUsed: 0,
|
|
5090
|
-
lastInputTokens: 0,
|
|
5091
|
-
archivedCount: 0,
|
|
5092
|
-
loadedAtSpawn: undefined,
|
|
5093
|
-
messageCountAtSpawn: undefined,
|
|
5094
|
-
}, ch);
|
|
5095
|
-
const mediaPaths = this.historyDb.clearSession(dbChatId, sessionId);
|
|
5096
|
-
media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, agentId, mediaPaths);
|
|
5097
|
-
this.restartProcess(sessionId).catch(() => { });
|
|
5098
|
-
result = { success: true };
|
|
5099
|
-
responseText = 'Session cleared.';
|
|
5180
|
+
await this.clearContext(sessionId);
|
|
5181
|
+
result = { success: true, historyUnchanged: true, historyLimit: 50 };
|
|
5182
|
+
responseText = 'Claude Code context reset. Chat history is unchanged. Your next message starts a new context with the latest 50 messages.';
|
|
5100
5183
|
}
|
|
5101
5184
|
else if (cmd === '/compact') {
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
const contextWindow = await this.contextWindowFor(compactEffectiveModel);
|
|
5106
|
-
const compactor = new compactor_1.SessionCompactor(this.sessionStore);
|
|
5107
|
-
const compactResult = await compactor.compact(agentId, storeChatId, sessionId, compactEffectiveModel, contextWindow, ch);
|
|
5108
|
-
await this.sessionStore.updateSessionMeta(agentId, storeChatId, sessionId, {
|
|
5109
|
-
loadedAtSpawn: undefined,
|
|
5110
|
-
archivedCount: undefined,
|
|
5111
|
-
messageCountAtSpawn: undefined,
|
|
5112
|
-
}, ch);
|
|
5113
|
-
await this.restartProcess(sessionId);
|
|
5114
|
-
result = { success: true, keptMessages: compactResult.afterMessages, archivedMessages: compactResult.beforeMessages - compactResult.afterMessages };
|
|
5115
|
-
responseText = `Session compacted. Kept ${compactResult.afterMessages} messages, archived ${compactResult.beforeMessages - compactResult.afterMessages}.`;
|
|
5185
|
+
await this.compactContext(sessionId, opts?.model);
|
|
5186
|
+
result = { success: true, native: true, historyUnchanged: true };
|
|
5187
|
+
responseText = 'Claude Code context compacted. Chat history is unchanged.';
|
|
5116
5188
|
}
|
|
5117
5189
|
else {
|
|
5118
5190
|
// Passed the gate but has no dispatch branch — BUILTIN_COMMANDS drifted from this
|
|
@@ -5158,17 +5230,20 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5158
5230
|
return kind === 'direct' || kind === 'user';
|
|
5159
5231
|
}
|
|
5160
5232
|
/**
|
|
5161
|
-
* Restart
|
|
5233
|
+
* Restart legacy non-api sessions so a model change actually takes effect.
|
|
5162
5234
|
*
|
|
5163
5235
|
* `setModel` only rewrites config — a session process was spawned with the
|
|
5164
5236
|
* old model on its command line and keeps using it until it is restarted.
|
|
5165
5237
|
* Reporting "model set" without this is a false success: the next turn still
|
|
5166
|
-
* runs on the previous model.
|
|
5238
|
+
* runs on the previous model. Managed decisions already spawn per turn and
|
|
5239
|
+
* must finish with the model they started with. Returns whether anything restarted.
|
|
5167
5240
|
*/
|
|
5168
5241
|
async restartChannelSessions() {
|
|
5169
5242
|
const restartPromises = [];
|
|
5170
5243
|
for (const [key, session] of this.sessions) {
|
|
5171
|
-
|
|
5244
|
+
// Managed decisions spawn a new process on every turn. Killing the current
|
|
5245
|
+
// one here bypasses orchestration cancellation and reports PROCESS_EXITED.
|
|
5246
|
+
if (session.source !== 'api' && !session.runtimeProfile)
|
|
5172
5247
|
restartPromises.push(this.restartProcess(key));
|
|
5173
5248
|
}
|
|
5174
5249
|
await Promise.all(restartPromises);
|
|
@@ -5298,7 +5373,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5298
5373
|
async deleteApiSession(chatId, sessionId) {
|
|
5299
5374
|
await this.sessionStore.deleteTelegramSession(this.agentConfig.id, chatId, sessionId, 'api').catch((err) => {
|
|
5300
5375
|
// Legacy sessions (stored only in SQLite, no file index entry) are treated as already deleted
|
|
5301
|
-
if (err instanceof
|
|
5376
|
+
if (err instanceof store_2.SessionNotInIndexError)
|
|
5302
5377
|
return;
|
|
5303
5378
|
throw err;
|
|
5304
5379
|
});
|
|
@@ -5374,7 +5449,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
5374
5449
|
drain();
|
|
5375
5450
|
}
|
|
5376
5451
|
catch { /* Preserve the original stream/provider failure. */ }
|
|
5377
|
-
|
|
5452
|
+
const publicError = (0, response_errors_1.presentedResponseError)(error);
|
|
5453
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(publicError), publicError);
|
|
5378
5454
|
close();
|
|
5379
5455
|
};
|
|
5380
5456
|
void (async () => {
|