Skip to content

Interactive Telegram Node Browser and Mid-Graph Executor - #9

Merged
quickerup merged 1 commit into
mainfrom
jules-9490534594586566844-09f22b3a
Jul 15, 2026
Merged

Interactive Telegram Node Browser and Mid-Graph Executor#9
quickerup merged 1 commit into
mainfrom
jules-9490534594586566844-09f22b3a

Conversation

@quickerup

@quickerup quickerup commented Jul 15, 2026

Copy link
Copy Markdown
Owner

An interactive, slide-like node browsing and editing experience for Telegram Workflows, supporting graph branch visualizer, inline navigation, configuration edits, cascade node deletions, and mid-graph run triggers.


PR created automatically by Jules for task 9490534594586566844 started by @aethelred-agent-factory

Summary by CodeRabbit

  • New Features

    • Added /workflows and /browse commands for listing and exploring workflows.
    • Added interactive node browsing with pagination, configuration editing, deletion, and “run from here” execution.
    • Added support for starting workflow execution from a selected node.
    • Added authorization checks for interactive controls.
  • Bug Fixes

    • Improved handling of workflow execution start points and interactive callback actions.
  • Tests

    • Expanded end-to-end coverage for workflow browsing, editing, execution, and cleanup.

Features implemented:
- Added '/workflows', '📋 Workflows', and '/browse' commands to start the browser.
- Created an in-place pager using Telegram edited message text and inline keyboards.
- Developed a high-fidelity mathematically indented graph hierarchy visualizer.
- Supported secure 'Run from here' mid-graph workflow execution with '/confirm' token and 'start_node_id' dispatch.
- Implemented node configuration editing and cascade node deletion in D1.
- Extended 'scripts/test-builder-e2e.js' with all 22 tests.
- Re-verified full test suites pass completely.

Co-authored-by: aethelred-agent-factory <238771426+aethelred-agent-factory@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds Telegram workflow browsing with authorization, pagination, node editing and deletion, supports execution from a selected node through GitHub dispatch and the executor, and expands end-to-end tests for these flows.

Changes

Workflow browser and execution control

Layer / File(s) Summary
Browser entry points and authorization
worker/src/index.js
Adds workflow listing and browsing commands, callback routing, per-user authorization, and Telegram message helpers.
Node browsing and editing
worker/src/index.js
Stores browse and edit state in KV, renders node details and graph navigation, supports run/edit/delete actions, and persists configuration changes.
Partial execution dispatch
worker/src/index.js, scripts/executor.js
Passes start_node_id through confirmation and GitHub Actions dispatch, then starts execution at the selected node when valid.
End-to-end workflow coverage
scripts/test-builder-e2e.js
Adds workflow browser, pagination, editing, partial execution, deletion, and database cleanup tests, with dynamic update IDs and unauthorized-dispatch handling.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Telegram
  participant Worker
  participant D1
  participant KV
  participant GitHubActions
  participant Executor
  User->>Telegram: browse workflow
  Telegram->>Worker: command or callback
  Worker->>D1: load workflow and node state
  Worker->>KV: store browse or pending execution state
  Worker->>Telegram: render node actions
  User->>Telegram: run from selected node
  Telegram->>Worker: confirm execution
  Worker->>GitHubActions: dispatch with start_node_id
  GitHubActions->>Executor: provide client_payload
  Executor->>Executor: start at selected node
Loading

Possibly related PRs

Suggested reviewers: google-labs-jules[bot]

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main additions: an interactive node browser and mid-graph execution support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jules-9490534594586566844-09f22b3a

Comment @coderabbitai help to get the list of available commands.

@quickerup
quickerup merged commit 10e45c0 into main Jul 15, 2026
1 check was pending
@quickerup
quickerup deleted the jules-9490534594586566844-09f22b3a branch July 15, 2026 06:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
worker/src/index.js (1)

1540-1561: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

sendMessageAndReturn duplicates sendMessage (lines 2082-2100).

