grok-bot-sdk 0.1.0 → 0.75.8

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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 0.75.8
4
+
5
+ Protocol surface matched to the Grok Bot 0.75.8 desktop client. The package stays ESM, with no runtime dependencies.
6
+
7
+ - Default `x-cursor-client-version` is `0.75.8`.
8
+ - Login polling stops on HTTP 403 `{ "error": "sign_in_policy_violation" }` and throws `SIGN_IN_POLICY_VIOLATION`.
9
+ - Roster reads optional `role` and `addedTeamAgentsOnly`, and returns Slack and Teams connection records plus the additional `GrokBotAgent` scalars present in 0.75.8.
10
+ - `sendMessage` accepts `replyToId`, `replyQuote`, `taskId`, `language`, `richText`, and `machineId`. Delivery `4` (`REFUSED`) throws `SEND_REFUSED`.
11
+ - New read methods: `listSessions`, `listTranscript`, `listAgentStatuses`, `getSendStatus`, `listAgentTasks`, `listTasks`, `listSubagents`, `listAgentTodos`, `listAgentSkills`, `listDeletedAgentIds`, `getRuntimeCapabilities`, `getSandBoxRunState`.
12
+ - Watch events add `agent.state`, `computer.actions`, `box.state`, and `page.title.changed`. `bot.state.changed` includes `changedAtMs`.
13
+ - CLI read-only subcommands: `bots`, `sessions`, `transcript`, `tasks`, `capabilities`, `box`.
14
+ - Avatar derivation is unchanged (`grok-bot-0.70.2`). `pumpkin` is accepted as an explicit shape.
15
+
16
+ The checksum, ghost-mode default, and desktop chat enums `source=1`, `initiator=1`, `sendSource=1` are the same values the 0.75.8 client still sends for desktop chat.
17
+
18
+ ## 0.1.0
19
+
20
+ First release, matched to the Grok Bot 0.70.2 desktop client. From 0.75.8 on, the package version follows the Grok Bot version it targets.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <h1 align="center">grok-bot-sdk</h1>
9
9
 
10
10
  <p align="center"><strong>Unofficial Node.js SDK for the Grok Bot desktop client protocol</strong></p>
11
- <p align="center"><em>Browser login · Account and bot roster · Normalized avatars · Send · Connect replies forwarded as SSE</em></p>
11
+ <p align="center"><em>Browser login · Account and bot roster · Sessions, transcript, and tasks · Normalized avatars · Send · Connect replies forwarded as SSE</em></p>
12
12
  <p align="center"><a href="https://ronglecat.github.io/grok-bot-sdk/">https://ronglecat.github.io/grok-bot-sdk/</a></p>
13
13
 
14
14
  <p align="center">
@@ -21,7 +21,7 @@
21
21
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
22
22
  <a href="https://github.com/RongleCat/grok-bot-sdk/stargazers"><img src="https://img.shields.io/github/stars/RongleCat/grok-bot-sdk?style=social" alt="GitHub stars" /></a>
23
23
  <img src="https://img.shields.io/badge/node-%3E%3D22.13-339933?logo=node.js&logoColor=white" alt="Node.js" />
24
- <img src="https://img.shields.io/badge/client-Grok%20Bot%200.70.2-lightgrey" alt="Grok Bot 0.70.2" />
24
+ <img src="https://img.shields.io/badge/client-Grok%20Bot%200.75.8-lightgrey" alt="Grok Bot 0.75.8" />
25
25
  <img src="https://img.shields.io/badge/note-unofficial-yellow" alt="Unofficial" />
26
26
  </p>
27
27
 
@@ -42,7 +42,7 @@
42
42
  ---
43
43
 
44
44
  > [!NOTE]
