grok-telegram-bot 2.3.1 → 2.4.0
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/.env.example +26 -0
- package/CHANGELOG.md +37 -0
- package/package.json +1 -1
- package/scripts/analyze-jsonl.ts +33 -0
- package/scripts/delayed-restart.ps1 +29 -0
- package/scripts/probe-exit-response-shape.py +77 -0
- package/scripts/probe-plan-exit.py +60 -0
- package/scripts/probe-plan-exit2.py +48 -0
- package/scripts/probe-plan-fields.py +41 -0
- package/scripts/probe-plan-fields2.py +58 -0
- package/scripts/probe-plan-response-path.py +48 -0
- package/scripts/sample-claude-tooluse.ts +21 -0
- package/scripts/sample-kiro-events.ts +31 -0
- package/scripts/smoke-exit-plan.ts +274 -0
- package/scripts/smoke-exit-shapes.ts +252 -0
- package/scripts/smoke-import.mjs +82 -0
- package/scripts/smoke-import.ts +73 -0
- package/src/app/accounts.ts +84 -0
- package/src/app/instance-lock.ts +6 -0
- package/src/app/types.ts +19 -2
- package/src/app/updater.ts +17 -6
- package/src/app/usage.ts +204 -7
- package/src/bot/account-rotator.ts +10 -0
- package/src/bot/bot.ts +36 -0
- package/src/bot/chat-controller.ts +35 -0
- package/src/bot/commands.ts +2 -0
- package/src/bot/complexity-gate.ts +69 -0
- package/src/bot/deps.ts +19 -0
- package/src/bot/handlers/accounts.ts +51 -1
- package/src/bot/handlers/import-session.ts +290 -0
- package/src/bot/handlers/menu.ts +17 -38
- package/src/bot/handlers/message.ts +1 -0
- package/src/bot/handlers/running.ts +35 -5
- package/src/bot/handlers/session-card.ts +12 -0
- package/src/bot/handlers/sessions.ts +14 -3
- package/src/bot/handlers/usage.ts +118 -16
- package/src/bot/menu/keyboard.ts +5 -4
- package/src/bot/menu/status-panel.ts +19 -6
- package/src/bot/prompt-content.ts +4 -0
- package/src/bot/session-fork.ts +11 -0
- package/src/bot/session-runtime.ts +740 -58
- package/src/bot/suggestions.ts +429 -0
- package/src/config.ts +41 -0
- package/src/grok/client.ts +91 -16
- package/src/grok/plan-approval.ts +72 -0
- package/src/grok/session-log.ts +16 -0
- package/src/grok/types.ts +21 -2
- package/src/import/build-import.ts +132 -0
- package/src/import/history-readers.ts +681 -0
- package/src/import/list-running.ts +100 -0
- package/src/import/sources.ts +78 -0
- package/src/index.ts +179 -24
- package/src/render/diff.ts +11 -2
- package/src/render/file-summary.ts +31 -1
- package/src/render/markdown.ts +293 -35
- package/src/render/plan.ts +127 -0
- package/src/render/session-comment.ts +261 -0
- package/src/render/tool-call-detail.ts +400 -19
- package/src/render/tool-call-merge.ts +115 -0
- package/src/render/tool-call.ts +405 -142
- package/src/render/truncate.ts +85 -0
- package/src/service/windows.ts +14 -2
- package/src/sessions/history.ts +57 -0
- package/src/sessions/store.ts +3 -0
- package/src/sessions/types.ts +5 -0
- package/src/stream/streamer.ts +73 -9
- package/src/tasks/runner.ts +4 -3
package/.env.example
CHANGED
|
@@ -85,6 +85,32 @@ PROGRESS_FALLBACK=true
|
|
|
85
85
|
# session (marked "From other session"). true/false
|
|
86
86
|
NOTIFY_OTHER_SESSIONS=true
|
|
87
87
|
|
|
88
|
+
# ── Post-turn suggestions ────────────────────────────────────────────────────
|
|
89
|
+
# After a successful Done, quietly ask Grok for 1–3 follow-up steps (JSON with
|
|
90
|
+
# a need score 0–100). Shown as inline buttons on the Done message.
|
|
91
|
+
# SUGGESTIONS_ENABLED=true
|
|
92
|
+
# Auto-queue any suggestion with need ≥ this percent (0 = buttons only, no auto).
|
|
93
|
+
# Default 95. Unrelated suggestions are instructed to score ≤ 60.
|
|
94
|
+
# When several suggestions qualify, they are merged into ONE multi-step prompt:
|
|
95
|
+
# 1) first
|
|
96
|
+
# 2) second
|
|
97
|
+
# SUGGESTIONS_AUTO_APPROVE_PCT=95
|
|
98
|
+
|
|
99
|
+
# ── Self-recheck (once per user prompt, gated) ───────────────────────────────
|
|
100
|
+
# After a successful user turn (queue empty), the bot may run one quality pass.
|
|
101
|
+
# Flow:
|
|
102
|
+
# 1) Skip automatically if no files were modified this turn.
|
|
103
|
+
# 2) Quiet meta ask: should we recheck? AI may refuse (simple tasks, pure
|
|
104
|
+
# build/install, nothing worth re-verifying).
|
|
105
|
+
# 3) If needed, AI writes a focused recheck prompt; that prompt is submitted
|
|
106
|
+
# as the one-shot self-recheck turn (may use tools to fix real issues).
|
|
107
|
+
# 4) Only then: Done + suggestions (Done splits first-turn vs recheck files).
|
|
108
|
+
# Default true. Alias: SLEF_RECHECK (typo-tolerant).
|
|
109
|
+
# SELF_RECHECK=true
|
|
110
|
+
# Optional: when set AND the AI decides recheck is needed, use this template
|
|
111
|
+
# instead of the AI-written recheck body ({{USER}} / {{DONE}} placeholders).
|
|
112
|
+
# SELF_RECHECK_PROMPT=
|
|
113
|
+
|
|
88
114
|
# ── MCP servers (/mcp) ───────────────────────────────────────────────────────
|
|
89
115
|
# /mcp lists MCP servers the bot can discover and health-checks them. Grok Build
|
|
90
116
|
# manages its own MCP servers via `grok`'s /mcps modal and ~/.grok/config.toml,
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,42 @@ The latest section is published verbatim as the GitHub Release notes by
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [2.4.0] - 2026-08-01
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Post-turn suggestions.** After Done, quietly ask for 1–3 follow-ups with a
|
|
17
|
+
need score (0–100). Shown as inline buttons; items at/above
|
|
18
|
+
`SUGGESTIONS_AUTO_APPROVE_PCT` (default 95) run as **one** numbered multi-step
|
|
19
|
+
prompt (`1) …\n2) …`).
|
|
20
|
+
- **Gated self-recheck.** After a successful user turn with file changes, a
|
|
21
|
+
quiet AI decision may refuse recheck (simple tasks / pure build) or write a
|
|
22
|
+
focused recheck prompt that runs once before Done. Skipped when no files
|
|
23
|
+
modified. Env: `SELF_RECHECK` / `SLEF_RECHECK`, optional `SELF_RECHECK_PROMPT`.
|
|
24
|
+
- **Auto complexity steering.** First prompt of a session steers Simple vs
|
|
25
|
+
Complex without asking the user; plan-mode exit is auto-approved
|
|
26
|
+
(`_x.ai/exit_plan_mode`).
|
|
27
|
+
- **Plan board rendering.** Live plan entries with progress in the status stream.
|
|
28
|
+
- **Session import.** Import external session transcripts into a bot-controlled
|
|
29
|
+
session.
|
|
30
|
+
- **Richer tool / file / markdown rendering.** Improved tool-call detail, file
|
|
31
|
+
op summaries (including first-turn vs recheck split), session card comments,
|
|
32
|
+
and Markdown fence safety.
|
|
33
|
+
- **Usage / accounts menu polish.** Clearer usage and account surfaces in the
|
|
34
|
+
Telegram menu.
|
|
35
|
+
|
|
36
|
+
### Fixed / hardened
|
|
37
|
+
|
|
38
|
+
- **Near-100% bot lifetime.** Telegram long-poll restarts forever; uncaught
|
|
39
|
+
errors stay up; ACP and `createBot` boot retries; lock heartbeat; Windows VBS
|
|
40
|
+
forever-restart supervisor; stop kills the VBS host; self-relaunch / updater
|
|
41
|
+
stay alive if spawn fails.
|
|
42
|
+
- **Self-recheck / queue safety.** `mergeInputs` preserves `skipSelfRecheck`;
|
|
43
|
+
meta turns flush alone so auto-batches cannot re-arm recheck loops; cancel
|
|
44
|
+
during recheck decision does not queue a recheck.
|
|
45
|
+
- **Plan exit reliability.** Correct ACP method and approval payload for plan
|
|
46
|
+
mode exit.
|
|
47
|
+
|
|
12
48
|
## [2.3.1] - 2026-07-19
|
|
13
49
|
|
|
14
50
|
### Fixed
|
|
@@ -778,6 +814,7 @@ from a single chat and switch between them, on a redesigned, compact menu.
|
|
|
778
814
|
diffs, MarkdownV2 rendering, scheduled tasks, multi-image prompts, and a
|
|
779
815
|
cross-platform 24/7 background service.
|
|
780
816
|
|
|
817
|
+
[2.4.0]: https://github.com/artickc/grok-telegram-bot/releases/tag/v2.4.0
|
|
781
818
|
[2.3.1]: https://github.com/artickc/grok-telegram-bot/releases/tag/v2.3.1
|
|
782
819
|
[2.3.0]: https://github.com/artickc/grok-telegram-bot/releases/tag/v2.3.0
|
|
783
820
|
[2.2.4]: https://github.com/artickc/grok-telegram-bot/releases/tag/v2.2.4
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grok-telegram-bot",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Control the official Grok Build CLI from Telegram over the Agent Client Protocol (ACP). Sign in with your xAI account, switch projects, resume sessions, stream responses with diffs, queue follow-ups, manage multiple sign-ins, and run 24/7 as a cross-platform background service.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
|
|
4
|
+
const path = process.argv[2];
|
|
5
|
+
if (!path) {
|
|
6
|
+
console.error("usage: analyze-jsonl <path>");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const kinds = new Map<string, number>();
|
|
11
|
+
let lines = 0;
|
|
12
|
+
let bad = 0;
|
|
13
|
+
|
|
14
|
+
const rl = createInterface({ input: createReadStream(path, { encoding: "utf8" }) });
|
|
15
|
+
for await (const line of rl) {
|
|
16
|
+
if (!line.trim()) continue;
|
|
17
|
+
lines++;
|
|
18
|
+
try {
|
|
19
|
+
const o = JSON.parse(line) as { kind?: string; type?: string };
|
|
20
|
+
const k = o.kind || o.type || "(none)";
|
|
21
|
+
kinds.set(k, (kinds.get(k) || 0) + 1);
|
|
22
|
+
} catch {
|
|
23
|
+
bad++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(JSON.stringify({ path, lines, bad }, null, 2));
|
|
28
|
+
console.log(
|
|
29
|
+
[...kinds.entries()]
|
|
30
|
+
.sort((a, b) => b[1] - a[1])
|
|
31
|
+
.map(([k, n]) => `${n}\t${k}`)
|
|
32
|
+
.join("\n"),
|
|
33
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Set-Location "H:\Lucru\Domains\grok-telegram-bot"
|
|
2
|
+
$log = "H:\Lucru\Domains\grok-telegram-bot\logs\restart-verify.log"
|
|
3
|
+
function W($m) { $line = "$(Get-Date -Format o) $m"; Add-Content -Path $log -Value $line; Write-Output $line }
|
|
4
|
+
|
|
5
|
+
W "waiting 20s for idle/flush..."
|
|
6
|
+
Start-Sleep -Seconds 20
|
|
7
|
+
|
|
8
|
+
$old = Get-CimInstance Win32_Process -Filter "name='node.exe'" | Where-Object { $_.CommandLine -match 'grok-telegram-bot\\src\\index' } | Select-Object -ExpandProperty ProcessId
|
|
9
|
+
W "old pid(s): $($old -join ',')"
|
|
10
|
+
|
|
11
|
+
W "running: npx tsx src/cli.ts restart"
|
|
12
|
+
$out = npx tsx src/cli.ts restart 2>&1 | Out-String
|
|
13
|
+
W $out
|
|
14
|
+
|
|
15
|
+
Start-Sleep -Seconds 8
|
|
16
|
+
$new = Get-CimInstance Win32_Process -Filter "name='node.exe'" | Where-Object { $_.CommandLine -match 'grok-telegram-bot\\src\\index' } | Select-Object ProcessId,CreationDate,CommandLine
|
|
17
|
+
W "new process:"
|
|
18
|
+
W ($new | Format-List | Out-String)
|
|
19
|
+
|
|
20
|
+
# Confirm loaded code path mentions outcome approved by checking process start after our file mtime
|
|
21
|
+
$fixMtime = (Get-Item "H:\Lucru\Domains\grok-telegram-bot\src\grok\plan-approval.ts").LastWriteTime
|
|
22
|
+
W "plan-approval.ts mtime: $fixMtime"
|
|
23
|
+
if ($new) {
|
|
24
|
+
W "status:"
|
|
25
|
+
W (npx tsx src/cli.ts status 2>&1 | Out-String)
|
|
26
|
+
W "RESTART_OK"
|
|
27
|
+
} else {
|
|
28
|
+
W "RESTART_FAILED no process"
|
|
29
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Deep probe of grok.exe for ExitPlanMode reverse-request/response shapes."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
data = Path(r"C:\Users\artic\.grok\bin\grok.exe").read_bytes()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def nearby(label: bytes, n: int = 3, before: int = 120, after: int = 300) -> None:
|
|
11
|
+
start = 0
|
|
12
|
+
for i in range(n):
|
|
13
|
+
j = data.find(label, start)
|
|
14
|
+
if j < 0:
|
|
15
|
+
print(f"NOT FOUND: {label!r}")
|
|
16
|
+
return
|
|
17
|
+
chunk = data[max(0, j - before) : j + after]
|
|
18
|
+
text = "".join(chr(b) if 32 <= b < 127 else "|" for b in chunk)
|
|
19
|
+
print(f"\n=== {label!r} @{j} ===\n{text}")
|
|
20
|
+
start = j + len(label)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Key messages around reverse-request
|
|
24
|
+
for lab in [
|
|
25
|
+
b"sending ext_method to client",
|
|
26
|
+
b"client disconnected mid-approval",
|
|
27
|
+
b"no client wired",
|
|
28
|
+
b"ExitPlanModeExtRequest",
|
|
29
|
+
b"ExitPlanModeExtResponse",
|
|
30
|
+
b"Failed to parse ExitPlanMode",
|
|
31
|
+
b"x.ai/exit_plan_mode",
|
|
32
|
+
b"agent.ext_method",
|
|
33
|
+
b"ext_method",
|
|
34
|
+
b"session/request_permission",
|
|
35
|
+
]:
|
|
36
|
+
nearby(lab, n=2)
|
|
37
|
+
|
|
38
|
+
print("\n\n==== null-terminated strings containing ExitPlan or approved near ExtResponse ====")
|
|
39
|
+
idx = data.find(b"ExitPlanModeExtResponse serialization")
|
|
40
|
+
region = data[max(0, idx - 4000) : idx + 2000]
|
|
41
|
+
cur = bytearray()
|
|
42
|
+
strs: list[str] = []
|
|
43
|
+
for b in region:
|
|
44
|
+
if 32 <= b < 127:
|
|
45
|
+
cur.append(b)
|
|
46
|
+
else:
|
|
47
|
+
if 3 <= len(cur) <= 60:
|
|
48
|
+
s = cur.decode()
|
|
49
|
+
if any(
|
|
50
|
+
k in s.lower()
|
|
51
|
+
for k in (
|
|
52
|
+
"approv",
|
|
53
|
+
"abandon",
|
|
54
|
+
"feedback",
|
|
55
|
+
"decision",
|
|
56
|
+
"action",
|
|
57
|
+
"outcome",
|
|
58
|
+
"plan",
|
|
59
|
+
"comment",
|
|
60
|
+
"exit",
|
|
61
|
+
"request",
|
|
62
|
+
"session",
|
|
63
|
+
"content",
|
|
64
|
+
)
|
|
65
|
+
):
|
|
66
|
+
strs.append(s)
|
|
67
|
+
cur.clear()
|
|
68
|
+
for s in dict.fromkeys(strs):
|
|
69
|
+
print(repr(s))
|
|
70
|
+
|
|
71
|
+
print("\n\n==== method name candidates ====")
|
|
72
|
+
for m in sorted(set(re.findall(rb"x\.ai/[a-zA-Z0-9_/\-]{3,50}", data))):
|
|
73
|
+
s = m.decode("ascii", "ignore")
|
|
74
|
+
if any(k in s.lower() for k in ("plan", "exit", "ask", "approv", "question")):
|
|
75
|
+
# only print clean-looking
|
|
76
|
+
if re.fullmatch(r"x\.ai/[a-z0-9_/\-]+", s):
|
|
77
|
+
print(s)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Probe grok.exe for ExitPlanMode ACP extension request/response shape."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
path = Path(r"C:\Users\artic\.grok\bin\grok.exe")
|
|
8
|
+
data = path.read_bytes()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ctx(idx: int, before: int = 80, after: int = 200) -> str:
|
|
12
|
+
chunk = data[max(0, idx - before) : idx + after]
|
|
13
|
+
return "".join(chr(b) if 32 <= b < 127 else "|" for b in chunk)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
needles = [
|
|
17
|
+
b"ExitPlanModeExtRequest",
|
|
18
|
+
b"ExitPlanModeExtResponse",
|
|
19
|
+
b"Failed to parse ExitPlanModeExtRequest",
|
|
20
|
+
b"x.ai/exit_plan_mode",
|
|
21
|
+
b"[exit_plan_mode] user approved",
|
|
22
|
+
b"[exit_plan_mode] user abandoned",
|
|
23
|
+
b"request_changes",
|
|
24
|
+
b"requestChanges",
|
|
25
|
+
b"additionalFeedback",
|
|
26
|
+
b"additional_feedback",
|
|
27
|
+
b"planContent",
|
|
28
|
+
b"plan_content",
|
|
29
|
+
b"emptyPlan",
|
|
30
|
+
b"EmptyPlan",
|
|
31
|
+
b"PlanReady",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
for n in needles:
|
|
35
|
+
i = 0
|
|
36
|
+
c = 0
|
|
37
|
+
while c < 3:
|
|
38
|
+
j = data.find(n, i)
|
|
39
|
+
if j < 0:
|
|
40
|
+
break
|
|
41
|
+
print(f"\n=== {n!r} @ {j} ===")
|
|
42
|
+
print(ctx(j, 40, 160))
|
|
43
|
+
i = j + len(n)
|
|
44
|
+
c += 1
|
|
45
|
+
|
|
46
|
+
print("\n=== combos approved/abandoned ===")
|
|
47
|
+
for m in re.finditer(rb"approved.{0,30}abandoned|abandoned.{0,30}approved", data, re.I):
|
|
48
|
+
print(ctx(m.start(), 10, 80))
|
|
49
|
+
|
|
50
|
+
print("\n=== identifiers near first ExtResponse ===")
|
|
51
|
+
idx = data.find(b"ExitPlanModeExtResponse")
|
|
52
|
+
if idx >= 0:
|
|
53
|
+
region = data[idx : idx + 1200]
|
|
54
|
+
print(re.findall(rb"[A-Za-z_][A-Za-z0-9_]{2,40}", region)[:60])
|
|
55
|
+
|
|
56
|
+
print("\n=== identifiers near struct ExitPlanModeExtRequest with 3 ===")
|
|
57
|
+
idx = data.find(b"struct ExitPlanModeExtRequest with 3 elements")
|
|
58
|
+
if idx >= 0:
|
|
59
|
+
region = data[idx - 400 : idx + 400]
|
|
60
|
+
print([s.decode() for s in re.findall(rb"[A-Za-z_][A-Za-z0-9_]{2,40}", region)])
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
data = Path(r"C:\Users\artic\.grok\bin\grok.exe").read_bytes()
|
|
5
|
+
|
|
6
|
+
# Pull null-terminated strings of length 3-40 around key anchors
|
|
7
|
+
anchors = [
|
|
8
|
+
data.find(b"ExitPlanModeExtRequest serialization"),
|
|
9
|
+
data.find(b"ExitPlanModeExtResponse serialization"),
|
|
10
|
+
data.find(b"struct ExitPlanModeExtRequest with 3"),
|
|
11
|
+
data.find(b"struct ExitPlanModeExtResponse with 2"),
|
|
12
|
+
data.find(b"approvedabandonedAdditional feedback"),
|
|
13
|
+
data.find(b"[exit_plan_mode] intercepted"),
|
|
14
|
+
data.find(b"Plan approval requested"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
for a in anchors:
|
|
18
|
+
if a < 0:
|
|
19
|
+
continue
|
|
20
|
+
region = data[max(0, a - 800) : a + 800]
|
|
21
|
+
strs = []
|
|
22
|
+
cur = bytearray()
|
|
23
|
+
for b in region:
|
|
24
|
+
if 32 <= b < 127:
|
|
25
|
+
cur.append(b)
|
|
26
|
+
else:
|
|
27
|
+
if 3 <= len(cur) <= 48:
|
|
28
|
+
strs.append(cur.decode("ascii"))
|
|
29
|
+
cur.clear()
|
|
30
|
+
if 3 <= len(cur) <= 48:
|
|
31
|
+
strs.append(cur.decode("ascii"))
|
|
32
|
+
# unique preserve order
|
|
33
|
+
seen = set()
|
|
34
|
+
out = []
|
|
35
|
+
for s in strs:
|
|
36
|
+
if s not in seen:
|
|
37
|
+
seen.add(s)
|
|
38
|
+
out.append(s)
|
|
39
|
+
print("\n==== ANCHOR", a, "====")
|
|
40
|
+
for s in out:
|
|
41
|
+
print(s)
|
|
42
|
+
|
|
43
|
+
# Also list method names that look like reverse requests
|
|
44
|
+
print("\n==== x.ai methods clean ====")
|
|
45
|
+
for m in sorted(set(re.findall(rb"x\.ai/[a-z0-9_/\-]{3,60}", data))):
|
|
46
|
+
s = m.decode()
|
|
47
|
+
if any(k in s for k in ["plan", "ask", "question", "mode", "approv"]):
|
|
48
|
+
print(s)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
data = Path(r"C:\Users\artic\.grok\bin\grok.exe").read_bytes()
|
|
5
|
+
|
|
6
|
+
for n in [
|
|
7
|
+
b"additional_feedback",
|
|
8
|
+
b"additionalFeedback",
|
|
9
|
+
b"AdditionalFeedback",
|
|
10
|
+
b"review_comments",
|
|
11
|
+
b"reviewComments",
|
|
12
|
+
]:
|
|
13
|
+
print(n, "count", data.count(n), "at", data.find(n))
|
|
14
|
+
|
|
15
|
+
# Pull short identifiers within 2KB of ExitPlanModeExtResponse serialization
|
|
16
|
+
idx = data.find(b"ExitPlanModeExtResponse serialization")
|
|
17
|
+
region = data[idx - 2000 : idx + 500]
|
|
18
|
+
idents = re.findall(rb"[A-Za-z_][A-Za-z0-9_]{2,40}", region)
|
|
19
|
+
# filter boring
|
|
20
|
+
skip = {"struct", "with", "elements", "serialization", "should", "not", "fail", "crates", "codegen"}
|
|
21
|
+
seen = []
|
|
22
|
+
for s in idents:
|
|
23
|
+
t = s.decode()
|
|
24
|
+
if t in skip or t in seen:
|
|
25
|
+
continue
|
|
26
|
+
seen.append(t)
|
|
27
|
+
print("idents near serialization:", seen[:80])
|
|
28
|
+
|
|
29
|
+
# Look for schemars / json schema around ExitPlanModeExt
|
|
30
|
+
for pat in [rb'"decision"', rb'"action"', rb'"outcome"', rb'"approved"', rb'"abandoned"']:
|
|
31
|
+
c = 0
|
|
32
|
+
start = 0
|
|
33
|
+
while c < 5:
|
|
34
|
+
j = data.find(pat, start)
|
|
35
|
+
if j < 0:
|
|
36
|
+
break
|
|
37
|
+
window = data[j : j + 80]
|
|
38
|
+
if b"plan" in window.lower() or b"approv" in window or b"abandon" in window:
|
|
39
|
+
print(pat, j, window[:80])
|
|
40
|
+
c += 1
|
|
41
|
+
start = j + 1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
data = Path(r"C:\Users\artic\.grok\bin\grok.exe").read_bytes()
|
|
5
|
+
|
|
6
|
+
# Find null-separated sequences containing approved and abandoned as siblings
|
|
7
|
+
needle = b"approved\x00abandoned"
|
|
8
|
+
idx = data.find(needle)
|
|
9
|
+
print("approved\\0abandoned", idx)
|
|
10
|
+
if idx >= 0:
|
|
11
|
+
print(data[idx - 40 : idx + 80])
|
|
12
|
+
|
|
13
|
+
needle2 = b"abandoned\x00approved"
|
|
14
|
+
print("abandoned\\0approved", data.find(needle2))
|
|
15
|
+
|
|
16
|
+
# Also try without null (concatenated variants for Display)
|
|
17
|
+
print("concat", data.find(b"approvedabandoned"))
|
|
18
|
+
|
|
19
|
+
# Search for field name candidates near concat
|
|
20
|
+
idx = data.find(b"approvedabandoned")
|
|
21
|
+
region = data[idx - 100 : idx + 120]
|
|
22
|
+
# extract C strings
|
|
23
|
+
cur = bytearray()
|
|
24
|
+
strs = []
|
|
25
|
+
for b in region:
|
|
26
|
+
if 32 <= b < 127:
|
|
27
|
+
cur.append(b)
|
|
28
|
+
else:
|
|
29
|
+
if cur:
|
|
30
|
+
strs.append(cur.decode())
|
|
31
|
+
cur.clear()
|
|
32
|
+
print("near concat:", strs)
|
|
33
|
+
|
|
34
|
+
# Look at ExitPlanModeExtRequest 3 elements - field names for request
|
|
35
|
+
for label in [b"plan_content", b"plan_file_path", b"session_id", b"sessionId", b"tool_call_id", b"toolCallId", b"empty"]:
|
|
36
|
+
print(label, data.count(label))
|
|
37
|
+
|
|
38
|
+
# Find bytes: decision\0 or action\0 near approved
|
|
39
|
+
for field in [b"decision\x00", b"action\x00", b"outcome\x00", b"result\x00", b"choice\x00", b"feedback\x00", b"comments\x00", b"message\x00"]:
|
|
40
|
+
start = 0
|
|
41
|
+
hits = 0
|
|
42
|
+
while hits < 3:
|
|
43
|
+
j = data.find(field, start)
|
|
44
|
+
if j < 0:
|
|
45
|
+
break
|
|
46
|
+
win = data[j : j + 60]
|
|
47
|
+
if b"approv" in win or b"abandon" in win or b"plan" in win.lower():
|
|
48
|
+
print("FIELD", field, j, win)
|
|
49
|
+
hits += 1
|
|
50
|
+
start = j + 1
|
|
51
|
+
|
|
52
|
+
# Extract all strings of form [a-z_]+ that appear within 200 bytes after "ExitPlanModeExtResponse with 2"
|
|
53
|
+
idx = data.find(b"struct ExitPlanModeExtResponse with 2 elements")
|
|
54
|
+
print("struct at", idx)
|
|
55
|
+
if idx >= 0:
|
|
56
|
+
region = data[idx : idx + 300]
|
|
57
|
+
print(region)
|
|
58
|
+
print(re.findall(rb"[a-z_]{3,30}", region))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
data = Path(r"C:\Users\artic\.grok\bin\grok.exe").read_bytes()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def show(label: bytes, n: int = 3, before: int = 100, after: int = 250) -> None:
|
|
7
|
+
start = 0
|
|
8
|
+
for _ in range(n):
|
|
9
|
+
j = data.find(label, start)
|
|
10
|
+
if j < 0:
|
|
11
|
+
print(label, "not found")
|
|
12
|
+
return
|
|
13
|
+
chunk = data[max(0, j - before) : j + after]
|
|
14
|
+
text = "".join(chr(b) if 32 <= b < 127 else "|" for b in chunk)
|
|
15
|
+
print(f"\n=== {label!r} @ {j} ===")
|
|
16
|
+
print(text)
|
|
17
|
+
start = j + len(label)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
for lab in [
|
|
21
|
+
b"[exit_plan_mode] user approved",
|
|
22
|
+
b"[exit_plan_mode] user abandoned",
|
|
23
|
+
b"Opened plan approval view from ext_method",
|
|
24
|
+
b"Failed to parse ExitPlanModeExtResponse",
|
|
25
|
+
b"ExitPlanModeExtResponse serialization should not fail",
|
|
26
|
+
b"resume exit_plan_mode reverse-request failed",
|
|
27
|
+
b"Plan approval requested",
|
|
28
|
+
]:
|
|
29
|
+
show(lab, n=2)
|
|
30
|
+
|
|
31
|
+
# Print unique printable strings of length 4-24 in a 1KB window after
|
|
32
|
+
# "Opened plan approval view from ext_method"
|
|
33
|
+
idx = data.find(b"Opened plan approval view from ext_method")
|
|
34
|
+
region = data[idx : idx + 1500]
|
|
35
|
+
cur = bytearray()
|
|
36
|
+
seen = []
|
|
37
|
+
for b in region:
|
|
38
|
+
if 32 <= b < 127:
|
|
39
|
+
cur.append(b)
|
|
40
|
+
else:
|
|
41
|
+
if 4 <= len(cur) <= 32:
|
|
42
|
+
s = cur.decode()
|
|
43
|
+
if s not in seen:
|
|
44
|
+
seen.append(s)
|
|
45
|
+
cur.clear()
|
|
46
|
+
print("\nstrings after open plan approval:")
|
|
47
|
+
for s in seen:
|
|
48
|
+
print(" ", s)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const path =
|
|
4
|
+
"H:\\Lucru\\Domains\\claude-telegram-bot\\data\\sessions\\019f47bc-67bc-7043-a40a-f487a1d5af8b.jsonl";
|
|
5
|
+
const lines = readFileSync(path, "utf8").split(/\n/).filter(Boolean);
|
|
6
|
+
|
|
7
|
+
for (const line of lines) {
|
|
8
|
+
const o = JSON.parse(line) as { kind?: string; data?: Record<string, unknown> };
|
|
9
|
+
if (o.kind === "ToolUse") {
|
|
10
|
+
console.log("keys", Object.keys(o.data || {}));
|
|
11
|
+
console.log(JSON.stringify(o).slice(0, 1500));
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const o = JSON.parse(line) as { kind?: string };
|
|
17
|
+
if (o.kind === "AssistantMessage") {
|
|
18
|
+
console.log("ASSIST", JSON.stringify(o).slice(0, 800));
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const path = join(homedir(), ".kiro", "sessions", "cli", "8d37ff05-4658-4e7c-be14-3693e1c82d00.jsonl");
|
|
6
|
+
const lines = readFileSync(path, "utf8").split(/\n/).filter(Boolean);
|
|
7
|
+
|
|
8
|
+
for (const line of lines) {
|
|
9
|
+
const o = JSON.parse(line) as { kind?: string; data?: { content?: unknown; results?: unknown } };
|
|
10
|
+
if (o.kind === "ToolResults") {
|
|
11
|
+
console.log("TOOLRESULTS data keys", o.data ? Object.keys(o.data) : []);
|
|
12
|
+
console.log(JSON.stringify(o.data).slice(0, 1200));
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const o = JSON.parse(line) as { kind?: string; data?: { content?: unknown } };
|
|
18
|
+
if (o.kind === "AssistantMessage" && JSON.stringify(o).includes("toolUse")) {
|
|
19
|
+
console.log("ASSIST toolUse content");
|
|
20
|
+
console.log(JSON.stringify(o.data?.content).slice(0, 1200));
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
const o = JSON.parse(line) as { kind?: string; data?: { content?: unknown } };
|
|
26
|
+
if (o.kind === "AssistantMessage" && JSON.stringify(o).includes("thinking")) {
|
|
27
|
+
console.log("ASSIST thinking content");
|
|
28
|
+
console.log(JSON.stringify(o.data?.content).slice(0, 800));
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|