grok-dev 1.0.0-rc1

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.
Files changed (112) hide show
  1. package/.cursor/rules/development-workflow.mdc +66 -0
  2. package/.cursor/rules/project-overview.mdc +66 -0
  3. package/.cursor/rules/react-ink-components.mdc +45 -0
  4. package/.cursor/rules/tools-and-agent.mdc +62 -0
  5. package/.cursor/rules/typescript-conventions.mdc +54 -0
  6. package/.grok/settings.json +3 -0
  7. package/.husky/pre-commit +1 -0
  8. package/LICENSE +21 -0
  9. package/README.md +526 -0
  10. package/biome.json +51 -0
  11. package/dist/agent/agent.d.ts +62 -0
  12. package/dist/agent/agent.js +701 -0
  13. package/dist/agent/agent.js.map +1 -0
  14. package/dist/agent/delegations.d.ts +42 -0
  15. package/dist/agent/delegations.js +273 -0
  16. package/dist/agent/delegations.js.map +1 -0
  17. package/dist/grok/client.d.ts +12 -0
  18. package/dist/grok/client.js +37 -0
  19. package/dist/grok/client.js.map +1 -0
  20. package/dist/grok/models.d.ts +5 -0
  21. package/dist/grok/models.js +73 -0
  22. package/dist/grok/models.js.map +1 -0
  23. package/dist/grok/tools.d.ts +12 -0
  24. package/dist/grok/tools.js +230 -0
  25. package/dist/grok/tools.js.map +1 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +192 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/storage/db.d.ts +18 -0
  30. package/dist/storage/db.js +71 -0
  31. package/dist/storage/db.js.map +1 -0
  32. package/dist/storage/index.d.ts +4 -0
  33. package/dist/storage/index.js +5 -0
  34. package/dist/storage/index.js.map +1 -0
  35. package/dist/storage/migrations.d.ts +2 -0
  36. package/dist/storage/migrations.js +92 -0
  37. package/dist/storage/migrations.js.map +1 -0
  38. package/dist/storage/sessions.d.ts +15 -0
  39. package/dist/storage/sessions.js +135 -0
  40. package/dist/storage/sessions.js.map +1 -0
  41. package/dist/storage/transcript.d.ts +6 -0
  42. package/dist/storage/transcript.js +261 -0
  43. package/dist/storage/transcript.js.map +1 -0
  44. package/dist/storage/usage.d.ts +9 -0
  45. package/dist/storage/usage.js +58 -0
  46. package/dist/storage/usage.js.map +1 -0
  47. package/dist/storage/workspaces.d.ts +9 -0
  48. package/dist/storage/workspaces.js +60 -0
  49. package/dist/storage/workspaces.js.map +1 -0
  50. package/dist/telegram/bridge.d.ts +39 -0
  51. package/dist/telegram/bridge.js +164 -0
  52. package/dist/telegram/bridge.js.map +1 -0
  53. package/dist/telegram/index.d.ts +3 -0
  54. package/dist/telegram/index.js +4 -0
  55. package/dist/telegram/index.js.map +1 -0
  56. package/dist/telegram/limits.d.ts +3 -0
  57. package/dist/telegram/limits.js +12 -0
  58. package/dist/telegram/limits.js.map +1 -0
  59. package/dist/telegram/pairing.d.ts +9 -0
  60. package/dist/telegram/pairing.js +30 -0
  61. package/dist/telegram/pairing.js.map +1 -0
  62. package/dist/telegram/preview-stream.d.ts +22 -0
  63. package/dist/telegram/preview-stream.js +181 -0
  64. package/dist/telegram/preview-stream.js.map +1 -0
  65. package/dist/telegram/turn-coordinator.d.ts +7 -0
  66. package/dist/telegram/turn-coordinator.js +14 -0
  67. package/dist/telegram/turn-coordinator.js.map +1 -0
  68. package/dist/telegram/typing-refresh.d.ts +3 -0
  69. package/dist/telegram/typing-refresh.js +12 -0
  70. package/dist/telegram/typing-refresh.js.map +1 -0
  71. package/dist/tools/bash.d.ts +27 -0
  72. package/dist/tools/bash.js +261 -0
  73. package/dist/tools/bash.js.map +1 -0
  74. package/dist/tools/file.d.ts +15 -0
  75. package/dist/tools/file.js +94 -0
  76. package/dist/tools/file.js.map +1 -0
  77. package/dist/types/index.d.ts +151 -0
  78. package/dist/types/index.js +6 -0
  79. package/dist/types/index.js.map +1 -0
  80. package/dist/ui/app.d.ts +15 -0
  81. package/dist/ui/app.js +1720 -0
  82. package/dist/ui/app.js.map +1 -0
  83. package/dist/ui/markdown.d.ts +5 -0
  84. package/dist/ui/markdown.js +38 -0
  85. package/dist/ui/markdown.js.map +1 -0
  86. package/dist/ui/plan.d.ts +24 -0
  87. package/dist/ui/plan.js +122 -0
  88. package/dist/ui/plan.js.map +1 -0
  89. package/dist/ui/terminal-selection-text.d.ts +23 -0
  90. package/dist/ui/terminal-selection-text.js +59 -0
  91. package/dist/ui/terminal-selection-text.js.map +1 -0
  92. package/dist/ui/theme.d.ts +53 -0
  93. package/dist/ui/theme.js +53 -0
  94. package/dist/ui/theme.js.map +1 -0
  95. package/dist/utils/git-root.d.ts +1 -0
  96. package/dist/utils/git-root.js +16 -0
  97. package/dist/utils/git-root.js.map +1 -0
  98. package/dist/utils/host-clipboard.d.ts +4 -0
  99. package/dist/utils/host-clipboard.js +32 -0
  100. package/dist/utils/host-clipboard.js.map +1 -0
  101. package/dist/utils/instructions.d.ts +1 -0
  102. package/dist/utils/instructions.js +73 -0
  103. package/dist/utils/instructions.js.map +1 -0
  104. package/dist/utils/settings.d.ts +33 -0
  105. package/dist/utils/settings.js +88 -0
  106. package/dist/utils/settings.js.map +1 -0
  107. package/dist/utils/skills.d.ts +17 -0
  108. package/dist/utils/skills.js +161 -0
  109. package/dist/utils/skills.js.map +1 -0
  110. package/package.json +67 -0
  111. package/skills-lock.json +10 -0
  112. package/tmp/large_class.py +633 -0
