@desplega.ai/agent-swarm 1.68.0 โ 1.69.1
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/README.md +3 -3
- package/openapi.json +349 -1
- package/package.json +1 -1
- package/src/agentmail/handlers.ts +87 -6
- package/src/be/db-queries/mcp-oauth.ts +456 -0
- package/src/be/db.ts +44 -3
- package/src/be/migrations/041_mcp_oauth_tokens.sql +84 -0
- package/src/be/migrations/042_task_context_key.sql +13 -0
- package/src/github/handlers.ts +42 -10
- package/src/gitlab/handlers.ts +29 -5
- package/src/http/index.ts +8 -0
- package/src/http/mcp-oauth.ts +648 -0
- package/src/http/mcp-servers.ts +78 -48
- package/src/http/schedules.ts +4 -2
- package/src/http/tasks.ts +4 -2
- package/src/linear/sync.ts +22 -10
- package/src/oauth/ensure-mcp-token.ts +87 -0
- package/src/oauth/mcp-wrapper.ts +411 -0
- package/src/providers/claude-adapter.ts +1 -0
- package/src/scheduler/scheduler.ts +9 -10
- package/src/slack/actions.ts +10 -9
- package/src/slack/assistant.ts +8 -4
- package/src/slack/handlers.ts +8 -3
- package/src/slack/thread-buffer.ts +61 -72
- package/src/tasks/additive-buffer.ts +152 -0
- package/src/tasks/additive-ingress.ts +125 -0
- package/src/tasks/context-key.ts +245 -0
- package/src/tasks/sibling-awareness.ts +144 -0
- package/src/tasks/sibling-block.ts +164 -0
- package/src/tests/additive-buffer.test.ts +186 -0
- package/src/tests/additive-ingress.test.ts +111 -0
- package/src/tests/context-key-db.test.ts +87 -0
- package/src/tests/context-key.test.ts +173 -0
- package/src/tests/mcp-oauth-ensure-token.test.ts +190 -0
- package/src/tests/mcp-oauth-queries.test.ts +241 -0
- package/src/tests/mcp-oauth-resolve-secrets.test.ts +224 -0
- package/src/tests/mcp-oauth-wrapper.test.ts +477 -0
- package/src/tests/sibling-awareness-db.test.ts +172 -0
- package/src/tests/sibling-block.test.ts +232 -0
- package/src/types.ts +9 -0
- package/src/workflows/executors/agent-task.ts +21 -14
package/README.md
CHANGED
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
<a href="https://discord.gg/KZgfyyDVZa">
|
|
32
32
|
<img src="https://img.shields.io/badge/Discord-Join%20Community-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join Discord">
|
|
33
33
|
</a>
|
|
34
|
-
<a href="https://x.com/
|
|
35
|
-
<img src="https://img.shields.io/badge/๐-@
|
|
34
|
+
<a href="https://x.com/desplegalabs">
|
|
35
|
+
<img src="https://img.shields.io/badge/๐-@desplegalabs-000?style=for-the-badge&logo=x&logoColor=white" alt="Follow on X">
|
|
36
36
|
</a>
|
|
37
37
|
</p>
|
|
38
38
|
|
|
@@ -89,7 +89,7 @@ flowchart LR
|
|
|
89
89
|
- **Multi-channel inputs** โ Slack, GitHub, GitLab, email, Linear, and the HTTP API all create tasks. [Integrations](#integrations)
|
|
90
90
|
- **Workflow engine with Human-in-the-Loop** โ DAG-based automation with approval gates, retries, and structured I/O. [Workflows โ](https://docs.agent-swarm.dev/docs/concepts/workflows)
|
|
91
91
|
- **Scheduled & recurring tasks** โ cron-based automation for standing work. [Scheduling โ](https://docs.agent-swarm.dev/docs/concepts/scheduling)
|
|
92
|
-
- **Multi-provider** โ run with Claude Code, OpenAI Codex, or pi-mono. [Harness config โ](https://docs.agent-swarm.dev/docs/guides/harness-configuration)
|
|
92
|
+
- **Multi-provider** โ run with Claude Code, OpenAI Codex, or pi-mono. [Harness config โ](https://docs.agent-swarm.dev/docs/guides/harness-configuration) ยท [Add a new provider โ](https://docs.agent-swarm.dev/docs/guides/harness-providers)
|
|
93
93
|
- **Skills & MCP servers** โ reusable procedural knowledge and per-agent MCP servers with scope cascade. [MCP tools โ](https://docs.agent-swarm.dev/docs/reference/mcp-tools)
|
|
94
94
|
- **Real-time dashboard** โ monitor agents, tasks, and inter-agent chat. [app.agent-swarm.dev โ](https://app.agent-swarm.dev)
|
|
95
95
|
|
package/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Swarm API",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.69.1",
|
|
6
6
|
"description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
|
|
7
7
|
},
|
|
8
8
|
"servers": [
|
|
@@ -4724,6 +4724,351 @@
|
|
|
4724
4724
|
}
|
|
4725
4725
|
}
|
|
4726
4726
|
},
|
|
4727
|
+
"/api/mcp-oauth/{mcpServerId}/metadata": {
|
|
4728
|
+
"get": {
|
|
4729
|
+
"summary": "Probe OAuth metadata (PRMD + AS) for an MCP server",
|
|
4730
|
+
"tags": [
|
|
4731
|
+
"MCP OAuth"
|
|
4732
|
+
],
|
|
4733
|
+
"security": [
|
|
4734
|
+
{
|
|
4735
|
+
"bearerAuth": []
|
|
4736
|
+
}
|
|
4737
|
+
],
|
|
4738
|
+
"parameters": [
|
|
4739
|
+
{
|
|
4740
|
+
"schema": {
|
|
4741
|
+
"type": "string"
|
|
4742
|
+
},
|
|
4743
|
+
"required": true,
|
|
4744
|
+
"name": "mcpServerId",
|
|
4745
|
+
"in": "path"
|
|
4746
|
+
}
|
|
4747
|
+
],
|
|
4748
|
+
"responses": {
|
|
4749
|
+
"200": {
|
|
4750
|
+
"description": "OAuth metadata or { requiresOAuth: false }"
|
|
4751
|
+
},
|
|
4752
|
+
"400": {
|
|
4753
|
+
"description": "MCP has no URL / invalid transport"
|
|
4754
|
+
},
|
|
4755
|
+
"404": {
|
|
4756
|
+
"description": "MCP server not found"
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
},
|
|
4761
|
+
"/api/mcp-oauth/{mcpServerId}/status": {
|
|
4762
|
+
"get": {
|
|
4763
|
+
"summary": "Get the current OAuth connection status for an MCP server",
|
|
4764
|
+
"tags": [
|
|
4765
|
+
"MCP OAuth"
|
|
4766
|
+
],
|
|
4767
|
+
"security": [
|
|
4768
|
+
{
|
|
4769
|
+
"bearerAuth": []
|
|
4770
|
+
}
|
|
4771
|
+
],
|
|
4772
|
+
"parameters": [
|
|
4773
|
+
{
|
|
4774
|
+
"schema": {
|
|
4775
|
+
"type": "string"
|
|
4776
|
+
},
|
|
4777
|
+
"required": true,
|
|
4778
|
+
"name": "mcpServerId",
|
|
4779
|
+
"in": "path"
|
|
4780
|
+
},
|
|
4781
|
+
{
|
|
4782
|
+
"schema": {
|
|
4783
|
+
"type": "string"
|
|
4784
|
+
},
|
|
4785
|
+
"required": false,
|
|
4786
|
+
"name": "userId",
|
|
4787
|
+
"in": "query"
|
|
4788
|
+
}
|
|
4789
|
+
],
|
|
4790
|
+
"responses": {
|
|
4791
|
+
"200": {
|
|
4792
|
+
"description": "Token status (never includes the token value itself)"
|
|
4793
|
+
},
|
|
4794
|
+
"404": {
|
|
4795
|
+
"description": "MCP server not found"
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
},
|
|
4800
|
+
"/api/mcp-oauth/{mcpServerId}/authorize": {
|
|
4801
|
+
"get": {
|
|
4802
|
+
"summary": "Start an OAuth flow. Redirects to the provider.",
|
|
4803
|
+
"tags": [
|
|
4804
|
+
"MCP OAuth"
|
|
4805
|
+
],
|
|
4806
|
+
"security": [
|
|
4807
|
+
{
|
|
4808
|
+
"bearerAuth": []
|
|
4809
|
+
}
|
|
4810
|
+
],
|
|
4811
|
+
"parameters": [
|
|
4812
|
+
{
|
|
4813
|
+
"schema": {
|
|
4814
|
+
"type": "string"
|
|
4815
|
+
},
|
|
4816
|
+
"required": true,
|
|
4817
|
+
"name": "mcpServerId",
|
|
4818
|
+
"in": "path"
|
|
4819
|
+
},
|
|
4820
|
+
{
|
|
4821
|
+
"schema": {
|
|
4822
|
+
"type": "string"
|
|
4823
|
+
},
|
|
4824
|
+
"required": false,
|
|
4825
|
+
"name": "redirect",
|
|
4826
|
+
"in": "query"
|
|
4827
|
+
},
|
|
4828
|
+
{
|
|
4829
|
+
"schema": {
|
|
4830
|
+
"type": "string"
|
|
4831
|
+
},
|
|
4832
|
+
"required": false,
|
|
4833
|
+
"name": "userId",
|
|
4834
|
+
"in": "query"
|
|
4835
|
+
},
|
|
4836
|
+
{
|
|
4837
|
+
"schema": {
|
|
4838
|
+
"type": "string"
|
|
4839
|
+
},
|
|
4840
|
+
"required": false,
|
|
4841
|
+
"name": "scopes",
|
|
4842
|
+
"in": "query"
|
|
4843
|
+
}
|
|
4844
|
+
],
|
|
4845
|
+
"responses": {
|
|
4846
|
+
"302": {
|
|
4847
|
+
"description": "Redirect to authorization server"
|
|
4848
|
+
},
|
|
4849
|
+
"400": {
|
|
4850
|
+
"description": "MCP has no URL / does not require OAuth"
|
|
4851
|
+
},
|
|
4852
|
+
"404": {
|
|
4853
|
+
"description": "MCP server not found"
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
},
|
|
4858
|
+
"/api/mcp-oauth/callback": {
|
|
4859
|
+
"get": {
|
|
4860
|
+
"summary": "OAuth redirect target. Exchanges code -> tokens and redirects back to dashboard.",
|
|
4861
|
+
"tags": [
|
|
4862
|
+
"MCP OAuth"
|
|
4863
|
+
],
|
|
4864
|
+
"parameters": [
|
|
4865
|
+
{
|
|
4866
|
+
"schema": {
|
|
4867
|
+
"type": "string"
|
|
4868
|
+
},
|
|
4869
|
+
"required": false,
|
|
4870
|
+
"name": "code",
|
|
4871
|
+
"in": "query"
|
|
4872
|
+
},
|
|
4873
|
+
{
|
|
4874
|
+
"schema": {
|
|
4875
|
+
"type": "string"
|
|
4876
|
+
},
|
|
4877
|
+
"required": false,
|
|
4878
|
+
"name": "state",
|
|
4879
|
+
"in": "query"
|
|
4880
|
+
},
|
|
4881
|
+
{
|
|
4882
|
+
"schema": {
|
|
4883
|
+
"type": "string"
|
|
4884
|
+
},
|
|
4885
|
+
"required": false,
|
|
4886
|
+
"name": "error",
|
|
4887
|
+
"in": "query"
|
|
4888
|
+
},
|
|
4889
|
+
{
|
|
4890
|
+
"schema": {
|
|
4891
|
+
"type": "string"
|
|
4892
|
+
},
|
|
4893
|
+
"required": false,
|
|
4894
|
+
"name": "error_description",
|
|
4895
|
+
"in": "query"
|
|
4896
|
+
}
|
|
4897
|
+
],
|
|
4898
|
+
"responses": {
|
|
4899
|
+
"302": {
|
|
4900
|
+
"description": "Redirect back to dashboard with oauth=success or oauth=error"
|
|
4901
|
+
},
|
|
4902
|
+
"400": {
|
|
4903
|
+
"description": "Bad state / missing code"
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
},
|
|
4908
|
+
"/api/mcp-oauth/{mcpServerId}/refresh": {
|
|
4909
|
+
"post": {
|
|
4910
|
+
"summary": "Force-refresh the access token for an MCP server",
|
|
4911
|
+
"tags": [
|
|
4912
|
+
"MCP OAuth"
|
|
4913
|
+
],
|
|
4914
|
+
"security": [
|
|
4915
|
+
{
|
|
4916
|
+
"bearerAuth": []
|
|
4917
|
+
}
|
|
4918
|
+
],
|
|
4919
|
+
"parameters": [
|
|
4920
|
+
{
|
|
4921
|
+
"schema": {
|
|
4922
|
+
"type": "string"
|
|
4923
|
+
},
|
|
4924
|
+
"required": true,
|
|
4925
|
+
"name": "mcpServerId",
|
|
4926
|
+
"in": "path"
|
|
4927
|
+
}
|
|
4928
|
+
],
|
|
4929
|
+
"requestBody": {
|
|
4930
|
+
"content": {
|
|
4931
|
+
"application/json": {
|
|
4932
|
+
"schema": {
|
|
4933
|
+
"type": "object",
|
|
4934
|
+
"properties": {
|
|
4935
|
+
"userId": {
|
|
4936
|
+
"type": "string"
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
},
|
|
4943
|
+
"responses": {
|
|
4944
|
+
"200": {
|
|
4945
|
+
"description": "Refreshed token"
|
|
4946
|
+
},
|
|
4947
|
+
"404": {
|
|
4948
|
+
"description": "No token for this MCP server"
|
|
4949
|
+
},
|
|
4950
|
+
"500": {
|
|
4951
|
+
"description": "Refresh failed"
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
},
|
|
4956
|
+
"/api/mcp-oauth/{mcpServerId}": {
|
|
4957
|
+
"delete": {
|
|
4958
|
+
"summary": "Revoke and delete the OAuth token for an MCP server",
|
|
4959
|
+
"tags": [
|
|
4960
|
+
"MCP OAuth"
|
|
4961
|
+
],
|
|
4962
|
+
"security": [
|
|
4963
|
+
{
|
|
4964
|
+
"bearerAuth": []
|
|
4965
|
+
}
|
|
4966
|
+
],
|
|
4967
|
+
"parameters": [
|
|
4968
|
+
{
|
|
4969
|
+
"schema": {
|
|
4970
|
+
"type": "string"
|
|
4971
|
+
},
|
|
4972
|
+
"required": true,
|
|
4973
|
+
"name": "mcpServerId",
|
|
4974
|
+
"in": "path"
|
|
4975
|
+
},
|
|
4976
|
+
{
|
|
4977
|
+
"schema": {
|
|
4978
|
+
"type": "string"
|
|
4979
|
+
},
|
|
4980
|
+
"required": false,
|
|
4981
|
+
"name": "userId",
|
|
4982
|
+
"in": "query"
|
|
4983
|
+
}
|
|
4984
|
+
],
|
|
4985
|
+
"responses": {
|
|
4986
|
+
"200": {
|
|
4987
|
+
"description": "Token revoked/deleted"
|
|
4988
|
+
},
|
|
4989
|
+
"404": {
|
|
4990
|
+
"description": "No token for this MCP server"
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
},
|
|
4995
|
+
"/api/mcp-oauth/{mcpServerId}/manual-client": {
|
|
4996
|
+
"post": {
|
|
4997
|
+
"summary": "Register a pre-existing OAuth client (DCR fallback)",
|
|
4998
|
+
"tags": [
|
|
4999
|
+
"MCP OAuth"
|
|
5000
|
+
],
|
|
5001
|
+
"security": [
|
|
5002
|
+
{
|
|
5003
|
+
"bearerAuth": []
|
|
5004
|
+
}
|
|
5005
|
+
],
|
|
5006
|
+
"parameters": [
|
|
5007
|
+
{
|
|
5008
|
+
"schema": {
|
|
5009
|
+
"type": "string"
|
|
5010
|
+
},
|
|
5011
|
+
"required": true,
|
|
5012
|
+
"name": "mcpServerId",
|
|
5013
|
+
"in": "path"
|
|
5014
|
+
}
|
|
5015
|
+
],
|
|
5016
|
+
"requestBody": {
|
|
5017
|
+
"content": {
|
|
5018
|
+
"application/json": {
|
|
5019
|
+
"schema": {
|
|
5020
|
+
"type": "object",
|
|
5021
|
+
"properties": {
|
|
5022
|
+
"clientId": {
|
|
5023
|
+
"type": "string",
|
|
5024
|
+
"minLength": 1
|
|
5025
|
+
},
|
|
5026
|
+
"clientSecret": {
|
|
5027
|
+
"type": "string"
|
|
5028
|
+
},
|
|
5029
|
+
"authorizationServerIssuer": {
|
|
5030
|
+
"type": "string",
|
|
5031
|
+
"format": "uri"
|
|
5032
|
+
},
|
|
5033
|
+
"authorizeUrl": {
|
|
5034
|
+
"type": "string",
|
|
5035
|
+
"format": "uri"
|
|
5036
|
+
},
|
|
5037
|
+
"tokenUrl": {
|
|
5038
|
+
"type": "string",
|
|
5039
|
+
"format": "uri"
|
|
5040
|
+
},
|
|
5041
|
+
"revocationUrl": {
|
|
5042
|
+
"type": "string",
|
|
5043
|
+
"format": "uri"
|
|
5044
|
+
},
|
|
5045
|
+
"scopes": {
|
|
5046
|
+
"type": "array",
|
|
5047
|
+
"items": {
|
|
5048
|
+
"type": "string"
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
},
|
|
5052
|
+
"required": [
|
|
5053
|
+
"clientId"
|
|
5054
|
+
]
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
},
|
|
5059
|
+
"responses": {
|
|
5060
|
+
"200": {
|
|
5061
|
+
"description": "Pending client stored. Call /authorize to start the flow."
|
|
5062
|
+
},
|
|
5063
|
+
"400": {
|
|
5064
|
+
"description": "Bad input"
|
|
5065
|
+
},
|
|
5066
|
+
"404": {
|
|
5067
|
+
"description": "MCP server not found"
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
},
|
|
4727
5072
|
"/api/mcp-servers": {
|
|
4728
5073
|
"get": {
|
|
4729
5074
|
"summary": "List MCP servers with optional filters",
|
|
@@ -5399,6 +5744,9 @@
|
|
|
5399
5744
|
"outputSchema": {
|
|
5400
5745
|
"type": "object",
|
|
5401
5746
|
"additionalProperties": {}
|
|
5747
|
+
},
|
|
5748
|
+
"contextKey": {
|
|
5749
|
+
"type": "string"
|
|
5402
5750
|
}
|
|
5403
5751
|
},
|
|
5404
5752
|
"required": [
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createTaskExtended,
|
|
3
2
|
findTaskByAgentMailThread,
|
|
4
3
|
getAgentById,
|
|
5
4
|
getAgentMailInboxMapping,
|
|
@@ -7,11 +6,63 @@ import {
|
|
|
7
6
|
resolveUser,
|
|
8
7
|
} from "../be/db";
|
|
9
8
|
import { resolveTemplate } from "../prompts/resolver";
|
|
9
|
+
import { createIngressBuffer } from "../tasks/additive-ingress";
|
|
10
|
+
import { agentmailContextKey } from "../tasks/context-key";
|
|
11
|
+
import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
|
|
10
12
|
import { workflowEventBus } from "../workflows/event-bus";
|
|
11
13
|
// Side-effect import: registers all AgentMail event templates in the in-memory registry
|
|
12
14
|
import "./templates";
|
|
13
15
|
import type { AgentMailMessage, AgentMailWebhookPayload } from "./types";
|
|
14
16
|
|
|
17
|
+
const ACTIVE_TASK_STATUSES = new Set(["pending", "in_progress", "offered", "paused"]);
|
|
18
|
+
|
|
19
|
+
interface BufferedAgentMailMessage {
|
|
20
|
+
from: string;
|
|
21
|
+
subject: string;
|
|
22
|
+
inboxId: string;
|
|
23
|
+
threadId: string;
|
|
24
|
+
messageId: string;
|
|
25
|
+
preview: string;
|
|
26
|
+
agentId: string | null;
|
|
27
|
+
parentTaskId: string;
|
|
28
|
+
requestedByUserId: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const AGENTMAIL_BUFFER_TIMEOUT_MS = Number(process.env.ADDITIVE_AGENTMAIL_BUFFER_MS) || 10_000;
|
|
32
|
+
|
|
33
|
+
const agentmailBuffer = createIngressBuffer<BufferedAgentMailMessage>({
|
|
34
|
+
source: "agentmail",
|
|
35
|
+
envFlag: "ADDITIVE_AGENTMAIL",
|
|
36
|
+
timeoutMs: AGENTMAIL_BUFFER_TIMEOUT_MS,
|
|
37
|
+
onFlush: (items, contextKey) => {
|
|
38
|
+
if (items.length === 0) return;
|
|
39
|
+
const first = items[0]!;
|
|
40
|
+
const combinedPreview = items.map((m) => m.preview).join("\n---\n");
|
|
41
|
+
const followupResult = resolveTemplate("agentmail.email.followup", {
|
|
42
|
+
from: first.from,
|
|
43
|
+
subject: first.subject,
|
|
44
|
+
inbox_id: first.inboxId,
|
|
45
|
+
thread_id: first.threadId,
|
|
46
|
+
preview: `[${items.length} buffered message(s)]\n\n${combinedPreview}`,
|
|
47
|
+
});
|
|
48
|
+
if (followupResult.skipped) return;
|
|
49
|
+
const task = createTaskWithSiblingAwareness(followupResult.text, {
|
|
50
|
+
agentId: first.agentId,
|
|
51
|
+
source: "agentmail",
|
|
52
|
+
taskType: "agentmail-reply",
|
|
53
|
+
agentmailInboxId: first.inboxId,
|
|
54
|
+
agentmailMessageId: first.messageId,
|
|
55
|
+
agentmailThreadId: first.threadId,
|
|
56
|
+
parentTaskId: first.parentTaskId,
|
|
57
|
+
requestedByUserId: first.requestedByUserId,
|
|
58
|
+
contextKey,
|
|
59
|
+
});
|
|
60
|
+
console.log(
|
|
61
|
+
`[AgentMail] Buffered flush โ task ${task.id} (${items.length} messages, thread ${first.threadId})`,
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
15
66
|
/**
|
|
16
67
|
* Extract bare email address from a from_ field like "Taras Yarema <t@desplega.ai>" or "t@desplega.ai".
|
|
17
68
|
*/
|
|
@@ -126,6 +177,31 @@ export async function handleMessageReceived(
|
|
|
126
177
|
// Check for thread continuity - find existing task for this thread
|
|
127
178
|
const existingTask = findTaskByAgentMailThread(thread_id);
|
|
128
179
|
if (existingTask) {
|
|
180
|
+
const contextKey = agentmailContextKey({ threadId: thread_id });
|
|
181
|
+
const siblingInFlight = ACTIVE_TASK_STATUSES.has(existingTask.status);
|
|
182
|
+
|
|
183
|
+
// Opt-in: when ADDITIVE_AGENTMAIL is true, buffer rapid follow-ups while
|
|
184
|
+
// the prior task is still running โ coalesce into ONE follow-up task.
|
|
185
|
+
if (
|
|
186
|
+
agentmailBuffer.enabled &&
|
|
187
|
+
agentmailBuffer.maybeBuffer(contextKey, siblingInFlight, {
|
|
188
|
+
from,
|
|
189
|
+
subject,
|
|
190
|
+
inboxId: inbox_id,
|
|
191
|
+
threadId: thread_id,
|
|
192
|
+
messageId: message_id,
|
|
193
|
+
preview,
|
|
194
|
+
agentId: existingTask.agentId,
|
|
195
|
+
parentTaskId: existingTask.id,
|
|
196
|
+
requestedByUserId,
|
|
197
|
+
})
|
|
198
|
+
) {
|
|
199
|
+
console.log(
|
|
200
|
+
`[AgentMail] Buffered follow-up for thread ${thread_id} (parent ${existingTask.id}, status ${existingTask.status})`,
|
|
201
|
+
);
|
|
202
|
+
return { created: false };
|
|
203
|
+
}
|
|
204
|
+
|
|
129
205
|
// Create a follow-up task with parentTaskId to continue the session
|
|
130
206
|
const followupResult = resolveTemplate("agentmail.email.followup", {
|
|
131
207
|
from,
|
|
@@ -139,7 +215,7 @@ export async function handleMessageReceived(
|
|
|
139
215
|
return { created: false };
|
|
140
216
|
}
|
|
141
217
|
|
|
142
|
-
const task =
|
|
218
|
+
const task = createTaskWithSiblingAwareness(followupResult.text, {
|
|
143
219
|
agentId: existingTask.agentId,
|
|
144
220
|
source: "agentmail",
|
|
145
221
|
taskType: "agentmail-reply",
|
|
@@ -148,6 +224,7 @@ export async function handleMessageReceived(
|
|
|
148
224
|
agentmailThreadId: thread_id,
|
|
149
225
|
parentTaskId: existingTask.id,
|
|
150
226
|
requestedByUserId,
|
|
227
|
+
contextKey,
|
|
151
228
|
});
|
|
152
229
|
|
|
153
230
|
console.log(
|
|
@@ -177,7 +254,7 @@ export async function handleMessageReceived(
|
|
|
177
254
|
return { created: false };
|
|
178
255
|
}
|
|
179
256
|
|
|
180
|
-
const task =
|
|
257
|
+
const task = createTaskWithSiblingAwareness(leadResult.text, {
|
|
181
258
|
agentId: agent.id,
|
|
182
259
|
source: "agentmail",
|
|
183
260
|
taskType: "agentmail-message",
|
|
@@ -185,6 +262,7 @@ export async function handleMessageReceived(
|
|
|
185
262
|
agentmailMessageId: message_id,
|
|
186
263
|
agentmailThreadId: thread_id,
|
|
187
264
|
requestedByUserId,
|
|
265
|
+
contextKey: agentmailContextKey({ threadId: thread_id }),
|
|
188
266
|
});
|
|
189
267
|
|
|
190
268
|
console.log(
|
|
@@ -206,7 +284,7 @@ export async function handleMessageReceived(
|
|
|
206
284
|
return { created: false };
|
|
207
285
|
}
|
|
208
286
|
|
|
209
|
-
const task =
|
|
287
|
+
const task = createTaskWithSiblingAwareness(workerResult.text, {
|
|
210
288
|
agentId: agent.id,
|
|
211
289
|
source: "agentmail",
|
|
212
290
|
taskType: "agentmail-message",
|
|
@@ -214,6 +292,7 @@ export async function handleMessageReceived(
|
|
|
214
292
|
agentmailMessageId: message_id,
|
|
215
293
|
agentmailThreadId: thread_id,
|
|
216
294
|
requestedByUserId,
|
|
295
|
+
contextKey: agentmailContextKey({ threadId: thread_id }),
|
|
217
296
|
});
|
|
218
297
|
|
|
219
298
|
console.log(
|
|
@@ -239,7 +318,7 @@ export async function handleMessageReceived(
|
|
|
239
318
|
return { created: false };
|
|
240
319
|
}
|
|
241
320
|
|
|
242
|
-
const task =
|
|
321
|
+
const task = createTaskWithSiblingAwareness(unmappedResult.text, {
|
|
243
322
|
agentId: lead.id,
|
|
244
323
|
source: "agentmail",
|
|
245
324
|
taskType: "agentmail-message",
|
|
@@ -247,6 +326,7 @@ export async function handleMessageReceived(
|
|
|
247
326
|
agentmailMessageId: message_id,
|
|
248
327
|
agentmailThreadId: thread_id,
|
|
249
328
|
requestedByUserId,
|
|
329
|
+
contextKey: agentmailContextKey({ threadId: thread_id }),
|
|
250
330
|
});
|
|
251
331
|
|
|
252
332
|
console.log(
|
|
@@ -268,13 +348,14 @@ export async function handleMessageReceived(
|
|
|
268
348
|
return { created: false };
|
|
269
349
|
}
|
|
270
350
|
|
|
271
|
-
const task =
|
|
351
|
+
const task = createTaskWithSiblingAwareness(noAgentResult.text, {
|
|
272
352
|
source: "agentmail",
|
|
273
353
|
taskType: "agentmail-message",
|
|
274
354
|
agentmailInboxId: inbox_id,
|
|
275
355
|
agentmailMessageId: message_id,
|
|
276
356
|
agentmailThreadId: thread_id,
|
|
277
357
|
requestedByUserId,
|
|
358
|
+
contextKey: agentmailContextKey({ threadId: thread_id }),
|
|
278
359
|
});
|
|
279
360
|
|
|
280
361
|
console.log(`[AgentMail] Created unassigned task ${task.id} (no lead or mapping available)`);
|