Skip to content

feat(tool): Handle tool approval timeout state - #755

Merged
haiphucnguyen merged 6 commits into
askimo-ai:mainfrom
huynguyen20011:feature/improve-tool-notifications
Oct 9, 2026
Merged

haiphucnguyen merged 6 commits into
askimo-ai:mainfrom
huynguyen20011:feature/improve-tool-notifications

Conversation

@huynguyen20011

Copy link
Copy Markdown
Contributor

Summary

  • Adds onToolApprovalTimedOut callback to tool streaming workflows.
  • Ensures that the pending approval UI state is cleared automatically if the user fails to approve or deny the tool request in time.
  • Improves chat stability and user experience during asynchronous tool interactions.

Related Issue(s)

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📝 Documentation
  • ♻️ Refactor (no functional change)
  • 🧪 Tests
  • 🔧 Chore / tooling / CI

What Changed & Why

How to Test

./gradlew test

Screenshots / Recordings (if applicable)

Checklist

  • I have read the CONTRIBUTING guide
  • My branch is up to date with main
  • I ran ./gradlew spotlessApply and ./gradlew detekt locally
  • I ran ./gradlew test and all tests pass
  • I added/updated tests for my changes (if applicable)
  • I added/updated documentation (if applicable)
  • My commits are signed off (DCO) — git commit -s
  • This PR is focused on a single feature/fix

Additional Notes

Copilot AI 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.

🟡 Changes recommended

Asynchronous state updates can race with failure persistence and completion cleanup, leaving stale approval or tool status.

3 open findings
What changed in this PR

Adds timeout handling for tool-approval workflows and clears pending approval UI state.

Changes:

  • Adds an approval-timeout callback and failed-tool notifications.
  • Connects timeout cleanup to desktop session state.
  • Uses the card-provided contrast-safe text color.
File Description
ChatClientExtensions.kt Adds timeout and failed-tool callbacks.
SessionManager.kt Clears approval state after timeout.
ChatView.kt Inherits the approval card’s content color.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated
Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated

Copilot AI 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.

🟡 Changes recommended

The published API is broken incompatibly, and the await regression test does not verify that the future is actually awaited.

4 open findings
1 resolved since last review

🧠 Review effort: Balanced

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated

Copilot AI 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.

🟡 Changes recommended

The public JVM API breaks binary compatibility, and the synchronization regression test does not verify that completion is awaited.

5 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Assert timeout handling waits for callback future completion

shared-jvm/​src/​test/​kotlin/​io/​askimo/​core/​providers/​ChatClientExtensionsToolApprovalTest.kt:269

This completed future cannot prove that its completion was awaited: the callback appends onToolFinishedAwaitable before returning, so the asserted order remains identical if production code invokes the callback and ignores its future. Return a controllable/instrumented incomplete future and assert that its get/completion occurs before onToolApprovalTimedOut to make this regression test exercise the synchronization guarantee it documents.

🧠 Review effort: Balanced

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated
- Ensures that the tool approval awaitable is properly completed regardless of the exit path.
- Implements robust timeout handling within `SessionManager` to clear state on timeout.
- Exposes a dedicated callback in `ChatClient` for managing tool completion promises.

BREAKING CHANGE: The `sendStreamingMessageWithCallback` function now accepts `onToolFinishedAwaitable` to explicitly manage the completion of the tool's pending promise.
@huynguyen20011
huynguyen20011 force-pushed the feature/improve-tool-notifications branch from ec82f17 to f5df016 Compare October 9, 2026 02:12
@haiphucnguyen
haiphucnguyen requested a balanced review from Copilot October 9, 2026 02:12

Copilot AI 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.

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated

Copilot AI 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.

Comment thread desktop-shared/src/main/kotlin/io/askimo/ui/session/SessionManager.kt Outdated

Copilot AI 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.

🟡 Changes recommended

Stream registration remains race-prone, and timeout cleanup is dispatched asynchronously instead of occurring immediately.

1 open finding
2 resolved since last review

🧠 Review effort: Balanced

Comment thread desktop-shared/src/main/kotlin/io/askimo/ui/session/SessionManager.kt Outdated

Copilot AI 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.

🟡 Changes recommended

Parallel tool completion and approval-state races can leave persisted or visible state inconsistent.

2 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Reliably verify the returned future was awaited

shared-jvm/​src/​test/​kotlin/​io/​askimo/​core/​providers/​ChatClientExtensionsToolApprovalTest.kt:310

awaitableInvoked is released inside the callback before it returns, so observing callTask.isDone == false does not prove that the returned future was awaited; an implementation that ignores the future can still be between countDown() and task completion and pass once this test completes the future. Conversely, a delayed test thread can let the correct one-second get time out before this assertion. Use an instrumented CompletableFuture whose timed get signals a second latch, and assert that latch instead of task timing.

🧠 Review effort: Balanced

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated

Copilot AI 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.

🟡 Changes recommended

Nullable request IDs can fail at runtime, callback cleanup has reliability issues, and the published API is broken incompatibly.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment thread shared-jvm/src/main/kotlin/io/askimo/core/providers/ChatClientExtensions.kt Outdated

Copilot AI 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.

🔵 Needs a closer look

Approval-state races, stale-thread capacity counting, and nondeterministic tests remain unresolved.

0 open findings

2 resolved since last review
Previously missed (3)

In code that hasn't changed since last review

Medium severity Count only incomplete threads toward streaming capacity

desktop-shared/​src/​main/​kotlin/​io/​askimo/​ui/​session/​SessionManager.kt:312

This cap now counts completed threads that are intentionally retained until the ViewModel consumes them. Because the stale-entry replacement happens only later, a retry can be rejected when the map has 20 entries even though one or more are complete (including the same session, which the previous code removed before this check). Count only incomplete threads here so stale bookkeeping entries do not consume streaming capacity.

Medium severity Make awaiting-state assertion deterministic with a controlled future

shared-jvm/​src/​test/​kotlin/​io/​askimo/​core/​providers/​ChatClientExtensionsToolApprovalTest.kt:354

This check still has an elapsed-time race: the implementation waits on future for only 1 second, so a CI pause after awaitableInvoked is released can let that wait time out and complete callTask before isDone is inspected. A test-controlled CompletableFuture whose timed get blocks on a release latch would make the “currently awaiting” assertion deterministic.

Low severity Test sibling completion notification when approval request times out

shared-jvm/​src/​test/​kotlin/​io/​askimo/​core/​providers/​ChatClientExtensionsToolApprovalTest.kt:265

This setup does not exercise the scenario described by the test: only request A's beforeToolExecution runs, and A itself is the request that times out. Make A a non-approval tool, invoke both starts, and require approval only for B; then the assertion verifies that an already-started sibling receives its completion notification when B times out.

🧠 Review effort: Balanced

@haiphucnguyen haiphucnguyen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you @huynguyen20011 !

@haiphucnguyen
haiphucnguyen merged commit 4894bbb into askimo-ai:main Oct 9, 2026
9 of 10 checks passed
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.

3 participants