@@ -0,0 +1,60 @@
1
+ import { createHash } from "crypto";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { findGitRoot } from "../utils/git-root";
5
+ import { getDatabase } from "./db";
6
+ export function ensureWorkspace(cwd) {
7
+ const resolved = resolveWorkspace(cwd);
8
+ const now = new Date().toISOString();
9
+ const id = createHash("sha1").update(resolved.scopeKey).digest("hex").slice(0, 16);
10
+ const db = getDatabase();
11
+ db.prepare(`
12
+ INSERT INTO workspaces (id, scope_key, canonical_path, git_root, display_name, last_seen_at)
13
+ VALUES (@id, @scope_key, @canonical_path, @git_root, @display_name, @last_seen_at)
14
+ ON CONFLICT(scope_key) DO UPDATE SET
15
+ canonical_path = excluded.canonical_path,
16
+ git_root = excluded.git_root,
17
+ display_name = excluded.display_name,
18
+ last_seen_at = excluded.last_seen_at
19
+ `).run({
20
+ id,
21
+ scope_key: resolved.scopeKey,
22
+ canonical_path: resolved.canonicalPath,
23
+ git_root: resolved.gitRoot,
24
+ display_name: resolved.displayName,
25
+ last_seen_at: now,
26
+ });
27
+ const row = db
28
+ .prepare(`
29
+ SELECT id, scope_key, canonical_path, git_root, display_name, last_seen_at
30
+ FROM workspaces
31
+ WHERE scope_key = ?
32
+ `)
33
+ .get(resolved.scopeKey);
34
+ if (!row) {
35
+ throw new Error(`Failed to resolve workspace for ${cwd}`);
36
+ }
37
+ return toWorkspaceInfo(row);
38
+ }
39
+ export function resolveWorkspace(cwd) {
40
+ const canonicalPath = fs.realpathSync.native(cwd);
41
+ const gitRoot = findGitRoot(canonicalPath);
42
+ const scopePath = gitRoot || canonicalPath;
43
+ return {
44
+ scopeKey: scopePath,
45
+ canonicalPath,
46
+ gitRoot,
47
+ displayName: path.basename(scopePath) || "workspace",
48
+ };
49
+ }
50
+ function toWorkspaceInfo(row) {
51
+ return {
52
+ id: row.id,
53
+ scopeKey: row.scope_key,
54
+ canonicalPath: row.canonical_path,
55
+ gitRoot: row.git_root,
56
+ displayName: row.display_name,
57
+ lastSeenAt: new Date(row.last_seen_at),
58
+ };
59
+ }
60
+ //# sourceMappingURL=workspaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../src/storage/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAkBnC,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnF,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAEzB,EAAE,CAAC,OAAO,CAAC;;;;;;;;GAQV,CAAC,CAAC,GAAG,CAAC;QACL,EAAE;QACF,SAAS,EAAE,QAAQ,CAAC,QAAQ;QAC5B,cAAc,EAAE,QAAQ,CAAC,aAAa;QACtC,QAAQ,EAAE,QAAQ,CAAC,OAAO;QAC1B,YAAY,EAAE,QAAQ,CAAC,WAAW;QAClC,YAAY,EAAE,GAAG;KAClB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC;;;;GAIV,CAAC;SACC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAA6B,CAAC;IAEtD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,OAAO,IAAI,aAAa,CAAC;IAE3C,OAAO;QACL,QAAQ,EAAE,SAAS;QACnB,aAAa;QACb,OAAO;QACP,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,WAAW;KACrD,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,GAAiB;IACxC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,aAAa,EAAE,GAAG,CAAC,cAAc;QACjC,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,UAAU,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC;KACvC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { Agent } from "../agent/agent";
2
+ import type { ToolCall, ToolResult } from "../types/index";
3
+ import type { TurnCoordinator } from "./turn-coordinator";
4
+ export { splitTelegramMessage, TELEGRAM_MAX_MESSAGE } from "./limits";
5
+ export interface TelegramBridgeOptions {
6
+ token: string;
7
+ getApprovedUserIds: () => number[];
8
+ coordinator: TurnCoordinator;
9
+ getTelegramAgent: (userId: number) => Agent;
10
+ onUserMessage?: (event: {
11
+ turnKey: string;
12
+ userId: number;
13
+ content: string;
14
+ }) => void;
15
+ onAssistantMessage?: (event: {
16
+ turnKey: string;
17
+ userId: number;
18
+ content: string;
19
+ done: boolean;
20
+ }) => void;
21
+ onToolCalls?: (event: {
22
+ turnKey: string;
23
+ userId: number;
24
+ toolCalls: ToolCall[];
25
+ }) => void;
26
+ onToolResult?: (event: {
27
+ turnKey: string;
28
+ userId: number;
29
+ toolCall: ToolCall;
30
+ toolResult: ToolResult;
31
+ }) => void;
32
+ onError?: (message: string) => void;
33
+ }
34
+ export interface TelegramBridgeHandle {
35
+ start: () => void;
36
+ stop: () => Promise<void>;
37
+ sendDm: (userId: number, text: string) => Promise<void>;
38
+ }
39
+ export declare function createTelegramBridge(opts: TelegramBridgeOptions): TelegramBridgeHandle;
@@ -0,0 +1,164 @@
1
+ import { Bot } from "grammy";
2
+ import { loadUserSettings, resolveTelegramStreamSettings } from "../utils/settings";
3
+ import { splitTelegramMessage, TELEGRAM_MAX_MESSAGE } from "./limits";
4
+ import { registerPairingCode } from "./pairing";
5
+ import { runTelegramPartialReply } from "./preview-stream";
6
+ import { startTypingRefresh } from "./typing-refresh";
7
+ export { splitTelegramMessage, TELEGRAM_MAX_MESSAGE } from "./limits";
8
+ export function createTelegramBridge(opts) {
9
+ const bot = new Bot(opts.token);
10
+ let running = false;
11
+ bot.command("start", async (ctx) => {
12
+ await ctx.reply("Send /pair to link this chat to Grok CLI, then approve the code in the terminal.");
13
+ });
14
+ bot.command("pair", async (ctx) => {
15
+ const userId = ctx.from?.id;
16
+ if (userId === undefined)
17
+ return;
18
+ const code = registerPairingCode(userId);
19
+ await ctx.reply(`Your pairing code: ${code}\nEnter this code in Grok CLI (/remote-control → Telegram) to approve.`);
20
+ });
21
+ bot.on("message:text", async (ctx) => {
22
+ const text = ctx.message.text;
23
+ if (text.startsWith("/"))
24
+ return;
25
+ const userId = ctx.from?.id;
26
+ if (userId === undefined)
27
+ return;
28
+ const approved = opts.getApprovedUserIds();
29
+ if (!approved.includes(userId)) {
30
+ await ctx.reply("Not paired yet. Send /pair to get a code, then approve in Grok CLI.");
31
+ return;
32
+ }
33
+ await opts.coordinator.run(async () => {
34
+ try {
35
+ const turnKey = `telegram:${ctx.chat.id}:${ctx.message.message_id}`;
36
+ opts.onUserMessage?.({ turnKey, userId, content: text });
37
+ const agent = opts.getTelegramAgent(userId);
38
+ const stream = resolveTelegramStreamSettings(loadUserSettings().telegram);
39
+ if (stream.streaming === "off") {
40
+ const stopTyping = startTypingRefresh(bot.api, ctx.chat.id, ctx.message.message_thread_id, stream.typingIndicator);
41
+ let acc = "";
42
+ try {
43
+ for await (const chunk of agent.processMessage(text)) {
44
+ switch (chunk.type) {
45
+ case "content":
46
+ if (chunk.content) {
47
+ acc += chunk.content;
48
+ }
49
+ break;
50
+ case "tool_calls":
51
+ if (chunk.toolCalls) {
52
+ opts.onToolCalls?.({ turnKey, userId, toolCalls: chunk.toolCalls });
53
+ }
54
+ break;
55
+ case "tool_result":
56
+ if (chunk.toolCall && chunk.toolResult) {
57
+ opts.onToolResult?.({
58
+ turnKey,
59
+ userId,
60
+ toolCall: chunk.toolCall,
61
+ toolResult: chunk.toolResult,
62
+ });
63
+ }
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ catch (err) {
69
+ const msg = err instanceof Error ? err.message : String(err);
70
+ opts.onAssistantMessage?.({
71
+ turnKey,
72
+ userId,
73
+ content: `Error: ${msg.slice(0, TELEGRAM_MAX_MESSAGE)}`,
74
+ done: true,
75
+ });
76
+ await ctx.reply(`Error: ${msg.slice(0, TELEGRAM_MAX_MESSAGE)}`);
77
+ return;
78
+ }
79
+ finally {
80
+ stopTyping();
81
+ }
82
+ const trimmed = acc.trim() || "(no text output)";
83
+ opts.onAssistantMessage?.({ turnKey, userId, content: trimmed, done: true });
84
+ const parts = splitTelegramMessage(trimmed);
85
+ for (const part of parts) {
86
+ await ctx.reply(part);
87
+ }
88
+ return;
89
+ }
90
+ await runTelegramPartialReply(bot.api, {
91
+ chatId: ctx.chat.id,
92
+ messageThreadId: ctx.message.message_thread_id,
93
+ typingIndicator: stream.typingIndicator,
94
+ stream: agent.processMessage(text),
95
+ onAssistantMessage: (event) => {
96
+ opts.onAssistantMessage?.({
97
+ turnKey,
98
+ userId,
99
+ content: event.content,
100
+ done: event.done,
101
+ });
102
+ },
103
+ onToolCalls: (toolCalls) => {
104
+ opts.onToolCalls?.({ turnKey, userId, toolCalls });
105
+ },
106
+ onToolResult: (event) => {
107
+ opts.onToolResult?.({
108
+ turnKey,
109
+ userId,
110
+ toolCall: event.toolCall,
111
+ toolResult: event.toolResult,
112
+ });
113
+ },
114
+ });
115
+ }
116
+ catch (err) {
117
+ const msg = err instanceof Error ? err.message : String(err);
118
+ opts.onAssistantMessage?.({
119
+ turnKey: `telegram:${ctx.chat.id}:${ctx.message.message_id}`,
120
+ userId,
121
+ content: `Error: ${msg.slice(0, TELEGRAM_MAX_MESSAGE)}`,
122
+ done: true,
123
+ });
124
+ try {
125
+ await ctx.reply(`Error: ${msg.slice(0, TELEGRAM_MAX_MESSAGE)}`);
126
+ }
127
+ catch {
128
+ /* user blocked bot or chat forbids messages */
129
+ }
130
+ }
131
+ });
132
+ });
133
+ bot.catch((err) => {
134
+ opts.onError?.(err instanceof Error ? err.message : String(err));
135
+ });
136
+ return {
137
+ start() {
138
+ if (running)
139
+ return;
140
+ running = true;
141
+ void bot
142
+ .start({
143
+ allowed_updates: ["message"],
144
+ drop_pending_updates: true,
145
+ })
146
+ .catch((err) => {
147
+ running = false;
148
+ opts.onError?.(err instanceof Error ? err.message : String(err));
149
+ });
150
+ },
151
+ async stop() {
152
+ if (!running)
153
+ return;
154
+ await bot.stop();
155
+ running = false;
156
+ },
157
+ async sendDm(userId, text) {
158
+ for (const part of splitTelegramMessage(text)) {
159
+ await bot.api.sendMessage(userId, part);
160
+ }
161
+ },
162
+ };
163
+ }
164
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/telegram/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { createTelegramBridge, splitTelegramMessage } from "./bridge";
2
+ export { approvePairingCode, clearPairingStore, registerPairingCode } from "./pairing";
3
+ export { createTurnCoordinator } from "./turn-coordinator";
@@ -0,0 +1,4 @@
1
+ export { createTelegramBridge, splitTelegramMessage } from "./bridge";
2
+ export { approvePairingCode, clearPairingStore, registerPairingCode } from "./pairing";
3
+ export { createTurnCoordinator } from "./turn-coordinator";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telegram/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Telegram text message hard limit (characters). */
2
+ export declare const TELEGRAM_MAX_MESSAGE = 4096;
3
+ export declare function splitTelegramMessage(text: string): string[];
@@ -0,0 +1,12 @@
1
+ /** Telegram text message hard limit (characters). */
2
+ export const TELEGRAM_MAX_MESSAGE = 4096;
3
+ export function splitTelegramMessage(text) {
4
+ if (!text)
5
+ return [];
6
+ const parts = [];
7
+ for (let i = 0; i < text.length; i += TELEGRAM_MAX_MESSAGE) {
8
+ parts.push(text.slice(i, i + TELEGRAM_MAX_MESSAGE));
9
+ }
10
+ return parts;
11
+ }
12
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/telegram/limits.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,oBAAoB,EAAE,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function registerPairingCode(userId: number): string;
2
+ export declare function approvePairingCode(code: string): {
3
+ ok: true;
4
+ userId: number;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ };
9
+ export declare function clearPairingStore(): void;
@@ -0,0 +1,30 @@
1
+ import { randomBytes } from "node:crypto";
2
+ const PAIRING_TTL_MS = 60 * 60 * 1000;
3
+ /** code (uppercase) -> { userId, expiresAt } */
4
+ const pendingByCode = new Map();
5
+ function generateCode() {
6
+ const hex = randomBytes(4).toString("hex").slice(0, 6).toUpperCase();
7
+ return hex;
8
+ }
9
+ export function registerPairingCode(userId) {
10
+ const code = generateCode();
11
+ pendingByCode.set(code, { userId, expiresAt: Date.now() + PAIRING_TTL_MS });
12
+ return code;
13
+ }
14
+ export function approvePairingCode(code) {
15
+ const normalized = code.trim().toUpperCase();
16
+ const entry = pendingByCode.get(normalized);
17
+ if (!entry) {
18
+ return { ok: false, error: "Unknown or expired code." };
19
+ }
20
+ if (Date.now() > entry.expiresAt) {
21
+ pendingByCode.delete(normalized);
22
+ return { ok: false, error: "Code expired. Send /pair again in Telegram." };
23
+ }
24
+ pendingByCode.delete(normalized);
25
+ return { ok: true, userId: entry.userId };
26
+ }
27
+ export function clearPairingStore() {
28
+ pendingByCode.clear();
29
+ }
30
+ //# sourceMappingURL=pairing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/telegram/pairing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtC,gDAAgD;AAChD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE/E,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACrE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QACjC,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;IAC7E,CAAC;IACD,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Api } from "grammy";
2
+ import type { StreamChunk } from "../types/index";
3
+ export interface TelegramPartialReplyArgs {
4
+ chatId: number | string;
5
+ messageThreadId?: number;
6
+ typingIndicator: boolean;
7
+ stream: AsyncIterable<StreamChunk>;
8
+ onAssistantMessage?: (event: {
9
+ content: string;
10
+ done: boolean;
11
+ }) => void;
12
+ onToolCalls?: (toolCalls: NonNullable<StreamChunk["toolCalls"]>) => void;
13
+ onToolResult?: (event: {
14
+ toolCall: NonNullable<StreamChunk["toolCall"]>;
15
+ toolResult: NonNullable<StreamChunk["toolResult"]>;
16
+ }) => void;
17
+ }
18
+ /**
19
+ * Sends a live-updating preview (send + throttled editMessageText), then finalizes.
20
+ * Falls back to buffer-then-send if the initial message cannot be sent.
21
+ */
22
+ export declare function runTelegramPartialReply(api: Api, args: TelegramPartialReplyArgs): Promise<void>;
@@ -0,0 +1,181 @@
1
+ import { GrammyError } from "grammy";
2
+ import { splitTelegramMessage, TELEGRAM_MAX_MESSAGE } from "./limits";
3
+ import { startTypingRefresh } from "./typing-refresh";
4
+ const EDIT_THROTTLE_MS = 350;
5
+ const EDIT_MIN_CHARS = 48;
6
+ const MAX_API_RETRIES = 12;
7
+ /** Bot API allows `message_thread_id` for forum topics; @grammyjs/types may omit it on `editMessageText`. */
8
+ function editThreadOpts(messageThreadId) {
9
+ return messageThreadId !== undefined ? { message_thread_id: messageThreadId } : {};
10
+ }
11
+ async function withRetry(fn) {
12
+ let lastErr;
13
+ for (let i = 0; i < MAX_API_RETRIES; i++) {
14
+ try {
15
+ return await fn();
16
+ }
17
+ catch (e) {
18
+ lastErr = e;
19
+ if (e instanceof GrammyError && e.error_code === 429) {
20
+ const retryAfter = Number(e.parameters.retry_after ?? 1) * 1000;
21
+ await new Promise((r) => setTimeout(r, retryAfter));
22
+ continue;
23
+ }
24
+ throw e;
25
+ }
26
+ }
27
+ throw lastErr;
28
+ }
29
+ /** Same text as before — not an error; avoid treating as preview failure. */
30
+ function isMessageNotModified(err) {
31
+ return (err instanceof GrammyError &&
32
+ err.error_code === 400 &&
33
+ typeof err.description === "string" &&
34
+ err.description.toLowerCase().includes("message is not modified"));
35
+ }
36
+ /** Telegram requires non-empty text; use zero-width space so nothing visible appears before real tokens. */
37
+ const EMPTY_PREVIEW_PLACEHOLDER = "\u200b";
38
+ function previewBody(acc) {
39
+ if (!acc)
40
+ return EMPTY_PREVIEW_PLACEHOLDER;
41
+ return acc.length > TELEGRAM_MAX_MESSAGE ? acc.slice(0, TELEGRAM_MAX_MESSAGE) : acc;
42
+ }
43
+ /**
44
+ * Sends a live-updating preview (send + throttled editMessageText), then finalizes.
45
+ * Falls back to buffer-then-send if the initial message cannot be sent.
46
+ */
47
+ export async function runTelegramPartialReply(api, args) {
48
+ const { chatId, messageThreadId, typingIndicator, stream, onAssistantMessage, onToolCalls, onToolResult } = args;
49
+ const stopTyping = startTypingRefresh(api, chatId, messageThreadId, typingIndicator);
50
+ const sendParts = async (parts) => {
51
+ for (const part of parts) {
52
+ await withRetry(() => api.sendMessage(chatId, part, { message_thread_id: messageThreadId }));
53
+ }
54
+ };
55
+ let previewMessageId;
56
+ /** First sendMessage failed — finish with sendParts only. */
57
+ let previewCreateFailed = false;
58
+ let acc = "";
59
+ let previewBroken = false;
60
+ let lastEditAt = 0;
61
+ let lastEditLen = 0;
62
+ /** First real message is sent only after assistant text exists so sendChatAction(typing) stays visible until then. */
63
+ const ensurePreviewMessage = async () => {
64
+ if (previewMessageId !== undefined || previewCreateFailed || !acc)
65
+ return;
66
+ try {
67
+ const sent = await withRetry(() => api.sendMessage(chatId, previewBody(acc), { message_thread_id: messageThreadId }));
68
+ if (!sent.message_id) {
69
+ previewCreateFailed = true;
70
+ return;
71
+ }
72
+ previewMessageId = sent.message_id;
73
+ }
74
+ catch {
75
+ previewCreateFailed = true;
76
+ }
77
+ };
78
+ const flushEdit = async (force) => {
79
+ if (previewMessageId === undefined || previewBroken)
80
+ return;
81
+ const messageId = previewMessageId;
82
+ const now = Date.now();
83
+ const delta = acc.length - lastEditLen;
84
+ if (!force && now - lastEditAt < EDIT_THROTTLE_MS && delta < EDIT_MIN_CHARS)
85
+ return;
86
+ try {
87
+ await withRetry(() => api.editMessageText(chatId, messageId, previewBody(acc), editThreadOpts(messageThreadId)));
88
+ lastEditAt = Date.now();
89
+ lastEditLen = acc.length;
90
+ }
91
+ catch (e) {
92
+ if (isMessageNotModified(e)) {
93
+ lastEditAt = Date.now();
94
+ lastEditLen = acc.length;
95
+ return;
96
+ }
97
+ previewBroken = true;
98
+ }
99
+ };
100
+ try {
101
+ try {
102
+ for await (const chunk of stream) {
103
+ switch (chunk.type) {
104
+ case "content":
105
+ if (chunk.content) {
106
+ acc += chunk.content;
107
+ onAssistantMessage?.({ content: acc, done: false });
108
+ await ensurePreviewMessage();
109
+ await flushEdit(false);
110
+ }
111
+ break;
112
+ case "tool_calls":
113
+ if (chunk.toolCalls) {
114
+ onToolCalls?.(chunk.toolCalls);
115
+ }
116
+ break;
117
+ case "tool_result":
118
+ if (chunk.toolCall && chunk.toolResult) {
119
+ onToolResult?.({ toolCall: chunk.toolCall, toolResult: chunk.toolResult });
120
+ }
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ catch (err) {
126
+ await flushEdit(true);
127
+ const msg = err instanceof Error ? err.message : String(err);
128
+ const errText = `Error: ${msg.slice(0, TELEGRAM_MAX_MESSAGE)}`;
129
+ onAssistantMessage?.({ content: errText, done: true });
130
+ if (previewMessageId !== undefined && !previewBroken) {
131
+ const messageId = previewMessageId;
132
+ try {
133
+ await withRetry(() => api.editMessageText(chatId, messageId, errText, editThreadOpts(messageThreadId)));
134
+ }
135
+ catch {
136
+ await sendParts(splitTelegramMessage(errText));
137
+ }
138
+ }
139
+ else {
140
+ await sendParts(splitTelegramMessage(errText));
141
+ }
142
+ return;
143
+ }
144
+ await flushEdit(true);
145
+ const trimmed = acc.trim() || "(no text output)";
146
+ const parts = splitTelegramMessage(trimmed);
147
+ onAssistantMessage?.({ content: trimmed, done: true });
148
+ if (previewMessageId === undefined) {
149
+ await sendParts(parts);
150
+ return;
151
+ }
152
+ if (parts.length === 0) {
153
+ if (!previewBroken) {
154
+ const messageId = previewMessageId;
155
+ await withRetry(() => api.editMessageText(chatId, messageId, "(no text output)", editThreadOpts(messageThreadId)));
156
+ }
157
+ return;
158
+ }
159
+ if (previewBroken) {
160
+ await sendParts(parts);
161
+ return;
162
+ }
163
+ const messageId = previewMessageId;
164
+ try {
165
+ await withRetry(() => api.editMessageText(chatId, messageId, parts[0], editThreadOpts(messageThreadId)));
166
+ }
167
+ catch (e) {
168
+ if (!isMessageNotModified(e)) {
169
+ await sendParts(parts);
170
+ return;
171
+ }
172
+ }
173
+ if (parts.length > 1) {
174
+ await sendParts(parts.slice(1));
175
+ }
176
+ }
177
+ finally {
178
+ stopTyping();
179
+ }
180
+ }
181
+ //# sourceMappingURL=preview-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-stream.js","sourceRoot":"","sources":["../../src/telegram/preview-stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Serialize async turns (local TUI + Telegram) so only one Agent.processMessage runs at a time.
3
+ */
4
+ export declare function createTurnCoordinator(): {
5
+ run<T>(fn: () => Promise<T>): Promise<T>;
6
+ };
7
+ export type TurnCoordinator = ReturnType<typeof createTurnCoordinator>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Serialize async turns (local TUI + Telegram) so only one Agent.processMessage runs at a time.
3
+ */
4
+ export function createTurnCoordinator() {
5
+ let chain = Promise.resolve();
6
+ return {
7
+ run(fn) {
8
+ const result = chain.then(() => fn());
9
+ chain = result.then(() => { }, () => { });
10
+ return result;
11
+ },
12
+ };
13
+ }
14
+ //# sourceMappingURL=turn-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-coordinator.js","sourceRoot":"","sources":["../../src/telegram/turn-coordinator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,KAAK,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEhD,OAAO;QACL,GAAG,CAAI,EAAoB;YACzB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACtC,KAAK,GAAG,MAAM,CAAC,IAAI,CACjB,GAAG,EAAE,GAAE,CAAC,EACR,GAAG,EAAE,GAAE,CAAC,CACT,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Api } from "grammy";
2
+ /** Repeats `sendChatAction(typing)` until `stop()` is called. No-op when `enabled` is false. */
3
+ export declare function startTypingRefresh(api: Api, chatId: number | string, messageThreadId: number | undefined, enabled: boolean): () => void;
@@ -0,0 +1,12 @@
1
+ /** Telegram clears typing after ~5s; refresh before that so the indicator stays visible. */
2
+ const TYPING_REFRESH_MS = 3500;
3
+ /** Repeats `sendChatAction(typing)` until `stop()` is called. No-op when `enabled` is false. */
4
+ export function startTypingRefresh(api, chatId, messageThreadId, enabled) {
5
+ if (!enabled)
6
+ return () => { };
7
+ const tick = () => void api.sendChatAction(chatId, "typing", { message_thread_id: messageThreadId }).catch(() => { });
8
+ tick();
9
+ const id = setInterval(tick, TYPING_REFRESH_MS);
10
+ return () => clearInterval(id);
11
+ }
12
+ //# sourceMappingURL=typing-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing-refresh.js","sourceRoot":"","sources":["../../src/telegram/typing-refresh.ts"],"names":[],"mappings":"AAEA,4FAA4F;AAC5F,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,gGAAgG;AAChG,MAAM,UAAU,kBAAkB,CAChC,GAAQ,EACR,MAAuB,EACvB,eAAmC,EACnC,OAAgB;IAEhB,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrH,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAChD,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;AACjC,CAAC"}