45
- > **About this SDK.** grok-bot-sdk reproduces the browser login and the read, send, and reply-stream calls used by the Grok Bot **0.70.2** desktop client. It is an independent open-source library by [铁柱AGI](https://x.com/cgnot996). It is **not an xAI product, not a Cursor product, and not the official Grok Bot SDK**.
45
+ > **About this SDK.** grok-bot-sdk reproduces the browser login and the read, send, and reply-stream calls used by the Grok Bot **0.75.8** desktop client. It is an independent open-source library by [铁柱AGI](https://x.com/cgnot996). It is **not an xAI product, not a Cursor product, and not the official Grok Bot SDK**.
46
46
  >
47
47
  > The same author also builds [Grok App](https://github.com/RongleCat/grok-app), a desktop workbench for the local Grok Build CLI. Grok App and this SDK are different programs.
48
48
 
@@ -155,15 +155,17 @@ return client.botEventsSSE({
155
155
 
156
156
  ## What this release includes
157
157
 
158
- Verified against Grok Bot 0.70.2 on 14 October 2026:
158
+ Verified against Grok Bot 0.75.8:
159
159
 
160
160
  - One browser login continued into account read and the cloud bot roster.
161
161
  - The same read path works with the Mac client's existing session.
162
- - Roster records come back with one avatar shape, whether the server sent the fields or the SDK derived them.
162
+ - Roster records come back with one avatar shape, whether the server sent the fields or the SDK derived them. The avatar hash is still `grok-bot-0.70.2`. `pumpkin` is explicit only.
163
163
  - `WatchGrokBotTranscripts` connects with Connect JSON, and an existing reply was forwarded through `botEventsSSE`.
164
- - Offline tests cover login URL shape, avatar vectors, frame parsing, cursors, cancellation, SSE framing, and send results.
164
+ - Read calls: `listSessions`, `listTranscript`, `listAgentStatuses`, `getSendStatus`, `listAgentTasks`, `listTasks`, `listSubagents`, `listAgentTodos`, `listAgentSkills`, `listDeletedAgentIds`, `getRuntimeCapabilities`, and `getSandBoxRunState`.
165
+ - Watch events also include `agent.state`, `computer.actions`, `box.state`, and `page.title.changed`.
166
+ - Offline tests cover login URL shape, avatar vectors, frame parsing, cursors, cancellation, SSE framing, send results, and the new read records.
165
167
 
166
- The default client version string is `0.70.2`. Pass `version` when you know the client you are matching.
168
+ The default client version string is `0.75.8`. Pass `version` when you know the client you are matching. The CLI also has read-only subcommands: `bots`, `sessions`, `transcript`, `tasks`, `capabilities`, and `box`.
167
169
 
168
170
  ## Notes you should read
169
171
 
package/README_ZH.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <h1 align="center">grok-bot-sdk</h1>
9
9
 
10
10
  <p align="center"><strong>Grok Bot 桌面客户端协议的非官方 Node.js SDK</strong></p>
11
- <p align="center"><em>浏览器登录 · 账号与 Bot 名单 · 统一头像 · 发送 · Connect 回复转发为 SSE</em></p>
11
+ <p align="center"><em>浏览器登录 · 账号与 Bot 名单 · 会话、转录与任务 · 统一头像 · 发送 · Connect 回复转发为 SSE</em></p>
12
12
  <p align="center"><a href="https://ronglecat.github.io/grok-bot-sdk/zh/">https://ronglecat.github.io/grok-bot-sdk/zh/</a></p>
13
13
 
14
14
  <p align="center">
@@ -21,7 +21,7 @@
21
21
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
22
22
  <a href="https://github.com/RongleCat/grok-bot-sdk/stargazers"><img src="https://img.shields.io/github/stars/RongleCat/grok-bot-sdk?style=social" alt="GitHub stars" /></a>
23
23
  <img src="https://img.shields.io/badge/node-%3E%3D22.13-339933?logo=node.js&logoColor=white" alt="Node.js" />
24
- <img src="https://img.shields.io/badge/client-Grok%20Bot%200.70.2-lightgrey" alt="Grok Bot 0.70.2" />
24
+ <img src="https://img.shields.io/badge/client-Grok%20Bot%200.75.8-lightgrey" alt="Grok Bot 0.75.8" />
25
25
  <img src="https://img.shields.io/badge/note-unofficial-yellow" alt="非官方" />
26
26
  </p>
27
27
 
@@ -42,7 +42,7 @@
42
42
  ---
43
43
 
44
44
  > [!NOTE]
45
- > **关于这个 SDK。** grok-bot-sdk 复现 Grok Bot **0.70.2** 桌面客户端使用的浏览器登录,以及读取账号、读取名单、发送消息和订阅回复。作者是 [铁柱AGI](https://x.com/cgnot996)。它**不是 xAI 官方产品,不是 Cursor 官方产品,也不是 Grok Bot 官方 SDK**。
45
+ > **关于这个 SDK。** grok-bot-sdk 复现 Grok Bot **0.75.8** 桌面客户端使用的浏览器登录,以及读取账号、读取名单、发送消息和订阅回复。作者是 [铁柱AGI](https://x.com/cgnot996)。它**不是 xAI 官方产品,不是 Cursor 官方产品,也不是 Grok Bot 官方 SDK**。
46
46
  >
47
47
  > 同一作者的 [Grok App](https://github.com/RongleCat/grok-app) 是本机 Grok Build CLI 的桌面工作台。Grok App 和这个 SDK 是两个程序。
48
48
 
@@ -155,15 +155,17 @@ return client.botEventsSSE({
155
155
 
156
156
  ## 这一版包含什么
157
157
 
158
- 2026-10-14 对照 Grok Bot 0.70.2 核对过:
158
+ 对照 Grok Bot 0.75.8 核对过:
159
159
 
160
160
  - 一次浏览器登录可以接着读账号和云端 Bot 名单。
161
161
  - 用 Mac 客户端里已有的会话,可以走同一条读取路径。
162
- - 名单里的每条记录都带回同一种头像结构。服务端带了字段就用字段,缺了就按 UUID 推算。
162
+ - 名单里的每条记录都带回同一种头像结构。服务端带了字段就用字段,缺了就按 UUID 推算。头像哈希仍是 `grok-bot-0.70.2`。`pumpkin` 只在显式指定时出现。
163
163
  - `WatchGrokBotTranscripts` 以 Connect JSON 接通,既有回复可以经 `botEventsSSE` 转发。
164
- - 离线测试覆盖登录 URL、头像向量、帧解析、游标、取消、SSE 帧和发送结果。
164
+ - 只读调用:`listSessions`、`listTranscript`、`listAgentStatuses`、`getSendStatus`、`listAgentTasks`、`listTasks`、`listSubagents`、`listAgentTodos`、`listAgentSkills`、`listDeletedAgentIds`、`getRuntimeCapabilities`、`getSandBoxRunState`。
165
+ - 订阅事件还包含 `agent.state`、`computer.actions`、`box.state` 和 `page.title.changed`。
166
+ - 离线测试覆盖登录 URL、头像向量、帧解析、游标、取消、SSE 帧、发送结果,以及新增的只读记录。
165
167
 
166
- 默认客户端版本字符串是 `0.70.2`。你知道自己要匹配的客户端版本时,传入 `version`。
168
+ 默认客户端版本字符串是 `0.75.8`。你知道自己要匹配的客户端版本时,传入 `version`。CLI 还有只读子命令:`bots`、`sessions`、`transcript`、`tasks`、`capabilities`、`box`。
167
169
 
168
170
  ## 使用前要知道的事
169
171
 
package/avatar.mjs CHANGED
@@ -1,8 +1,11 @@
1
- // Parameter derivation verified against Grok Bot 0.70.2 static client code.
2
- // This module does not load or execute the official renderer or its assets.
1
+ // Parameter derivation verified against Grok Bot 0.70.2 and again against 0.75.8.
2
+ // 0.75.8 keeps the same hash, salts, default shapes, and palette. It adds one
3
+ // explicit shape, pumpkin, behind the sand_pumpkin_2026 picker gate. Pumpkin is
4
+ // not part of the default hash set. This module does not load or execute the
5
+ // official renderer or its assets.
3
6
  export const AVATAR_ALGORITHM_VERSION='grok-bot-0.70.2';
4
7
  export const DEFAULT_SHAPES=Object.freeze(['blob','pebble','squircle','tablet','wedge','hex','cloud','teardrop']);
5
- export const VALID_SHAPES=Object.freeze(['blob','pebble','bean','egg','squircle','tablet','capsule','cylinder','hex','gem','crystal','wedge','shield','dome','arch','cloud','teardrop','leaf','square','sparkle','clover','flower','heart','house','star']);
8
+ export const VALID_SHAPES=Object.freeze(['blob','pebble','bean','egg','squircle','tablet','capsule','cylinder','hex','gem','crystal','wedge','shield','dome','arch','cloud','teardrop','leaf','square','sparkle','clover','flower','heart','house','star','pumpkin']);
6
9
  export const DEFAULT_COLORS=Object.freeze(['brown','red','orange','yellow','green','cyan','blue','violet','magenta','gray']);
7
10
  const PALETTE=Object.freeze(Object.fromEntries([
8
11
  ['black','#000000','#000000','#FFFFFF'],
@@ -19,10 +19,17 @@ const failed = code => {
19
19
  function usage() {
20
20
  console.log(`grok-bot-sdk login [--no-open] --out <file>
21
21
  grok-bot-sdk whoami [--token-file <file>]
22
+ grok-bot-sdk bots [--token-file <file>]
23
+ grok-bot-sdk sessions --bot <id> [--token-file <file>]
24
+ grok-bot-sdk transcript --bot <id> [--session <id>] [--limit <n>] [--token-file <file>]
25
+ grok-bot-sdk tasks --bot <id> [--session <id>] [--token-file <file>]
26
+ grok-bot-sdk capabilities [--token-file <file>]
27
+ grok-bot-sdk box [--token-file <file>]
22
28
 
23
29
  login opens the browser, waits for approval, and writes the session to <file> (mode 0600).
24
30
  The command prints the login page URL and a redacted summary. It does not print tokens.
25
- whoami reads GROK_BOT_ACCESS_TOKEN, or the accessToken field inside --token-file, and prints only whether account fields exist.`);
31
+ whoami reads GROK_BOT_ACCESS_TOKEN, or the accessToken field inside --token-file, and prints only whether account fields exist.
32
+ The other commands are read-only. They print roster, session, transcript, task, capability, or box fields for the saved token.`);
26
33
  process.exitCode = 2;
27
34
  }
28
35
 
@@ -73,13 +80,17 @@ async function loadToken() {
73
80
  return { accessToken, version: process.env.GROK_BOT_CLIENT_VERSION };
74
81
  }
75
82
 
76
- async function whoami() {
83
+ async function clientFromToken() {
77
84
  const loaded = await loadToken();
78
- const client = new GrokBotClient({
85
+ return new GrokBotClient({
79
86
  accessToken: loaded.accessToken,
80
87
  ...loaded.version ? { version: loaded.version } : {},
81
88
  ...loaded.teamId !== undefined ? { teamId: loaded.teamId } : {},
82
89
  });
90
+ }
91
+
92
+ async function whoami() {
93
+ const client = await clientFromToken();
83
94
  const me = await client.getMe();
84
95
  console.log(JSON.stringify({
85
96
  status: 'ok',
@@ -89,9 +100,105 @@ async function whoami() {
89
100
  }));
90
101
  }
91
102
 
103
+ function requireBot() {
104
+ const bot = option('--bot');
105
+ if (!bot || bot.startsWith('-')) {
106
+ usage();
107
+ return undefined;
108
+ }
109
+ return bot;
110
+ }
111
+
112
+ async function bots() {
113
+ const roster = await (await clientFromToken()).listBots();
114
+ console.log(JSON.stringify({
115
+ status: 'ok',
116
+ count: roster.bots.length,
117
+ truncated: roster.truncated,
118
+ slackConnectionCount: roster.slackConnections.length,
119
+ teamsConnectionCount: roster.teamsConnections.length,
120
+ bots: roster.bots.map(bot => ({
121
+ id: bot.id,
122
+ name: bot.name,
123
+ kind: bot.kind,
124
+ role: bot.role,
125
+ sessionId: bot.sessionId,
126
+ })),
127
+ }));
128
+ }
129
+
130
+ async function sessions() {
131
+ const bot = requireBot();
132
+ if (!bot) return;
133
+ const result = await (await clientFromToken()).listSessions(bot);
134
+ console.log(JSON.stringify({ status: 'ok', count: result.sessions.length, sessions: result.sessions }));
135
+ }
136
+
137
+ async function transcript() {
138
+ const bot = requireBot();
139
+ if (!bot) return;
140
+ const limitText = option('--limit');
141
+ const limit = limitText === undefined ? 20 : Number(limitText);
142
+ const result = await (await clientFromToken()).listTranscript({
143
+ botId: bot,
144
+ sessionId: option('--session') ?? '',
145
+ limit,
146
+ });
147
+ console.log(JSON.stringify({
148
+ status: 'ok',
149
+ generation: result.generation,
150
+ count: result.entries.length,
151
+ cloudAgentPeerIdsKnown: result.cloudAgentPeerIdsKnown,
152
+ peerCount: result.cloudAgentPeerIds.length,
153
+ unreadAnchor: result.unreadAnchor,
154
+ entries: result.entries.map(entry => ({
155
+ seq: entry.seq,
156
+ updatedSeq: entry.updatedSeq,
157
+ entryId: entry.entryId,
158
+ entryKind: entry.entryKind,
159
+ bodyOmitted: entry.bodyOmitted,
160
+ bodyKind: typeof entry.body?.kind === 'string' ? entry.body.kind : null,
161
+ })),
162
+ }));
163
+ }
164
+
165
+ async function tasks() {
166
+ const bot = requireBot();
167
+ if (!bot) return;
168
+ const client = await clientFromToken();
169
+ const sessionId = option('--session') ?? '';
170
+ const [agentTasks, taskList] = await Promise.all([
171
+ client.listAgentTasks({ botId: bot }),
172
+ client.listTasks({ botId: bot, sessionId }),
173
+ ]);
174
+ console.log(JSON.stringify({
175
+ status: 'ok',
176
+ agentTaskCount: agentTasks.tasks.length,
177
+ taskCount: taskList.tasks.length,
178
+ agentTasks: agentTasks.tasks,
179
+ tasks: taskList.tasks,
180
+ }));
181
+ }
182
+
183
+ async function capabilities() {
184
+ const result = await (await clientFromToken()).getRuntimeCapabilities();
185
+ console.log(JSON.stringify({ status: 'ok', ...result }));
186
+ }
187
+
188
+ async function box() {
189
+ const result = await (await clientFromToken()).getSandBoxRunState();
190
+ console.log(JSON.stringify({ status: 'ok', ...result }));
191
+ }
192
+
92
193
  try {
93
194
  if (command === 'login') await login();
94
195
  else if (command === 'whoami') await whoami();
196
+ else if (command === 'bots') await bots();
197
+ else if (command === 'sessions') await sessions();
198
+ else if (command === 'transcript') await transcript();
199
+ else if (command === 'tasks') await tasks();
200
+ else if (command === 'capabilities') await capabilities();
201
+ else if (command === 'box') await box();
95
202
  else usage();
96
203
  } catch (error) {
97
204
  const code = error instanceof Error && /^[A-Z_]{1,80}$/.test(error.message) ? error.message : 'COMMAND_FAILED';
package/core.mjs CHANGED
@@ -73,7 +73,8 @@ export async function requestJSON(url,{method='POST',headers={},body,fetchImpl=f
73
73
  if(Buffer.byteLength(text)>8*1024*1024) return {ok:false,httpStatus:response.status,code:'RESPONSE_TOO_LARGE'};
74
74
  let data;
75
75
  try {data=JSON.parse(text);} catch {return {ok:false,httpStatus:response.status,code:'NON_JSON_RESPONSE'};}
76
- if(!response.ok) return {ok:false,httpStatus:response.status,code:typeof data?.code==='string' && /^[a-z_]{1,48}$/.test(data.code)?data.code:'HTTP_ERROR'};
76
+ const rawCode=typeof data?.code==='string'?data.code:typeof data?.error==='string'?data.error:'';
77
+ if(!response.ok) return {ok:false,httpStatus:response.status,code:/^[a-z_]{1,48}$/.test(rawCode)?rawCode:'HTTP_ERROR'};
77
78
  return {ok:true,httpStatus:response.status,data};
78
79
  } catch {return {ok:false,code:'NETWORK_OR_TLS_ERROR'};}
79
80
  }
@@ -125,6 +126,7 @@ export async function pollLogin(flow,{fetchImpl=fetch,sleep=delay,maxAttempts=15
125
126
  for(let attempt=0;attempt<maxAttempts;attempt++){
126
127
  const result=await requestJSON(url,{method:'GET',headers:{'content-type':'application/json'},fetchImpl});
127
128
  if(result.ok && typeof result.data.accessToken==='string' && typeof result.data.refreshToken==='string')return result.data;
129
+ if(result.httpStatus===403 && result.code==='sign_in_policy_violation')throw fixedError('SIGN_IN_POLICY_VIOLATION');
128
130
  if(result.httpStatus===404){errors=0;onPending(attempt+1);}
129
131
  else if(++errors>=3)throw fixedError('LOGIN_POLL_FAILED');
130
132
  await sleep(Math.min(1000*1.2**attempt,10000));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "grok-bot-sdk",
3
- "version": "0.1.0",
4
- "description": "Unofficial Node.js SDK for the Grok Bot 0.70.2 desktop client protocol: login, roster, avatars, send, and Connect-to-SSE replies.",
3
+ "version": "0.75.8",
4
+ "description": "Unofficial Node.js SDK for the Grok Bot 0.75.8 desktop client protocol: login, roster, sessions, transcript, tasks, send, and Connect-to-SSE replies.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "author": "RongleCat (铁柱AGI)",
@@ -22,7 +22,7 @@
22
22
  "node": ">=22.13"
23
23
  },
24
24
  "bin": {
25
- "grok-bot-sdk": "bin/grok-bot-sdk.mjs"
25
+ "grok-bot-sdk": "./bin/grok-bot-sdk.mjs"
26
26
  },
27
27
  "exports": {
28
28
  ".": {
@@ -45,6 +45,7 @@
45
45
  "bin",
46
46
  "README.md",
47
47
  "README_ZH.md",
48
+ "CHANGELOG.md",
48
49
  "LICENSE"
49
50
  ],
50
51
  "scripts": {
package/sdk.d.mts CHANGED
@@ -12,6 +12,7 @@ export interface ClientOptions {
12
12
  export interface Bot {
13
13
  id: string;
14
14
  serverId: string | null;
15
+ legacyAgentId: string;
15
16
  name: string;
16
17
  description: string;
17
18
  title: string;
@@ -19,11 +20,188 @@ export interface Bot {
19
20
  avatarColor: string;
20
21
  avatar: ResolvedAvatar;
21
22
  harness: string;
23
+ role: string;
22
24
  kind: string;
25
+ visibility: string;
23
26
  sessionId: string;
24
27
  teamId: number | null;
25
28
  viewerIsOwner: boolean;
26
29
  viewerCanManage: boolean;
30
+ teamDefaultForViewer: boolean;
31
+ ownerDisplayName: string;
32
+ draftingDisabled: boolean;
33
+ accessGrantsOnly: boolean;
34
+ memberAgentIds: string[];
35
+ viewerAllowedActions: string[];
36
+ createdAtMs: string | null;
37
+ updatedAtMs: string | null;
38
+ voiceId: string;
39
+ voiceSpeed: number | null;
40
+ voiceLanguage: string;
41
+ }
42
+ export interface SlackConnection {
43
+ agentId: string;
44
+ connected: boolean;
45
+ workspaceName: string | null;
46
+ installUrl: string | null;
47
+ unavailableReason: string | null;
48
+ pendingAdminApproval: boolean;
49
+ needsManifestUpdate: boolean;
50
+ reinstallUrl: string | null;
51
+ approvalDenied: boolean;
52
+ appUnreachable: boolean;
53
+ removedInSlack: boolean;
54
+ approvalRequestUnsent: boolean;
55
+ }
56
+ export interface TeamsInstalledTeam {
57
+ teamId: string;
58
+ displayName: string;
59
+ rscGranted: boolean;
60
+ installedAtMs: string | null;
61
+ }
62
+ export interface TeamsConnection {
63
+ agentId: string;
64
+ installState: string;
65
+ tenantId: string | null;
66
+ tenantDisplayName: string | null;
67
+ installedTeamCount: number;
68
+ installedTeams: TeamsInstalledTeam[];
69
+ }
70
+ export interface BotSession {
71
+ agentId: string;
72
+ sessionId: string;
73
+ kind: string;
74
+ createdAtMs: string | null;
75
+ updatedAtMs: string | null;
76
+ lastActivityAtMs: string | null;
77
+ }
78
+ export interface TranscriptEntry {
79
+ seq: string;
80
+ updatedSeq: string;
81
+ entryId: string | null;
82
+ entryKind: string;
83
+ bodyOmitted: boolean;
84
+ blobHash: string | null;
85
+ body: Record<string, unknown> | null;
86
+ }
87
+ export interface UnreadAnchor {
88
+ hasNewerRows: boolean;
89
+ entryId: string;
90
+ seq: string | null;
91
+ newerRows: number;
92
+ }
93
+ export interface AgentLiveState {
94
+ agentId: string;
95
+ sessionId: string;
96
+ isRunning: boolean;
97
+ isComposingMessage: boolean;
98
+ isRetrying: boolean;
99
+ updatedAtMs: string | null;
100
+ staleAfterMs: string | null;
101
+ hasRunningSubagents: boolean;
102
+ surfaceTaskId: string;
103
+ workingTaskIds: string[];
104
+ activityKind: string;
105
+ }
106
+ export interface AgentClientState {
107
+ agentId: string;
108
+ lastViewedAtMs: string | null;
109
+ unreadCount: number;
110
+ lastActivityAtMs: string | null;
111
+ lastEntryId: string;
112
+ lastEntryKind: string;
113
+ lastMessageId: string;
114
+ lastMessagePreview: string;
115
+ notificationsEnabled: boolean;
116
+ notifyOnUpdatesEnabled: boolean;
117
+ hiddenFromSidebar: boolean;
118
+ updatedAtMs: string | null;
119
+ lastMessageTaskId: string;
120
+ removedFromSidebar: boolean;
121
+ }
122
+ export interface AgentStatus {
123
+ agentId: string;
124
+ isRunning: boolean;
125
+ live: AgentLiveState | null;
126
+ client: AgentClientState | null;
127
+ }
128
+ export interface AgentTask {
129
+ id: string;
130
+ name: string;
131
+ description: string;
132
+ status: string;
133
+ updatedAtMs: string | null;
134
+ unreadEntryId: string | null;
135
+ unreadPreview: string | null;
136
+ unreadAtMs: string | null;
137
+ pinnedAtMs: string | null;
138
+ subagentIds: string[];
139
+ }
140
+ export interface BotTask {
141
+ id: string;
142
+ title: string;
143
+ state: string;
144
+ note: string;
145
+ createdAtMs: string | null;
146
+ doneAtMs: string | null;
147
+ seenDoneAtMs: string | null;
148
+ }
149
+ export interface RunningSubagent {
150
+ subagentId: string;
151
+ subagentType: string;
152
+ title: string;
153
+ startedAtMs: string | null;
154
+ freshUntilMs: string | null;
155
+ }
156
+ export interface AgentTodo {
157
+ id: string;
158
+ content: string;
159
+ status: string;
160
+ createdAtMs: string | null;
161
+ updatedAtMs: string | null;
162
+ dependencies: string[];
163
+ }
164
+ export interface AgentSkill {
165
+ id: string;
166
+ name: string;
167
+ description: string;
168
+ body: string;
169
+ source: string;
170
+ }
171
+ export interface RuntimeCapabilities {
172
+ durableIdentityEnabled: boolean;
173
+ durableIdentityWritesEnabled: boolean;
174
+ temporalCreationEnabled: boolean;
175
+ agentMessagingEnabled: boolean;
176
+ serverRoomsEnabled: boolean;
177
+ }
178
+ export interface SandBoxImageInfo {
179
+ registryTag: string | null;
180
+ staticSnapshotPath: string | null;
181
+ }
182
+ export interface SandBoxRunState {
183
+ runState: string;
184
+ imageUpdateAvailable: boolean;
185
+ egressTunnelOwnership: boolean;
186
+ image: SandBoxImageInfo | null;
187
+ }
188
+ export interface BoxState {
189
+ runState: string;
190
+ recreateInFlight: boolean;
191
+ imageUpdateAvailable: boolean;
192
+ hostVersion: string | null;
193
+ hostUpdateAvailable: boolean;
194
+ diskPressure: string;
195
+ updatedAtMs: string | null;
196
+ }
197
+ export interface ComputerAction {
198
+ agentId: string;
199
+ type: string;
200
+ x: number | null;
201
+ y: number | null;
202
+ button: string;
203
+ count: number;
204
+ atMs: string | null;
27
205
  }
28
206
  export interface BotCursor { generation: number; afterUpdatedSeq: string; }
29
207
  export interface WatchOptions {
@@ -53,8 +231,12 @@ export type BotEvent =
53
231
  | { type: 'reset'; id: string; data: { botId: string; generation: number } }
54
232
  | { type: 'resync-required'; data: { botId: string; generation: number } }
55
233
  | { type: 'turn.failed'; data: { botId: string; turnId: string; code: number | string; summary: string } }
56
- | { type: 'bot.state.changed'; data: { botId: string; families: string[] } }
57
- | { type: 'bot.updated'; data: { botId: string; changedFields: Array<string | number>; bot: Bot | null } };
234
+ | { type: 'bot.state.changed'; data: { botId: string; families: string[]; changedAtMs: string | null } }
235
+ | { type: 'bot.updated'; data: { botId: string; changedFields: Array<string | number>; bot: Bot | null } }
236
+ | { type: 'agent.state'; data: { botId: string; sessionId: string; snapshot: boolean; live: AgentLiveState[]; client: AgentClientState[] } }
237
+ | { type: 'computer.actions'; data: { botId: string; actions: ComputerAction[] } }
238
+ | { type: 'box.state'; data: { botId: string; snapshot: boolean; state: BoxState | null } }
239
+ | { type: 'page.title.changed'; data: { botId: string; storeId: string; canvasId: string | null; documentId: string | null; title: string; changedAtMs: string | null } };
58
240
  export interface SendOptions {
59
241
  botId: string;
60
242
  text: string;
@@ -62,6 +244,20 @@ export interface SendOptions {
62
244
  messageId?: string;
63
245
  attachmentPaths?: string[];
64
246
  attachmentNames?: string[];
247
+ replyToId?: string;
248
+ replyQuote?: string;
249
+ taskId?: string;
250
+ language?: string;
251
+ richText?: string;
252
+ machineId?: string;
253
+ }
254
+ export interface TranscriptQuery {
255
+ botId: string;
256
+ sessionId?: string;
257
+ limit?: number;
258
+ generation?: number;
259
+ beforeSeq?: string;
260
+ unreadBoundaryMs?: string;
65
261
  }
66
262
  export class SDKError extends Error {
67
263
  code: string;
@@ -72,8 +268,20 @@ export function normalizeBot(agent: CloudAvatarRecord): Bot;
72
268
  export class GrokBotClient {
73
269
  constructor(options: ClientOptions);
74
270
  getMe(): Promise<Record<string, unknown>>;
75
- listBots(options?: { includeTeamAgents?: boolean }): Promise<{ bots: Bot[]; truncated: boolean }>;
271
+ listBots(options?: { includeTeamAgents?: boolean; role?: string; addedTeamAgentsOnly?: boolean }): Promise<{ bots: Bot[]; truncated: boolean; slackConnections: SlackConnection[]; teamsConnections: TeamsConnection[] }>;
76
272
  sendMessage(options: SendOptions): Promise<{ botId: string; messageId: string; delivery: 'accepted-box' | 'accepted-temporal' | 'duplicate'; dispatched: boolean }>;
273
+ listSessions(botId: string): Promise<{ sessions: BotSession[] }>;
274
+ listTranscript(options: TranscriptQuery): Promise<{ generation: number; entries: TranscriptEntry[]; cloudAgentPeerIds: string[]; cloudAgentPeerIdsKnown: boolean; unreadAnchor: UnreadAnchor | null }>;
275
+ listAgentStatuses(agentIds?: string[]): Promise<{ statuses: AgentStatus[] }>;
276
+ getSendStatus(options: { botId: string; messageId: string; sessionId?: string; firstSentAgeMs?: string }): Promise<{ status: string; echoEntryId: string | null; rejectionCode: string | null; acceptedAtMs: string | null }>;
277
+ listAgentTasks(options: { botId: string; includeRecentlyDone?: boolean }): Promise<{ tasks: AgentTask[] }>;
278
+ listTasks(options: { botId: string; sessionId?: string }): Promise<{ tasks: BotTask[] }>;
279
+ listSubagents(botId: string): Promise<{ subagents: RunningSubagent[] }>;
280
+ listAgentTodos(botId: string): Promise<{ todos: AgentTodo[] }>;
281
+ listAgentSkills(botId: string): Promise<{ skills: AgentSkill[] }>;
282
+ listDeletedAgentIds(agentIds?: string[]): Promise<{ agentIds: string[] }>;
283
+ getRuntimeCapabilities(): Promise<RuntimeCapabilities>;
284
+ getSandBoxRunState(): Promise<SandBoxRunState>;
77
285
  watchBot(options: WatchOptions): AsyncGenerator<BotEvent, void, unknown>;
78
286
  botEventsSSE(options: WatchOptions & { keepAliveMs?: number }): Response;
79
287
  }
package/sdk.mjs CHANGED
@@ -9,7 +9,19 @@ export {createLogin,pollLogin} from './core.mjs';
9
9
  export {deriveDefaultAvatar,resolveAgentAvatar} from './avatar.mjs';
10
10
  export {SDKError} from './errors.mjs';
11
11
 
12
+ const CLIENT_VERSION='0.75.8';
13
+ const SEND_STATUS={0:'unspecified',1:'not-found',2:'accepted',3:'rejected',4:'pending',5:'unknown-durability',GROK_BOT_SEND_STATUS_UNSPECIFIED:'unspecified',GROK_BOT_SEND_STATUS_NOT_FOUND:'not-found',GROK_BOT_SEND_STATUS_ACCEPTED:'accepted',GROK_BOT_SEND_STATUS_REJECTED:'rejected',GROK_BOT_SEND_STATUS_PENDING:'pending',GROK_BOT_SEND_STATUS_UNKNOWN_DURABILITY:'unknown-durability'};
14
+ const RUN_STATE={0:'unspecified',1:'absent',2:'hibernated',3:'running',4:'starting',SAND_BOX_RUN_STATE_UNSPECIFIED:'unspecified',SAND_BOX_RUN_STATE_ABSENT:'absent',SAND_BOX_RUN_STATE_HIBERNATED:'hibernated',SAND_BOX_RUN_STATE_RUNNING:'running',SAND_BOX_RUN_STATE_STARTING:'starting'};
15
+
12
16
  function botKey(value){if(typeof value!=='string'||!value.length||value.length>128||!/^[A-Za-z0-9_-]+$/.test(value))throw new SDKError('BOT_ID_REQUIRED');return value;}
17
+ function botIds(values,{allowEmpty=false}={}){if(!Array.isArray(values)||(!allowEmpty&&!values.length))throw new SDKError('BOT_ID_REQUIRED');return values.map(botKey);}
18
+ function asText(value){return typeof value==='string'?value:'';}
19
+ function wireText(value){if(typeof value==='string')return value;if(typeof value==='number'&&Number.isFinite(value))return String(value);return '';}
20
+ function msText(value){if(typeof value==='string'&&/^-?\d{1,20}$/.test(value))return value;if(typeof value==='number'&&Number.isSafeInteger(value))return String(value);return null;}
21
+ function stringList(value){return Array.isArray(value)?value.filter(item=>typeof item==='string'):[];}
22
+ function wireList(value){return Array.isArray(value)?value.map(wireText).filter(item=>item.length>0):[];}
23
+ function mappedEnum(value,table){if(Object.hasOwn(table,value))return table[value];return wireText(value)||'unspecified';}
24
+ function objectRows(value){return Array.isArray(value)?value.filter(row=>row&&typeof row==='object'&&!Array.isArray(row)):[];}
13
25
  function uint64(value){
14
26
  if(typeof value==='number'&&!Number.isSafeInteger(value))throw new SDKError('CURSOR_INVALID');
15
27
  const text=String(value??0);if(!/^\d{1,20}$/.test(text)||BigInt(text)>18446744073709551615n)throw new SDKError('CURSOR_INVALID');return String(BigInt(text));
@@ -27,38 +39,84 @@ function decodeEntry(entry){
27
39
  try{const body=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(Buffer.from(entry.body,'base64')));return body&&typeof body==='object'&&!Array.isArray(body)?body:null;}catch{return null;}
28
40
  }
29
41
 
42
+ function normalizeSlackConnection(row){
43
+ return {agentId:asText(row.agentId),connected:row.connected===true,workspaceName:typeof row.workspaceName==='string'?row.workspaceName:null,installUrl:typeof row.installUrl==='string'?row.installUrl:null,unavailableReason:typeof row.unavailableReason==='string'?row.unavailableReason:null,pendingAdminApproval:row.pendingAdminApproval===true,needsManifestUpdate:row.needsManifestUpdate===true,reinstallUrl:typeof row.reinstallUrl==='string'?row.reinstallUrl:null,approvalDenied:row.approvalDenied===true,appUnreachable:row.appUnreachable===true,removedInSlack:row.removedInSlack===true,approvalRequestUnsent:row.approvalRequestUnsent===true};
44
+ }
45
+ function normalizeInstalledTeam(row){
46
+ if(!row||typeof row!=='object'||Array.isArray(row))return null;
47
+ return {teamId:asText(row.teamId),displayName:asText(row.displayName),rscGranted:row.rscGranted===true,installedAtMs:msText(row.installedAtMs)};
48
+ }
49
+ function normalizeTeamsConnection(row){
50
+ return {agentId:asText(row.agentId),installState:wireText(row.installState),tenantId:typeof row.tenantId==='string'?row.tenantId:null,tenantDisplayName:typeof row.tenantDisplayName==='string'?row.tenantDisplayName:null,installedTeamCount:Number.isInteger(row.installedTeamCount)?row.installedTeamCount:0,installedTeams:objectRows(row.installedTeams).map(normalizeInstalledTeam).filter(Boolean)};
51
+ }
52
+ function normalizeSession(row){
53
+ return {agentId:asText(row.agentId),sessionId:asText(row.sessionId),kind:wireText(row.kind),createdAtMs:msText(row.createdAtMs),updatedAtMs:msText(row.updatedAtMs),lastActivityAtMs:msText(row.lastActivityAtMs)};
54
+ }
55
+ function normalizeTranscriptEntry(entry){
56
+ return {seq:uint64(entry?.seq),updatedSeq:uint64(entry?.updatedSeq??entry?.seq),entryId:typeof entry?.entryId==='string'?entry.entryId:null,entryKind:asText(entry?.entryKind),bodyOmitted:entry?.bodyOmitted===true,blobHash:typeof entry?.blobHash==='string'?entry.blobHash:null,body:decodeEntry(entry??{})};
57
+ }
58
+ function normalizeUnreadAnchor(anchor){
59
+ if(!anchor||typeof anchor!=='object'||Array.isArray(anchor))return null;
60
+ return {hasNewerRows:anchor.hasNewerRows===true,entryId:asText(anchor.entryId),seq:msText(anchor.seq),newerRows:Number.isInteger(anchor.newerRows)?anchor.newerRows:0};
61
+ }
62
+ function normalizeLiveState(row){
63
+ return {agentId:asText(row.agentId),sessionId:asText(row.sessionId),isRunning:row.isRunning===true,isComposingMessage:row.isComposingMessage===true,isRetrying:row.isRetrying===true,updatedAtMs:msText(row.updatedAtMs),staleAfterMs:msText(row.staleAfterMs),hasRunningSubagents:row.hasRunningSubagents===true,surfaceTaskId:asText(row.surfaceTaskId),workingTaskIds:stringList(row.workingTaskIds),activityKind:asText(row.activity?.kind)};
64
+ }
65
+ function normalizeClientState(row){
66
+ return {agentId:asText(row.agentId),lastViewedAtMs:msText(row.lastViewedAtMs),unreadCount:Number.isInteger(row.unreadCount)?row.unreadCount:0,lastActivityAtMs:msText(row.lastActivityAtMs),lastEntryId:asText(row.lastEntryId),lastEntryKind:asText(row.lastEntryKind),lastMessageId:asText(row.lastMessageId),lastMessagePreview:asText(row.lastMessagePreview),notificationsEnabled:row.notificationsEnabled===true,notifyOnUpdatesEnabled:row.notifyOnUpdatesEnabled===true,hiddenFromSidebar:row.hiddenFromSidebar===true,updatedAtMs:msText(row.updatedAtMs),lastMessageTaskId:asText(row.lastMessageTaskId),removedFromSidebar:row.removedFromSidebar===true};
67
+ }
68
+ function normalizeBoxState(state){
69
+ if(!state||typeof state!=='object'||Array.isArray(state))return null;
70
+ return {runState:mappedEnum(state.runState,RUN_STATE),recreateInFlight:state.recreateInFlight===true,imageUpdateAvailable:state.imageUpdateAvailable===true,hostVersion:typeof state.hostVersion==='string'?state.hostVersion:null,hostUpdateAvailable:state.hostUpdateAvailable===true,diskPressure:wireText(state.diskPressure),updatedAtMs:msText(state.updatedAtMs)};
71
+ }
72
+ function normalizeComputerAction(row){
73
+ const number=value=>typeof value==='number'&&Number.isFinite(value)?value:null;
74
+ return {agentId:asText(row.agentId),type:asText(row.type),x:number(row.x),y:number(row.y),button:asText(row.button),count:Number.isInteger(row.count)?row.count:0,atMs:msText(row.atMs)};
75
+ }
76
+
30
77
  export function normalizeBot(agent){
31
78
  if(!agent||typeof agent.agentId!=='string'||!agent.agentId)throw new SDKError('INVALID_BOT_RECORD');
32
79
  const avatar=resolveAgentAvatar(agent);
33
- const text=value=>typeof value==='string'?value:'';
34
- return {id:agent.agentId,serverId:typeof agent.id==='string'?agent.id:null,name:text(agent.name),description:text(agent.description),title:text(agent.title),avatarShape:avatar.character.shape,avatarColor:avatar.character.color,avatar,harness:text(agent.harness),kind:text(agent.kind),sessionId:text(agent.viewerSessionId),teamId:Number.isInteger(agent.teamId)?agent.teamId:null,viewerIsOwner:agent.viewerIsOwner===true,viewerCanManage:agent.viewerCanManage===true};
80
+ return {id:agent.agentId,serverId:typeof agent.id==='string'?agent.id:null,legacyAgentId:asText(agent.legacyAgentId),name:asText(agent.name),description:asText(agent.description),title:asText(agent.title),avatarShape:avatar.character.shape,avatarColor:avatar.character.color,avatar,harness:asText(agent.harness),role:asText(agent.role),kind:wireText(agent.kind),visibility:wireText(agent.visibility),sessionId:asText(agent.viewerSessionId),teamId:Number.isInteger(agent.teamId)?agent.teamId:null,viewerIsOwner:agent.viewerIsOwner===true,viewerCanManage:agent.viewerCanManage===true,teamDefaultForViewer:agent.teamDefaultForViewer===true,ownerDisplayName:asText(agent.ownerDisplayName),draftingDisabled:agent.draftingDisabled===true,accessGrantsOnly:agent.accessGrantsOnly===true,memberAgentIds:stringList(agent.memberAgentIds),viewerAllowedActions:wireList(agent.viewerAllowedActions),createdAtMs:msText(agent.createdAtMs),updatedAtMs:msText(agent.updatedAtMs),voiceId:asText(agent.voiceId),voiceSpeed:typeof agent.voiceSpeed==='number'&&Number.isFinite(agent.voiceSpeed)?agent.voiceSpeed:null,voiceLanguage:asText(agent.voiceLanguage)};
35
81
  }
36
82
 
37
83
  export class GrokBotClient{
38
84
  #options;
39
85
  constructor(options){
40
86
  if(typeof options?.accessToken!=='string'||!options.accessToken)throw new SDKError('MISSING_ACCESS_TOKEN');
41
- this.#options={...options,version:options.version??'0.70.2'};
87
+ this.#options={...options,version:options.version??CLIENT_VERSION};
42
88
  }
43
89
  async #rpc(service,method,body,signal){
44
90
  return requestJSON(`${API_ORIGIN}/aiserver.v1.${service}/${method}`,{headers:clientHeaders(this.#options),body,fetchImpl:this.#options.fetchImpl,signal});
45
91
  }
92
+ async #data(service,method,body){
93
+ const response=await this.#rpc(service,method,body);
94
+ if(!response.ok)throw new SDKError('RPC_FAILED',response.httpStatus);
95
+ return response.data??{};
96
+ }
46
97
  async getMe(){
47
98
  const response=await this.#rpc('DashboardService','GetMe',{});
48
99
  if(!response.ok||!(response.data?.authId||response.data?.userId))throw new SDKError('ACCOUNT_AUTH_FAILED',response.httpStatus);
49
100
  return response.data;
50
101
  }
51
- async listBots({includeTeamAgents=true}={}){
52
- const response=await this.#rpc('GrokBotService','ListGrokBotAgents',{includeTeamAgents});
102
+ async listBots({includeTeamAgents=true,role,addedTeamAgentsOnly}={}){
103
+ const body={includeTeamAgents};
104
+ if(role!==undefined){if(typeof role!=='string')throw new SDKError('MESSAGE_INVALID');body.role=role;}
105
+ if(addedTeamAgentsOnly!==undefined)body.addedTeamAgentsOnly=addedTeamAgentsOnly===true;
106
+ const response=await this.#rpc('GrokBotService','ListGrokBotAgents',body);
53
107
  if(!response.ok)throw new SDKError('RPC_FAILED',response.httpStatus);
54
108
  if(!Array.isArray(response.data?.agents))throw new SDKError('INVALID_ROSTER_RESPONSE');
55
- return {bots:response.data.agents.map(normalizeBot),truncated:response.data.truncated===true};
109
+ return {bots:response.data.agents.map(normalizeBot),truncated:response.data.truncated===true,slackConnections:objectRows(response.data.slackConnections).map(normalizeSlackConnection),teamsConnections:objectRows(response.data.teamsConnections).map(normalizeTeamsConnection)};
56
110
  }
57
111
 
58
- async sendMessage({botId,text,sessionId='',messageId=randomUUID(),attachmentPaths=[],attachmentNames=[]}={}){
112
+ async sendMessage({botId,text,sessionId='',messageId=randomUUID(),attachmentPaths=[],attachmentNames=[],replyToId,replyQuote,taskId,language,richText,machineId}={}){
59
113
  botKey(botId);
60
114
  if(typeof text!=='string'||!text.trim()||typeof sessionId!=='string'||typeof messageId!=='string'||!messageId||!Array.isArray(attachmentPaths)||!Array.isArray(attachmentNames)||attachmentPaths.length!==attachmentNames.length||[...attachmentPaths,...attachmentNames].some(value=>typeof value!=='string'||!value))throw new SDKError('MESSAGE_INVALID');
61
- const response=await this.#rpc('GrokBotService','SendGrokBotUserMessage',{agentId:botId,text,sessionId,messageId,attachmentPaths,attachmentNames,sentAtMs:String(Date.now()),isFork:false,source:1,initiator:1,sendSource:1});
115
+ const extra={};
116
+ const put=(key,value)=>{if(value===undefined)return;if(typeof value!=='string'||!value)throw new SDKError('MESSAGE_INVALID');extra[key]=value;};
117
+ put('replyToId',replyToId);put('replyQuote',replyQuote);put('taskId',taskId);put('language',language);put('machineId',machineId);
118
+ if(richText!==undefined){if(typeof richText!=='string')throw new SDKError('MESSAGE_INVALID');extra.richText=richText;}
119
+ const response=await this.#rpc('GrokBotService','SendGrokBotUserMessage',{agentId:botId,text,sessionId,messageId,attachmentPaths,attachmentNames,sentAtMs:String(Date.now()),isFork:false,source:1,initiator:1,sendSource:1,...extra});
62
120
  if(!response.ok)throw new SDKError('SEND_UNCONFIRMED',response.httpStatus);
63
121
  const deliveries={1:'accepted-box',2:'accepted-temporal',3:'duplicate',GROK_BOT_USER_MESSAGE_DELIVERY_ACCEPTED_BOX:'accepted-box',GROK_BOT_USER_MESSAGE_DELIVERY_ACCEPTED_TEMPORAL:'accepted-temporal',GROK_BOT_USER_MESSAGE_DELIVERY_DUPLICATE:'duplicate'};
64
122
  const delivery=deliveries[response.data?.delivery];
@@ -66,6 +124,99 @@ export class GrokBotClient{
66
124
  return {botId,messageId,delivery,dispatched:response.data.dispatched===true};
67
125
  }
68
126
 
127
+ async listSessions(botId){
128
+ botKey(botId);
129
+ const data=await this.#data('GrokBotService','ListGrokBotAgentSessions',{agentId:botId});
130
+ if(!Array.isArray(data.sessions))throw new SDKError('RPC_FAILED');
131
+ return {sessions:objectRows(data.sessions).map(normalizeSession)};
132
+ }
133
+
134
+ async listTranscript({botId,sessionId='',limit=50,generation:generationValue,beforeSeq,unreadBoundaryMs}={}){
135
+ botKey(botId);
136
+ if(typeof sessionId!=='string'||sessionId.length>256||!Number.isInteger(limit)||limit<1||limit>4294967295)throw new SDKError('CURSOR_INVALID');
137
+ const body={agentId:botId,sessionId,limit};
138
+ if(generationValue!==undefined)body.generation=generation(generationValue);
139
+ if(beforeSeq!==undefined)body.beforeSeq=uint64(beforeSeq);
140
+ if(unreadBoundaryMs!==undefined)body.unreadBoundaryMs=uint64(unreadBoundaryMs);
141
+ const data=await this.#data('GrokBotService','ListGrokBotTranscriptEntries',body);
142
+ if(!Array.isArray(data.entries))throw new SDKError('RPC_FAILED');
143
+ return {generation:generation(data.generation),entries:data.entries.map(normalizeTranscriptEntry),cloudAgentPeerIds:stringList(data.cloudAgentPeerIds),cloudAgentPeerIdsKnown:data.cloudAgentPeerIdsKnown===true,unreadAnchor:normalizeUnreadAnchor(data.unreadAnchor)};
144
+ }
145
+
146
+ async listAgentStatuses(agentIds=[]){
147
+ const ids=botIds(agentIds,{allowEmpty:true});
148
+ const data=await this.#data('GrokBotService','ListGrokBotAgentStatuses',{agentIds:ids});
149
+ if(!Array.isArray(data.statuses))throw new SDKError('RPC_FAILED');
150
+ return {statuses:objectRows(data.statuses).map(row=>({agentId:asText(row.agentId),isRunning:row.isRunning===true,live:row.live&&typeof row.live==='object'&&!Array.isArray(row.live)?normalizeLiveState(row.live):null,client:row.client&&typeof row.client==='object'&&!Array.isArray(row.client)?normalizeClientState(row.client):null}))};
151
+ }
152
+
153
+ async getSendStatus({botId,messageId,sessionId='',firstSentAgeMs}={}){
154
+ botKey(botId);
155
+ if(typeof messageId!=='string'||!messageId||typeof sessionId!=='string')throw new SDKError('MESSAGE_INVALID');
156
+ const body={agentId:botId,messageId,sessionId};
157
+ if(firstSentAgeMs!==undefined)body.firstSentAgeMs=uint64(firstSentAgeMs);
158
+ const data=await this.#data('GrokBotService','GetGrokBotSendStatus',body);
159
+ return {status:mappedEnum(data.status,SEND_STATUS),echoEntryId:typeof data.echoEntryId==='string'?data.echoEntryId:null,rejectionCode:typeof data.rejectionCode==='string'?data.rejectionCode:null,acceptedAtMs:msText(data.acceptedAtMs)};
160
+ }
161
+
162
+ async listAgentTasks({botId,includeRecentlyDone}={}){
163
+ botKey(botId);
164
+ const body={agentId:botId};
165
+ if(includeRecentlyDone!==undefined)body.includeRecentlyDone=includeRecentlyDone===true;
166
+ const data=await this.#data('GrokBotService','ListGrokBotAgentTasks',body);
167
+ if(!Array.isArray(data.tasks))throw new SDKError('RPC_FAILED');
168
+ return {tasks:objectRows(data.tasks).map(row=>({id:asText(row.id),name:asText(row.name),description:asText(row.description),status:asText(row.status),updatedAtMs:msText(row.updatedAtMs),unreadEntryId:typeof row.unreadEntryId==='string'?row.unreadEntryId:null,unreadPreview:typeof row.unreadPreview==='string'?row.unreadPreview:null,unreadAtMs:msText(row.unreadAtMs),pinnedAtMs:msText(row.pinnedAtMs),subagentIds:stringList(row.subagentIds)}))};
169
+ }
170
+
171
+ async listTasks({botId,sessionId=''}={}){
172
+ botKey(botId);
173
+ if(typeof sessionId!=='string'||sessionId.length>256)throw new SDKError('CURSOR_INVALID');
174
+ const data=await this.#data('GrokBotService','ListGrokBotTasks',{agentId:botId,sessionId});
175
+ if(!Array.isArray(data.tasks))throw new SDKError('RPC_FAILED');
176
+ return {tasks:objectRows(data.tasks).map(row=>({id:asText(row.id),title:asText(row.title),state:wireText(row.state),note:asText(row.note),createdAtMs:msText(row.createdAtMs),doneAtMs:msText(row.doneAtMs),seenDoneAtMs:msText(row.seenDoneAtMs)}))};
177
+ }
178
+
179
+ async listSubagents(botId){
180
+ botKey(botId);
181
+ const data=await this.#data('GrokBotService','ListGrokBotSubagents',{agentId:botId});
182
+ if(!Array.isArray(data.subagents))throw new SDKError('RPC_FAILED');
183
+ return {subagents:objectRows(data.subagents).map(row=>({subagentId:asText(row.subagentId),subagentType:asText(row.subagentType),title:asText(row.title),startedAtMs:msText(row.startedAtMs),freshUntilMs:msText(row.freshUntilMs)}))};
184
+ }
185
+
186
+ async listAgentTodos(botId){
187
+ botKey(botId);
188
+ const data=await this.#data('GrokBotService','ListGrokBotAgentTodos',{agentId:botId});
189
+ if(!Array.isArray(data.todos))throw new SDKError('RPC_FAILED');
190
+ return {todos:objectRows(data.todos).map(row=>({id:asText(row.id),content:asText(row.content),status:wireText(row.status),createdAtMs:msText(row.createdAt),updatedAtMs:msText(row.updatedAt),dependencies:stringList(row.dependencies)}))};
191
+ }
192
+
193
+ async listAgentSkills(botId){
194
+ botKey(botId);
195
+ const data=await this.#data('GrokBotService','ListGrokBotAgentSkills',{agentId:botId});
196
+ if(!Array.isArray(data.skills))throw new SDKError('RPC_FAILED');
197
+ return {skills:objectRows(data.skills).map(row=>({id:asText(row.id),name:asText(row.name),description:asText(row.description),body:asText(row.body),source:asText(row.source)}))};
198
+ }
199
+
200
+ async listDeletedAgentIds(agentIds=[]){
201
+ const ids=botIds(agentIds,{allowEmpty:true});
202
+ const data=await this.#data('GrokBotService','ListDeletedGrokBotAgentIds',{agentIds:ids});
203
+ if(!Array.isArray(data.agentIds))throw new SDKError('RPC_FAILED');
204
+ return {agentIds:stringList(data.agentIds)};
205
+ }
206
+
207
+ async getRuntimeCapabilities(){
208
+ const data=await this.#data('GrokBotService','GetGrokBotRuntimeCapabilities',{});
209
+ const caps=data.capabilities;
210
+ if(!caps||typeof caps!=='object'||Array.isArray(caps))throw new SDKError('RPC_FAILED');
211
+ return {durableIdentityEnabled:caps.durableIdentityEnabled===true,durableIdentityWritesEnabled:caps.durableIdentityWritesEnabled===true,temporalCreationEnabled:caps.temporalCreationEnabled===true,agentMessagingEnabled:caps.agentMessagingEnabled===true,serverRoomsEnabled:caps.serverRoomsEnabled===true};
212
+ }
213
+
214
+ async getSandBoxRunState(){
215
+ const data=await this.#data('GrokBotService','GetSandBoxRunState',{});
216
+ const image=data.image&&typeof data.image==='object'&&!Array.isArray(data.image)?{registryTag:typeof data.image.registryTag==='string'?data.image.registryTag:null,staticSnapshotPath:typeof data.image.staticSnapshotPath==='string'?data.image.staticSnapshotPath:null}:null;
217
+ return {runState:mappedEnum(data.state,RUN_STATE),imageUpdateAvailable:data.imageUpdateAvailable===true,egressTunnelOwnership:data.egressTunnelOwnership===true,image};
218
+ }
219
+
69
220
  async *watchBot({botId,sessionId='',cursor,resumeFrom,signal,inlineBodyMaxBytes=262144}={}){
70
221
  botKey(botId);
71
222
  if(typeof sessionId!=='string'||sessionId.length>256||cursor&&resumeFrom)throw new SDKError('CURSOR_INVALID');
@@ -113,8 +264,12 @@ export class GrokBotClient{
113
264
  if(frame.cleared?.agentId===botId&&(frame.cleared.sessionId??'')===sessionId){state={generation:generation(frame.cleared.newGeneration),afterUpdatedSeq:'0'};yield {type:'reset',id:cursorID(botId,sessionId,state),data:{botId,generation:state.generation}};}
114
265
  if(frame.cursorTooOld?.agentId===botId&&(frame.cursorTooOld.sessionId??'')===sessionId){yield {type:'resync-required',data:{botId,generation:generation(frame.cursorTooOld.generation)}};return;}
115
266
  if(frame.turnFailed?.agentId===botId&&(frame.turnFailed.sessionId??'')===sessionId)yield {type:'turn.failed',data:{botId,turnId:frame.turnFailed.turnId??'',code:frame.turnFailed.code??0,summary:frame.turnFailed.summary??''}};
116
- if(frame.agentStateChanged?.agentId===botId)yield {type:'bot.state.changed',data:{botId,families:frame.agentStateChanged.families??[]}};
267
+ if(frame.agentStateChanged?.agentId===botId)yield {type:'bot.state.changed',data:{botId,families:stringList(frame.agentStateChanged.families),changedAtMs:msText(frame.agentStateChanged.changedAtMs)}};
117
268
  if(frame.rosterChanged?.agentId===botId){const changed=frame.rosterChanged;if(!changed.agent||changed.agent.agentId===botId)yield {type:'bot.updated',data:{botId,changedFields:changed.changedFields??[],bot:changed.agent?normalizeBot(changed.agent):null}};}
269
+ if(frame.agentState){const live=objectRows(frame.agentState.live).filter(row=>row.agentId===botId&&asText(row.sessionId)===sessionId).map(normalizeLiveState);const client=objectRows(frame.agentState.client).filter(row=>row.agentId===botId).map(normalizeClientState);if(frame.agentState.snapshot===true||live.length||client.length)yield {type:'agent.state',data:{botId,sessionId,snapshot:frame.agentState.snapshot===true,live,client}};}
270
+ if(frame.computerActions){const actions=objectRows(frame.computerActions.actions).filter(row=>row.agentId===botId).map(normalizeComputerAction);if(actions.length)yield {type:'computer.actions',data:{botId,actions}};}
271
+ if(frame.boxState)yield {type:'box.state',data:{botId,snapshot:frame.boxState.snapshot===true,state:normalizeBoxState(frame.boxState.state)}};
272
+ if(frame.pageTitleChanged){const page=frame.pageTitleChanged;const wrapped=page.page&&typeof page.page==='object'?page.page:null;yield {type:'page.title.changed',data:{botId,storeId:asText(page.storeId),canvasId:wrapped?.case==='canvasId'?asText(wrapped.value):typeof page.canvasId==='string'?page.canvasId:null,documentId:wrapped?.case==='documentId'?asText(wrapped.value):typeof page.documentId==='string'?page.documentId:null,title:asText(page.title),changedAtMs:msText(page.changedAtMs)}};}
118
273
  }
119
274
  }catch(error){
120
275
  if(signal?.aborted)return;