@botcord/daemon 0.2.49 → 0.2.50
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/dist/gateway/dispatcher.js +31 -6
- package/dist/gateway/index.d.ts +2 -0
- package/dist/gateway/index.js +2 -0
- package/dist/gateway/runtimes/deepseek-tui.d.ts +44 -0
- package/dist/gateway/runtimes/deepseek-tui.js +560 -0
- package/dist/gateway/runtimes/kimi.d.ts +32 -0
- package/dist/gateway/runtimes/kimi.js +204 -0
- package/dist/gateway/runtimes/registry.d.ts +4 -0
- package/dist/gateway/runtimes/registry.js +23 -0
- package/package.json +1 -1
- package/src/gateway/__tests__/deepseek-tui-adapter.test.ts +212 -0
- package/src/gateway/__tests__/dispatcher.test.ts +53 -3
- package/src/gateway/__tests__/kimi-adapter.test.ts +174 -0
- package/src/gateway/dispatcher.ts +31 -6
- package/src/gateway/index.ts +6 -0
- package/src/gateway/runtimes/deepseek-tui.ts +640 -0
- package/src/gateway/runtimes/kimi.ts +245 -0
- package/src/gateway/runtimes/registry.ts +26 -0
|
@@ -24,6 +24,13 @@ const MAX_BATCH_BUFFER_CHARS = 16000;
|
|
|
24
24
|
* cancel-previous bursts on a fast user can otherwise trip 429 silently.
|
|
25
25
|
*/
|
|
26
26
|
const TYPING_DEBOUNCE_MS = 2000;
|
|
27
|
+
/**
|
|
28
|
+
* Most provider typing APIs are short-lived one-shots. Telegram's
|
|
29
|
+
* `sendChatAction(typing)`, for example, must be refreshed while the runtime
|
|
30
|
+
* is still working or the visible typing indicator disappears before the
|
|
31
|
+
* reply lands.
|
|
32
|
+
*/
|
|
33
|
+
const TYPING_REFRESH_MS = 4000;
|
|
27
34
|
/** LRU cap on the typing-recency map so long-running daemons don't grow unbounded. */
|
|
28
35
|
const TYPING_RECENCY_CAP = 1024;
|
|
29
36
|
/**
|
|
@@ -591,7 +598,9 @@ export class Dispatcher {
|
|
|
591
598
|
const trustLevel = route.trustLevel ?? "trusted";
|
|
592
599
|
const streamable = msg.trace?.streamable === true;
|
|
593
600
|
const traceId = msg.trace?.id;
|
|
594
|
-
const canType =
|
|
601
|
+
const canType = typeof traceId === "string" &&
|
|
602
|
+
typeof channel.typing === "function" &&
|
|
603
|
+
(streamable || !isBotCordChannel(channel));
|
|
595
604
|
const canStream = streamable && typeof traceId === "string" && typeof channel.streamBlock === "function";
|
|
596
605
|
const recordBlock = (block) => {
|
|
597
606
|
const summary = { type: block.kind };
|
|
@@ -611,7 +620,8 @@ export class Dispatcher {
|
|
|
611
620
|
// arrival), and `thinking` is auto-synthesized on the first non-assistant
|
|
612
621
|
// block so adapters that emit nothing-but-blocks still drive the
|
|
613
622
|
// "Thinking..." UI.
|
|
614
|
-
let
|
|
623
|
+
let typingLoopStarted = false;
|
|
624
|
+
let typingRefreshTimer = null;
|
|
615
625
|
let thinkingActive = false;
|
|
616
626
|
/**
|
|
617
627
|
* Sticky: once we've forwarded any assistant_text to the wire, we stop
|
|
@@ -670,10 +680,9 @@ export class Dispatcher {
|
|
|
670
680
|
// actually emitted, not daemon-synthesized lifecycle frames.
|
|
671
681
|
forwardBlockToChannel(synth);
|
|
672
682
|
};
|
|
673
|
-
const
|
|
674
|
-
if (!canType
|
|
683
|
+
const sendTypingPing = () => {
|
|
684
|
+
if (!canType)
|
|
675
685
|
return;
|
|
676
|
-
typingFired = true;
|
|
677
686
|
const key = `${msg.accountId}:${msg.conversation.id}`;
|
|
678
687
|
const now = Date.now();
|
|
679
688
|
const last = this.recentTypingPings.get(key);
|
|
@@ -719,7 +728,22 @@ export class Dispatcher {
|
|
|
719
728
|
});
|
|
720
729
|
}
|
|
721
730
|
};
|
|
722
|
-
const
|
|
731
|
+
const fireTypingIfNeeded = () => {
|
|
732
|
+
if (!canType || typingLoopStarted)
|
|
733
|
+
return;
|
|
734
|
+
typingLoopStarted = true;
|
|
735
|
+
sendTypingPing();
|
|
736
|
+
typingRefreshTimer = setInterval(sendTypingPing, TYPING_REFRESH_MS);
|
|
737
|
+
if (typeof typingRefreshTimer.unref === "function")
|
|
738
|
+
typingRefreshTimer.unref();
|
|
739
|
+
};
|
|
740
|
+
const stopTypingRefresh = () => {
|
|
741
|
+
if (!typingRefreshTimer)
|
|
742
|
+
return;
|
|
743
|
+
clearInterval(typingRefreshTimer);
|
|
744
|
+
typingRefreshTimer = null;
|
|
745
|
+
};
|
|
746
|
+
const onStatus = canType || canStream
|
|
723
747
|
? (event) => {
|
|
724
748
|
// Drop runtime callbacks after this turn's controller aborts —
|
|
725
749
|
// NDJSON/ACP adapters keep parsing stdout until the child exits
|
|
@@ -1123,6 +1147,7 @@ export class Dispatcher {
|
|
|
1123
1147
|
});
|
|
1124
1148
|
}
|
|
1125
1149
|
finally {
|
|
1150
|
+
stopTypingRefresh();
|
|
1126
1151
|
// Emit a final thinking.stopped on terminal paths so the frontend
|
|
1127
1152
|
// never sticks at "Thinking..." when no assistant_text ever landed
|
|
1128
1153
|
// (timeout, error, gated reply). Skipped on cancel-previous: the
|
package/dist/gateway/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export { createTranscriptWriter, resolveTranscriptEnabled, defaultTranscriptRoot
|
|
|
12
12
|
export { safePathSegment, transcriptFilePath, transcriptRoomDir, transcriptAgentRoot, } from "./transcript-paths.js";
|
|
13
13
|
export { ClaudeCodeAdapter, probeClaude, resolveClaudeCommand, } from "./runtimes/claude-code.js";
|
|
14
14
|
export { CodexAdapter, probeCodex, resolveCodexCommand } from "./runtimes/codex.js";
|
|
15
|
+
export { DeepseekTuiAdapter, probeDeepseekTui, resolveDeepseekCommand, } from "./runtimes/deepseek-tui.js";
|
|
16
|
+
export { KimiAdapter, probeKimi, resolveKimiCommand } from "./runtimes/kimi.js";
|
|
15
17
|
export { GeminiAdapter, probeGemini, resolveGeminiCommand } from "./runtimes/gemini.js";
|
|
16
18
|
export { NdjsonStreamAdapter, type NdjsonEventCtx, type NdjsonRunState, } from "./runtimes/ndjson-stream.js";
|
|
17
19
|
export { firstExistingPath, readCommandVersion, resolveCommandOnPath, resolveHomePath, type ProbeDeps, } from "./runtimes/probe.js";
|
package/dist/gateway/index.js
CHANGED
|
@@ -12,6 +12,8 @@ export { createTranscriptWriter, resolveTranscriptEnabled, defaultTranscriptRoot
|
|
|
12
12
|
export { safePathSegment, transcriptFilePath, transcriptRoomDir, transcriptAgentRoot, } from "./transcript-paths.js";
|
|
13
13
|
export { ClaudeCodeAdapter, probeClaude, resolveClaudeCommand, } from "./runtimes/claude-code.js";
|
|
14
14
|
export { CodexAdapter, probeCodex, resolveCodexCommand } from "./runtimes/codex.js";
|
|
15
|
+
export { DeepseekTuiAdapter, probeDeepseekTui, resolveDeepseekCommand, } from "./runtimes/deepseek-tui.js";
|
|
16
|
+
export { KimiAdapter, probeKimi, resolveKimiCommand } from "./runtimes/kimi.js";
|
|
15
17
|
export { GeminiAdapter, probeGemini, resolveGeminiCommand } from "./runtimes/gemini.js";
|
|
16
18
|
export { NdjsonStreamAdapter, } from "./runtimes/ndjson-stream.js";
|
|
17
19
|
export { firstExistingPath, readCommandVersion, resolveCommandOnPath, resolveHomePath, } from "./runtimes/probe.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { type ProbeDeps } from "./probe.js";
|
|
3
|
+
import type { RuntimeAdapter, RuntimeProbeResult, RuntimeRunOptions, RuntimeRunResult } from "../types.js";
|
|
4
|
+
interface DeepseekAdapterDeps {
|
|
5
|
+
binary?: string;
|
|
6
|
+
/** Test seam: use an already-running compatible server instead of spawning `deepseek`. */
|
|
7
|
+
serverUrl?: string;
|
|
8
|
+
authToken?: string;
|
|
9
|
+
fetchFn?: typeof fetch;
|
|
10
|
+
spawnFn?: typeof spawn;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve the `deepseek` dispatcher CLI on PATH. */
|
|
13
|
+
export declare function resolveDeepseekCommand(deps?: ProbeDeps): string | null;
|
|
14
|
+
/** Probe whether DeepSeek TUI is installed and report its version. */
|
|
15
|
+
export declare function probeDeepseekTui(deps?: ProbeDeps): RuntimeProbeResult;
|
|
16
|
+
/**
|
|
17
|
+
* DeepSeek TUI adapter.
|
|
18
|
+
*
|
|
19
|
+
* Drives the headless runtime API exposed by `deepseek serve --http`, not the
|
|
20
|
+
* interactive TUI and not ACP. The HTTP/SSE API is the documented complete
|
|
21
|
+
* runtime surface; ACP is currently a conservative editor baseline.
|
|
22
|
+
*/
|
|
23
|
+
export declare class DeepseekTuiAdapter implements RuntimeAdapter {
|
|
24
|
+
readonly id: "deepseek-tui";
|
|
25
|
+
private readonly explicitBinary;
|
|
26
|
+
private readonly explicitServerUrl;
|
|
27
|
+
private readonly explicitAuthToken;
|
|
28
|
+
private readonly fetchFn;
|
|
29
|
+
private readonly spawnFn;
|
|
30
|
+
private resolvedBinary;
|
|
31
|
+
constructor(deps?: DeepseekAdapterDeps);
|
|
32
|
+
probe(): RuntimeProbeResult;
|
|
33
|
+
run(opts: RuntimeRunOptions): Promise<RuntimeRunResult>;
|
|
34
|
+
private resolveBinary;
|
|
35
|
+
private acquireHandle;
|
|
36
|
+
private spawnEnv;
|
|
37
|
+
private createThread;
|
|
38
|
+
private patchThreadSystemContext;
|
|
39
|
+
private startTurnAndReadEvents;
|
|
40
|
+
private readEvents;
|
|
41
|
+
private requestJson;
|
|
42
|
+
}
|
|
43
|
+
export declare function __resetDeepseekTuiPoolForTests(): void;
|
|
44
|
+
export {};
|