{"id":13548048,"url":"https://github.com/dialogflow/agent-human-handoff-nodejs","last_synced_at":"2025-10-31T05:55:12.322Z","repository":{"id":82729590,"uuid":"95465322","full_name":"dialogflow/agent-human-handoff-nodejs","owner":"dialogflow","description":"A simple Dialogflow agent, a server, and a web interface that shows an approach for handling text-based conversations between a Dialogflow agent and a human operator","archived":false,"fork":false,"pushed_at":"2019-01-08T17:45:52.000Z","size":29,"stargazers_count":211,"open_issues_count":4,"forks_count":96,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-05-07T15:09:40.341Z","etag":null,"topics":["dialogflow","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dialogflow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-06-26T16:11:18.000Z","updated_at":"2025-02-26T08:03:59.000Z","dependencies_parsed_at":"2023-04-15T19:32:50.928Z","dependency_job_id":null,"html_url":"https://github.com/dialogflow/agent-human-handoff-nodejs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dialogflow/agent-human-handoff-nodejs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialogflow%2Fagent-human-handoff-nodejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialogflow%2Fagent-human-handoff-nodejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialogflow%2Fagent-human-handoff-nodejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialogflow%2Fagent-human-handoff-nodejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dialogflow","download_url":"https://codeload.github.com/dialogflow/agent-human-handoff-nodejs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialogflow%2Fagent-human-handoff-nodejs/sbom","scorecard":{"id":340710,"data":{"date":"2025-08-11","repo":{"name":"github.com/dialogflow/agent-human-handoff-nodejs","commit":"96982623e21aab3cd9f59bb8d06180a5a81bcbc6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/4 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T05:46:41.602Z","repository_id":82729590,"created_at":"2025-08-18T05:46:41.602Z","updated_at":"2025-08-18T05:46:41.602Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281937758,"owners_count":26586774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-31T02:00:07.401Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dialogflow","nodejs"],"created_at":"2024-08-01T12:01:05.021Z","updated_at":"2025-10-31T05:55:12.300Z","avatar_url":"https://github.com/dialogflow.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Dialogflow: Agent to Human Handoff Sample\n\nThis sample consists of a simple Dialogflow agent, a Node.js server, and a web\ninterface that together demonstrate an approach for handling text-based conversations\nfrom a Dialogflow agent to a human operator.\n\n**This sample is designed as a learning tool and a platform for\nexperimentation, not as a finished solution.**\n\n### Features\n* Agent will escalate to a human operator on-demand\n* Agent will escalate to a human operator after repeated failure to match an intent\n* Server routes conversation between agent and human operator depending on context\n* Server supports multiple concurrent conversations between customer, agent and operator\n* Customer web client supports real-time chat with agent or operator\n* Operator web client supports real-time observation and participation in multiple customer conversations\n* Operator web client demonstrates alerting of operator in case of customer escalation\n\n### Limitations\n* **There is no authentication of customers or operators. In this limited demonstration, communication\nbetween users is not secure. This code is provided as an example, not a finished solution.**\n* Active conversations are stored in-memory with no on-disk persistence (though code could\neasily be modified to support this).\n* Operators can only monitor and communicate with customers that have subsequently connected. Previously\nconnected customers are ignored.\n* User interface is very rudimentary.\n\n### Technology Stack\n* [Dialogflow](https://dialogflow.com/)\n* [Node.js](https://nodejs.org/en/) v6.9.1+\n* [Express.js](https://expressjs.com/)\n* [Socket.IO](https://socket.io/)\n* [JQuery](https://jquery.com/)\n\n## Setup Instructions\n\n### Part A: Steps for Dialogflow\n1. [Sign-up](https://console.dialogflow.com/api-client/authorize_url_google/nopopup) or [Log-in](https://console.dialogflow.com/api-client/#/login) to your Dialogflow account.\n2. In [Dialogflow's console](https://console.dialogflow.com), select **Create Agent** in the left navigation and fill in the required fields and **Save**.\n  + Name for your agent, i.e. `agent-human-handoff-sample`\n4. Select `Create`.\n5. Go to the settings ⚙ \u003e **Export and Import** tab \u003e  **Restore from zip**.\n    + Upload the `HumanHandoffDemonstrationAgent.zip` file located in this repo.\n\n\n### Part B: Steps for Node.js\n1. Download/clone this repo.\n2. Ensure at least Node.js v6.9.1 is installed.\n3. Within the repo directory, $ **npm install** to install all of the project's dependencies.\n4. To connect to your agent, you will need your Dialogflow Project ID.\n  + Back in Dialogflow console under **Settings** ⚙ \u003e **General** tab \u003e copy **Project ID**.\n5. For authentication, you will need to download a service account key.\n  + Right below **Project ID** \u003e click **Service Account** email address.  \n  + In the resulting page, find the\nsame service account email address in the table, and select the three dots under the **Actions** column \u003e **Create key**, **JSON**, and then **CREATE** to download a key file.\n6. In terminal, to start the server:\n```shell\n$ DF_PROJECT_ID=\u003cproject ID\u003e DF_SERVICE_ACCOUNT_PATH=\u003cpath to key file\u003e node app.js\n```\nFor example:\n **$ DF_PROJECT_ID=**`agent-human-handoff-sample` **DF_SERVICE_ACCOUNT_PATH=**`~/Download/keyfile.json`** node app.js**\n\n7. In your terminal, you should see **Listening on *:3000** and the server is now running.\u003csup\u003eA.\u003c/sup\u003e\n\n  \u003csup\u003eA.\u003c/sup\u003e If you see `Error: listen EADDRINUSE :::3000` when starting the server, there is\n  already a process listening on port 3000 on your system. Identify and close that process, or edit `app.js`\n  to connect to a different port.\n\n### Part C: Demonstration\n1. With the server running, go to the [Operator interface](http://localhost:3000/operator) in your browser.\n  + You should see a mostly empty page with the word 'Operator' and a chat form.\n2. Open up a new browser tab and go to [Customer interface](http://localhost:3000/customer).\n  + After loading,\nthe client should immediately connect to the Dialogflow agent. You'll see the welcome message from the agent\nappear beneath the Customer title.\n3. Return to the Operator interface tab.\n  + You will see that a tab has appeared that represents the conversation\nwith this customer. It will currently be empty.\n4. Return to the Customer interface tab.\n  + You can use the form to chat with the agent With **Send** or return.\n  + To demonstrate functional  conversation, ask `what is your purpose?`.\n5. In Operator interface, you will see the history of messages between the customer and the agent. If you\nattempt to enter a message, you will see a warning that the customer has not yet been escalated.\n6. Return to the Customer interface.\n  + To request an operator, ask a variation of `let me talk to a person`.\n  + The Operator interface will pop up a dialog box to alert the operator that a new escalation has occurred.\n7. If the Operator interface took control as a result of alert dialog, return to the Customer interface.\nYou will see the system has responded with an introduction message from the operator. After this point,\nthe operator can respond to the customer's messages freely.\n8. Still on the Customer interface, send another message (for example, `I'm having difficulty with my product`).\n9. Open the Operator interface. You will see the customer's message. You can use the form to respond.\n10. Switch back to the Customer interface.\n  + If you responded to their message in the previous step, you will\nsee your response in the chat log.\n11. In a new browser tab and go open a second [Customer interface](http://localhost:3000/customer).\n12. In this new Customer interface, ask the agent something it doesn't understand (e.g. `I would like to book a flight`).\n The agent will ask for clarification.\n13. Switch to the Operator interface.\n  + You will see that a second tab has appeared, representing the second customer.\nClick this tab to view the conversation logs.\n14. Switch back to the new Customer interface and enter another arbitrary statement (e.g. `I want to book a flight`).\n  + With\nthe agent unable to determine the customer's intent twice in a row, it will automatically escalate to an operator.\n\n## Explanation\n* The server communicates with the clients via Socket.IO. It initially sends all customer messages to Dialogflow\nand returns the responses to the customer.\n* The server monitors the `context` property of Dialogflow's response for a specific context named `operator_request`.\nWhen it sees this context, it stops routing this customer's messages to Dialogflow and allows the operator to respond.\n* The Dialogflow agent can apply this context in two modes.\n    * In one mode, it is applied to the `Output contexts` of the `Operator Request` intent, thus switching\n    the customer to an operator when they explicitly ask for it.\n    * In the other mode, it is applied to the `Output contexts` of the intent `Default Fallback Intent - fallback`.\n    To see this intent in the Dialogflow interface, click the downward arrow on the intent named `Default Fallback Intent`.\n    Because this intent is specified as the fallback intent for the default fallback intent, it will be triggered\n    only if the user has failed to provide a matchable input twice in a row. The first unmatched input triggers the\n    `Default Fallback Intent`. The second unmatched input triggers `Default Fallback Intent - fallback`. This causes\n    the `operator_request` context to be applied, triggering escalation by the server.\n\n### Part D: Custom Implementation\nYou can modify this sample as a basis for your own implementation, but please be aware of the limitations expressed above.\n\nWhen creating your own Dialogflow agent, you may wish to make use of the\n[Support Prebuilt Agent](https://console.dialogflow.com/api-client/#/agent//prebuiltAgents/Support), which provides a framework\nfor answering common support-related inquiries and providing a channel for users to contact you.\n\n## References and How to report bugs\n* If you find any issues, please open a bug here on GitHub.\n* Questions? [StackOverflow](https://stackoverflow.com/questions/tagged/dialogflow).\n\n## How to make contributions?\nPlease read and follow the steps in the CONTRIBUTING.md.\n\n## License\nSee LICENSE.md.\n\n## Terms\nYour use of this sample is subject to, and by using or downloading the sample files you agree to comply with, the\n[Google APIs Terms of Service](https://developers.google.com/terms/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdialogflow%2Fagent-human-handoff-nodejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdialogflow%2Fagent-human-handoff-nodejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdialogflow%2Fagent-human-handoff-nodejs/lists"}