Both functions build the same request body and call the same sendMessage Telegram endpoint with near-identical error handling; the only difference is the return value. Consider having sendMessage delegate to sendMessageAndReturn (or vice versa) to avoid two copies of this logic diverging over time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 1540 - 1561, Refactor sendMessage and
sendMessageAndReturn to share one implementation for constructing the request,
calling Telegram’s sendMessage endpoint, and handling errors. Have one function
delegate to the other while preserving sendMessageAndReturn’s response behavior
and sendMessage’s existing caller-facing behavior; remove the duplicated
endpoint logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@worker/src/index.js`:
- Around line 1837-1853: Update handleCancel to delete the edit_state:${chatId}
entry from WORKFLOW_STATE alongside pending:${chatId} and builder:${chatId},
ensuring /cancel fully clears node-edit mode before sending the existing
“Discarded.” message.
- Around line 870-882: Update the fallback flow around handleNodeConfigEdit and
its edit-state handling so non-text messages do not reach text.trim() with an
undefined value. Validate message.text before processing active edits and return
the existing graceful prompt or handled result for stickers, photos, voice
messages, and other non-text inputs, while preserving normal text edit behavior.
- Around line 1588-1631: Update the wf_sel: callback path in
handleBrowserCallback to answer the callback query after invoking startBrowsing,
and update startBrowsing’s DB-missing, workflow-not-found, and no-nodes branches
to use editMessage with messageIdToEdit when provided, falling back to
sendMessage otherwise. Preserve the existing success rendering behavior and
ensure every workflow-selection callback is answered.
- Around line 1882-1896: Update the callback handling around editMessage so the
success notification is sent only when the edit succeeds. Handle network errors
and all non-OK responses, including non-429 HTTP failures, as failures; preserve
the existing rate-limit alert for status 429 and provide an appropriate failure
response for other errors instead of announcing that paging succeeded.
- Around line 1854-1880: Make node and edge deletion atomic in the wf_nav:delete
branch by replacing the two sequential env.DB.prepare(...).run() calls with a
single env.DB.batch([...]) containing both DELETE statements. Preserve the
existing workflow_id and selectedNode.id conditions and keep the subsequent
callback, remaining-node query, and state handling unchanged.

---

Nitpick comments:
In `@worker/src/index.js`:
- Around line 1540-1561: Refactor sendMessage and sendMessageAndReturn to share
one implementation for constructing the request, calling Telegram’s sendMessage
endpoint, and handling errors. Have one function delegate to the other while
preserving sendMessageAndReturn’s response behavior and sendMessage’s existing
caller-facing behavior; remove the duplicated endpoint logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fb9c4aef-38c9-4c38-a45d-6963cacd60da

📥 Commits

Reviewing files that changed from the base of the PR and between 8638e58 and 9700e00.

📒 Files selected for processing (3)
  • scripts/executor.js
  • scripts/test-builder-e2e.js
  • worker/src/index.js

Comment thread worker/src/index.js
Comment on lines 870 to 882
} else {
const handled = await handleBuilderState(env, chatId, message.text);
if (!handled) {
if (message.text) {
await sendMessage(env, chatId,
"Send me a workflow as a .json file attachment. I'll validate it and show you " +
"a summary — nothing runs until you reply /confirm <token>. Send /whoami to see your chat ID.\n\n" +
"Or design a workflow directly in Telegram using: /newworkflow");
const editHandled = await handleNodeConfigEdit(env, chatId, message.text);
if (!editHandled) {
const handled = await handleBuilderState(env, chatId, message.text);
if (!handled) {
if (message.text) {
await sendMessage(env, chatId,
"Send me a workflow as a .json file attachment. I'll validate it and show you " +
"a summary — nothing runs until you reply /confirm <token>. Send /whoami to see your chat ID.\n\n" +
"Or design a workflow directly in Telegram using: /newworkflow");
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

handleNodeConfigEdit crashes on non-text messages sent during an active edit session.

The fallback path calls handleNodeConfigEdit(env, chatId, message.text) unconditionally; message.text is undefined for stickers/photos/voice/etc. If an edit_state is active, text.trim() at line 1923 throws TypeError: Cannot read properties of undefined (reading 'trim'). It's caught by the outer try/catch and surfaced as a raw Error: ... message rather than a graceful prompt.

🔧 Proposed fix
   const editState = JSON.parse(rawState);
   const { workflow_id, node_id, message_id } = editState;
 
+  if (typeof text !== 'string') {
+    await sendMessage(env, chatId, "Please send the new configuration as a text message.");
+    return true;
+  }
+
   if (!env.DB) {

Also applies to: 1899-1925

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 870 - 882, Update the fallback flow around
handleNodeConfigEdit and its edit-state handling so non-text messages do not
reach text.trim() with an undefined value. Validate message.text before
processing active edits and return the existing graceful prompt or handled
result for stickers, photos, voice messages, and other non-text inputs, while
preserving normal text edit behavior.

Comment thread worker/src/index.js
Comment on lines +1588 to +1631
async function startBrowsing(env, chatId, workflowId, messageIdToEdit = null) {
if (!env.DB) {
await sendMessage(env, chatId, "Database is not configured.");
return;
}

const workflow = await env.DB.prepare(
"SELECT id, name FROM workflows WHERE id = ?"
).bind(workflowId).first();

if (!workflow) {
await sendMessage(env, chatId, `Workflow with ID "${workflowId}" not found.`);
return;
}

const nodes = await env.DB.prepare(
"SELECT id FROM nodes WHERE workflow_id = ? ORDER BY id ASC"
).bind(workflowId).all();

if (!nodes.results || nodes.results.length === 0) {
await sendMessage(env, chatId, `Workflow "${workflow.name}" has no nodes to browse.`);
return;
}

const browseState = {
workflow_id: workflowId,
node_index: 0,
message_id: messageIdToEdit
};

const rendered = await renderNodeBrowser(env, chatId, workflowId, 0);
if (!rendered) return;

if (messageIdToEdit) {
await editMessage(env, chatId, messageIdToEdit, rendered.text, rendered.reply_markup);
} else {
const sentMsg = await sendMessageAndReturn(env, chatId, rendered.text, rendered.reply_markup);
if (sentMsg && sentMsg.result) {
browseState.message_id = sentMsg.result.message_id;
}
}

await env.WORKFLOW_STATE.put(`browse:${chatId}`, JSON.stringify(browseState));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

wf_sel: workflow selection never answers the callback query.

handleBrowserCallback's wf_sel: branch calls startBrowsing(...) and returns, but neither it nor startBrowsing ever calls answerCallbackQuery. Per the Telegram Bot API, clients show a loading spinner on the pressed button until the query is answered (or ~10-15s until it expires) — this fires on every single workflow selection, the primary entry point of this feature. Additionally, startBrowsing's error branches (DB not configured / workflow not found / no nodes) always call sendMessage, even when messageIdToEdit is supplied (i.e. called from this callback context) — leaving the original message with its now-stale inline keyboard unedited, on top of the missing callback answer.

🔧 Proposed fix
   if (data.startsWith('wf_sel:')) {
     const workflowId = data.substring(7);
-    await startBrowsing(env, chatId, workflowId, callbackQuery.message.message_id);
+    await startBrowsing(env, chatId, workflowId, callbackQuery.message.message_id);
+    await answerCallbackQuery(env, callbackQuery.id);
     return;
   }

And inside startBrowsing, prefer editMessage over sendMessage in the error branches whenever messageIdToEdit is set.

Also applies to: 1722-1730

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 1588 - 1631, Update the wf_sel: callback
path in handleBrowserCallback to answer the callback query after invoking
startBrowsing, and update startBrowsing’s DB-missing, workflow-not-found, and
no-nodes branches to use editMessage with messageIdToEdit when provided, falling
back to sendMessage otherwise. Preserve the existing success rendering behavior
and ensure every workflow-selection callback is answered.

Comment thread worker/src/index.js
Comment on lines +1837 to +1853
} else if (data === 'wf_nav:edit') {
const selectedNode = nodesRows[nodeIndex];
const editState = {
workflow_id: workflowId,
node_id: selectedNode.id,
state: 'EDITING_NODE_CONFIG',
message_id: callbackQuery.message.message_id
};

await env.WORKFLOW_STATE.put(`edit_state:${chatId}`, JSON.stringify(editState));
await answerCallbackQuery(env, callbackQuery.id, { text: "Editing node..." });
await sendMessage(
env, chatId,
`Editing configuration for node [${selectedNode.id}] (${selectedNode.type}).\n` +
`Send the new configuration as a JSON object, or a single text/numeric value (e.g., the URL, command, ms, or message):`
);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

/cancel doesn't clear edit_state:${chatId}, so "cancelling" a node edit silently leaves it armed.

Once wf_nav:edit stores edit_state:${chatId} (with no TTL), the only escape hatch a user has is /cancel — but handleCancel only deletes pending:${chatId} and builder:${chatId}. After sending /cancel and seeing "Discarded.", the next ordinary text message the user sends (which doesn't match any of the special-cased prefixes) is routed through handleNodeConfigEdit first and gets silently written into the node's config, overwriting it without the user's intent.

🔧 Proposed fix (outside this range, in the existing handleCancel)
async function handleCancel(env, chatId) {
  await env.WORKFLOW_STATE.delete(`pending:${chatId}`);
  await env.WORKFLOW_STATE.delete(`builder:${chatId}`);
  await env.WORKFLOW_STATE.delete(`edit_state:${chatId}`);
  await sendMessage(env, chatId, 'Discarded.');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 1837 - 1853, Update handleCancel to delete
the edit_state:${chatId} entry from WORKFLOW_STATE alongside pending:${chatId}
and builder:${chatId}, ensuring /cancel fully clears node-edit mode before
sending the existing “Discarded.” message.

Comment thread worker/src/index.js
Comment on lines +1854 to +1880
} else if (data === 'wf_nav:delete') {
const selectedNode = nodesRows[nodeIndex];

await env.DB.prepare(
"DELETE FROM nodes WHERE workflow_id = ? AND id = ?"
).bind(workflowId, selectedNode.id).run();

await env.DB.prepare(
"DELETE FROM edges WHERE workflow_id = ? AND (source = ? OR target = ?)"
).bind(workflowId, selectedNode.id, selectedNode.id).run();

await answerCallbackQuery(env, callbackQuery.id, { text: "Node deleted!" });

const { results: remainingNodes } = await env.DB.prepare(
"SELECT id FROM nodes WHERE workflow_id = ? ORDER BY id ASC"
).bind(workflowId).all();

if (!remainingNodes || remainingNodes.length === 0) {
await env.WORKFLOW_STATE.delete(`browse:${chatId}`);
await editMessage(env, chatId, callbackQuery.message.message_id, `🔧 ${workflow.name} has no nodes left.`);
return;
}

if (nodeIndex >= remainingNodes.length) {
nodeIndex = remainingNodes.length - 1;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Node/edge deletion isn't atomic.

The DELETE FROM nodes and DELETE FROM edges calls are two separate, sequential .run() calls. If the process fails between them (worker eviction, D1 transient error), the node is gone but its edges remain — dangling edges that no longer round-trip cleanly (the graph renderer masks this by labelling the missing target 'unknown', but /api/workflows/:id and dispatch payloads would still carry the orphaned edge). The rest of the codebase already uses env.DB.batch([...]) for this kind of multi-statement write (e.g. handleConfirm's node/edge inserts), and Cloudflare documents batched statements as transactional (all-or-nothing).

🔧 Proposed fix
-    await env.DB.prepare(
-      "DELETE FROM nodes WHERE workflow_id = ? AND id = ?"
-    ).bind(workflowId, selectedNode.id).run();
-
-    await env.DB.prepare(
-      "DELETE FROM edges WHERE workflow_id = ? AND (source = ? OR target = ?)"
-    ).bind(workflowId, selectedNode.id, selectedNode.id).run();
+    await env.DB.batch([
+      env.DB.prepare("DELETE FROM nodes WHERE workflow_id = ? AND id = ?").bind(workflowId, selectedNode.id),
+      env.DB.prepare("DELETE FROM edges WHERE workflow_id = ? AND (source = ? OR target = ?)").bind(workflowId, selectedNode.id, selectedNode.id)
+    ]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (data === 'wf_nav:delete') {
const selectedNode = nodesRows[nodeIndex];
await env.DB.prepare(
"DELETE FROM nodes WHERE workflow_id = ? AND id = ?"
).bind(workflowId, selectedNode.id).run();
await env.DB.prepare(
"DELETE FROM edges WHERE workflow_id = ? AND (source = ? OR target = ?)"
).bind(workflowId, selectedNode.id, selectedNode.id).run();
await answerCallbackQuery(env, callbackQuery.id, { text: "Node deleted!" });
const { results: remainingNodes } = await env.DB.prepare(
"SELECT id FROM nodes WHERE workflow_id = ? ORDER BY id ASC"
).bind(workflowId).all();
if (!remainingNodes || remainingNodes.length === 0) {
await env.WORKFLOW_STATE.delete(`browse:${chatId}`);
await editMessage(env, chatId, callbackQuery.message.message_id, `🔧 ${workflow.name} has no nodes left.`);
return;
}
if (nodeIndex >= remainingNodes.length) {
nodeIndex = remainingNodes.length - 1;
}
}
} else if (data === 'wf_nav:delete') {
const selectedNode = nodesRows[nodeIndex];
await env.DB.batch([
env.DB.prepare("DELETE FROM nodes WHERE workflow_id = ? AND id = ?").bind(workflowId, selectedNode.id),
env.DB.prepare("DELETE FROM edges WHERE workflow_id = ? AND (source = ? OR target = ?)").bind(workflowId, selectedNode.id, selectedNode.id)
]);
await answerCallbackQuery(env, callbackQuery.id, { text: "Node deleted!" });
const { results: remainingNodes } = await env.DB.prepare(
"SELECT id FROM nodes WHERE workflow_id = ? ORDER BY id ASC"
).bind(workflowId).all();
if (!remainingNodes || remainingNodes.length === 0) {
await env.WORKFLOW_STATE.delete(`browse:${chatId}`);
await editMessage(env, chatId, callbackQuery.message.message_id, `🔧 ${workflow.name} has no nodes left.`);
return;
}
if (nodeIndex >= remainingNodes.length) {
nodeIndex = remainingNodes.length - 1;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 1854 - 1880, Make node and edge deletion
atomic in the wf_nav:delete branch by replacing the two sequential
env.DB.prepare(...).run() calls with a single env.DB.batch([...]) containing
both DELETE statements. Preserve the existing workflow_id and selectedNode.id
conditions and keep the subsequent callback, remaining-node query, and state
handling unchanged.

Comment thread worker/src/index.js
Comment on lines +1882 to +1896
browseState.node_index = nodeIndex;
await env.WORKFLOW_STATE.put(`browse:${chatId}`, JSON.stringify(browseState));

const rendered = await renderNodeBrowser(env, chatId, workflowId, nodeIndex);
if (rendered) {
const editRes = await editMessage(env, chatId, callbackQuery.message.message_id, rendered.text, rendered.reply_markup);
if (editRes && !editRes.ok && editRes.status === 429) {
await answerCallbackQuery(env, callbackQuery.id, {
text: "Too fast! Please wait a moment...",
show_alert: true
});
} else {
await answerCallbackQuery(env, callbackQuery.id, { text: `Paged to node ${nodeIndex + 1}` });
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

editMessage failures other than HTTP 429 are reported to the user as success.

The condition only distinguishes editRes.status === 429; any other failure (network error from the catch block, or a non-429 HTTP error such as "message is not modified") falls into the else branch, which announces "Paged to node ${nodeIndex + 1}" as if the edit succeeded, even though the displayed message may be stale/unchanged.

🔧 Proposed fix
   const rendered = await renderNodeBrowser(env, chatId, workflowId, nodeIndex);
   if (rendered) {
     const editRes = await editMessage(env, chatId, callbackQuery.message.message_id, rendered.text, rendered.reply_markup);
-    if (editRes && !editRes.ok && editRes.status === 429) {
+    if (editRes && editRes.ok) {
+      await answerCallbackQuery(env, callbackQuery.id, { text: `Paged to node ${nodeIndex + 1}` });
+    } else if (editRes && editRes.status === 429) {
       await answerCallbackQuery(env, callbackQuery.id, {
         text: "Too fast! Please wait a moment...",
         show_alert: true
       });
     } else {
-      await answerCallbackQuery(env, callbackQuery.id, { text: `Paged to node ${nodeIndex + 1}` });
+      await answerCallbackQuery(env, callbackQuery.id, {
+        text: "Failed to update. Please try again.",
+        show_alert: true
+      });
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
browseState.node_index = nodeIndex;
await env.WORKFLOW_STATE.put(`browse:${chatId}`, JSON.stringify(browseState));
const rendered = await renderNodeBrowser(env, chatId, workflowId, nodeIndex);
if (rendered) {
const editRes = await editMessage(env, chatId, callbackQuery.message.message_id, rendered.text, rendered.reply_markup);
if (editRes && !editRes.ok && editRes.status === 429) {
await answerCallbackQuery(env, callbackQuery.id, {
text: "Too fast! Please wait a moment...",
show_alert: true
});
} else {
await answerCallbackQuery(env, callbackQuery.id, { text: `Paged to node ${nodeIndex + 1}` });
}
}
browseState.node_index = nodeIndex;
await env.WORKFLOW_STATE.put(`browse:${chatId}`, JSON.stringify(browseState));
const rendered = await renderNodeBrowser(env, chatId, workflowId, nodeIndex);
if (rendered) {
const editRes = await editMessage(env, chatId, callbackQuery.message.message_id, rendered.text, rendered.reply_markup);
if (editRes && editRes.ok) {
await answerCallbackQuery(env, callbackQuery.id, { text: `Paged to node ${nodeIndex + 1}` });
} else if (editRes && editRes.status === 429) {
await answerCallbackQuery(env, callbackQuery.id, {
text: "Too fast! Please wait a moment...",
show_alert: true
});
} else {
await answerCallbackQuery(env, callbackQuery.id, {
text: "Failed to update. Please try again.",
show_alert: true
});
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@worker/src/index.js` around lines 1882 - 1896, Update the callback handling
around editMessage so the success notification is sent only when the edit
succeeds. Handle network errors and all non-OK responses, including non-429 HTTP
failures, as failures; preserve the existing rate-limit alert for status 429 and
provide an appropriate failure response for other errors instead of announcing
that paging succeeded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant