Repository: webex/webex-js-sdk
Pull request #4125: feat(plugin-cc): user state second timer implemented
Author: login: rsarika
Labels: name: validated
description: If the pull request is validated for automation.
Created: 2025-03-01T05:32:03Z | Merged: 2025-03-03T05:54:07Z

<!--
Hey there,\
Thank you for taking the time to improve our code! 🙂\
Please let us know why this change is necessary and what testing you have performed. \
This ensures our reviewers understand the impact of your change. \

**IMPORTANT**
FAILING TO FILL OUT THIS TEMPLATE WILL RESULT IN REJECTION OF YOUR PULL REQUEST
This is for compliance purposes with FedRAMP program.
-->

# COMPLETES #https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-568107

## This pull request addresses

showing the second timer for idle code change in user-state

## by making the following changes

added new field lastIdleCodeChangeTimestamp to profile to get the idle code change time stamp.
changed the timestamp type to number to make it consistent in other API's like login, state-change

<!-- You may include screenshots -->
https://app.vidcast.io/share/aa797417-9f3f-45be-8a33-9eacbbd5a823
### Change Type

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update
- [ ] Tooling change
- [ ] Internal code refactor

## The following scenarios were tested
after login.
state change to available => one timer should show and start from 0
state change to Idle1(meeting) => one timer should show and start from 0
state change to idle2(lunch break) => two timer should show one should start from 0 other should continue from chnage from available.
state change to available => one timer should show and start from 0

reload also should persist the previous timers.

Updated CC SDK Test plan(https://confluence-eng-gpk2.cisco.com/conf/display/WSDK/CC+SDK+Test+Plans)

### I certified that

- [x] I have read and followed [contributing guidelines](https://github.com/webex/webex-js-sdk/blob/master/CONTRIBUTING.md#submitting-a-pull-request)
- [x] I discussed changes with code owners prior to submitting this pull request

- [x] I have not skipped any automated checks
- [x] All existing and new tests passed
- [ ] I have updated the documentation accordingly

---

Make sure to have followed the [contributing guidelines](https://github.com/webex/webex-js-sdk/blob/master/CONTRIBUTING.md#submitting-a-pull-request) before submitting.



Diff:
diff --git a/docs/samples/contact-center/app.js b/docs/samples/contact-center/app.js
index c1df1c1f3e1..98240319cef 100644
--- a/docs/samples/contact-center/app.js
+++ b/docs/samples/contact-center/app.js
@@ -581,21 +581,38 @@ function initWebex(e) {
 
 credentialsFormElm.addEventListener('submit', initWebex);
 
-function startStateTimer(startTime) {
+function startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp) {
+
+  if (lastStateChangeTimestamp === null) {
+    return;
+  }
+  
   if (stateTimer) {
     clearInterval(stateTimer);
   }
 
   stateTimer = setInterval(() => {
     const currentTime = new Date().getTime();
-    const timeDifference = currentTime - startTime;
-
-    const hours = String(Math.floor(timeDifference / (1000 * 60 * 60))).padStart(2, '0');
-    const minutes = String(Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60))).padStart(2, '0');
-    const seconds = String(Math.floor((timeDifference % (1000 * 60)) / 1000)).padStart(2, '0');
-    if(timerElm)
-    {
-      timerElm.innerHTML = `${hours}:${minutes}:${seconds}`;
+    const stateTimeDifference = currentTime - new Date(lastStateChangeTimestamp).getTime();
+    const idleCodeChangeTimeDifference = lastIdleCodeChangeTimestamp ? currentTime - new Date(lastIdleCodeChangeTimestamp).getTime() : null;
+
+    const stateHours = String(Math.floor(stateTimeDifference / (1000 * 60 * 60))).padStart(2, '0');
+    const stateMinutes = String(Math.floor((stateTimeDifference % (1000 * 60 * 60)) / (1000 * 60))).padStart(2, '0');
+    const stateSeconds = String(Math.floor((stateTimeDifference % (1000 * 60)) / 1000)).padStart(2, '0');
+
+    let timerDisplay = `${stateHours}:${stateMinutes}:${stateSeconds}`;
+
+    if (idleCodeChangeTimeDifference !== null && lastStateChangeTimestamp !== lastIdleCodeChangeTimestamp) {
+      console.log('Idle code change time difference: ', lastStateChangeTimestamp, " ",lastIdleCodeChangeTimestamp);
+      const idleCodeChangeHours = String(Math.floor(idleCodeChangeTimeDifference / (1000 * 60 * 60))).padStart(2, '0');
+      const idleCodeChangeMinutes = String(Math.floor((idleCodeChangeTimeDifference % (1000 * 60 * 60)) / (1000 * 60))).padStart(2, '0');
+      const idleCodeChangeSeconds = String(Math.floor((idleCodeChangeTimeDifference % (1000 * 60)) / 1000)).padStart(2, '0');
+
+      timerDisplay = `${idleCodeChangeHours}:${idleCodeChangeMinutes}:${idleCodeChangeSeconds}`+ " / " + timerDisplay;
+    }
+
+    if (timerElm) {
+      timerElm.innerHTML = timerDisplay;
     }
   }, 1000);
 }
@@ -646,7 +663,7 @@ function register() {
             if (agentProfile.lastStateAuxCodeId && agentProfile.lastStateAuxCodeId === idleCodes.id)
             {
               option.selected = true;
-              startStateTimer(agentProfile.lastStateChangeTimestamp)
+              startStateTimer(agentProfile.lastStateChangeTimestamp, agentProfile.lastIdleCodeChangeTimestamp);
             }
             idleCodesDropdown.add(option);
           }
@@ -670,7 +687,7 @@ function register() {
       if (data && typeof data === 'object' && data.type === 'AgentStateChangeSuccess') {
         const DEFAULT_CODE = '0'; // Default code when no aux code is present
         idleCodesDropdown.value = data.auxCodeId?.trim() !== '' ? data.auxCodeId : DEFAULT_CODE;
-        startStateTimer(data.lastStateChangeTimestamp);
+        startStateTimer(data.lastStateChangeTimestamp, data.lastIdleCodeChangeTimestamp);
       }
     });
 
@@ -791,9 +808,10 @@ function doAgentLogin() {
     const DEFAULT_CODE = '0'; // Default code when no aux code is present
     const auxCodeId = response.data.auxCodeId?.trim() !== '' ? response.data.auxCodeId : DEFAULT_CODE;
     const lastStateChangeTimestamp = response.data.lastStateChangeTimestamp;
+    const lastIdleCodeChangeTimestamp = response.data.lastIdleCodeChangeTimestamp;
     const index = [...idleCodesDropdown.options].findIndex(option => option.value === auxCodeId);
     idleCodesDropdown.selectedIndex = index !== -1 ? index : 0;
-        startStateTimer(new Date(lastStateChangeTimestamp));
+    startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp);
     
   }).catch((error) => {
     console.log('Agent Login failed', error);
diff --git a/packages/@webex/plugin-cc/src/cc.ts b/packages/@webex/plugin-cc/src/cc.ts
index f659e01343b..e927ff88566 100644
--- a/packages/@webex/plugin-cc/src/cc.ts
+++ b/packages/@webex/plugin-cc/src/cc.ts
@@ -349,12 +349,17 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
   private async silentRelogin(): Promise<void> {
     try {
       const reLoginResponse = await this.services.agent.reload();
-      const {agentId, lastStateChangeReason, deviceType, dn, lastStateChangeTimestamp} =
-        reLoginResponse.data;
+      const {
+        agentId,
+        lastStateChangeReason,
+        deviceType,
+        dn,
+        lastStateChangeTimestamp,
+        lastIdleCodeChangeTimestamp,
+      } = reLoginResponse.data;
       let {auxCodeId} = reLoginResponse.data;
-      this.agentConfig.lastStateChangeTimestamp = lastStateChangeTimestamp
-        ? new Date(lastStateChangeTimestamp)
-        : new Date();
+      this.agentConfig.lastStateChangeTimestamp = lastStateChangeTimestamp;
+      this.agentConfig.lastIdleCodeChangeTimestamp = lastIdleCodeChangeTimestamp;
 
       // To handle re-registration of event listeners on silent relogin
       this.incomingTaskListener();
@@ -375,10 +380,11 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
           const agentStatusResponse = (await this.setAgentState(
             stateChangeData
           )) as StateChangeSuccess;
-          this.agentConfig.lastStateChangeTimestamp = agentStatusResponse.data
-            .lastStateChangeTimestamp
-            ? new Date(agentStatusResponse.data.lastStateChangeTimestamp)
-            : new Date();
+          this.agentConfig.lastStateChangeTimestamp =
+            agentStatusResponse.data.lastStateChangeTimestamp;
+
+          this.agentConfig.lastIdleCodeChangeTimestamp =
+            agentStatusResponse.data.lastIdleCodeChangeTimestamp;
         } catch (error) {
           LoggerProxy.error(
             `event=requestAutoStateChange | Error requesting state change to available on socket reconnect: ${error}`,
diff --git a/packages/@webex/plugin-cc/src/services/config/types.ts b/packages/@webex/plugin-cc/src/services/config/types.ts
index f81c51b1fe7..7e01fad32fa 100644
--- a/packages/@webex/plugin-cc/src/services/config/types.ts
+++ b/packages/@webex/plugin-cc/src/services/config/types.ts
@@ -568,5 +568,6 @@ export type Profile = {
   maskSensitiveData?: boolean;
   isAgentLoggedIn?: boolean;
   lastStateAuxCodeId?: string;
-  lastStateChangeTimestamp?: Date;
+  lastStateChangeTimestamp?: number;
+  lastIdleCodeChangeTimestamp?: number;
 };
diff --git a/packages/@webex/plugin-cc/test/unit/spec/cc.ts b/packages/@webex/plugin-cc/test/unit/spec/cc.ts
index 60d9b52d08f..f21bb587e56 100644
--- a/packages/@webex/plugin-cc/test/unit/spec/cc.ts
+++ b/packages/@webex/plugin-cc/test/unit/spec/cc.ts
@@ -774,6 +774,7 @@ describe('webex.cc', () => {
           agentId: 'agentId',
           lastStateChangeReason: 'agent-wss-disconnect',
           lastStateChangeTimestamp: 1738575135188,
+          lastIdleCodeChangeTimestamp: 1738575135189,
           deviceType: LoginOption.BROWSER,
           dn: '12345',
         },
@@ -788,7 +789,7 @@ describe('webex.cc', () => {
 
       const date = new Date();
       const setAgentStateSpy = jest.spyOn(webex.cc, 'setAgentState').mockResolvedValue({
-        data: {lastStateChangeTimestamp: date.getTime()},
+        data: {lastStateChangeTimestamp: 1234, lastIdleCodeChangeTimestamp: 12345},
       } as unknown as SetStateResponse);
       jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue(mockReLoginResponse);
 
@@ -812,7 +813,8 @@ describe('webex.cc', () => {
       });
       expect(webex.cc.agentConfig.isAgentLoggedIn).toBe(true);
       expect(webex.cc.agentConfig.lastStateAuxCodeId).toBe('0');
-      expect(webex.cc.agentConfig.lastStateChangeTimestamp).toStrictEqual(date); // it should be updated with the new timestamp of setAgentState response
+      expect(webex.cc.agentConfig.lastStateChangeTimestamp).toStrictEqual(1234); // it should be updated with the new timestamp of setAgentState response
+      expect(webex.cc.agentConfig.lastIdleCodeChangeTimestamp).toStrictEqual(12345);
       expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.BROWSER);
       expect(registerWebCallingLineSpy).toHaveBeenCalled();
       expect(incomingTaskListenerSpy).toHaveBeenCalled();
@@ -856,6 +858,7 @@ describe('webex.cc', () => {
           deviceType: LoginOption.EXTENSION,
           dn: '12345',
           lastStateChangeTimestamp: 1738575135188,
+          lastIdleCodeChangeTimestamp: 1738575135189,
         },
       };
 
@@ -877,7 +880,8 @@ describe('webex.cc', () => {
       expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.EXTENSION);
       expect(webex.cc.agentConfig.defaultDn).toBe('12345');
       expect(webex.cc.agentConfig.lastStateAuxCodeId).toBe('auxCodeId');
-      expect(webex.cc.agentConfig.lastStateChangeTimestamp).toStrictEqual(new Date(1738575135188));
+      expect(webex.cc.agentConfig.lastStateChangeTimestamp).toStrictEqual(1738575135188);
+      expect(webex.cc.agentConfig.lastIdleCodeChangeTimestamp).toStrictEqual(1738575135189);
     });
 
     it('should update agentConfig with deviceType during silent relogin for AGENT_DN', async () => {


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>docs/samples/contact-center/app.js (1)</summary><blockquote>

`689-690`: **Add null check for lastIdleCodeChangeTimestamp.**

When handling the agent state change event, it's important to verify that the new timestamp property exists in the response.

Update the code to handle potential missing data:

```diff
- startStateTimer(data.lastStateChangeTimestamp, data.lastIdleCodeChangeTimestamp);
+ startStateTimer(data.lastStateChangeTimestamp, data.lastIdleCodeChangeTimestamp || null);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between f5a1ab2d1ae79b1e2dc49d4055bf2271a462729e and fff74d4d2a7fb134d20cbb9b655c99f2d767f2a2.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `docs/samples/contact-center/app.js` (4 hunks)
* `packages/@webex/plugin-cc/src/cc.ts` (2 hunks)
* `packages/@webex/plugin-cc/src/services/config/types.ts` (1 hunks)
* `packages/@webex/plugin-cc/test/unit/spec/cc.ts` (5 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (6)</summary>

* GitHub Check: Test - Style
* GitHub Check: Test - Unit
* GitHub Check: Test - Syntax
* GitHub Check: Test - Integration
* GitHub Check: Test - Browser
* GitHub Check: AWS Amplify Console Web Preview

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>packages/@webex/plugin-cc/src/cc.ts (2)</summary>

`352-362`: **Properly handling the new lastIdleCodeChangeTimestamp in relogin response.**

The implementation correctly extracts and stores the new `lastIdleCodeChangeTimestamp` property from the relogin response data. This supports the new feature for tracking idle state changes.

---

`383-388`: **Good implementation of timestamp updates during state change.**

The code properly assigns both `lastStateChangeTimestamp` and `lastIdleCodeChangeTimestamp` from the agent status response to the agent configuration, ensuring the timers have the correct values to display.

</details>
<details>
<summary>packages/@webex/plugin-cc/src/services/config/types.ts (1)</summary>

`571-572`: <details>
<summary>❓ Verification inconclusive</summary>

**Type changes align with timer implementation requirements.**

The type change for `lastStateChangeTimestamp` from `Date` to `number` and the addition of `lastIdleCodeChangeTimestamp` as a number property correctly support the implementation of the second timer for tracking idle state changes.


Make sure all consumers of `lastStateChangeTimestamp` handle it correctly as a number rather than a Date object:

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for usages of lastStateChangeTimestamp to verify type compatibility
rg "lastStateChangeTimestamp" --type js --type ts
```

Length of output: 2667

---



</details>

**Numeric Timestamp Verification – Manual Confirmation Needed**

The type changes (switching `lastStateChangeTimestamp` from a Date to a number and adding `lastIdleCodeChangeTimestamp` as a number) generally support the timer implementation as intended. Our search across the codebase shows that:
  
- **Core functionality:** Files within `packages/@webex/plugin-cc` and relevant tests now correctly treat `lastStateChangeTimestamp` as a number.
- **Sample usage caution:** In `docs/samples/contact-center/app.js`, the code still creates a Date object from `lastStateChangeTimestamp` (via `new Date(lastStateChangeTimestamp).getTime()`). This conversion is valid for numerical timestamps but should be reviewed to ensure it’s intentional.
- **Comparison checks:** Some parts (e.g., a `=== null` check) may assume an absence marker that might need re-evaluation now that the field is numeric.

Please verify that every consumer of `lastStateChangeTimestamp` intentionally handles it as a number to avoid any unintended type mismatches.

</details>
<details>
<summary>docs/samples/contact-center/app.js (2)</summary>

`584-588`: **Good error handling with early return.**

The updated function signature now accepts both timestamp parameters, and the early return if `lastStateChangeTimestamp` is null prevents unnecessary processing.

---

`594-613`: **Well-implemented dual timer display.**

The implementation correctly calculates and displays both timers - one for the state change and another for the idle code change when they differ. This provides users with both timestamps as required.

</details>
<details>
<summary>packages/@webex/plugin-cc/test/unit/spec/cc.ts (5)</summary>

`777-777`: **Test coverage for new timestamp property.**

The test now properly includes the new `lastIdleCodeChangeTimestamp` in the mock response, ensuring complete test coverage.

---

`792-793`: **Mock response updated to include both timestamps.**

The mock response is correctly updated to include both `lastStateChangeTimestamp` and `lastIdleCodeChangeTimestamp` for testing.

---

`816-818`: **Test assertions for both timestamps.**

The test now verifies that both timestamps are correctly stored in the agent configuration, which is essential for validating the new functionality.

---

`861-861`: **Mock data consistency.**

The consistent inclusion of `lastIdleCodeChangeTimestamp` in all relevant mock responses ensures thorough testing of the new property.

---

`883-885`: **Complete verification of timestamp handling.**

The assertions correctly validate that both timestamps are properly stored with the expected values, ensuring that the feature works as intended.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T05:42:49Z
user: login: rsarika
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T09:58:55Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T09:59:14Z
user: login: mkesavan13
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T05:52:25Z
user: login: adhmenon
body: LGTM.

state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T05:54:06Z

Review comments:
diff_hunk: @@ -793,7 +810,7 @@ function doAgentLogin() {
     const lastStateChangeTimestamp = response.data.lastStateChangeTimestamp;
     const index = [...idleCodesDropdown.options].findIndex(option => option.value === auxCodeId);
     idleCodesDropdown.selectedIndex = index !== -1 ? index : 0;
-        startStateTimer(new Date(lastStateChangeTimestamp));
+        startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp);
path: docs/samples/contact-center/app.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing parameter in startStateTimer call.**

The `lastIdleCodeChangeTimestamp` parameter is missing in this function call, which could lead to undefined errors.

Fix the parameter by adding it to the function call:

```diff
- startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp);
+ startStateTimer(lastStateChangeTimestamp, response.data.lastIdleCodeChangeTimestamp);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
        startStateTimer(lastStateChangeTimestamp, response.data.lastIdleCodeChangeTimestamp);
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-01T05:42:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -793,7 +810,7 @@ function doAgentLogin() {
     const lastStateChangeTimestamp = response.data.lastStateChangeTimestamp;
     const index = [...idleCodesDropdown.options].findIndex(option => option.value === auxCodeId);
     idleCodesDropdown.selectedIndex = index !== -1 ? index : 0;
-        startStateTimer(new Date(lastStateChangeTimestamp));
+        startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp);
path: docs/samples/contact-center/app.js
user: login: rsarika
body: addressed
created_at: 2025-03-01T09:58:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -793,7 +810,7 @@ function doAgentLogin() {
     const lastStateChangeTimestamp = response.data.lastStateChangeTimestamp;
     const index = [...idleCodesDropdown.options].findIndex(option => option.value === auxCodeId);
     idleCodesDropdown.selectedIndex = index !== -1 ? index : 0;
-        startStateTimer(new Date(lastStateChangeTimestamp));
+        startStateTimer(lastStateChangeTimestamp, lastIdleCodeChangeTimestamp);
path: docs/samples/contact-center/app.js
user: login: coderabbitai[bot]
body: @rsarika Thanks for addressing the issue! The function call will now correctly use the `lastIdleCodeChangeTimestamp` from the response data.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%F0%9F%9A%A8%20Just%20received%20a%20crucial%20code%20review%20from%20%40coderabbitai!%20Missing%20a%20parameter%20in%20%60startStateTimer%60%20could%20lead%20to%20undefined%20errors.%20Always%20double-check%20your%20function%20calls!%20%F0%9F%9B%A0%EF%B8%8F&url=https%3A%2F%2Fgithub.com%2Fwebex%2Fwebex-js-sdk%2Fpull%2F4125%23discussion_r1976311397)?
							Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-01T09:59:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-01T05:32:09Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on base/target branches other than the default branch.
> 
> 
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough

The changes update the handling of agent state timestamps across multiple components. In the contact center application, the `startStateTimer` function now accepts two parameters—`lastStateChangeTimestamp` and `lastIdleCodeChangeTimestamp`—and modifies its timer logic accordingly. In the `ContactCenter` class, the `silentRelogin` method is updated to extract and store the new `lastIdleCodeChangeTimestamp` from response data. The profile type definitions are adjusted by changing the type of `lastStateChangeTimestamp` from `Date` to `number` and adding the `lastIdleCodeChangeTimestamp` as a number. Additionally, unit tests have been revised to include the new timestamp property, ensuring both state change and idle code change timestamps are validated in agent state responses.

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: rsarika
created_at: 2025-03-01T05:38:59Z
body: @coderabbitai review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-03-01T05:39:03Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR
user: login: aws-amplify-us-east-2[bot]
created_at: 2025-03-01T05:44:13Z
body: This pull request is automatically being deployed by Amplify Hosting ([learn more](https://aws.amazon.com/amplify/hosting/)).<br/><br/>Access this pull request here: https://pr-4125.d3m3l2kee0btzx.amplifyapp.com
author_association: NONE